Commit fd455ea8 authored by Kevin Hilman's avatar Kevin Hilman

OMAP2/3/4: UART: Allow per-UART disabling wakeup for serial ports

This patch causes the OMAP uarts to honor the sysfs power/wakeup file
for IOPAD wakeups. Before the OMAP was always woken up from off mode
on a rs232 signal change.  This patch also creates a different
platform device for each serial port so that the wakeup properties can
be control per port.

By default, IOPAD wakeups are enabled for each UART.  To disable,

  # echo disabled > /sys/devices/platform/serial8250.0/power/wakeup

Where serial8250.0 can be replaced by .1, or .2 to control the other
ports.

Original idea and original patch from Russ Dill <russ.dill@gmail.com>

Cc: Russ Dill <russ.dill@gmail.com>
Signed-off-by: default avatarKevin Hilman <khilman@deeprootsystems.com>
parent 2466211e
...@@ -54,6 +54,7 @@ struct omap_uart_state { ...@@ -54,6 +54,7 @@ struct omap_uart_state {
struct plat_serial8250_port *p; struct plat_serial8250_port *p;
struct list_head node; struct list_head node;
struct platform_device pdev;
#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
int context_valid; int context_valid;
...@@ -68,10 +69,9 @@ struct omap_uart_state { ...@@ -68,10 +69,9 @@ struct omap_uart_state {
#endif #endif
}; };
static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS];
static LIST_HEAD(uart_list); static LIST_HEAD(uart_list);
static struct plat_serial8250_port serial_platform_data[] = { static struct plat_serial8250_port serial_platform_data0[] = {
{ {
.membase = IO_ADDRESS(OMAP_UART1_BASE), .membase = IO_ADDRESS(OMAP_UART1_BASE),
.mapbase = OMAP_UART1_BASE, .mapbase = OMAP_UART1_BASE,
...@@ -81,6 +81,12 @@ static struct plat_serial8250_port serial_platform_data[] = { ...@@ -81,6 +81,12 @@ static struct plat_serial8250_port serial_platform_data[] = {
.regshift = 2, .regshift = 2,
.uartclk = OMAP24XX_BASE_BAUD * 16, .uartclk = OMAP24XX_BASE_BAUD * 16,
}, { }, {
.flags = 0
}
};
static struct plat_serial8250_port serial_platform_data1[] = {
{
.membase = IO_ADDRESS(OMAP_UART2_BASE), .membase = IO_ADDRESS(OMAP_UART2_BASE),
.mapbase = OMAP_UART2_BASE, .mapbase = OMAP_UART2_BASE,
.irq = 73, .irq = 73,
...@@ -89,6 +95,12 @@ static struct plat_serial8250_port serial_platform_data[] = { ...@@ -89,6 +95,12 @@ static struct plat_serial8250_port serial_platform_data[] = {
.regshift = 2, .regshift = 2,
.uartclk = OMAP24XX_BASE_BAUD * 16, .uartclk = OMAP24XX_BASE_BAUD * 16,
}, { }, {
.flags = 0
}
};
static struct plat_serial8250_port serial_platform_data2[] = {
{
.membase = IO_ADDRESS(OMAP_UART3_BASE), .membase = IO_ADDRESS(OMAP_UART3_BASE),
.mapbase = OMAP_UART3_BASE, .mapbase = OMAP_UART3_BASE,
.irq = 74, .irq = 74,
...@@ -217,6 +229,40 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) ...@@ -217,6 +229,40 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
clk_disable(uart->fck); clk_disable(uart->fck);
} }
static void omap_uart_enable_wakeup(struct omap_uart_state *uart)
{
/* Set wake-enable bit */
if (uart->wk_en && uart->wk_mask) {
u32 v = __raw_readl(uart->wk_en);
v |= uart->wk_mask;
__raw_writel(v, uart->wk_en);
}
/* Ensure IOPAD wake-enables are set */
if (cpu_is_omap34xx() && uart->padconf) {
u16 v = omap_ctrl_readw(uart->padconf);
v |= OMAP3_PADCONF_WAKEUPENABLE0;
omap_ctrl_writew(v, uart->padconf);
}
}
static void omap_uart_disable_wakeup(struct omap_uart_state *uart)
{
/* Clear wake-enable bit */
if (uart->wk_en && uart->wk_mask) {
u32 v = __raw_readl(uart->wk_en);
v &= ~uart->wk_mask;
__raw_writel(v, uart->wk_en);
}
/* Ensure IOPAD wake-enables are cleared */
if (cpu_is_omap34xx() && uart->padconf) {
u16 v = omap_ctrl_readw(uart->padconf);
v &= ~OMAP3_PADCONF_WAKEUPENABLE0;
omap_ctrl_writew(v, uart->padconf);
}
}
static void omap_uart_smart_idle_enable(struct omap_uart_state *uart, static void omap_uart_smart_idle_enable(struct omap_uart_state *uart,
int enable) int enable)
{ {
...@@ -246,6 +292,11 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart) ...@@ -246,6 +292,11 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart)
static void omap_uart_allow_sleep(struct omap_uart_state *uart) static void omap_uart_allow_sleep(struct omap_uart_state *uart)
{ {
if (device_may_wakeup(&uart->pdev.dev))
omap_uart_enable_wakeup(uart);
else
omap_uart_disable_wakeup(uart);
if (!uart->clocked) if (!uart->clocked)
return; return;
...@@ -292,7 +343,6 @@ void omap_uart_resume_idle(int num) ...@@ -292,7 +343,6 @@ void omap_uart_resume_idle(int num)
/* Check for normal UART wakeup */ /* Check for normal UART wakeup */
if (__raw_readl(uart->wk_st) & uart->wk_mask) if (__raw_readl(uart->wk_st) & uart->wk_mask)
omap_uart_block_sleep(uart); omap_uart_block_sleep(uart);
return; return;
} }
} }
...@@ -346,16 +396,13 @@ static irqreturn_t omap_uart_interrupt(int irq, void *dev_id) ...@@ -346,16 +396,13 @@ static irqreturn_t omap_uart_interrupt(int irq, void *dev_id)
return IRQ_NONE; return IRQ_NONE;
} }
static u32 sleep_timeout = DEFAULT_TIMEOUT;
static void omap_uart_idle_init(struct omap_uart_state *uart) static void omap_uart_idle_init(struct omap_uart_state *uart)
{ {
u32 v;
struct plat_serial8250_port *p = uart->p; struct plat_serial8250_port *p = uart->p;
int ret; int ret;
uart->can_sleep = 0; uart->can_sleep = 0;
uart->timeout = sleep_timeout; uart->timeout = DEFAULT_TIMEOUT;
setup_timer(&uart->timer, omap_uart_idle_timer, setup_timer(&uart->timer, omap_uart_idle_timer,
(unsigned long) uart); (unsigned long) uart);
mod_timer(&uart->timer, jiffies + uart->timeout); mod_timer(&uart->timer, jiffies + uart->timeout);
...@@ -413,22 +460,6 @@ static void omap_uart_idle_init(struct omap_uart_state *uart) ...@@ -413,22 +460,6 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
uart->padconf = 0; uart->padconf = 0;
} }
/* Set wake-enable bit */
if (uart->wk_en && uart->wk_mask) {
v = __raw_readl(uart->wk_en);
v |= uart->wk_mask;
__raw_writel(v, uart->wk_en);
}
/* Ensure IOPAD wake-enables are set */
if (cpu_is_omap34xx() && uart->padconf) {
u16 v;
v = omap_ctrl_readw(uart->padconf);
v |= OMAP3_PADCONF_WAKEUPENABLE0;
omap_ctrl_writew(v, uart->padconf);
}
p->flags |= UPF_SHARE_IRQ; p->flags |= UPF_SHARE_IRQ;
ret = request_irq(p->irq, omap_uart_interrupt, IRQF_SHARED, ret = request_irq(p->irq, omap_uart_interrupt, IRQF_SHARED,
"serial idle", (void *)uart); "serial idle", (void *)uart);
...@@ -449,54 +480,81 @@ void omap_uart_enable_irqs(int enable) ...@@ -449,54 +480,81 @@ void omap_uart_enable_irqs(int enable)
} }
} }
static ssize_t sleep_timeout_show(struct kobject *kobj, static ssize_t sleep_timeout_show(struct device *dev,
struct kobj_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
return sprintf(buf, "%u\n", sleep_timeout / HZ); struct platform_device *pdev = container_of(dev,
struct platform_device, dev);
struct omap_uart_state *uart = container_of(pdev,
struct omap_uart_state, pdev);
return sprintf(buf, "%u\n", uart->timeout / HZ);
} }
static ssize_t sleep_timeout_store(struct kobject *kobj, static ssize_t sleep_timeout_store(struct device *dev,
struct kobj_attribute *attr, struct device_attribute *attr,
const char *buf, size_t n) const char *buf, size_t n)
{ {
struct omap_uart_state *uart; struct platform_device *pdev = container_of(dev,
struct platform_device, dev);
struct omap_uart_state *uart = container_of(pdev,
struct omap_uart_state, pdev);
unsigned int value; unsigned int value;
if (sscanf(buf, "%u", &value) != 1) { if (sscanf(buf, "%u", &value) != 1) {
printk(KERN_ERR "sleep_timeout_store: Invalid value\n"); printk(KERN_ERR "sleep_timeout_store: Invalid value\n");
return -EINVAL; return -EINVAL;
} }
sleep_timeout = value * HZ;
list_for_each_entry(uart, &uart_list, node) { uart->timeout = value * HZ;
uart->timeout = sleep_timeout;
if (uart->timeout) if (uart->timeout)
mod_timer(&uart->timer, jiffies + uart->timeout); mod_timer(&uart->timer, jiffies + uart->timeout);
else else
/* A zero value means disable timeout feature */ /* A zero value means disable timeout feature */
omap_uart_block_sleep(uart); omap_uart_block_sleep(uart);
}
return n; return n;
} }
static struct kobj_attribute sleep_timeout_attr = DEVICE_ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store);
__ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store); #define DEV_CREATE_FILE(dev, attr) WARN_ON(device_create_file(dev, attr))
#else #else
static inline void omap_uart_idle_init(struct omap_uart_state *uart) {} static inline void omap_uart_idle_init(struct omap_uart_state *uart) {}
#define DEV_CREATE_FILE(dev, attr)
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
static struct platform_device serial_device = { static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS] = {
{
.pdev = {
.name = "serial8250", .name = "serial8250",
.id = PLAT8250_DEV_PLATFORM, .id = PLAT8250_DEV_PLATFORM,
.dev = { .dev = {
.platform_data = serial_platform_data, .platform_data = serial_platform_data0,
},
},
}, {
.pdev = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM1,
.dev = {
.platform_data = serial_platform_data1,
},
},
}, {
.pdev = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM2,
.dev = {
.platform_data = serial_platform_data2,
},
},
}, },
}; };
void __init omap_serial_init(void) void __init omap_serial_init(void)
{ {
int i, err; int i;
const struct omap_uart_config *info; const struct omap_uart_config *info;
char name[16]; char name[16];
...@@ -512,8 +570,10 @@ void __init omap_serial_init(void) ...@@ -512,8 +570,10 @@ void __init omap_serial_init(void)
return; return;
for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { for (i = 0; i < OMAP_MAX_NR_PORTS; i++) {
struct plat_serial8250_port *p = serial_platform_data + i;
struct omap_uart_state *uart = &omap_uart[i]; struct omap_uart_state *uart = &omap_uart[i];
struct platform_device *pdev = &uart->pdev;
struct device *dev = &pdev->dev;
struct plat_serial8250_port *p = dev->platform_data;
if (!(info->enabled_uarts & (1 << i))) { if (!(info->enabled_uarts & (1 << i))) {
p->membase = NULL; p->membase = NULL;
...@@ -549,15 +609,13 @@ void __init omap_serial_init(void) ...@@ -549,15 +609,13 @@ void __init omap_serial_init(void)
omap_uart_enable_clocks(uart); omap_uart_enable_clocks(uart);
omap_uart_reset(uart); omap_uart_reset(uart);
omap_uart_idle_init(uart); omap_uart_idle_init(uart);
}
err = platform_device_register(&serial_device);
#ifdef CONFIG_PM
if (!err)
err = sysfs_create_file(&serial_device.dev.kobj,
&sleep_timeout_attr.attr);
#endif
if (WARN_ON(platform_device_register(pdev)))
continue;
if ((cpu_is_omap34xx() && uart->padconf) ||
(uart->wk_en && uart->wk_mask)) {
device_init_wakeup(dev, true);
DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout);
}
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment