Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wake up from a serial port

Enable wakeup from serial ports, make it run-time configurable over sysfs,
e.g.,

echo enabled > /sys/devices/platform/serial8250.0/tty/ttyS0/power/wakeup

Requires

# CONFIG_SYSFS_DEPRECATED is not set

Following suggestions from Alan and Russell moved the may_wake_up checks
to serial_core.c. This time actually tested - it does even work. Could
someone, please, verify, that put_device after device_find_child is
correct?

Also would be nice to test with a Natsemi UART, that can wake up the system,
if such systems exist.

For this you just have to apply the patch below, issue the above "echo"
command to one of your Natsemi port, suspend and resume your system, and
verify that your Natsemi port still works. If you are actually capable of
waking up the system from that port, would be nice to test that as well.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Guennadi Liakhovetski and committed by
Linus Torvalds
b3b708fa aa5346a2

+41 -2
+39 -1
drivers/serial/serial_core.c
··· 1938 1938 } 1939 1939 } 1940 1940 1941 + struct uart_match { 1942 + struct uart_port *port; 1943 + struct uart_driver *driver; 1944 + }; 1945 + 1946 + static int serial_match_port(struct device *dev, void *data) 1947 + { 1948 + struct uart_match *match = data; 1949 + dev_t devt = MKDEV(match->driver->major, match->driver->minor) + match->port->line; 1950 + 1951 + return dev->devt == devt; /* Actually, only one tty per port */ 1952 + } 1953 + 1941 1954 int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) 1942 1955 { 1943 1956 struct uart_state *state = drv->state + port->line; 1957 + struct device *tty_dev; 1958 + struct uart_match match = {port, drv}; 1944 1959 1945 1960 mutex_lock(&state->mutex); 1946 1961 ··· 1965 1950 return 0; 1966 1951 } 1967 1952 #endif 1953 + 1954 + tty_dev = device_find_child(port->dev, &match, serial_match_port); 1955 + if (device_may_wakeup(tty_dev)) { 1956 + enable_irq_wake(port->irq); 1957 + put_device(tty_dev); 1958 + mutex_unlock(&state->mutex); 1959 + return 0; 1960 + } 1961 + port->suspended = 1; 1968 1962 1969 1963 if (state->info && state->info->flags & UIF_INITIALIZED) { 1970 1964 const struct uart_ops *ops = port->ops; ··· 2022 1998 return 0; 2023 1999 } 2024 2000 #endif 2001 + 2002 + if (!port->suspended) { 2003 + disable_irq_wake(port->irq); 2004 + mutex_unlock(&state->mutex); 2005 + return 0; 2006 + } 2007 + port->suspended = 0; 2025 2008 2026 2009 uart_change_pm(state, 0); 2027 2010 ··· 2309 2278 { 2310 2279 struct uart_state *state; 2311 2280 int ret = 0; 2281 + struct device *tty_dev; 2312 2282 2313 2283 BUG_ON(in_interrupt()); 2314 2284 ··· 2346 2314 * Register the port whether it's detected or not. This allows 2347 2315 * setserial to be used to alter this ports parameters. 2348 2316 */ 2349 - tty_register_device(drv->tty_driver, port->line, port->dev); 2317 + tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); 2318 + if (likely(!IS_ERR(tty_dev))) { 2319 + device_can_wakeup(tty_dev) = 1; 2320 + device_set_wakeup_enable(tty_dev, 0); 2321 + } else 2322 + printk(KERN_ERR "Cannot register tty device on line %d\n", 2323 + port->line); 2350 2324 2351 2325 /* 2352 2326 * Ensure UPF_DEAD is not set.
+2 -1
include/linux/serial_core.h
··· 291 291 resource_size_t mapbase; /* for ioremap */ 292 292 struct device *dev; /* parent device */ 293 293 unsigned char hub6; /* this should be in the 8250 driver */ 294 - unsigned char unused[3]; 294 + unsigned char suspended; 295 + unsigned char unused[2]; 295 296 void *private_data; /* generic platform data pointer */ 296 297 }; 297 298