diff options
author | Kevin Hilman <khilman@deeprootsystems.com> | 2009-04-27 15:27:36 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@deeprootsystems.com> | 2009-08-05 12:10:53 -0400 |
commit | fd455ea899b5a14a8cdd276e15f3b47696526f92 (patch) | |
tree | a03bfc66f4c86c17a347d6b84bfa2b6bbde7db65 /arch/arm/mach-omap2/serial.c | |
parent | 2466211e5debd205fc550d871fe0ab9b9a6d02ed (diff) |
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: Kevin Hilman <khilman@deeprootsystems.com>
Diffstat (limited to 'arch/arm/mach-omap2/serial.c')
-rw-r--r-- | arch/arm/mach-omap2/serial.c | 174 |
1 files changed, 116 insertions, 58 deletions
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 5352d05b42d6..6f35a7e4893f 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c | |||
@@ -54,6 +54,7 @@ struct omap_uart_state { | |||
54 | 54 | ||
55 | struct plat_serial8250_port *p; | 55 | struct plat_serial8250_port *p; |
56 | struct list_head node; | 56 | struct list_head node; |
57 | struct platform_device pdev; | ||
57 | 58 | ||
58 | #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) | 59 | #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) |
59 | int context_valid; | 60 | int context_valid; |
@@ -68,10 +69,9 @@ struct omap_uart_state { | |||
68 | #endif | 69 | #endif |
69 | }; | 70 | }; |
70 | 71 | ||
71 | static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS]; | ||
72 | static LIST_HEAD(uart_list); | 72 | static LIST_HEAD(uart_list); |
73 | 73 | ||
74 | static struct plat_serial8250_port serial_platform_data[] = { | 74 | static struct plat_serial8250_port serial_platform_data0[] = { |
75 | { | 75 | { |
76 | .membase = IO_ADDRESS(OMAP_UART1_BASE), | 76 | .membase = IO_ADDRESS(OMAP_UART1_BASE), |
77 | .mapbase = OMAP_UART1_BASE, | 77 | .mapbase = OMAP_UART1_BASE, |
@@ -81,6 +81,12 @@ static struct plat_serial8250_port serial_platform_data[] = { | |||
81 | .regshift = 2, | 81 | .regshift = 2, |
82 | .uartclk = OMAP24XX_BASE_BAUD * 16, | 82 | .uartclk = OMAP24XX_BASE_BAUD * 16, |
83 | }, { | 83 | }, { |
84 | .flags = 0 | ||
85 | } | ||
86 | }; | ||
87 | |||
88 | static struct plat_serial8250_port serial_platform_data1[] = { | ||
89 | { | ||
84 | .membase = IO_ADDRESS(OMAP_UART2_BASE), | 90 | .membase = IO_ADDRESS(OMAP_UART2_BASE), |
85 | .mapbase = OMAP_UART2_BASE, | 91 | .mapbase = OMAP_UART2_BASE, |
86 | .irq = 73, | 92 | .irq = 73, |
@@ -89,6 +95,12 @@ static struct plat_serial8250_port serial_platform_data[] = { | |||
89 | .regshift = 2, | 95 | .regshift = 2, |
90 | .uartclk = OMAP24XX_BASE_BAUD * 16, | 96 | .uartclk = OMAP24XX_BASE_BAUD * 16, |
91 | }, { | 97 | }, { |
98 | .flags = 0 | ||
99 | } | ||
100 | }; | ||
101 | |||
102 | static struct plat_serial8250_port serial_platform_data2[] = { | ||
103 | { | ||
92 | .membase = IO_ADDRESS(OMAP_UART3_BASE), | 104 | .membase = IO_ADDRESS(OMAP_UART3_BASE), |
93 | .mapbase = OMAP_UART3_BASE, | 105 | .mapbase = OMAP_UART3_BASE, |
94 | .irq = 74, | 106 | .irq = 74, |
@@ -217,6 +229,40 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) | |||
217 | clk_disable(uart->fck); | 229 | clk_disable(uart->fck); |
218 | } | 230 | } |
219 | 231 | ||
232 | static void omap_uart_enable_wakeup(struct omap_uart_state *uart) | ||
233 | { | ||
234 | /* Set wake-enable bit */ | ||
235 | if (uart->wk_en && uart->wk_mask) { | ||
236 | u32 v = __raw_readl(uart->wk_en); | ||
237 | v |= uart->wk_mask; | ||
238 | __raw_writel(v, uart->wk_en); | ||
239 | } | ||
240 | |||
241 | /* Ensure IOPAD wake-enables are set */ | ||
242 | if (cpu_is_omap34xx() && uart->padconf) { | ||
243 | u16 v = omap_ctrl_readw(uart->padconf); | ||
244 | v |= OMAP3_PADCONF_WAKEUPENABLE0; | ||
245 | omap_ctrl_writew(v, uart->padconf); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void omap_uart_disable_wakeup(struct omap_uart_state *uart) | ||
250 | { | ||
251 | /* Clear wake-enable bit */ | ||
252 | if (uart->wk_en && uart->wk_mask) { | ||
253 | u32 v = __raw_readl(uart->wk_en); | ||
254 | v &= ~uart->wk_mask; | ||
255 | __raw_writel(v, uart->wk_en); | ||
256 | } | ||
257 | |||
258 | /* Ensure IOPAD wake-enables are cleared */ | ||
259 | if (cpu_is_omap34xx() && uart->padconf) { | ||
260 | u16 v = omap_ctrl_readw(uart->padconf); | ||
261 | v &= ~OMAP3_PADCONF_WAKEUPENABLE0; | ||
262 | omap_ctrl_writew(v, uart->padconf); | ||
263 | } | ||
264 | } | ||
265 | |||
220 | static void omap_uart_smart_idle_enable(struct omap_uart_state *uart, | 266 | static void omap_uart_smart_idle_enable(struct omap_uart_state *uart, |
221 | int enable) | 267 | int enable) |
222 | { | 268 | { |
@@ -246,6 +292,11 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart) | |||
246 | 292 | ||
247 | static void omap_uart_allow_sleep(struct omap_uart_state *uart) | 293 | static void omap_uart_allow_sleep(struct omap_uart_state *uart) |
248 | { | 294 | { |
295 | if (device_may_wakeup(&uart->pdev.dev)) | ||
296 | omap_uart_enable_wakeup(uart); | ||
297 | else | ||
298 | omap_uart_disable_wakeup(uart); | ||
299 | |||
249 | if (!uart->clocked) | 300 | if (!uart->clocked) |
250 | return; | 301 | return; |
251 | 302 | ||
@@ -292,7 +343,6 @@ void omap_uart_resume_idle(int num) | |||
292 | /* Check for normal UART wakeup */ | 343 | /* Check for normal UART wakeup */ |
293 | if (__raw_readl(uart->wk_st) & uart->wk_mask) | 344 | if (__raw_readl(uart->wk_st) & uart->wk_mask) |
294 | omap_uart_block_sleep(uart); | 345 | omap_uart_block_sleep(uart); |
295 | |||
296 | return; | 346 | return; |
297 | } | 347 | } |
298 | } | 348 | } |
@@ -346,16 +396,13 @@ static irqreturn_t omap_uart_interrupt(int irq, void *dev_id) | |||
346 | return IRQ_NONE; | 396 | return IRQ_NONE; |
347 | } | 397 | } |
348 | 398 | ||
349 | static u32 sleep_timeout = DEFAULT_TIMEOUT; | ||
350 | |||
351 | static void omap_uart_idle_init(struct omap_uart_state *uart) | 399 | static void omap_uart_idle_init(struct omap_uart_state *uart) |
352 | { | 400 | { |
353 | u32 v; | ||
354 | struct plat_serial8250_port *p = uart->p; | 401 | struct plat_serial8250_port *p = uart->p; |
355 | int ret; | 402 | int ret; |
356 | 403 | ||
357 | uart->can_sleep = 0; | 404 | uart->can_sleep = 0; |
358 | uart->timeout = sleep_timeout; | 405 | uart->timeout = DEFAULT_TIMEOUT; |
359 | setup_timer(&uart->timer, omap_uart_idle_timer, | 406 | setup_timer(&uart->timer, omap_uart_idle_timer, |
360 | (unsigned long) uart); | 407 | (unsigned long) uart); |
361 | mod_timer(&uart->timer, jiffies + uart->timeout); | 408 | mod_timer(&uart->timer, jiffies + uart->timeout); |
@@ -413,22 +460,6 @@ static void omap_uart_idle_init(struct omap_uart_state *uart) | |||
413 | uart->padconf = 0; | 460 | uart->padconf = 0; |
414 | } | 461 | } |
415 | 462 | ||
416 | /* Set wake-enable bit */ | ||
417 | if (uart->wk_en && uart->wk_mask) { | ||
418 | v = __raw_readl(uart->wk_en); | ||
419 | v |= uart->wk_mask; | ||
420 | __raw_writel(v, uart->wk_en); | ||
421 | } | ||
422 | |||
423 | /* Ensure IOPAD wake-enables are set */ | ||
424 | if (cpu_is_omap34xx() && uart->padconf) { | ||
425 | u16 v; | ||
426 | |||
427 | v = omap_ctrl_readw(uart->padconf); | ||
428 | v |= OMAP3_PADCONF_WAKEUPENABLE0; | ||
429 | omap_ctrl_writew(v, uart->padconf); | ||
430 | } | ||
431 | |||
432 | p->flags |= UPF_SHARE_IRQ; | 463 | p->flags |= UPF_SHARE_IRQ; |
433 | ret = request_irq(p->irq, omap_uart_interrupt, IRQF_SHARED, | 464 | ret = request_irq(p->irq, omap_uart_interrupt, IRQF_SHARED, |
434 | "serial idle", (void *)uart); | 465 | "serial idle", (void *)uart); |
@@ -449,54 +480,81 @@ void omap_uart_enable_irqs(int enable) | |||
449 | } | 480 | } |
450 | } | 481 | } |
451 | 482 | ||
452 | static ssize_t sleep_timeout_show(struct kobject *kobj, | 483 | static ssize_t sleep_timeout_show(struct device *dev, |
453 | struct kobj_attribute *attr, | 484 | struct device_attribute *attr, |
454 | char *buf) | 485 | char *buf) |
455 | { | 486 | { |
456 | return sprintf(buf, "%u\n", sleep_timeout / HZ); | 487 | struct platform_device *pdev = container_of(dev, |
488 | struct platform_device, dev); | ||
489 | struct omap_uart_state *uart = container_of(pdev, | ||
490 | struct omap_uart_state, pdev); | ||
491 | |||
492 | return sprintf(buf, "%u\n", uart->timeout / HZ); | ||
457 | } | 493 | } |
458 | 494 | ||
459 | static ssize_t sleep_timeout_store(struct kobject *kobj, | 495 | static ssize_t sleep_timeout_store(struct device *dev, |
460 | struct kobj_attribute *attr, | 496 | struct device_attribute *attr, |
461 | const char *buf, size_t n) | 497 | const char *buf, size_t n) |
462 | { | 498 | { |
463 | struct omap_uart_state *uart; | 499 | struct platform_device *pdev = container_of(dev, |
500 | struct platform_device, dev); | ||
501 | struct omap_uart_state *uart = container_of(pdev, | ||
502 | struct omap_uart_state, pdev); | ||
464 | unsigned int value; | 503 | unsigned int value; |
465 | 504 | ||
466 | if (sscanf(buf, "%u", &value) != 1) { | 505 | if (sscanf(buf, "%u", &value) != 1) { |
467 | printk(KERN_ERR "sleep_timeout_store: Invalid value\n"); | 506 | printk(KERN_ERR "sleep_timeout_store: Invalid value\n"); |
468 | return -EINVAL; | 507 | return -EINVAL; |
469 | } | 508 | } |
470 | sleep_timeout = value * HZ; | 509 | |
471 | list_for_each_entry(uart, &uart_list, node) { | 510 | uart->timeout = value * HZ; |
472 | uart->timeout = sleep_timeout; | 511 | if (uart->timeout) |
473 | if (uart->timeout) | 512 | mod_timer(&uart->timer, jiffies + uart->timeout); |
474 | mod_timer(&uart->timer, jiffies + uart->timeout); | 513 | else |
475 | else | 514 | /* A zero value means disable timeout feature */ |
476 | /* A zero value means disable timeout feature */ | 515 | omap_uart_block_sleep(uart); |
477 | omap_uart_block_sleep(uart); | 516 | |
478 | } | ||
479 | return n; | 517 | return n; |
480 | } | 518 | } |
481 | 519 | ||
482 | static struct kobj_attribute sleep_timeout_attr = | 520 | DEVICE_ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store); |
483 | __ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store); | 521 | #define DEV_CREATE_FILE(dev, attr) WARN_ON(device_create_file(dev, attr)) |
484 | |||
485 | #else | 522 | #else |
486 | static inline void omap_uart_idle_init(struct omap_uart_state *uart) {} | 523 | static inline void omap_uart_idle_init(struct omap_uart_state *uart) {} |
524 | #define DEV_CREATE_FILE(dev, attr) | ||
487 | #endif /* CONFIG_PM */ | 525 | #endif /* CONFIG_PM */ |
488 | 526 | ||
489 | static struct platform_device serial_device = { | 527 | static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS] = { |
490 | .name = "serial8250", | 528 | { |
491 | .id = PLAT8250_DEV_PLATFORM, | 529 | .pdev = { |
492 | .dev = { | 530 | .name = "serial8250", |
493 | .platform_data = serial_platform_data, | 531 | .id = PLAT8250_DEV_PLATFORM, |
532 | .dev = { | ||
533 | .platform_data = serial_platform_data0, | ||
534 | }, | ||
535 | }, | ||
536 | }, { | ||
537 | .pdev = { | ||
538 | .name = "serial8250", | ||
539 | .id = PLAT8250_DEV_PLATFORM1, | ||
540 | .dev = { | ||
541 | .platform_data = serial_platform_data1, | ||
542 | }, | ||
543 | }, | ||
544 | }, { | ||
545 | .pdev = { | ||
546 | .name = "serial8250", | ||
547 | .id = PLAT8250_DEV_PLATFORM2, | ||
548 | .dev = { | ||
549 | .platform_data = serial_platform_data2, | ||
550 | }, | ||
551 | }, | ||
494 | }, | 552 | }, |
495 | }; | 553 | }; |
496 | 554 | ||
497 | void __init omap_serial_init(void) | 555 | void __init omap_serial_init(void) |
498 | { | 556 | { |
499 | int i, err; | 557 | int i; |
500 | const struct omap_uart_config *info; | 558 | const struct omap_uart_config *info; |
501 | char name[16]; | 559 | char name[16]; |
502 | 560 | ||
@@ -512,8 +570,10 @@ void __init omap_serial_init(void) | |||
512 | return; | 570 | return; |
513 | 571 | ||
514 | for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { | 572 | for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { |
515 | struct plat_serial8250_port *p = serial_platform_data + i; | ||
516 | struct omap_uart_state *uart = &omap_uart[i]; | 573 | struct omap_uart_state *uart = &omap_uart[i]; |
574 | struct platform_device *pdev = &uart->pdev; | ||
575 | struct device *dev = &pdev->dev; | ||
576 | struct plat_serial8250_port *p = dev->platform_data; | ||
517 | 577 | ||
518 | if (!(info->enabled_uarts & (1 << i))) { | 578 | if (!(info->enabled_uarts & (1 << i))) { |
519 | p->membase = NULL; | 579 | p->membase = NULL; |
@@ -549,15 +609,13 @@ void __init omap_serial_init(void) | |||
549 | omap_uart_enable_clocks(uart); | 609 | omap_uart_enable_clocks(uart); |
550 | omap_uart_reset(uart); | 610 | omap_uart_reset(uart); |
551 | omap_uart_idle_init(uart); | 611 | omap_uart_idle_init(uart); |
552 | } | ||
553 | |||
554 | err = platform_device_register(&serial_device); | ||
555 | |||
556 | #ifdef CONFIG_PM | ||
557 | if (!err) | ||
558 | err = sysfs_create_file(&serial_device.dev.kobj, | ||
559 | &sleep_timeout_attr.attr); | ||
560 | #endif | ||
561 | 612 | ||
613 | if (WARN_ON(platform_device_register(pdev))) | ||
614 | continue; | ||
615 | if ((cpu_is_omap34xx() && uart->padconf) || | ||
616 | (uart->wk_en && uart->wk_mask)) { | ||
617 | device_init_wakeup(dev, true); | ||
618 | DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout); | ||
619 | } | ||
620 | } | ||
562 | } | 621 | } |
563 | |||