diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-22 15:05:24 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-02-18 18:15:43 -0500 |
commit | 33237616771bfc29a97f17e74efe3799bb790343 (patch) | |
tree | 4ad660084434e190150dab67170d3b65cf2e8a3a | |
parent | a3364409c4af8bae42d04def48dc11409787e503 (diff) |
MFD: ucb1x00-core: add wakeup support
Add genirq wakeup support for the ucb1x00 device. This allows an
attached gpio_keys driver to wakeup the system. Touchscreen is also
possible.
When there are no wakeup sources, ask the platform to assert the reset
signal to avoid any unexpected behaviour; this also puts the reset
signal at the right level when power is removed from the device.
Acked-by: Jochen Friedrich <jochen@scram.de>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | drivers/mfd/ucb1x00-core.c | 59 | ||||
-rw-r--r-- | include/linux/mfd/ucb1x00.h | 4 |
2 files changed, 63 insertions, 0 deletions
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 400604d38780..70f02daeb22a 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c | |||
@@ -362,12 +362,32 @@ static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) | |||
362 | return 0; | 362 | return 0; |
363 | } | 363 | } |
364 | 364 | ||
365 | static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on) | ||
366 | { | ||
367 | struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); | ||
368 | struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data; | ||
369 | unsigned mask = 1 << (data->irq - ucb->irq_base); | ||
370 | |||
371 | if (!pdata || !pdata->can_wakeup) | ||
372 | return -EINVAL; | ||
373 | |||
374 | raw_spin_lock(&ucb->irq_lock); | ||
375 | if (on) | ||
376 | ucb->irq_wake |= mask; | ||
377 | else | ||
378 | ucb->irq_wake &= ~mask; | ||
379 | raw_spin_unlock(&ucb->irq_lock); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
365 | static struct irq_chip ucb1x00_irqchip = { | 384 | static struct irq_chip ucb1x00_irqchip = { |
366 | .name = "ucb1x00", | 385 | .name = "ucb1x00", |
367 | .irq_ack = ucb1x00_irq_noop, | 386 | .irq_ack = ucb1x00_irq_noop, |
368 | .irq_mask = ucb1x00_irq_mask, | 387 | .irq_mask = ucb1x00_irq_mask, |
369 | .irq_unmask = ucb1x00_irq_unmask, | 388 | .irq_unmask = ucb1x00_irq_unmask, |
370 | .irq_set_type = ucb1x00_irq_set_type, | 389 | .irq_set_type = ucb1x00_irq_set_type, |
390 | .irq_set_wake = ucb1x00_irq_set_wake, | ||
371 | }; | 391 | }; |
372 | 392 | ||
373 | static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) | 393 | static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) |
@@ -565,6 +585,9 @@ static int ucb1x00_probe(struct mcp *mcp) | |||
565 | 585 | ||
566 | mcp_set_drvdata(mcp, ucb); | 586 | mcp_set_drvdata(mcp, ucb); |
567 | 587 | ||
588 | if (pdata) | ||
589 | device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); | ||
590 | |||
568 | INIT_LIST_HEAD(&ucb->devs); | 591 | INIT_LIST_HEAD(&ucb->devs); |
569 | mutex_lock(&ucb1x00_mutex); | 592 | mutex_lock(&ucb1x00_mutex); |
570 | list_add_tail(&ucb->node, &ucb1x00_devices); | 593 | list_add_tail(&ucb->node, &ucb1x00_devices); |
@@ -648,6 +671,7 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) | |||
648 | 671 | ||
649 | static int ucb1x00_suspend(struct device *dev) | 672 | static int ucb1x00_suspend(struct device *dev) |
650 | { | 673 | { |
674 | struct ucb1x00_plat_data *pdata = dev->platform_data; | ||
651 | struct ucb1x00 *ucb = dev_get_drvdata(dev); | 675 | struct ucb1x00 *ucb = dev_get_drvdata(dev); |
652 | struct ucb1x00_dev *udev; | 676 | struct ucb1x00_dev *udev; |
653 | 677 | ||
@@ -657,18 +681,53 @@ static int ucb1x00_suspend(struct device *dev) | |||
657 | udev->drv->suspend(udev); | 681 | udev->drv->suspend(udev); |
658 | } | 682 | } |
659 | mutex_unlock(&ucb1x00_mutex); | 683 | mutex_unlock(&ucb1x00_mutex); |
684 | |||
685 | if (ucb->irq_wake) { | ||
686 | unsigned long flags; | ||
687 | |||
688 | raw_spin_lock_irqsave(&ucb->irq_lock, flags); | ||
689 | ucb1x00_enable(ucb); | ||
690 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & | ||
691 | ucb->irq_wake); | ||
692 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & | ||
693 | ucb->irq_wake); | ||
694 | ucb1x00_disable(ucb); | ||
695 | raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); | ||
696 | |||
697 | enable_irq_wake(ucb->irq); | ||
698 | } else if (pdata && pdata->reset) | ||
699 | pdata->reset(UCB_RST_SUSPEND); | ||
700 | |||
660 | return 0; | 701 | return 0; |
661 | } | 702 | } |
662 | 703 | ||
663 | static int ucb1x00_resume(struct device *dev) | 704 | static int ucb1x00_resume(struct device *dev) |
664 | { | 705 | { |
706 | struct ucb1x00_plat_data *pdata = dev->platform_data; | ||
665 | struct ucb1x00 *ucb = dev_get_drvdata(dev); | 707 | struct ucb1x00 *ucb = dev_get_drvdata(dev); |
666 | struct ucb1x00_dev *udev; | 708 | struct ucb1x00_dev *udev; |
667 | 709 | ||
710 | if (!ucb->irq_wake && pdata && pdata->reset) | ||
711 | pdata->reset(UCB_RST_RESUME); | ||
712 | |||
668 | ucb1x00_enable(ucb); | 713 | ucb1x00_enable(ucb); |
669 | ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); | 714 | ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); |
670 | ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); | 715 | ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); |
716 | |||
717 | if (ucb->irq_wake) { | ||
718 | unsigned long flags; | ||
719 | |||
720 | raw_spin_lock_irqsave(&ucb->irq_lock, flags); | ||
721 | ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & | ||
722 | ucb->irq_mask); | ||
723 | ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & | ||
724 | ucb->irq_mask); | ||
725 | raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); | ||
726 | |||
727 | disable_irq_wake(ucb->irq); | ||
728 | } | ||
671 | ucb1x00_disable(ucb); | 729 | ucb1x00_disable(ucb); |
730 | |||
672 | mutex_lock(&ucb1x00_mutex); | 731 | mutex_lock(&ucb1x00_mutex); |
673 | list_for_each_entry(udev, &ucb->devs, dev_node) { | 732 | list_for_each_entry(udev, &ucb->devs, dev_node) { |
674 | if (udev->drv->resume) | 733 | if (udev->drv->resume) |
diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 6fb907446c33..28af41756360 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h | |||
@@ -106,6 +106,8 @@ | |||
106 | 106 | ||
107 | enum ucb1x00_reset { | 107 | enum ucb1x00_reset { |
108 | UCB_RST_PROBE, | 108 | UCB_RST_PROBE, |
109 | UCB_RST_RESUME, | ||
110 | UCB_RST_SUSPEND, | ||
109 | UCB_RST_REMOVE, | 111 | UCB_RST_REMOVE, |
110 | UCB_RST_PROBE_FAIL, | 112 | UCB_RST_PROBE_FAIL, |
111 | }; | 113 | }; |
@@ -114,6 +116,7 @@ struct ucb1x00_plat_data { | |||
114 | void (*reset)(enum ucb1x00_reset); | 116 | void (*reset)(enum ucb1x00_reset); |
115 | unsigned irq_base; | 117 | unsigned irq_base; |
116 | int gpio_base; | 118 | int gpio_base; |
119 | unsigned can_wakeup; | ||
117 | }; | 120 | }; |
118 | 121 | ||
119 | struct ucb1x00 { | 122 | struct ucb1x00 { |
@@ -130,6 +133,7 @@ struct ucb1x00 { | |||
130 | u16 irq_fal_enbl; | 133 | u16 irq_fal_enbl; |
131 | u16 irq_ris_enbl; | 134 | u16 irq_ris_enbl; |
132 | u16 irq_mask; | 135 | u16 irq_mask; |
136 | u16 irq_wake; | ||
133 | struct device dev; | 137 | struct device dev; |
134 | struct list_head node; | 138 | struct list_head node; |
135 | struct list_head devs; | 139 | struct list_head devs; |