diff options
-rw-r--r-- | arch/arm/plat-nomadik/gpio.c | 114 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/gpio.h | 3 |
2 files changed, 100 insertions, 17 deletions
diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c index 45b1cf95e378..70620426ee55 100644 --- a/arch/arm/plat-nomadik/gpio.c +++ b/arch/arm/plat-nomadik/gpio.c | |||
@@ -50,6 +50,10 @@ struct nmk_gpio_chip { | |||
50 | /* Keep track of configured edges */ | 50 | /* Keep track of configured edges */ |
51 | u32 edge_rising; | 51 | u32 edge_rising; |
52 | u32 edge_falling; | 52 | u32 edge_falling; |
53 | u32 real_wake; | ||
54 | u32 rwimsc; | ||
55 | u32 fwimsc; | ||
56 | u32 slpm; | ||
53 | }; | 57 | }; |
54 | 58 | ||
55 | static struct nmk_gpio_chip * | 59 | static struct nmk_gpio_chip * |
@@ -534,8 +538,20 @@ static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, | |||
534 | } | 538 | } |
535 | } | 539 | } |
536 | 540 | ||
537 | static int nmk_gpio_irq_modify(struct irq_data *d, enum nmk_gpio_irq_type which, | 541 | static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip, |
538 | bool enable) | 542 | int gpio, bool on) |
543 | { | ||
544 | #ifdef CONFIG_ARCH_U8500 | ||
545 | if (cpu_is_u8500v2()) { | ||
546 | __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, | ||
547 | on ? NMK_GPIO_SLPM_WAKEUP_ENABLE | ||
548 | : NMK_GPIO_SLPM_WAKEUP_DISABLE); | ||
549 | } | ||
550 | #endif | ||
551 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); | ||
552 | } | ||
553 | |||
554 | static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) | ||
539 | { | 555 | { |
540 | int gpio; | 556 | int gpio; |
541 | struct nmk_gpio_chip *nmk_chip; | 557 | struct nmk_gpio_chip *nmk_chip; |
@@ -548,45 +564,55 @@ static int nmk_gpio_irq_modify(struct irq_data *d, enum nmk_gpio_irq_type which, | |||
548 | if (!nmk_chip) | 564 | if (!nmk_chip) |
549 | return -EINVAL; | 565 | return -EINVAL; |
550 | 566 | ||
551 | spin_lock_irqsave(&nmk_chip->lock, flags); | 567 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); |
552 | __nmk_gpio_irq_modify(nmk_chip, gpio, which, enable); | 568 | spin_lock(&nmk_chip->lock); |
553 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | 569 | |
570 | __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, enable); | ||
571 | |||
572 | if (!(nmk_chip->real_wake & bitmask)) | ||
573 | __nmk_gpio_set_wake(nmk_chip, gpio, enable); | ||
574 | |||
575 | spin_unlock(&nmk_chip->lock); | ||
576 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); | ||
554 | 577 | ||
555 | return 0; | 578 | return 0; |
556 | } | 579 | } |
557 | 580 | ||
558 | static void nmk_gpio_irq_mask(struct irq_data *d) | 581 | static void nmk_gpio_irq_mask(struct irq_data *d) |
559 | { | 582 | { |
560 | nmk_gpio_irq_modify(d, NORMAL, false); | 583 | nmk_gpio_irq_maskunmask(d, false); |
561 | } | 584 | } |
562 | 585 | ||
563 | static void nmk_gpio_irq_unmask(struct irq_data *d) | 586 | static void nmk_gpio_irq_unmask(struct irq_data *d) |
564 | { | 587 | { |
565 | nmk_gpio_irq_modify(d, NORMAL, true); | 588 | nmk_gpio_irq_maskunmask(d, true); |
566 | } | 589 | } |
567 | 590 | ||
568 | static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) | 591 | static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) |
569 | { | 592 | { |
593 | struct irq_desc *desc = irq_to_desc(d->irq); | ||
594 | bool enabled = !(desc->status & IRQ_DISABLED); | ||
570 | struct nmk_gpio_chip *nmk_chip; | 595 | struct nmk_gpio_chip *nmk_chip; |
571 | unsigned long flags; | 596 | unsigned long flags; |
597 | u32 bitmask; | ||
572 | int gpio; | 598 | int gpio; |
573 | 599 | ||
574 | gpio = NOMADIK_IRQ_TO_GPIO(d->irq); | 600 | gpio = NOMADIK_IRQ_TO_GPIO(d->irq); |
575 | nmk_chip = irq_data_get_irq_chip_data(d); | 601 | nmk_chip = irq_data_get_irq_chip_data(d); |
576 | if (!nmk_chip) | 602 | if (!nmk_chip) |
577 | return -EINVAL; | 603 | return -EINVAL; |
604 | bitmask = nmk_gpio_get_bitmask(gpio); | ||
578 | 605 | ||
579 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); | 606 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); |
580 | spin_lock(&nmk_chip->lock); | 607 | spin_lock(&nmk_chip->lock); |
581 | 608 | ||
582 | #ifdef CONFIG_ARCH_U8500 | 609 | if (!enabled) |
583 | if (cpu_is_u8500v2()) { | 610 | __nmk_gpio_set_wake(nmk_chip, gpio, on); |
584 | __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, | 611 | |
585 | on ? NMK_GPIO_SLPM_WAKEUP_ENABLE | 612 | if (on) |
586 | : NMK_GPIO_SLPM_WAKEUP_DISABLE); | 613 | nmk_chip->real_wake |= bitmask; |
587 | } | 614 | else |
588 | #endif | 615 | nmk_chip->real_wake &= ~bitmask; |
589 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); | ||
590 | 616 | ||
591 | spin_unlock(&nmk_chip->lock); | 617 | spin_unlock(&nmk_chip->lock); |
592 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); | 618 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); |
@@ -620,7 +646,7 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) | |||
620 | if (enabled) | 646 | if (enabled) |
621 | __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); | 647 | __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); |
622 | 648 | ||
623 | if (wake) | 649 | if (enabled || wake) |
624 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, false); | 650 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, false); |
625 | 651 | ||
626 | nmk_chip->edge_rising &= ~bitmask; | 652 | nmk_chip->edge_rising &= ~bitmask; |
@@ -634,7 +660,7 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) | |||
634 | if (enabled) | 660 | if (enabled) |
635 | __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, true); | 661 | __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, true); |
636 | 662 | ||
637 | if (wake) | 663 | if (enabled || wake) |
638 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); | 664 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); |
639 | 665 | ||
640 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | 666 | spin_unlock_irqrestore(&nmk_chip->lock, flags); |
@@ -870,6 +896,60 @@ static struct gpio_chip nmk_gpio_template = { | |||
870 | .can_sleep = 0, | 896 | .can_sleep = 0, |
871 | }; | 897 | }; |
872 | 898 | ||
899 | /* | ||
900 | * Called from the suspend/resume path to only keep the real wakeup interrupts | ||
901 | * (those that have had set_irq_wake() called on them) as wakeup interrupts, | ||
902 | * and not the rest of the interrupts which we needed to have as wakeups for | ||
903 | * cpuidle. | ||
904 | * | ||
905 | * PM ops are not used since this needs to be done at the end, after all the | ||
906 | * other drivers are done with their suspend callbacks. | ||
907 | */ | ||
908 | void nmk_gpio_wakeups_suspend(void) | ||
909 | { | ||
910 | int i; | ||
911 | |||
912 | for (i = 0; i < NUM_BANKS; i++) { | ||
913 | struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; | ||
914 | |||
915 | if (!chip) | ||
916 | break; | ||
917 | |||
918 | chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); | ||
919 | chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); | ||
920 | |||
921 | writel(chip->rwimsc & chip->real_wake, | ||
922 | chip->addr + NMK_GPIO_RWIMSC); | ||
923 | writel(chip->fwimsc & chip->real_wake, | ||
924 | chip->addr + NMK_GPIO_FWIMSC); | ||
925 | |||
926 | if (cpu_is_u8500v2()) { | ||
927 | chip->slpm = readl(chip->addr + NMK_GPIO_SLPC); | ||
928 | |||
929 | /* 0 -> wakeup enable */ | ||
930 | writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC); | ||
931 | } | ||
932 | } | ||
933 | } | ||
934 | |||
935 | void nmk_gpio_wakeups_resume(void) | ||
936 | { | ||
937 | int i; | ||
938 | |||
939 | for (i = 0; i < NUM_BANKS; i++) { | ||
940 | struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; | ||
941 | |||
942 | if (!chip) | ||
943 | break; | ||
944 | |||
945 | writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC); | ||
946 | writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC); | ||
947 | |||
948 | if (cpu_is_u8500v2()) | ||
949 | writel(chip->slpm, chip->addr + NMK_GPIO_SLPC); | ||
950 | } | ||
951 | } | ||
952 | |||
873 | static int __devinit nmk_gpio_probe(struct platform_device *dev) | 953 | static int __devinit nmk_gpio_probe(struct platform_device *dev) |
874 | { | 954 | { |
875 | struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; | 955 | struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; |
diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h index e3a4837e86f4..1b9f6f0843d1 100644 --- a/arch/arm/plat-nomadik/include/plat/gpio.h +++ b/arch/arm/plat-nomadik/include/plat/gpio.h | |||
@@ -75,6 +75,9 @@ extern int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull); | |||
75 | extern int nmk_gpio_set_mode(int gpio, int gpio_mode); | 75 | extern int nmk_gpio_set_mode(int gpio, int gpio_mode); |
76 | extern int nmk_gpio_get_mode(int gpio); | 76 | extern int nmk_gpio_get_mode(int gpio); |
77 | 77 | ||
78 | extern void nmk_gpio_wakeups_suspend(void); | ||
79 | extern void nmk_gpio_wakeups_resume(void); | ||
80 | |||
78 | /* | 81 | /* |
79 | * Platform data to register a block: only the initial gpio/irq number. | 82 | * Platform data to register a block: only the initial gpio/irq number. |
80 | */ | 83 | */ |