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-01-08 10:29:59 +0000
commit16ec22a9d15deab72200f8c76dd5612d1327117b (patch)
tree7483acb1dc247179e1601e817348012e67a0027e
parentccf4f611d138527b0d18b617ab27d7d821014890 (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 be7bcd75d9f4..19f332b44e7d 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