diff options
author | Weike Chen <alvin.chen@intel.com> | 2014-09-17 12:18:42 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2014-09-23 11:51:38 -0400 |
commit | 1e960dbb7b12886d2095df05adf8754eef1c26d0 (patch) | |
tree | 80930f1c90826c8bc3e12db2a21dfd1222d48b0a | |
parent | 5d60d9efe1447b46f33075fb5841fd83247cdbb2 (diff) |
GPIO: gpio-dwapb: Suspend & Resume PM enabling
This patch enables suspend and resume mode for the power management, and
it is based on Josef Ahmad's previous work.
Reviewed-by: Hock Leong Kweh <hock.leong.kweh@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Weike Chen <alvin.chen@intel.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/gpio/gpio-dwapb.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index db059bb8edc3..7feaf9d3f3ca 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c | |||
@@ -51,10 +51,28 @@ | |||
51 | 51 | ||
52 | struct dwapb_gpio; | 52 | struct dwapb_gpio; |
53 | 53 | ||
54 | #ifdef CONFIG_PM_SLEEP | ||
55 | /* Store GPIO context across system-wide suspend/resume transitions */ | ||
56 | struct dwapb_context { | ||
57 | u32 data; | ||
58 | u32 dir; | ||
59 | u32 ext; | ||
60 | u32 int_en; | ||
61 | u32 int_mask; | ||
62 | u32 int_type; | ||
63 | u32 int_pol; | ||
64 | u32 int_deb; | ||
65 | }; | ||
66 | #endif | ||
67 | |||
54 | struct dwapb_gpio_port { | 68 | struct dwapb_gpio_port { |
55 | struct bgpio_chip bgc; | 69 | struct bgpio_chip bgc; |
56 | bool is_registered; | 70 | bool is_registered; |
57 | struct dwapb_gpio *gpio; | 71 | struct dwapb_gpio *gpio; |
72 | #ifdef CONFIG_PM_SLEEP | ||
73 | struct dwapb_context *ctx; | ||
74 | #endif | ||
75 | unsigned int idx; | ||
58 | }; | 76 | }; |
59 | 77 | ||
60 | struct dwapb_gpio { | 78 | struct dwapb_gpio { |
@@ -381,6 +399,13 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, | |||
381 | 399 | ||
382 | port = &gpio->ports[offs]; | 400 | port = &gpio->ports[offs]; |
383 | port->gpio = gpio; | 401 | port->gpio = gpio; |
402 | port->idx = pp->idx; | ||
403 | |||
404 | #ifdef CONFIG_PM_SLEEP | ||
405 | port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL); | ||
406 | if (!port->ctx) | ||
407 | return -ENOMEM; | ||
408 | #endif | ||
384 | 409 | ||
385 | dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE); | 410 | dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE); |
386 | set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE); | 411 | set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE); |
@@ -586,10 +611,100 @@ static const struct of_device_id dwapb_of_match[] = { | |||
586 | }; | 611 | }; |
587 | MODULE_DEVICE_TABLE(of, dwapb_of_match); | 612 | MODULE_DEVICE_TABLE(of, dwapb_of_match); |
588 | 613 | ||
614 | #ifdef CONFIG_PM_SLEEP | ||
615 | static int dwapb_gpio_suspend(struct device *dev) | ||
616 | { | ||
617 | struct platform_device *pdev = to_platform_device(dev); | ||
618 | struct dwapb_gpio *gpio = platform_get_drvdata(pdev); | ||
619 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
620 | unsigned long flags; | ||
621 | int i; | ||
622 | |||
623 | spin_lock_irqsave(&bgc->lock, flags); | ||
624 | for (i = 0; i < gpio->nr_ports; i++) { | ||
625 | unsigned int offset; | ||
626 | unsigned int idx = gpio->ports[i].idx; | ||
627 | struct dwapb_context *ctx = gpio->ports[i].ctx; | ||
628 | |||
629 | BUG_ON(ctx == 0); | ||
630 | |||
631 | offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE; | ||
632 | ctx->dir = dwapb_read(gpio, offset); | ||
633 | |||
634 | offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE; | ||
635 | ctx->data = dwapb_read(gpio, offset); | ||
636 | |||
637 | offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE; | ||
638 | ctx->ext = dwapb_read(gpio, offset); | ||
639 | |||
640 | /* Only port A can provide interrupts */ | ||
641 | if (idx == 0) { | ||
642 | ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK); | ||
643 | ctx->int_en = dwapb_read(gpio, GPIO_INTEN); | ||
644 | ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY); | ||
645 | ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); | ||
646 | ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); | ||
647 | |||
648 | /* Mask out interrupts */ | ||
649 | dwapb_write(gpio, GPIO_INTMASK, 0xffffffff); | ||
650 | } | ||
651 | } | ||
652 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
653 | |||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | static int dwapb_gpio_resume(struct device *dev) | ||
658 | { | ||
659 | struct platform_device *pdev = to_platform_device(dev); | ||
660 | struct dwapb_gpio *gpio = platform_get_drvdata(pdev); | ||
661 | struct bgpio_chip *bgc = &gpio->ports[0].bgc; | ||
662 | unsigned long flags; | ||
663 | int i; | ||
664 | |||
665 | spin_lock_irqsave(&bgc->lock, flags); | ||
666 | for (i = 0; i < gpio->nr_ports; i++) { | ||
667 | unsigned int offset; | ||
668 | unsigned int idx = gpio->ports[i].idx; | ||
669 | struct dwapb_context *ctx = gpio->ports[i].ctx; | ||
670 | |||
671 | BUG_ON(ctx == 0); | ||
672 | |||
673 | offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE; | ||
674 | dwapb_write(gpio, offset, ctx->data); | ||
675 | |||
676 | offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE; | ||
677 | dwapb_write(gpio, offset, ctx->dir); | ||
678 | |||
679 | offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE; | ||
680 | dwapb_write(gpio, offset, ctx->ext); | ||
681 | |||
682 | /* Only port A can provide interrupts */ | ||
683 | if (idx == 0) { | ||
684 | dwapb_write(gpio, GPIO_INTTYPE_LEVEL, ctx->int_type); | ||
685 | dwapb_write(gpio, GPIO_INT_POLARITY, ctx->int_pol); | ||
686 | dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, ctx->int_deb); | ||
687 | dwapb_write(gpio, GPIO_INTEN, ctx->int_en); | ||
688 | dwapb_write(gpio, GPIO_INTMASK, ctx->int_mask); | ||
689 | |||
690 | /* Clear out spurious interrupts */ | ||
691 | dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff); | ||
692 | } | ||
693 | } | ||
694 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
695 | |||
696 | return 0; | ||
697 | } | ||
698 | #endif | ||
699 | |||
700 | static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, | ||
701 | dwapb_gpio_resume); | ||
702 | |||
589 | static struct platform_driver dwapb_gpio_driver = { | 703 | static struct platform_driver dwapb_gpio_driver = { |
590 | .driver = { | 704 | .driver = { |
591 | .name = "gpio-dwapb", | 705 | .name = "gpio-dwapb", |
592 | .owner = THIS_MODULE, | 706 | .owner = THIS_MODULE, |
707 | .pm = &dwapb_gpio_pm_ops, | ||
593 | .of_match_table = of_match_ptr(dwapb_of_match), | 708 | .of_match_table = of_match_ptr(dwapb_of_match), |
594 | }, | 709 | }, |
595 | .probe = dwapb_gpio_probe, | 710 | .probe = dwapb_gpio_probe, |