summaryrefslogtreecommitdiff
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
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>
-rw-r--r--arch/arm/mach-sa1100/h3xxx.c22
-rw-r--r--drivers/tty/serial/sa1100.c76
-rw-r--r--include/linux/platform_data/sa11x0-serial.h9
3 files changed, 83 insertions, 24 deletions
diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c
index d685f03f51f3..9afa12b1a580 100644
--- a/arch/arm/mach-sa1100/h3xxx.c
+++ b/arch/arm/mach-sa1100/h3xxx.c
@@ -96,27 +96,11 @@ static void h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
}
}
-/*
- * Enable/Disable wake up events for this serial port.
- * Obviously, we only support this on the normal COM port.
- */
-static int h3xxx_uart_set_wake(struct uart_port *port, u_int enable)
-{
- int err = -EINVAL;
-
- if (port->mapbase == _Ser3UTCR0) {
- if (enable)
- PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */
- else
- PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */
- err = 0;
- }
- return err;
-}
-
static struct sa1100_port_fns h3xxx_port_fns __initdata = {
.pm = h3xxx_uart_pm,
- .set_wake = h3xxx_uart_set_wake,
+ .wake = {
+ [0] = SA11X0_WAKE_CTS | SA11X0_WAKE_DCD,
+ },
};
static struct gpiod_lookup_table h3xxx_uart3_gpio_table = {
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);
diff --git a/include/linux/platform_data/sa11x0-serial.h b/include/linux/platform_data/sa11x0-serial.h
index 8b79ab08af45..319fcc566626 100644
--- a/include/linux/platform_data/sa11x0-serial.h
+++ b/include/linux/platform_data/sa11x0-serial.h
@@ -12,6 +12,13 @@
struct uart_port;
struct uart_info;
+enum {
+ SA11X0_WAKE_CTS = BIT(0),
+ SA11X0_WAKE_DCD = BIT(1),
+ SA11X0_WAKE_DSR = BIT(2),
+ SA11X0_WAKE_RI = BIT(3),
+};
+
/*
* This is a temporary structure for registering these
* functions; it is intended to be discarded after boot.
@@ -20,7 +27,7 @@ struct sa1100_port_fns {
void (*set_mctrl)(struct uart_port *, u_int);
u_int (*get_mctrl)(struct uart_port *);
void (*pm)(struct uart_port *, u_int, u_int);
- int (*set_wake)(struct uart_port *, u_int);
+ unsigned wake[3];
};
#ifdef CONFIG_SERIAL_SA1100