brcm47xx: add new led and button support
[openwrt.git] / target / linux / brcm47xx / patches-3.10 / 100-bcma-gpio-add-own-IRQ-domain.patch
1 From 37dc57c2400b2998868a5c103756f36b8dc53a17 Mon Sep 17 00:00:00 2001
2 From: Rafa? Mi?ecki <zajec5@gmail.com>
3 Date: Thu, 12 Dec 2013 13:46:03 +0100
4 Subject: [PATCH 088/110] bcma: gpio: add own IRQ domain
5
6 Input GPIO changes can generate interrupts, but we need kind of ACK for
7 them by changing IRQ polarity. This is required to stop hardware from
8 keep generating interrupts and generate another one on the next GPIO
9 state change.
10 This code allows using GPIOs with standard interrupts and add for
11 example GPIO buttons support.
12
13 Signed-off-by: Rafa? Mi?ecki <zajec5@gmail.com>
14 Acked-by: Hauke Mehrtens <hauke@hauke-m.de>
15 Acked-by: John Crispin <blogic@openwrt.org>
16 Patchwork: http://patchwork.linux-mips.org/patch/6216/
17 ---
18  drivers/bcma/Kconfig                        |    1 +
19  drivers/bcma/driver_gpio.c                  |  135 ++++++++++++++++++++++++++-
20  include/linux/bcma/bcma_driver_chipcommon.h |    1 +
21  3 files changed, 134 insertions(+), 3 deletions(-)
22
23 --- a/drivers/bcma/Kconfig
24 +++ b/drivers/bcma/Kconfig
25 @@ -76,6 +76,7 @@ config BCMA_DRIVER_GMAC_CMN
26  config BCMA_DRIVER_GPIO
27         bool "BCMA GPIO driver"
28         depends on BCMA && GPIOLIB
29 +       select IRQ_DOMAIN if BCMA_HOST_SOC
30         help
31           Driver to provide access to the GPIO pins of the bcma bus.
32  
33 --- a/drivers/bcma/driver_gpio.c
34 +++ b/drivers/bcma/driver_gpio.c
35 @@ -9,6 +9,9 @@
36   */
37  
38  #include <linux/gpio.h>
39 +#include <linux/irq.h>
40 +#include <linux/interrupt.h>
41 +#include <linux/irqdomain.h>
42  #include <linux/export.h>
43  #include <linux/bcma/bcma.h>
44  
45 @@ -73,19 +76,133 @@ static void bcma_gpio_free(struct gpio_c
46         bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
47  }
48  
49 +#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
50  static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
51  {
52         struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
53  
54         if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
55 -               return bcma_core_irq(cc->core);
56 +               return irq_find_mapping(cc->irq_domain, gpio);
57         else
58                 return -EINVAL;
59  }
60  
61 +static void bcma_gpio_irq_unmask(struct irq_data *d)
62 +{
63 +       struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d);
64 +       int gpio = irqd_to_hwirq(d);
65 +
66 +       bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio));
67 +}
68 +
69 +static void bcma_gpio_irq_mask(struct irq_data *d)
70 +{
71 +       struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d);
72 +       int gpio = irqd_to_hwirq(d);
73 +
74 +       bcma_chipco_gpio_intmask(cc, BIT(gpio), 0);
75 +}
76 +
77 +static struct irq_chip bcma_gpio_irq_chip = {
78 +       .name           = "BCMA-GPIO",
79 +       .irq_mask       = bcma_gpio_irq_mask,
80 +       .irq_unmask     = bcma_gpio_irq_unmask,
81 +};
82 +
83 +static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id)
84 +{
85 +       struct bcma_drv_cc *cc = dev_id;
86 +       u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN);
87 +       u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ);
88 +       u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL);
89 +       u32 irqs = (val ^ pol) & mask;
90 +       int gpio;
91 +
92 +       if (!irqs)
93 +               return IRQ_NONE;
94 +
95 +       for_each_set_bit(gpio, (unsigned long *)&irqs, cc->gpio.ngpio)
96 +               generic_handle_irq(bcma_gpio_to_irq(&cc->gpio, gpio));
97 +       bcma_chipco_gpio_polarity(cc, irqs, val & irqs);
98 +
99 +       return IRQ_HANDLED;
100 +}
101 +
102 +static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc)
103 +{
104 +       struct gpio_chip *chip = &cc->gpio;
105 +       int gpio, hwirq, err;
106 +
107 +       if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC)
108 +               return 0;
109 +
110 +       cc->irq_domain = irq_domain_add_linear(NULL, chip->ngpio,
111 +                                              &irq_domain_simple_ops, cc);
112 +       if (!cc->irq_domain) {
113 +               err = -ENODEV;
114 +               goto err_irq_domain;
115 +       }
116 +       for (gpio = 0; gpio < chip->ngpio; gpio++) {
117 +               int irq = irq_create_mapping(cc->irq_domain, gpio);
118 +
119 +               irq_set_chip_data(irq, cc);
120 +               irq_set_chip_and_handler(irq, &bcma_gpio_irq_chip,
121 +                                        handle_simple_irq);
122 +       }
123 +
124 +       hwirq = bcma_core_irq(cc->core);
125 +       err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio",
126 +                         cc);
127 +       if (err)
128 +               goto err_req_irq;
129 +
130 +       bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO);
131 +
132 +       return 0;
133 +
134 +err_req_irq:
135 +       for (gpio = 0; gpio < chip->ngpio; gpio++) {
136 +               int irq = irq_find_mapping(cc->irq_domain, gpio);
137 +
138 +               irq_dispose_mapping(irq);
139 +       }
140 +       irq_domain_remove(cc->irq_domain);
141 +err_irq_domain:
142 +       return err;
143 +}
144 +
145 +static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc)
146 +{
147 +       struct gpio_chip *chip = &cc->gpio;
148 +       int gpio;
149 +
150 +       if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC)
151 +               return;
152 +
153 +       bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO);
154 +       free_irq(bcma_core_irq(cc->core), cc);
155 +       for (gpio = 0; gpio < chip->ngpio; gpio++) {
156 +               int irq = irq_find_mapping(cc->irq_domain, gpio);
157 +
158 +               irq_dispose_mapping(irq);
159 +       }
160 +       irq_domain_remove(cc->irq_domain);
161 +}
162 +#else
163 +static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc)
164 +{
165 +       return 0;
166 +}
167 +
168 +static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc)
169 +{
170 +}
171 +#endif
172 +
173  int bcma_gpio_init(struct bcma_drv_cc *cc)
174  {
175         struct gpio_chip *chip = &cc->gpio;
176 +       int err;
177  
178         chip->label             = "bcma_gpio";
179         chip->owner             = THIS_MODULE;
180 @@ -95,7 +212,8 @@ int bcma_gpio_init(struct bcma_drv_cc *c
181         chip->set               = bcma_gpio_set_value;
182         chip->direction_input   = bcma_gpio_direction_input;
183         chip->direction_output  = bcma_gpio_direction_output;
184 -       chip->to_irq            = bcma_gpio_to_irq;
185 +       if (IS_BUILTIN(CONFIG_BCMA_HOST_SOC))
186 +               chip->to_irq            = bcma_gpio_to_irq;
187         chip->ngpio             = 16;
188         /* There is just one SoC in one device and its GPIO addresses should be
189          * deterministic to address them more easily. The other buses could get
190 @@ -105,10 +223,21 @@ int bcma_gpio_init(struct bcma_drv_cc *c
191         else
192                 chip->base              = -1;
193  
194 -       return gpiochip_add(chip);
195 +       err = bcma_gpio_irq_domain_init(cc);
196 +       if (err)
197 +               return err;
198 +
199 +       err = gpiochip_add(chip);
200 +       if (err) {
201 +               bcma_gpio_irq_domain_exit(cc);
202 +               return err;
203 +       }
204 +
205 +       return 0;
206  }
207  
208  int bcma_gpio_unregister(struct bcma_drv_cc *cc)
209  {
210 +       bcma_gpio_irq_domain_exit(cc);
211         return gpiochip_remove(&cc->gpio);
212  }
213 --- a/include/linux/bcma/bcma_driver_chipcommon.h
214 +++ b/include/linux/bcma/bcma_driver_chipcommon.h
215 @@ -640,6 +640,7 @@ struct bcma_drv_cc {
216         spinlock_t gpio_lock;
217  #ifdef CONFIG_BCMA_DRIVER_GPIO
218         struct gpio_chip gpio;
219 +       struct irq_domain *irq_domain;
220  #endif
221  };
222