diff options
author | Russell King | 2005-11-13 15:28:51 +0000 |
---|---|---|
committer | Russell King | 2005-11-13 15:28:51 +0000 |
commit | ee31b337852ca8a65840702544ff5c64d37740f5 (patch) | |
tree | c3da8df25c4084c321e6a96888f7179272a78eaa | |
parent | 270c7a721548d116d9e054f48469e75cb0f35288 (diff) |
[SERIAL] Fix Bug 4900: S3 resume oops with irattach - Thinkpad A21m
If we fail to re-startup a serial port on resume, shut it down
immediately and mark it as an error condition.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | drivers/serial/serial_core.c | 84 |
1 files changed, 49 insertions, 35 deletions
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 427a23858076..2331296e1e17 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -209,33 +209,45 @@ static void uart_shutdown(struct uart_state *state) struct uart_info *info = state->info; struct uart_port *port = state->port; - if (!(info->flags & UIF_INITIALIZED)) - return; - /* - * Turn off DTR and RTS early. + * Set the TTY IO error marker */ - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free - * the irq here so the queue might never be woken up. Note - * that we won't end up waiting on delta_msr_wait again since - * any outstanding file descriptors should be pointing at - * hung_up_tty_fops now. - */ - wake_up_interruptible(&info->delta_msr_wait); + if (info->flags & UIF_INITIALIZED) { + info->flags &= ~UIF_INITIALIZED; - /* - * Free the IRQ and disable the port. - */ - port->ops->shutdown(port); + /* + * Turn off DTR and RTS early. + */ + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + port->ops->shutdown(port); + + /* + * Ensure that the IRQ handler isn't running on another CPU. + */ + synchronize_irq(port->irq); + } /* - * Ensure that the IRQ handler isn't running on another CPU. + * kill off our tasklet */ - synchronize_irq(port->irq); + tasklet_kill(&info->tlet); /* * Free the transmit buffer page. @@ -244,15 +256,6 @@ static void uart_shutdown(struct uart_state *state) free_page((unsigned long)info->xmit.buf); info->xmit.buf = NULL; } - - /* - * kill off our tasklet - */ - tasklet_kill(&info->tlet); - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - info->flags &= ~UIF_INITIALIZED; } /** @@ -1928,14 +1931,25 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) if (state->info && state->info->flags & UIF_INITIALIZED) { struct uart_ops *ops = port->ops; + int ret; ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port); - spin_unlock_irq(&port->lock); + ret = ops->startup(port); + if (ret == 0) { + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port); + spin_unlock_irq(&port->lock); + } else { + /* + * Failed to resume - maybe hardware went away? + * Clear the "initialized" flag so we won't try + * to call the low level drivers shutdown method. + */ + state->info->flags &= ~UIF_INITIALIZED; + uart_shutdown(state); + } } up(&state->sem); |