From 4bf4ea9dca4ba1984abeb592f429265b9bacac42 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 30 Dec 2014 20:28:15 -0500 Subject: serial: omap_8250: Fix RTS handling The OMAP3 UART ignores MCR[1] (ie., UART_MCR_RTS) when in autoRTS mode (UPF_HARD_FLOW + CRTSCTS). This makes it impossible for either the serial core or userspace to manually flow control the sender. Disable autoRTS mode when RTS is lowered and restore the previous mode when RTS is raised. Note that the OMAP3 UART provides no mechanism for switching from autoRTS mode without corrupting incoming data; to access the necessary register, the line control settings must be set to 8-e-2 and thus any data received during that time will be interpreted with those settings. This corruption has been observed in practice. Cc: Sebastian Andrzej Siewior Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 11c66856ba2f..3bfcfdb57d05 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1924,7 +1924,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) return ret; } -static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_8250_port *up = up_to_u8250p(port); unsigned char mcr = 0; @@ -1944,6 +1944,14 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) serial_port_out(port, UART_MCR, mcr); } +EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl); + +static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + if (port->set_mctrl) + return port->set_mctrl(port, mctrl); + return serial8250_do_set_mctrl(port, mctrl); +} static void serial8250_break_ctl(struct uart_port *port, int break_state) { @@ -3605,6 +3613,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.set_mctrl) + uart->port.set_mctrl = up->port.set_mctrl; if (up->port.startup) uart->port.startup = up->port.startup; if (up->port.shutdown) -- cgit v1.2.3 From fddceb8b5399083bcc042266f188116cc779fc80 Mon Sep 17 00:00:00 2001 From: Vijay Rai Date: Mon, 5 Jan 2015 21:14:42 +0530 Subject: tty: 8250: Add 64byte UART support for FSL platforms Some of FSL SoCs like T1040 has new version of UART controller which can support 64byte FiFo. To enable 64 byte support, following needs to be done: -FCR[EN64] needs to be programmed to 1 to enable it. -Also, when FCR[EN64]==1, RTL bits to be used as below to define various Receive Trigger Levels: -FCR[RTL] = 00 1 byte -FCR[RTL] = 01 16 bytes -FCR[RTL] = 10 32 bytes -FCR[RTL] = 11 56 bytes -tx_loadsz is set to 63-bytes instead of 64-bytes to implement workaround of errata A-008006 which states that tx_loadsz should be configured less than Maximum supported fifo bytes Signed-off-by: Vijay Rai Signed-off-by: Priyanka Jain Signed-off-by: Poonam Aggrwal Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 23 ++++++++++++++++++++++- include/uapi/linux/serial_core.h | 3 ++- include/uapi/linux/serial_reg.h | 3 ++- 3 files changed, 26 insertions(+), 3 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3bfcfdb57d05..689169c11f0f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -329,6 +329,17 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, +/* tx_loadsz is set to 63-bytes instead of 64-bytes to implement +workaround of errata A-008006 which states that tx_loadsz should be +configured less than Maximum supported fifo bytes */ + [PORT_16550A_FSL64] = { + .name = "16550A_FSL64", + .fifo_size = 64, + .tx_loadsz = 63, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR7_64BYTE, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -956,7 +967,17 @@ static void autoconfig_16550a(struct uart_8250_port *up) up->port.type = PORT_16650; up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; } else { - DEBUG_AUTOCONF("Motorola 8xxx DUART "); + serial_out(up, UART_LCR, 0); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) >> 5; + serial_out(up, UART_FCR, 0); + serial_out(up, UART_LCR, 0); + + if (status1 == 7) + up->port.type = PORT_16550A_FSL64; + else + DEBUG_AUTOCONF("Motorola 8xxx DUART "); } serial_out(up, UART_EFR, 0); return; diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 9124e837c181..ee628c489e47 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -55,7 +55,8 @@ #define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */ #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */ #define PORT_RT2880 29 /* Ralink RT2880 internal UART */ -#define PORT_MAX_8250 29 /* max port ID */ +#define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */ +#define PORT_MAX_8250 30 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 53af3b790129..00adb01fa5f3 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -86,7 +86,8 @@ #define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */ #define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */ #define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */ -#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750) */ +#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750 and + some Freescale UARTs) */ #define UART_FCR_R_TRIG_SHIFT 6 #define UART_FCR_R_TRIG_BITS(x) \ -- cgit v1.2.3 From a39d1da11b675b2cc2e0ffaafa8952ac7c3bd104 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Jan 2015 14:40:26 +0200 Subject: serial: 8250_core: remove redundant else keyword Since we return in the first branch the second one doesn't require an additional else keyword. The patch removes it. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 689169c11f0f..df8a5aa96bc6 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1376,9 +1376,11 @@ static void serial8250_start_tx(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); serial8250_rpm_get_tx(up); - if (up->dma && !up->dma->tx_dma(up)) { + + if (up->dma && !up->dma->tx_dma(up)) return; - } else if (!(up->ier & UART_IER_THRI)) { + + if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_port_out(port, UART_IER, up->ier); -- cgit v1.2.3 From d1f2f219319813070c022e841f48bfd0fe17fd34 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:26 -0500 Subject: serial: 8250: Move UART_BUG_QUOT workaround The UART_BUG_QUOT workaround adjusts the divisor computed from the baud rate by serial8250_get_divisor(). Move the workaround into serial8250_get_divisor(), so that divisor-from-baud computation is encapsulated. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index df8a5aa96bc6..8e3302c6bbff 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2413,8 +2413,9 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) +static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud) { + struct uart_port *port = &up->port; unsigned int quot; /* @@ -2430,6 +2431,12 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int else quot = uart_get_divisor(port, baud); + /* + * Oxford Semi 952 rev B workaround + */ + if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) + quot++; + return quot; } @@ -2478,13 +2485,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16); - quot = serial8250_get_divisor(port, baud); - - /* - * Oxford Semi 952 rev B workaround - */ - if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) - quot++; + quot = serial8250_get_divisor(up, baud); if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ -- cgit v1.2.3 From 0ec3f585cfb503a18b82cd181c9bf32b45a24f5e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:27 -0500 Subject: serial: 8250: Refactor LCR computation Refactor the computation of the LCR register value from termios c_cflag into a new local function, serial8250_compute_lcr(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 8e3302c6bbff..b63b9c352cc2 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2440,16 +2440,12 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i return quot; } -void -serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, + tcflag_t c_cflag) { - struct uart_8250_port *up = up_to_u8250p(port); unsigned char cval; - unsigned long flags; - unsigned int baud, quot; - switch (termios->c_cflag & CSIZE) { + switch (c_cflag & CSIZE) { case CS5: cval = UART_LCR_WLEN5; break; @@ -2465,20 +2461,34 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, break; } - if (termios->c_cflag & CSTOPB) + if (c_cflag & CSTOPB) cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) { + if (c_cflag & PARENB) { cval |= UART_LCR_PARITY; if (up->bugs & UART_BUG_PARITY) up->fifo_bug = true; } - if (!(termios->c_cflag & PARODD)) + if (!(c_cflag & PARODD)) cval |= UART_LCR_EPAR; #ifdef CMSPAR - if (termios->c_cflag & CMSPAR) + if (c_cflag & CMSPAR) cval |= UART_LCR_SPAR; #endif + return cval; +} + +void +serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char cval; + unsigned long flags; + unsigned int baud, quot; + + cval = serial8250_compute_lcr(up, termios->c_cflag); + /* * Ask the core to calculate the divisor for us. */ -- cgit v1.2.3 From e72abd5da0730dfc4c8f566f7896c15c2d1bb88f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:28 -0500 Subject: serial: 8250: Refactor divisor programming Refactor divisor register programming into a new function, serial8250_set_divisor; this allows serial console to reinitialize early after resume from suspend. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 87 ++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 39 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b63b9c352cc2..fd9e723926cd 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2478,6 +2478,50 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, return cval; } +void serial8250_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (is_omap1510_8250(up)) { + if (baud == 115200) { + quot = 1; + serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0); + } + + /* + * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2, + * otherwise just set DLAB + */ + if (up->capabilities & UART_NATSEMI) + serial_port_out(port, UART_LCR, 0xe0); + else + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + + serial_dl_write(up, quot); + + /* + * XR17V35x UARTs have an extra fractional divisor register (DLD) + * + * We need to recalculate all of the registers, because DLM and DLL + * are already rounded to a whole integer. + * + * When recalculating we use a 32x clock instead of a 16x clock to + * allow 1-bit for rounding in the fractional part. + */ + if (up->port.type == PORT_XR17V35X) { + unsigned int baud_x32 = (port->uartclk * 2) / baud; + u16 quot = baud_x32 / 32; + u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); + + serial_dl_write(up, quot); + serial_port_out(port, 0x2, quot_frac & 0xf); + } +} + void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -2526,6 +2570,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); + up->lcr = cval; /* Save computed LCR */ + /* * Update the per-port timeout. */ @@ -2590,43 +2636,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } - /* Workaround to enable 115200 baud on OMAP1510 internal ports */ - if (is_omap1510_8250(up)) { - if (baud == 115200) { - quot = 1; - serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1); - } else - serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0); - } - - /* - * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2, - * otherwise just set DLAB - */ - if (up->capabilities & UART_NATSEMI) - serial_port_out(port, UART_LCR, 0xe0); - else - serial_port_out(port, UART_LCR, cval | UART_LCR_DLAB); - - serial_dl_write(up, quot); - - /* - * XR17V35x UARTs have an extra fractional divisor register (DLD) - * - * We need to recalculate all of the registers, because DLM and DLL - * are already rounded to a whole integer. - * - * When recalculating we use a 32x clock instead of a 16x clock to - * allow 1-bit for rounding in the fractional part. - */ - if (up->port.type == PORT_XR17V35X) { - unsigned int baud_x32 = (port->uartclk * 2) / baud; - u16 quot = baud_x32 / 32; - u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); - - serial_dl_write(up, quot); - serial_port_out(port, 0x2, quot_frac & 0xf); - } + serial8250_set_divisor(port, baud, quot); /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR @@ -2635,8 +2645,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (port->type == PORT_16750) serial_port_out(port, UART_FCR, up->fcr); - serial_port_out(port, UART_LCR, cval); /* reset DLAB */ - up->lcr = cval; /* Save LCR */ + serial_port_out(port, UART_LCR, up->lcr); /* reset DLAB */ if (port->type != PORT_16750) { /* emulated UARTs (Lucent Venus 167x) need two steps */ if (up->fcr & UART_FCR_ENABLE_FIFO) -- cgit v1.2.3 From 06a4c710673184f5c750bdb2f8579e0ae1cc252c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:29 -0500 Subject: serial: 8250: Refactor XR17V35X divisor calculation Exar XR17V35X PCIe uarts support a 4-bit fractional divisor register. Refactor the divisor calculation from the divisor programming. Allow a fractional result from serial8250_get_divisor() and pass this result to serial8250_dl_write(). Simplify the calculation for quot and quot_frac. This was verified to be identical to the results of the original calculation with a test jig. NB: The results were also compared with the divisor value chart on pg 33 of the Exar XR17V352 datasheet, rev 1.0.3, here: http://www.exar.com/common/content/document.ashx?id=1585 which differs from the calculated values by 1 in the fractional result. This is because the calculated values are still rounded in the fractional result, whereas the table values are truncated. Note that the data error rate % values in the datasheet are for rounded fractional results, as the truncated fractional results have more error. Cc: Joe Schultz Cc: Aaron Sierra Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 52 +++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index fd9e723926cd..c8ecfaf49eb4 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2413,7 +2413,26 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } -static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud) +/* + * XR17V35x UARTs have an extra fractional divisor register (DLD) + * Calculate divisor with extra 4-bit fractional portion + */ +static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, + unsigned int baud, + unsigned int *frac) +{ + struct uart_port *port = &up->port; + unsigned int quot_16; + + quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud); + *frac = quot_16 & 0x0f; + + return quot_16 >> 4; +} + +static unsigned int serial8250_get_divisor(struct uart_8250_port *up, + unsigned int baud, + unsigned int *frac) { struct uart_port *port = &up->port; unsigned int quot; @@ -2421,6 +2440,7 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i /* * Handle magic divisors for baud rates above baud_base on * SMSC SuperIO chips. + * */ if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/4)) @@ -2428,6 +2448,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i else if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/8)) quot = 0x8002; + else if (up->port.type == PORT_XR17V35X) + quot = xr17v35x_get_divisor(up, baud, frac); else quot = uart_get_divisor(port, baud); @@ -2479,7 +2501,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, } void serial8250_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot) + unsigned int quot, unsigned int quot_frac) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2503,23 +2525,9 @@ void serial8250_set_divisor(struct uart_port *port, unsigned int baud, serial_dl_write(up, quot); - /* - * XR17V35x UARTs have an extra fractional divisor register (DLD) - * - * We need to recalculate all of the registers, because DLM and DLL - * are already rounded to a whole integer. - * - * When recalculating we use a 32x clock instead of a 16x clock to - * allow 1-bit for rounding in the fractional part. - */ - if (up->port.type == PORT_XR17V35X) { - unsigned int baud_x32 = (port->uartclk * 2) / baud; - u16 quot = baud_x32 / 32; - u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); - - serial_dl_write(up, quot); - serial_port_out(port, 0x2, quot_frac & 0xf); - } + /* XR17V35x UARTs have an extra fractional divisor register (DLD) */ + if (up->port.type == PORT_XR17V35X) + serial_port_out(port, 0x2, quot_frac); } void @@ -2529,7 +2537,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct uart_8250_port *up = up_to_u8250p(port); unsigned char cval; unsigned long flags; - unsigned int baud, quot; + unsigned int baud, quot, frac = 0; cval = serial8250_compute_lcr(up, termios->c_cflag); @@ -2539,7 +2547,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16); - quot = serial8250_get_divisor(up, baud); + quot = serial8250_get_divisor(up, baud, &frac); if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ @@ -2636,7 +2644,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } - serial8250_set_divisor(port, baud, quot); + serial8250_set_divisor(port, baud, quot, frac); /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR -- cgit v1.2.3 From 4516d50aabedbe5ae334155193e4d35c02390d9a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:30 -0500 Subject: serial: 8250: Use canary to restart console after suspend When using no_console_suspend, the serial console may be powered off anyway during system sleep. Upon resume, the port may be in its default power-on state, but is expected to continue console i/o before the device has received its pm callback. The resultant garbage i/o can cause all kinds of havoc on the remote end. Use the scratch register as a canary to discover if the console has been powered-off. Write a non-zero value to the scratch register at port suspend and reprogram the port before any console i/o if the scratch register != canary before port resume. This workaround is disabled for omap_8250 (which uses different divisor programming). Credit to Doug Anderson for the idea of using the scratch register canary to discover port power-down. Cc: Doug Anderson Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/serial_8250.h | 3 +++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index c8ecfaf49eb4..1449c56506b7 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -3267,6 +3267,27 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) else serial_port_out(port, UART_IER, 0); + /* check scratch reg to see if port powered off during system sleep */ + if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { + struct ktermios termios; + unsigned int baud, quot, frac = 0; + + termios.c_cflag = port->cons->cflag; + if (port->state->port.tty && termios.c_cflag == 0) + termios.c_cflag = port->state->port.tty->termios.c_cflag; + + baud = uart_get_baud_rate(port, &termios, NULL, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + quot = serial8250_get_divisor(up, baud, &frac); + + serial8250_set_divisor(port, baud, quot, frac); + serial_port_out(port, UART_LCR, up->lcr); + serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + + up->canary = 0; + } + uart_console_write(port, s, count, serial8250_console_putchar); /* @@ -3417,7 +3438,17 @@ int __init early_serial_setup(struct uart_port *port) */ void serial8250_suspend_port(int line) { - uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); + struct uart_8250_port *up = &serial8250_ports[line]; + struct uart_port *port = &up->port; + + if (!console_suspend_enabled && uart_console(port) && + port->type != PORT_8250) { + unsigned char canary = 0xa5; + serial_out(up, UART_SCR, canary); + up->canary = canary; + } + + uart_suspend_port(&serial8250_reg, port); } /** @@ -3431,6 +3462,8 @@ void serial8250_resume_port(int line) struct uart_8250_port *up = &serial8250_ports[line]; struct uart_port *port = &up->port; + up->canary = 0; + if (up->capabilities & UART_NATSEMI) { /* Ensure it's still in high speed mode */ serial_port_out(port, UART_LCR, 0xE0); diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 245b959f1ff6..a8efa235b7c1 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -85,6 +85,9 @@ struct uart_8250_port { unsigned char mcr_force; /* mask of forced bits */ unsigned char cur_iotype; /* Running I/O type */ unsigned int rpm_tx_active; + unsigned char canary; /* non-zero during system sleep + * if no_console_suspend + */ /* * Some bits in registers are cleared on a read, so they must -- cgit v1.2.3 From e9cef8625e997a5ade9cddd2d501257dfee7cb62 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 22 Jan 2015 12:24:31 -0500 Subject: serial: 8250: Prevent concurrent updates to shadow registers The port shadow registers, ->fcr and ->mcr, must be protected from concurrent updates. Relocate the shadow register updates in serial8250_do_set_termios() to the port lock critical section. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 1449c56506b7..57d13b058a48 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2549,6 +2549,15 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, port->uartclk / 16); quot = serial8250_get_divisor(up, baud, &frac); + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + serial8250_rpm_get(up); + spin_lock_irqsave(&port->lock, flags); + + up->lcr = cval; /* Save computed LCR */ + if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ if ((baud < 2400 && !up->dma) || up->fifo_bug) { @@ -2571,15 +2580,6 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr |= UART_MCR_AFE; } - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - serial8250_rpm_get(up); - spin_lock_irqsave(&port->lock, flags); - - up->lcr = cval; /* Save computed LCR */ - /* * Update the per-port timeout. */ -- cgit v1.2.3 From eb1527a8738bcfe12a42475aeef105914d63615a Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sat, 31 Jan 2015 08:58:34 +0800 Subject: tty/serial: serial8250_set_divisor() can be static drivers/tty/serial/8250/8250_core.c:2503:6: sparse: symbol 'serial8250_set_divisor' was not declared. Should it be static? Signed-off-by: Fengguang Wu Cc: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 57d13b058a48..46ae9fda68b6 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2500,7 +2500,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, return cval; } -void serial8250_set_divisor(struct uart_port *port, unsigned int baud, +static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot, unsigned int quot_frac) { struct uart_8250_port *up = up_to_u8250p(port); -- cgit v1.2.3 From c09babfab7ae8c7d79a5dce9d866fbb28b82dde4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 2 Feb 2015 15:47:07 -0500 Subject: serial: 8250: Fix UART_BUG_TXEN workaround UARTs which do not trigger THRE interrupt if the fifo is already empty when the interrupt is enabled need tx primed manually. These UARTs are identified by the UART_BUG_TXEN flag to enable the required workaround. However, the current workaround is broken; if the fifo is already empty but the shifter is still transmitting, then serial8250_tx_chars() will not be called but no further THRE interrupt will occur, and tx will stall. The appropriate check is for fifo empty (THRE), not transmitter empty (TEMT). Signed-off-by: Dick Hollenbeck [pjh: rewrote commit log] Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_core.c') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 46ae9fda68b6..e3b9570a1eff 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1388,7 +1388,7 @@ static void serial8250_start_tx(struct uart_port *port) unsigned char lsr; lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if (lsr & UART_LSR_TEMT) + if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); } } -- cgit v1.2.3