This variant can select the polarity of RTS# when TX is active with RS485. Add code to support that, too. Signed-off-by: Juergen Beisert <jbe@xxxxxxxxxxxxxx> Signed-off-by: Wolfram Sang <w.sang@xxxxxxxxxxxxxx> --- drivers/tty/serial/8250.c | 51 +++++++++++++++++++++++++++++++++++++------ include/linux/serial_core.h | 3 +- include/linux/serial_reg.h | 1 + 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 8cf6d76..5039e60 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -247,6 +247,13 @@ static const struct serial8250_config uart_config[] = { UART_FCR_T_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, + [PORT_16V2750] = { + .name = "XR16V2750", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, [PORT_16850] = { .name = "XR16850", .fifo_size = 128, @@ -838,6 +845,7 @@ static void autoconfig_has_efr(struct uart_8250_port *up) * We check for a XR16C850 by setting DLL and DLM to 0, and then * reading back DLL and DLM. The chip type depends on the DLM * value read back: + * 0x0a - XR16V2750 * 0x10 - XR16C850 and the DLL contains the chip revision. * 0x12 - XR16C2850. * 0x14 - XR16C854. @@ -850,6 +858,10 @@ static void autoconfig_has_efr(struct uart_8250_port *up) up->port.type = PORT_16850; return; } + if (id2 == 0x0a) { + up->port.type = PORT_16V2750; + return; + } /* * It wasn't an XR16C850. @@ -2060,7 +2072,7 @@ static int serial8250_startup(struct uart_port *port) /* * For a XR16C850, we need to set the trigger levels */ - if (up->port.type == PORT_16850) { + if (up->port.type == PORT_16850 || up->port.type == PORT_16V2750) { unsigned char fctr; serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); @@ -2726,9 +2738,9 @@ static int serial8250_ioctl_port(struct uart_port *port, case TIOCSRS485: { struct serial_rs485 rs485ctrl; - unsigned char fctr, lcr; + unsigned char fctr, lcr, emsr; - if (port->type != PORT_16850) + if (port->type != PORT_16850 && port->type != PORT_16V2750) return -ENOTTY; if (copy_from_user(&rs485ctrl, (struct serial_rs485 *)arg, @@ -2739,6 +2751,34 @@ static int serial8250_ioctl_port(struct uart_port *port, return -EOPNOTSUPP; spin_lock_irqsave(&up->port.lock, flags); + if (up->port.type == PORT_16V2750) { + /* make EMSR visible */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + fctr = serial_inp(up, UART_FCTR); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_SCR_SWAP); + serial_outp(up, UART_LCR, 0); + + /* set RTS polarity */ + emsr = serial_inp(up, UART_EMSR) & ~UART_EMSR_RS485_INV; + if (rs485ctrl.flags & SER_RS485_RTS_ON_SEND) { + emsr |= UART_EMSR_RS485_INV; + rs485ctrl.flags &= ~SER_RS485_RTS_AFTER_SEND; + } else { + rs485ctrl.flags |= SER_RS485_RTS_AFTER_SEND; + } + serial_outp(up, UART_EMSR, emsr); + + /* make the EMSR invisible */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + fctr = serial_inp(up, UART_FCTR) & ~UART_FCTR_SCR_SWAP; + serial_outp(up, UART_FCTR, fctr); + serial_outp(up, UART_LCR, 0); + } else { + /* only mode supported by the 16C850 */ + rs485ctrl.flags &= ~SER_RS485_RTS_ON_SEND; + rs485ctrl.flags |= SER_RS485_RTS_AFTER_SEND; + } + lcr = serial_inp(up, UART_LCR); serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); fctr = serial_inp(up, UART_FCTR); @@ -2750,9 +2790,6 @@ static int serial8250_ioctl_port(struct uart_port *port, serial_outp(up, UART_LCR, lcr); spin_unlock_irqrestore(&up->port.lock, flags); - /* only mode supported by the 16C850 */ - rs485ctrl.flags &= ~SER_RS485_RTS_ON_SEND; - rs485ctrl.flags |= SER_RS485_RTS_AFTER_SEND; up->rs485_flags = rs485ctrl.flags; return 0; @@ -2762,7 +2799,7 @@ static int serial8250_ioctl_port(struct uart_port *port, { struct serial_rs485 rs485ctrl; - if (port->type != PORT_16850) + if (port->type != PORT_16850 && port->type != PORT_16V2750) return -ENOTTY; memset(&rs485ctrl, 0, sizeof(rs485ctrl)); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index eadf33d..72118c5 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -47,7 +47,8 @@ #define PORT_U6_16550A 19 /* ST-Ericsson U6xxx internal UART */ #define PORT_TEGRA 20 /* NVIDIA Tegra internal UART */ #define PORT_XR17D15X 21 /* Exar XR17D15x UART */ -#define PORT_MAX_8250 21 /* max port ID */ +#define PORT_16V2750 22 /* Exar XR16V2750 */ +#define PORT_MAX_8250 22 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h index fb37c4f..0267bab6 100644 --- a/include/linux/serial_reg.h +++ b/include/linux/serial_reg.h @@ -216,6 +216,7 @@ #define UART_EMSR 7 /* Extended Mode Select Register */ #define UART_EMSR_FIFO_COUNT 0x01 /* Rx/Tx select */ #define UART_EMSR_ALT_COUNT 0x02 /* Alternating count select */ +#define UART_EMSR_RS485_INV 0x08 /* RS485 RTS output inversion */ /* * The Intel XScale on-chip UARTs define these bits -- 1.7.7.3 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html