aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-01-22 12:24:30 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-02-02 13:11:27 -0500
commit4516d50aabedbe5ae334155193e4d35c02390d9a (patch)
tree19bcf6aba42e65701008dce683481dce7008fef9
parent06a4c710673184f5c750bdb2f8579e0ae1cc252c (diff)
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 <dianders@chromium.org> for the idea of using the scratch register canary to discover port power-down. Cc: Doug Anderson <dianders@chromium.org> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/serial/8250/8250_core.c35
-rw-r--r--include/linux/serial_8250.h3
2 files changed, 37 insertions, 1 deletions
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)
3267 else 3267 else
3268 serial_port_out(port, UART_IER, 0); 3268 serial_port_out(port, UART_IER, 0);
3269 3269
3270 /* check scratch reg to see if port powered off during system sleep */
3271 if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
3272 struct ktermios termios;
3273 unsigned int baud, quot, frac = 0;
3274
3275 termios.c_cflag = port->cons->cflag;
3276 if (port->state->port.tty && termios.c_cflag == 0)
3277 termios.c_cflag = port->state->port.tty->termios.c_cflag;
3278
3279 baud = uart_get_baud_rate(port, &termios, NULL,
3280 port->uartclk / 16 / 0xffff,
3281 port->uartclk / 16);
3282 quot = serial8250_get_divisor(up, baud, &frac);
3283
3284 serial8250_set_divisor(port, baud, quot, frac);
3285 serial_port_out(port, UART_LCR, up->lcr);
3286 serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
3287
3288 up->canary = 0;
3289 }
3290
3270 uart_console_write(port, s, count, serial8250_console_putchar); 3291 uart_console_write(port, s, count, serial8250_console_putchar);
3271 3292
3272 /* 3293 /*
@@ -3417,7 +3438,17 @@ int __init early_serial_setup(struct uart_port *port)
3417 */ 3438 */
3418void serial8250_suspend_port(int line) 3439void serial8250_suspend_port(int line)
3419{ 3440{
3420 uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); 3441 struct uart_8250_port *up = &serial8250_ports[line];
3442 struct uart_port *port = &up->port;
3443
3444 if (!console_suspend_enabled && uart_console(port) &&
3445 port->type != PORT_8250) {
3446 unsigned char canary = 0xa5;
3447 serial_out(up, UART_SCR, canary);
3448 up->canary = canary;
3449 }
3450
3451 uart_suspend_port(&serial8250_reg, port);
3421} 3452}
3422 3453
3423/** 3454/**
@@ -3431,6 +3462,8 @@ void serial8250_resume_port(int line)
3431 struct uart_8250_port *up = &serial8250_ports[line]; 3462 struct uart_8250_port *up = &serial8250_ports[line];
3432 struct uart_port *port = &up->port; 3463 struct uart_port *port = &up->port;
3433 3464
3465 up->canary = 0;
3466
3434 if (up->capabilities & UART_NATSEMI) { 3467 if (up->capabilities & UART_NATSEMI) {
3435 /* Ensure it's still in high speed mode */ 3468 /* Ensure it's still in high speed mode */
3436 serial_port_out(port, UART_LCR, 0xE0); 3469 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 {
85 unsigned char mcr_force; /* mask of forced bits */ 85 unsigned char mcr_force; /* mask of forced bits */
86 unsigned char cur_iotype; /* Running I/O type */ 86 unsigned char cur_iotype; /* Running I/O type */
87 unsigned int rpm_tx_active; 87 unsigned int rpm_tx_active;
88 unsigned char canary; /* non-zero during system sleep
89 * if no_console_suspend
90 */
88 91
89 /* 92 /*
90 * Some bits in registers are cleared on a read, so they must 93 * Some bits in registers are cleared on a read, so they must