summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-09-13 12:09:18 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-03-26 12:09:04 +0000
commit4414c4b0491cea98b8d3795d6e87c88ad0e9cdd0 (patch)
tree2313e34fa70065839387f38d7e9122cc1b41cdb3 /drivers
parente6d491b50b4396a00f624cebc60a07602907c907 (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.c76
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);