aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial
diff options
context:
space:
mode:
authorStanislav Brabec <sbrabec@suse.cz>2009-12-02 10:20:56 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-01-20 18:03:29 -0500
commit4547be7809a3b775ce750ec7f8b5748954741523 (patch)
treec4da7e9e0e461c42501299c355c03a9920ccdce6 /drivers/serial
parent6d34855d9aa281f72c533ecb827405139d1b0fe9 (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>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/serial_core.c88
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);