diff options
author | Stanislav Brabec <sbrabec@suse.cz> | 2009-12-02 10:20:56 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-01-20 18:03:29 -0500 |
commit | 4547be7809a3b775ce750ec7f8b5748954741523 (patch) | |
tree | c4da7e9e0e461c42501299c355c03a9920ccdce6 | |
parent | 6d34855d9aa281f72c533ecb827405139d1b0fe9 (diff) |
serial-core: resume serial hardware with no_console_suspend
Perform a tricky suspend/resume even with no_console_suspend.
With no_console_suspend, kernel skips serial port suspend/resume and the
serial hardware may remain in undefined state after resume. It actually
happens on devices that don't have BIOS that handle serial
initialization. It makes impossible to use serial console after resume.
Devices affected by this problem include:
Sharp Zaurus devices
Several PXA based ARM embedded boards
The patch does:
- Save the hardware state
- Perform buffer flush in time of its suspend call
- Tell the driver that port is suspended
- But still accept new data
- And keep console hardware in state that allows to send them
It allows to capture late console messages without breaking console
after resume.
This is just a resend of a patch discussed in these threads, as the
patch was not yet applied.
"Possible suspend/resume regression in .32-rc?" (Nov 1-5, 2009, ARM
list, later LKML)
"serial-core: resume serial hardware with no_console_suspend" (Sep
15-Oct 18, 2009, LKML & ARM lists)
Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
Tested-by: Haojian Zhuang <haojian.zhuang@gmail.com>
Tested-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/serial/serial_core.c | 88 |
1 files changed, 33 insertions, 55 deletions
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 047530b285bb..fa4f170f2e86 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c | |||
@@ -2006,12 +2006,6 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) | |||
2006 | 2006 | ||
2007 | mutex_lock(&port->mutex); | 2007 | mutex_lock(&port->mutex); |
2008 | 2008 | ||
2009 | if (!console_suspend_enabled && uart_console(uport)) { | ||
2010 | /* we're going to avoid suspending serial console */ | ||
2011 | mutex_unlock(&port->mutex); | ||
2012 | return 0; | ||
2013 | } | ||
2014 | |||
2015 | tty_dev = device_find_child(uport->dev, &match, serial_match_port); | 2009 | tty_dev = device_find_child(uport->dev, &match, serial_match_port); |
2016 | if (device_may_wakeup(tty_dev)) { | 2010 | if (device_may_wakeup(tty_dev)) { |
2017 | enable_irq_wake(uport->irq); | 2011 | enable_irq_wake(uport->irq); |
@@ -2019,20 +2013,23 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) | |||
2019 | mutex_unlock(&port->mutex); | 2013 | mutex_unlock(&port->mutex); |
2020 | return 0; | 2014 | return 0; |
2021 | } | 2015 | } |
2022 | uport->suspended = 1; | 2016 | if (console_suspend_enabled || !uart_console(uport)) |
2017 | uport->suspended = 1; | ||
2023 | 2018 | ||
2024 | if (port->flags & ASYNC_INITIALIZED) { | 2019 | if (port->flags & ASYNC_INITIALIZED) { |
2025 | const struct uart_ops *ops = uport->ops; | 2020 | const struct uart_ops *ops = uport->ops; |
2026 | int tries; | 2021 | int tries; |
2027 | 2022 | ||
2028 | set_bit(ASYNCB_SUSPENDED, &port->flags); | 2023 | if (console_suspend_enabled || !uart_console(uport)) { |
2029 | clear_bit(ASYNCB_INITIALIZED, &port->flags); | 2024 | set_bit(ASYNCB_SUSPENDED, &port->flags); |
2025 | clear_bit(ASYNCB_INITIALIZED, &port->flags); | ||
2030 | 2026 | ||
2031 | spin_lock_irq(&uport->lock); | 2027 | spin_lock_irq(&uport->lock); |
2032 | ops->stop_tx(uport); | 2028 | ops->stop_tx(uport); |
2033 | ops->set_mctrl(uport, 0); | 2029 | ops->set_mctrl(uport, 0); |
2034 | ops->stop_rx(uport); | 2030 | ops->stop_rx(uport); |
2035 | spin_unlock_irq(&uport->lock); | 2031 | spin_unlock_irq(&uport->lock); |
2032 | } | ||
2036 | 2033 | ||
2037 | /* | 2034 | /* |
2038 | * Wait for the transmitter to empty. | 2035 | * Wait for the transmitter to empty. |
@@ -2047,16 +2044,18 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) | |||
2047 | drv->dev_name, | 2044 | drv->dev_name, |
2048 | drv->tty_driver->name_base + uport->line); | 2045 | drv->tty_driver->name_base + uport->line); |
2049 | 2046 | ||
2050 | ops->shutdown(uport); | 2047 | if (console_suspend_enabled || !uart_console(uport)) |
2048 | ops->shutdown(uport); | ||
2051 | } | 2049 | } |
2052 | 2050 | ||
2053 | /* | 2051 | /* |
2054 | * Disable the console device before suspending. | 2052 | * Disable the console device before suspending. |
2055 | */ | 2053 | */ |
2056 | if (uart_console(uport)) | 2054 | if (console_suspend_enabled && uart_console(uport)) |
2057 | console_stop(uport->cons); | 2055 | console_stop(uport->cons); |
2058 | 2056 | ||
2059 | uart_change_pm(state, 3); | 2057 | if (console_suspend_enabled || !uart_console(uport)) |
2058 | uart_change_pm(state, 3); | ||
2060 | 2059 | ||
2061 | mutex_unlock(&port->mutex); | 2060 | mutex_unlock(&port->mutex); |
2062 | 2061 | ||
@@ -2073,29 +2072,6 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) | |||
2073 | 2072 | ||
2074 | mutex_lock(&port->mutex); | 2073 | mutex_lock(&port->mutex); |
2075 | 2074 | ||
2076 | if (!console_suspend_enabled && uart_console(uport)) { | ||
2077 | /* no need to resume serial console, it wasn't suspended */ | ||
2078 | /* | ||
2079 | * First try to use the console cflag setting. | ||
2080 | */ | ||
2081 | memset(&termios, 0, sizeof(struct ktermios)); | ||
2082 | termios.c_cflag = uport->cons->cflag; | ||
2083 | /* | ||
2084 | * If that's unset, use the tty termios setting. | ||
2085 | */ | ||
2086 | if (termios.c_cflag == 0) | ||
2087 | termios = *state->port.tty->termios; | ||
2088 | else { | ||
2089 | termios.c_ispeed = termios.c_ospeed = | ||
2090 | tty_termios_input_baud_rate(&termios); | ||
2091 | termios.c_ispeed = termios.c_ospeed = | ||
2092 | tty_termios_baud_rate(&termios); | ||
2093 | } | ||
2094 | uport->ops->set_termios(uport, &termios, NULL); | ||
2095 | mutex_unlock(&port->mutex); | ||
2096 | return 0; | ||
2097 | } | ||
2098 | |||
2099 | tty_dev = device_find_child(uport->dev, &match, serial_match_port); | 2075 | tty_dev = device_find_child(uport->dev, &match, serial_match_port); |
2100 | if (!uport->suspended && device_may_wakeup(tty_dev)) { | 2076 | if (!uport->suspended && device_may_wakeup(tty_dev)) { |
2101 | disable_irq_wake(uport->irq); | 2077 | disable_irq_wake(uport->irq); |
@@ -2121,21 +2097,23 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) | |||
2121 | spin_lock_irq(&uport->lock); | 2097 | spin_lock_irq(&uport->lock); |
2122 | ops->set_mctrl(uport, 0); | 2098 | ops->set_mctrl(uport, 0); |
2123 | spin_unlock_irq(&uport->lock); | 2099 | spin_unlock_irq(&uport->lock); |
2124 | ret = ops->startup(uport); | 2100 | if (console_suspend_enabled || !uart_console(uport)) { |
2125 | if (ret == 0) { | 2101 | ret = ops->startup(uport); |
2126 | uart_change_speed(state, NULL); | 2102 | if (ret == 0) { |
2127 | spin_lock_irq(&uport->lock); | 2103 | uart_change_speed(state, NULL); |
2128 | ops->set_mctrl(uport, uport->mctrl); | 2104 | spin_lock_irq(&uport->lock); |
2129 | ops->start_tx(uport); | 2105 | ops->set_mctrl(uport, uport->mctrl); |
2130 | spin_unlock_irq(&uport->lock); | 2106 | ops->start_tx(uport); |
2131 | set_bit(ASYNCB_INITIALIZED, &port->flags); | 2107 | spin_unlock_irq(&uport->lock); |
2132 | } else { | 2108 | set_bit(ASYNCB_INITIALIZED, &port->flags); |
2133 | /* | 2109 | } else { |
2134 | * Failed to resume - maybe hardware went away? | 2110 | /* |
2135 | * Clear the "initialized" flag so we won't try | 2111 | * Failed to resume - maybe hardware went away? |
2136 | * to call the low level drivers shutdown method. | 2112 | * Clear the "initialized" flag so we won't try |
2137 | */ | 2113 | * to call the low level drivers shutdown method. |
2138 | uart_shutdown(state); | 2114 | */ |
2115 | uart_shutdown(state); | ||
2116 | } | ||
2139 | } | 2117 | } |
2140 | 2118 | ||
2141 | clear_bit(ASYNCB_SUSPENDED, &port->flags); | 2119 | clear_bit(ASYNCB_SUSPENDED, &port->flags); |