diff options
author | Andrew Jeffery <andrew@aj.id.au> | 2017-04-07 08:59:01 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2017-04-24 08:49:53 -0400 |
commit | 5ae4cb94b3133d00857bb2909dae779782db40cb (patch) | |
tree | db147b3a31d25174bc443168db106511b9a4b5ab | |
parent | 9d7163f5167fa60e71071ab6dcb60da0f7230beb (diff) |
gpio: aspeed: Add debounce support
Each GPIO in the Aspeed GPIO controller can choose one of four input
debounce states: to disable debouncing for an input, or select from one
of three programmable debounce timer values. Each GPIO in a
four-bank-set is assigned one bit in each of two debounce configuration
registers dedicated to the set, and selects a debounce state by
configuring the two bits to select one of the four options.
The limitation on debounce timer values is managed by mapping offsets
onto a configured timer value and keeping count of the number of users
a timer has. Timer values are configured on a first-come-first-served
basis.
A small twist in the hardware design is that the debounce configuration
register numbering is reversed with respect to the binary representation
of the debounce timer of interest (i.e. debounce register 1 represents
bit 1, and debounce register 2 represents bit 0 of the timer numbering).
Tested on an AST2500EVB with additional inspection under QEMU's
romulus-bmc machine.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/gpio/gpio-aspeed.c | 281 |
1 files changed, 276 insertions, 5 deletions
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index fb16cc771c0d..3327a48df862 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c | |||
@@ -9,14 +9,18 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/module.h> | 12 | #include <asm/div64.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/clk.h> |
14 | #include <linux/gpio/driver.h> | ||
15 | #include <linux/hashtable.h> | ||
14 | #include <linux/init.h> | 16 | #include <linux/init.h> |
15 | #include <linux/io.h> | 17 | #include <linux/io.h> |
16 | #include <linux/spinlock.h> | 18 | #include <linux/kernel.h> |
17 | #include <linux/platform_device.h> | 19 | #include <linux/module.h> |
18 | #include <linux/gpio/driver.h> | ||
19 | #include <linux/pinctrl/consumer.h> | 20 | #include <linux/pinctrl/consumer.h> |
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/string.h> | ||
20 | 24 | ||
21 | struct aspeed_bank_props { | 25 | struct aspeed_bank_props { |
22 | unsigned int bank; | 26 | unsigned int bank; |
@@ -29,59 +33,85 @@ struct aspeed_gpio_config { | |||
29 | const struct aspeed_bank_props *props; | 33 | const struct aspeed_bank_props *props; |
30 | }; | 34 | }; |
31 | 35 | ||
36 | /* | ||
37 | * @offset_timer: Maps an offset to an @timer_users index, or zero if disabled | ||
38 | * @timer_users: Tracks the number of users for each timer | ||
39 | * | ||
40 | * The @timer_users has four elements but the first element is unused. This is | ||
41 | * to simplify accounting and indexing, as a zero value in @offset_timer | ||
42 | * represents disabled debouncing for the GPIO. Any other value for an element | ||
43 | * of @offset_timer is used as an index into @timer_users. This behaviour of | ||
44 | * the zero value aligns with the behaviour of zero built from the timer | ||
45 | * configuration registers (i.e. debouncing is disabled). | ||
46 | */ | ||
32 | struct aspeed_gpio { | 47 | struct aspeed_gpio { |
33 | struct gpio_chip chip; | 48 | struct gpio_chip chip; |
34 | spinlock_t lock; | 49 | spinlock_t lock; |
35 | void __iomem *base; | 50 | void __iomem *base; |
36 | int irq; | 51 | int irq; |
37 | const struct aspeed_gpio_config *config; | 52 | const struct aspeed_gpio_config *config; |
53 | |||
54 | u8 *offset_timer; | ||
55 | unsigned int timer_users[4]; | ||
56 | struct clk *clk; | ||
38 | }; | 57 | }; |
39 | 58 | ||
40 | struct aspeed_gpio_bank { | 59 | struct aspeed_gpio_bank { |
41 | uint16_t val_regs; | 60 | uint16_t val_regs; |
42 | uint16_t irq_regs; | 61 | uint16_t irq_regs; |
62 | uint16_t debounce_regs; | ||
43 | const char names[4][3]; | 63 | const char names[4][3]; |
44 | }; | 64 | }; |
45 | 65 | ||
66 | static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 }; | ||
67 | |||
46 | static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { | 68 | static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { |
47 | { | 69 | { |
48 | .val_regs = 0x0000, | 70 | .val_regs = 0x0000, |
49 | .irq_regs = 0x0008, | 71 | .irq_regs = 0x0008, |
72 | .debounce_regs = 0x0040, | ||
50 | .names = { "A", "B", "C", "D" }, | 73 | .names = { "A", "B", "C", "D" }, |
51 | }, | 74 | }, |
52 | { | 75 | { |
53 | .val_regs = 0x0020, | 76 | .val_regs = 0x0020, |
54 | .irq_regs = 0x0028, | 77 | .irq_regs = 0x0028, |
78 | .debounce_regs = 0x0048, | ||
55 | .names = { "E", "F", "G", "H" }, | 79 | .names = { "E", "F", "G", "H" }, |
56 | }, | 80 | }, |
57 | { | 81 | { |
58 | .val_regs = 0x0070, | 82 | .val_regs = 0x0070, |
59 | .irq_regs = 0x0098, | 83 | .irq_regs = 0x0098, |
84 | .debounce_regs = 0x00b0, | ||
60 | .names = { "I", "J", "K", "L" }, | 85 | .names = { "I", "J", "K", "L" }, |
61 | }, | 86 | }, |
62 | { | 87 | { |
63 | .val_regs = 0x0078, | 88 | .val_regs = 0x0078, |
64 | .irq_regs = 0x00e8, | 89 | .irq_regs = 0x00e8, |
90 | .debounce_regs = 0x0100, | ||
65 | .names = { "M", "N", "O", "P" }, | 91 | .names = { "M", "N", "O", "P" }, |
66 | }, | 92 | }, |
67 | { | 93 | { |
68 | .val_regs = 0x0080, | 94 | .val_regs = 0x0080, |
69 | .irq_regs = 0x0118, | 95 | .irq_regs = 0x0118, |
96 | .debounce_regs = 0x0130, | ||
70 | .names = { "Q", "R", "S", "T" }, | 97 | .names = { "Q", "R", "S", "T" }, |
71 | }, | 98 | }, |
72 | { | 99 | { |
73 | .val_regs = 0x0088, | 100 | .val_regs = 0x0088, |
74 | .irq_regs = 0x0148, | 101 | .irq_regs = 0x0148, |
102 | .debounce_regs = 0x0160, | ||
75 | .names = { "U", "V", "W", "X" }, | 103 | .names = { "U", "V", "W", "X" }, |
76 | }, | 104 | }, |
77 | { | 105 | { |
78 | .val_regs = 0x01E0, | 106 | .val_regs = 0x01E0, |
79 | .irq_regs = 0x0178, | 107 | .irq_regs = 0x0178, |
108 | .debounce_regs = 0x0190, | ||
80 | .names = { "Y", "Z", "AA", "AB" }, | 109 | .names = { "Y", "Z", "AA", "AB" }, |
81 | }, | 110 | }, |
82 | { | 111 | { |
83 | .val_regs = 0x01E8, | 112 | .val_regs = 0x01E8, |
84 | .irq_regs = 0x01A8, | 113 | .irq_regs = 0x01A8, |
114 | .debounce_regs = 0x01c0, | ||
85 | .names = { "AC", "", "", "" }, | 115 | .names = { "AC", "", "", "" }, |
86 | }, | 116 | }, |
87 | }; | 117 | }; |
@@ -99,6 +129,13 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { | |||
99 | #define GPIO_IRQ_TYPE2 0x0c | 129 | #define GPIO_IRQ_TYPE2 0x0c |
100 | #define GPIO_IRQ_STATUS 0x10 | 130 | #define GPIO_IRQ_STATUS 0x10 |
101 | 131 | ||
132 | #define GPIO_DEBOUNCE_SEL1 0x00 | ||
133 | #define GPIO_DEBOUNCE_SEL2 0x04 | ||
134 | |||
135 | #define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o)) | ||
136 | #define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1) | ||
137 | #define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0) | ||
138 | |||
102 | static const struct aspeed_gpio_bank *to_bank(unsigned int offset) | 139 | static const struct aspeed_gpio_bank *to_bank(unsigned int offset) |
103 | { | 140 | { |
104 | unsigned int bank = GPIO_BANK(offset); | 141 | unsigned int bank = GPIO_BANK(offset); |
@@ -144,6 +181,7 @@ static inline bool have_input(struct aspeed_gpio *gpio, unsigned int offset) | |||
144 | } | 181 | } |
145 | 182 | ||
146 | #define have_irq(g, o) have_input((g), (o)) | 183 | #define have_irq(g, o) have_input((g), (o)) |
184 | #define have_debounce(g, o) have_input((g), (o)) | ||
147 | 185 | ||
148 | static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset) | 186 | static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset) |
149 | { | 187 | { |
@@ -506,6 +544,227 @@ static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset) | |||
506 | pinctrl_free_gpio(chip->base + offset); | 544 | pinctrl_free_gpio(chip->base + offset); |
507 | } | 545 | } |
508 | 546 | ||
547 | static inline void __iomem *bank_debounce_reg(struct aspeed_gpio *gpio, | ||
548 | const struct aspeed_gpio_bank *bank, | ||
549 | unsigned int reg) | ||
550 | { | ||
551 | return gpio->base + bank->debounce_regs + reg; | ||
552 | } | ||
553 | |||
554 | static int usecs_to_cycles(struct aspeed_gpio *gpio, unsigned long usecs, | ||
555 | u32 *cycles) | ||
556 | { | ||
557 | u64 rate; | ||
558 | u64 n; | ||
559 | u32 r; | ||
560 | |||
561 | rate = clk_get_rate(gpio->clk); | ||
562 | if (!rate) | ||
563 | return -ENOTSUPP; | ||
564 | |||
565 | n = rate * usecs; | ||
566 | r = do_div(n, 1000000); | ||
567 | |||
568 | if (n >= U32_MAX) | ||
569 | return -ERANGE; | ||
570 | |||
571 | /* At least as long as the requested time */ | ||
572 | *cycles = n + (!!r); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | /* Call under gpio->lock */ | ||
578 | static int register_allocated_timer(struct aspeed_gpio *gpio, | ||
579 | unsigned int offset, unsigned int timer) | ||
580 | { | ||
581 | if (WARN(gpio->offset_timer[offset] != 0, | ||
582 | "Offset %d already allocated timer %d\n", | ||
583 | offset, gpio->offset_timer[offset])) | ||
584 | return -EINVAL; | ||
585 | |||
586 | if (WARN(gpio->timer_users[timer] == UINT_MAX, | ||
587 | "Timer user count would overflow\n")) | ||
588 | return -EPERM; | ||
589 | |||
590 | gpio->offset_timer[offset] = timer; | ||
591 | gpio->timer_users[timer]++; | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | /* Call under gpio->lock */ | ||
597 | static int unregister_allocated_timer(struct aspeed_gpio *gpio, | ||
598 | unsigned int offset) | ||
599 | { | ||
600 | if (WARN(gpio->offset_timer[offset] == 0, | ||
601 | "No timer allocated to offset %d\n", offset)) | ||
602 | return -EINVAL; | ||
603 | |||
604 | if (WARN(gpio->timer_users[gpio->offset_timer[offset]] == 0, | ||
605 | "No users recorded for timer %d\n", | ||
606 | gpio->offset_timer[offset])) | ||
607 | return -EINVAL; | ||
608 | |||
609 | gpio->timer_users[gpio->offset_timer[offset]]--; | ||
610 | gpio->offset_timer[offset] = 0; | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /* Call under gpio->lock */ | ||
616 | static inline bool timer_allocation_registered(struct aspeed_gpio *gpio, | ||
617 | unsigned int offset) | ||
618 | { | ||
619 | return gpio->offset_timer[offset] > 0; | ||
620 | } | ||
621 | |||
622 | /* Call under gpio->lock */ | ||
623 | static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset, | ||
624 | unsigned int timer) | ||
625 | { | ||
626 | const struct aspeed_gpio_bank *bank = to_bank(offset); | ||
627 | const u32 mask = GPIO_BIT(offset); | ||
628 | void __iomem *addr; | ||
629 | u32 val; | ||
630 | |||
631 | addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL1); | ||
632 | val = ioread32(addr); | ||
633 | iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr); | ||
634 | |||
635 | addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL2); | ||
636 | val = ioread32(addr); | ||
637 | iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr); | ||
638 | } | ||
639 | |||
640 | static int enable_debounce(struct gpio_chip *chip, unsigned int offset, | ||
641 | unsigned long usecs) | ||
642 | { | ||
643 | struct aspeed_gpio *gpio = gpiochip_get_data(chip); | ||
644 | u32 requested_cycles; | ||
645 | unsigned long flags; | ||
646 | int rc; | ||
647 | int i; | ||
648 | |||
649 | rc = usecs_to_cycles(gpio, usecs, &requested_cycles); | ||
650 | if (rc < 0) { | ||
651 | dev_warn(chip->parent, "Failed to convert %luus to cycles at %luHz: %d\n", | ||
652 | usecs, clk_get_rate(gpio->clk), rc); | ||
653 | return rc; | ||
654 | } | ||
655 | |||
656 | spin_lock_irqsave(&gpio->lock, flags); | ||
657 | |||
658 | if (timer_allocation_registered(gpio, offset)) { | ||
659 | rc = unregister_allocated_timer(gpio, offset); | ||
660 | if (rc < 0) | ||
661 | goto out; | ||
662 | } | ||
663 | |||
664 | /* Try to find a timer already configured for the debounce period */ | ||
665 | for (i = 1; i < ARRAY_SIZE(debounce_timers); i++) { | ||
666 | u32 cycles; | ||
667 | |||
668 | cycles = ioread32(gpio->base + debounce_timers[i]); | ||
669 | if (requested_cycles == cycles) | ||
670 | break; | ||
671 | } | ||
672 | |||
673 | if (i == ARRAY_SIZE(debounce_timers)) { | ||
674 | int j; | ||
675 | |||
676 | /* | ||
677 | * As there are no timers configured for the requested debounce | ||
678 | * period, find an unused timer instead | ||
679 | */ | ||
680 | for (j = 1; j < ARRAY_SIZE(gpio->timer_users); j++) { | ||
681 | if (gpio->timer_users[j] == 0) | ||
682 | break; | ||
683 | } | ||
684 | |||
685 | if (j == ARRAY_SIZE(gpio->timer_users)) { | ||
686 | dev_warn(chip->parent, | ||
687 | "Debounce timers exhausted, cannot debounce for period %luus\n", | ||
688 | usecs); | ||
689 | |||
690 | rc = -EPERM; | ||
691 | |||
692 | /* | ||
693 | * We already adjusted the accounting to remove @offset | ||
694 | * as a user of its previous timer, so also configure | ||
695 | * the hardware so @offset has timers disabled for | ||
696 | * consistency. | ||
697 | */ | ||
698 | configure_timer(gpio, offset, 0); | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | i = j; | ||
703 | |||
704 | iowrite32(requested_cycles, gpio->base + debounce_timers[i]); | ||
705 | } | ||
706 | |||
707 | if (WARN(i == 0, "Cannot register index of disabled timer\n")) { | ||
708 | rc = -EINVAL; | ||
709 | goto out; | ||
710 | } | ||
711 | |||
712 | register_allocated_timer(gpio, offset, i); | ||
713 | configure_timer(gpio, offset, i); | ||
714 | |||
715 | out: | ||
716 | spin_unlock_irqrestore(&gpio->lock, flags); | ||
717 | |||
718 | return rc; | ||
719 | } | ||
720 | |||
721 | static int disable_debounce(struct gpio_chip *chip, unsigned int offset) | ||
722 | { | ||
723 | struct aspeed_gpio *gpio = gpiochip_get_data(chip); | ||
724 | unsigned long flags; | ||
725 | int rc; | ||
726 | |||
727 | spin_lock_irqsave(&gpio->lock, flags); | ||
728 | |||
729 | rc = unregister_allocated_timer(gpio, offset); | ||
730 | if (!rc) | ||
731 | configure_timer(gpio, offset, 0); | ||
732 | |||
733 | spin_unlock_irqrestore(&gpio->lock, flags); | ||
734 | |||
735 | return rc; | ||
736 | } | ||
737 | |||
738 | static int set_debounce(struct gpio_chip *chip, unsigned int offset, | ||
739 | unsigned long usecs) | ||
740 | { | ||
741 | struct aspeed_gpio *gpio = gpiochip_get_data(chip); | ||
742 | |||
743 | if (!have_debounce(gpio, offset)) | ||
744 | return -ENOTSUPP; | ||
745 | |||
746 | if (usecs) | ||
747 | return enable_debounce(chip, offset, usecs); | ||
748 | |||
749 | return disable_debounce(chip, offset); | ||
750 | } | ||
751 | |||
752 | static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, | ||
753 | unsigned long config) | ||
754 | { | ||
755 | unsigned long param = pinconf_to_config_param(config); | ||
756 | u32 arg = pinconf_to_config_argument(config); | ||
757 | |||
758 | if (param == PIN_CONFIG_INPUT_DEBOUNCE) | ||
759 | return set_debounce(chip, offset, arg); | ||
760 | else if (param == PIN_CONFIG_BIAS_DISABLE || | ||
761 | param == PIN_CONFIG_BIAS_PULL_DOWN || | ||
762 | param == PIN_CONFIG_DRIVE_STRENGTH) | ||
763 | return pinctrl_gpio_set_config(offset, config); | ||
764 | |||
765 | return -ENOTSUPP; | ||
766 | } | ||
767 | |||
509 | /* | 768 | /* |
510 | * Any banks not specified in a struct aspeed_bank_props array are assumed to | 769 | * Any banks not specified in a struct aspeed_bank_props array are assumed to |
511 | * have the properties: | 770 | * have the properties: |
@@ -565,8 +824,16 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) | |||
565 | if (!gpio_id) | 824 | if (!gpio_id) |
566 | return -EINVAL; | 825 | return -EINVAL; |
567 | 826 | ||
827 | gpio->clk = of_clk_get(pdev->dev.of_node, 0); | ||
828 | if (IS_ERR(gpio->clk)) { | ||
829 | dev_warn(&pdev->dev, | ||
830 | "No HPLL clock phandle provided, debouncing disabled\n"); | ||
831 | gpio->clk = NULL; | ||
832 | } | ||
833 | |||
568 | gpio->config = gpio_id->data; | 834 | gpio->config = gpio_id->data; |
569 | 835 | ||
836 | gpio->chip.parent = &pdev->dev; | ||
570 | gpio->chip.ngpio = gpio->config->nr_gpios; | 837 | gpio->chip.ngpio = gpio->config->nr_gpios; |
571 | gpio->chip.parent = &pdev->dev; | 838 | gpio->chip.parent = &pdev->dev; |
572 | gpio->chip.direction_input = aspeed_gpio_dir_in; | 839 | gpio->chip.direction_input = aspeed_gpio_dir_in; |
@@ -576,6 +843,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) | |||
576 | gpio->chip.free = aspeed_gpio_free; | 843 | gpio->chip.free = aspeed_gpio_free; |
577 | gpio->chip.get = aspeed_gpio_get; | 844 | gpio->chip.get = aspeed_gpio_get; |
578 | gpio->chip.set = aspeed_gpio_set; | 845 | gpio->chip.set = aspeed_gpio_set; |
846 | gpio->chip.set_config = aspeed_gpio_set_config; | ||
579 | gpio->chip.label = dev_name(&pdev->dev); | 847 | gpio->chip.label = dev_name(&pdev->dev); |
580 | gpio->chip.base = -1; | 848 | gpio->chip.base = -1; |
581 | gpio->chip.irq_need_valid_mask = true; | 849 | gpio->chip.irq_need_valid_mask = true; |
@@ -584,6 +852,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) | |||
584 | if (rc < 0) | 852 | if (rc < 0) |
585 | return rc; | 853 | return rc; |
586 | 854 | ||
855 | gpio->offset_timer = | ||
856 | devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL); | ||
857 | |||
587 | return aspeed_gpio_setup_irqs(gpio, pdev); | 858 | return aspeed_gpio_setup_irqs(gpio, pdev); |
588 | } | 859 | } |
589 | 860 | ||