Skip to content

Commit b3b708f

Browse files
lyakhLinus Torvalds
authored andcommitted
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 <[email protected]> Cc: Alan Cox <[email protected]> Cc: Russell King <[email protected]> Cc: Kay Sievers <[email protected]> Cc: Greg KH <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent aa5346a commit b3b708f

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

drivers/serial/serial_core.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1938,9 +1938,24 @@ static void uart_change_pm(struct uart_state *state, int pm_state)
19381938
}
19391939
}
19401940

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+
19411954
int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
19421955
{
19431956
struct uart_state *state = drv->state + port->line;
1957+
struct device *tty_dev;
1958+
struct uart_match match = {port, drv};
19441959

19451960
mutex_lock(&state->mutex);
19461961

@@ -1951,6 +1966,15 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
19511966
}
19521967
#endif
19531968

1969+
tty_dev = device_find_child(port->dev, &match, serial_match_port);
1970+
if (device_may_wakeup(tty_dev)) {
1971+
enable_irq_wake(port->irq);
1972+
put_device(tty_dev);
1973+
mutex_unlock(&state->mutex);
1974+
return 0;
1975+
}
1976+
port->suspended = 1;
1977+
19541978
if (state->info && state->info->flags & UIF_INITIALIZED) {
19551979
const struct uart_ops *ops = port->ops;
19561980

@@ -1999,6 +2023,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
19992023
}
20002024
#endif
20012025

2026+
if (!port->suspended) {
2027+
disable_irq_wake(port->irq);
2028+
mutex_unlock(&state->mutex);
2029+
return 0;
2030+
}
2031+
port->suspended = 0;
2032+
20022033
uart_change_pm(state, 0);
20032034

20042035
/*
@@ -2278,6 +2309,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
22782309
{
22792310
struct uart_state *state;
22802311
int ret = 0;
2312+
struct device *tty_dev;
22812313

22822314
BUG_ON(in_interrupt());
22832315

@@ -2314,7 +2346,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
23142346
* Register the port whether it's detected or not. This allows
23152347
* setserial to be used to alter this ports parameters.
23162348
*/
2317-
tty_register_device(drv->tty_driver, port->line, port->dev);
2349+
tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev);
2350+
if (likely(!IS_ERR(tty_dev))) {
2351+
device_can_wakeup(tty_dev) = 1;
2352+
device_set_wakeup_enable(tty_dev, 0);
2353+
} else
2354+
printk(KERN_ERR "Cannot register tty device on line %d\n",
2355+
port->line);
23182356

23192357
/*
23202358
* Ensure UPF_DEAD is not set.

include/linux/serial_core.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ struct uart_port {
291291
resource_size_t mapbase; /* for ioremap */
292292
struct device *dev; /* parent device */
293293
unsigned char hub6; /* this should be in the 8250 driver */
294-
unsigned char unused[3];
294+
unsigned char suspended;
295+
unsigned char unused[2];
295296
void *private_data; /* generic platform data pointer */
296297
};
297298

0 commit comments

Comments
 (0)