diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2016-09-13 12:09:18 +0100 |
---|---|---|
committer | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2024-03-26 12:09:04 +0000 |
commit | 4414c4b0491cea98b8d3795d6e87c88ad0e9cdd0 (patch) | |
tree | 2313e34fa70065839387f38d7e9122cc1b41cdb3 /drivers | |
parent | e6d491b50b4396a00f624cebc60a07602907c907 (diff) |
tty: serial: sa1100: fix iPAQ serial port wakeup
The iPAQ H3xxx platforms used to wake up on DCD or DSR transitions.
However, at some point this got broken and fails to work today. Fix
this by using the newly added modem control gpio layer's wakeup
controls.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/serial/sa1100.c | 76 |
1 files changed, 72 insertions, 4 deletions
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 79c794fa6545..93f3c64f4773 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -76,6 +76,7 @@ struct sa1100_port { struct timer_list timer; unsigned int old_status; struct mctrl_gpios *gpios; + unsigned wake; }; /* @@ -622,16 +623,17 @@ static void __init sa1100_init_ports(void) void sa1100_register_uart_fns(struct sa1100_port_fns *fns) { + unsigned int i; + if (fns->get_mctrl) sa1100_pops.get_mctrl = fns->get_mctrl; if (fns->set_mctrl) sa1100_pops.set_mctrl = fns->set_mctrl; sa1100_pops.pm = fns->pm; - /* - * FIXME: fns->set_wake is unused - this should be called from - * the suspend() callback if device_may_wakeup(dev)) is set. - */ + + for (i = 0; i < NR_PORTS; i++) + sa1100_ports[i].wake = fns->wake[i]; } void __init sa1100_register_uart(int idx, int port) @@ -804,19 +806,83 @@ static struct uart_driver sa1100_reg = { .cons = SA1100_CONSOLE, }; +static unsigned int sa1100_translate_mctrl_wake_mask(unsigned int wake_bits) +{ + unsigned int wake = 0; + + if (wake_bits & SA11X0_WAKE_CTS) + wake |= BIT(UART_GPIO_CTS); + if (wake_bits & SA11X0_WAKE_DCD) + wake |= BIT(UART_GPIO_DCD); + if (wake_bits & SA11X0_WAKE_DSR) + wake |= BIT(UART_GPIO_DSR); + if (wake_bits & SA11X0_WAKE_RI) + wake |= BIT(UART_GPIO_RI); + + return wake; +} + static int sa1100_serial_suspend(struct platform_device *dev, pm_message_t state) { struct sa1100_port *sport = platform_get_drvdata(dev); + unsigned int i, enabled = 0, wake; + int ret; + + wake = sa1100_translate_mctrl_wake_mask(sport->wake); + + for (i = 0; i < UART_GPIO_MAX; i++) { + struct gpio_desc *g; + int irq; + + if (!(wake & BIT(i))) + continue; + + g = mctrl_gpio_to_gpiod(sport->gpios, i); + if (!g) + continue; + + irq = gpiod_to_irq(g); + if (irq > 0) { + ret = enable_irq_wake(irq); + if (ret) + goto disable; + } + + enabled |= BIT(i); + } if (sport) uart_suspend_port(&sa1100_reg, &sport->port); return 0; + + disable: + for (i = 0; i < UART_GPIO_MAX; i++) + if (enabled & BIT(i)) + mctrl_gpio_disable_wake(sport->gpios, i); + return ret; } static int sa1100_serial_resume(struct platform_device *dev) { struct sa1100_port *sport = platform_get_drvdata(dev); + unsigned int i, wake = sa1100_translate_mctrl_wake_mask(sport->wake); + + for (i = 0; i < UART_GPIO_MAX; i++) { + struct gpio_desc *g; + int irq; + + if (!(wake & BIT(i))) + continue; + + g = mctrl_gpio_to_gpiod(sport->gpios, i); + if (!g) + continue; + + irq = gpiod_to_irq(g); + if (irq > 0) + disable_irq_wake(irq); + } if (sport) uart_resume_port(&sa1100_reg, &sport->port); @@ -845,6 +911,8 @@ static int sa1100_serial_add_one_port(struct sa1100_port *sport, struct platform sport->gpios = NULL; } + device_init_wakeup(sport->port.dev, !!sport->wake); + platform_set_drvdata(dev, sport); return uart_add_one_port(&sa1100_reg, &sport->port); |