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>

authored by

Stanislav Brabec and committed by
Greg Kroah-Hartman
4547be78 6d34855d

+33 -55
+33 -55
drivers/serial/serial_core.c
··· 2006 2007 mutex_lock(&port->mutex); 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); 2016 if (device_may_wakeup(tty_dev)) { 2017 enable_irq_wake(uport->irq); ··· 2013 mutex_unlock(&port->mutex); 2014 return 0; 2015 } 2016 - uport->suspended = 1; 2017 2018 if (port->flags & ASYNC_INITIALIZED) { 2019 const struct uart_ops *ops = uport->ops; 2020 int tries; 2021 2022 - set_bit(ASYNCB_SUSPENDED, &port->flags); 2023 - clear_bit(ASYNCB_INITIALIZED, &port->flags); 2024 2025 - spin_lock_irq(&uport->lock); 2026 - ops->stop_tx(uport); 2027 - ops->set_mctrl(uport, 0); 2028 - ops->stop_rx(uport); 2029 - spin_unlock_irq(&uport->lock); 2030 2031 /* 2032 * Wait for the transmitter to empty. ··· 2044 drv->dev_name, 2045 drv->tty_driver->name_base + uport->line); 2046 2047 - ops->shutdown(uport); 2048 } 2049 2050 /* 2051 * Disable the console device before suspending. 2052 */ 2053 - if (uart_console(uport)) 2054 console_stop(uport->cons); 2055 2056 - uart_change_pm(state, 3); 2057 2058 mutex_unlock(&port->mutex); 2059 ··· 2071 struct ktermios termios; 2072 2073 mutex_lock(&port->mutex); 2074 - 2075 - if (!console_suspend_enabled && uart_console(uport)) { 2076 - /* no need to resume serial console, it wasn't suspended */ 2077 - /* 2078 - * First try to use the console cflag setting. 2079 - */ 2080 - memset(&termios, 0, sizeof(struct ktermios)); 2081 - termios.c_cflag = uport->cons->cflag; 2082 - /* 2083 - * If that's unset, use the tty termios setting. 2084 - */ 2085 - if (termios.c_cflag == 0) 2086 - termios = *state->port.tty->termios; 2087 - else { 2088 - termios.c_ispeed = termios.c_ospeed = 2089 - tty_termios_input_baud_rate(&termios); 2090 - termios.c_ispeed = termios.c_ospeed = 2091 - tty_termios_baud_rate(&termios); 2092 - } 2093 - uport->ops->set_termios(uport, &termios, NULL); 2094 - mutex_unlock(&port->mutex); 2095 - return 0; 2096 - } 2097 2098 tty_dev = device_find_child(uport->dev, &match, serial_match_port); 2099 if (!uport->suspended && device_may_wakeup(tty_dev)) { ··· 2097 spin_lock_irq(&uport->lock); 2098 ops->set_mctrl(uport, 0); 2099 spin_unlock_irq(&uport->lock); 2100 - ret = ops->startup(uport); 2101 - if (ret == 0) { 2102 - uart_change_speed(state, NULL); 2103 - spin_lock_irq(&uport->lock); 2104 - ops->set_mctrl(uport, uport->mctrl); 2105 - ops->start_tx(uport); 2106 - spin_unlock_irq(&uport->lock); 2107 - set_bit(ASYNCB_INITIALIZED, &port->flags); 2108 - } else { 2109 - /* 2110 - * Failed to resume - maybe hardware went away? 2111 - * Clear the "initialized" flag so we won't try 2112 - * to call the low level drivers shutdown method. 2113 - */ 2114 - uart_shutdown(state); 2115 } 2116 2117 clear_bit(ASYNCB_SUSPENDED, &port->flags);
··· 2006 2007 mutex_lock(&port->mutex); 2008 2009 tty_dev = device_find_child(uport->dev, &match, serial_match_port); 2010 if (device_may_wakeup(tty_dev)) { 2011 enable_irq_wake(uport->irq); ··· 2019 mutex_unlock(&port->mutex); 2020 return 0; 2021 } 2022 + if (console_suspend_enabled || !uart_console(uport)) 2023 + uport->suspended = 1; 2024 2025 if (port->flags & ASYNC_INITIALIZED) { 2026 const struct uart_ops *ops = uport->ops; 2027 int tries; 2028 2029 + if (console_suspend_enabled || !uart_console(uport)) { 2030 + set_bit(ASYNCB_SUSPENDED, &port->flags); 2031 + clear_bit(ASYNCB_INITIALIZED, &port->flags); 2032 2033 + spin_lock_irq(&uport->lock); 2034 + ops->stop_tx(uport); 2035 + ops->set_mctrl(uport, 0); 2036 + ops->stop_rx(uport); 2037 + spin_unlock_irq(&uport->lock); 2038 + } 2039 2040 /* 2041 * Wait for the transmitter to empty. ··· 2047 drv->dev_name, 2048 drv->tty_driver->name_base + uport->line); 2049 2050 + if (console_suspend_enabled || !uart_console(uport)) 2051 + ops->shutdown(uport); 2052 } 2053 2054 /* 2055 * Disable the console device before suspending. 2056 */ 2057 + if (console_suspend_enabled && uart_console(uport)) 2058 console_stop(uport->cons); 2059 2060 + if (console_suspend_enabled || !uart_console(uport)) 2061 + uart_change_pm(state, 3); 2062 2063 mutex_unlock(&port->mutex); 2064 ··· 2072 struct ktermios termios; 2073 2074 mutex_lock(&port->mutex); 2075 2076 tty_dev = device_find_child(uport->dev, &match, serial_match_port); 2077 if (!uport->suspended && device_may_wakeup(tty_dev)) { ··· 2121 spin_lock_irq(&uport->lock); 2122 ops->set_mctrl(uport, 0); 2123 spin_unlock_irq(&uport->lock); 2124 + if (console_suspend_enabled || !uart_console(uport)) { 2125 + ret = ops->startup(uport); 2126 + if (ret == 0) { 2127 + uart_change_speed(state, NULL); 2128 + spin_lock_irq(&uport->lock); 2129 + ops->set_mctrl(uport, uport->mctrl); 2130 + ops->start_tx(uport); 2131 + spin_unlock_irq(&uport->lock); 2132 + set_bit(ASYNCB_INITIALIZED, &port->flags); 2133 + } else { 2134 + /* 2135 + * Failed to resume - maybe hardware went away? 2136 + * Clear the "initialized" flag so we won't try 2137 + * to call the low level drivers shutdown method. 2138 + */ 2139 + uart_shutdown(state); 2140 + } 2141 } 2142 2143 clear_bit(ASYNCB_SUSPENDED, &port->flags);