aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bcma
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2013-12-12 07:46:03 -0500
committerRalf Baechle <ralf@linux-mips.org>2014-01-23 07:02:37 -0500
commit2997609eb4c9e296d9129d45ea1f2ec99cc7c4da (patch)
tree247988896cfc281f218bf407338ec329d76839ac /drivers/bcma
parent87c99203fea897fbdd84b681ad9fced2517dcf98 (diff)
bcma: gpio: add own IRQ domain
Input GPIO changes can generate interrupts, but we need kind of ACK for them by changing IRQ polarity. This is required to stop hardware from keep generating interrupts and generate another one on the next GPIO state change. This code allows using GPIOs with standard interrupts and add for example GPIO buttons support. Signed-off-by: Rafał Miłecki <zajec5@gmail.com> Acked-by: Hauke Mehrtens <hauke@hauke-m.de> Signed-off-by: John Crispin <blogic@openwrt.org> Patchwork: http://patchwork.linux-mips.org/patch/6216/
Diffstat (limited to 'drivers/bcma')
-rw-r--r--drivers/bcma/Kconfig1
-rw-r--r--drivers/bcma/driver_gpio.c134
2 files changed, 133 insertions, 2 deletions
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index 7c081b38ef3e..0ee48be23837 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -75,6 +75,7 @@ config BCMA_DRIVER_GMAC_CMN
75config BCMA_DRIVER_GPIO 75config BCMA_DRIVER_GPIO
76 bool "BCMA GPIO driver" 76 bool "BCMA GPIO driver"
77 depends on BCMA && GPIOLIB 77 depends on BCMA && GPIOLIB
78 select IRQ_DOMAIN if BCMA_HOST_SOC
78 help 79 help
79 Driver to provide access to the GPIO pins of the bcma bus. 80 Driver to provide access to the GPIO pins of the bcma bus.
80 81
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index 45f0996a3752..9d87b97341a3 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -9,6 +9,9 @@
9 */ 9 */
10 10
11#include <linux/gpio.h> 11#include <linux/gpio.h>
12#include <linux/irq.h>
13#include <linux/interrupt.h>
14#include <linux/irqdomain.h>
12#include <linux/export.h> 15#include <linux/export.h>
13#include <linux/bcma/bcma.h> 16#include <linux/bcma/bcma.h>
14 17
@@ -73,19 +76,133 @@ static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
73 bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); 76 bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
74} 77}
75 78
79#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
76static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) 80static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
77{ 81{
78 struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); 82 struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
79 83
80 if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) 84 if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
81 return bcma_core_irq(cc->core); 85 return irq_find_mapping(cc->irq_domain, gpio);
82 else 86 else
83 return -EINVAL; 87 return -EINVAL;
84} 88}
85 89
90static void bcma_gpio_irq_unmask(struct irq_data *d)
91{
92 struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d);
93 int gpio = irqd_to_hwirq(d);
94
95 bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio));
96}
97
98static void bcma_gpio_irq_mask(struct irq_data *d)
99{
100 struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d);
101 int gpio = irqd_to_hwirq(d);
102
103 bcma_chipco_gpio_intmask(cc, BIT(gpio), 0);
104}
105
106static struct irq_chip bcma_gpio_irq_chip = {
107 .name = "BCMA-GPIO",
108 .irq_mask = bcma_gpio_irq_mask,
109 .irq_unmask = bcma_gpio_irq_unmask,
110};
111
112static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id)
113{
114 struct bcma_drv_cc *cc = dev_id;
115 u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN);
116 u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ);
117 u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL);
118 u32 irqs = (val ^ pol) & mask;
119 int gpio;
120
121 if (!irqs)
122 return IRQ_NONE;
123
124 for_each_set_bit(gpio, (unsigned long *)&irqs, cc->gpio.ngpio)
125 generic_handle_irq(bcma_gpio_to_irq(&cc->gpio, gpio));
126 bcma_chipco_gpio_polarity(cc, irqs, val & irqs);
127
128 return IRQ_HANDLED;
129}
130
131static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc)
132{
133 struct gpio_chip *chip = &cc->gpio;
134 int gpio, hwirq, err;
135
136 if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC)
137 return 0;
138
139 cc->irq_domain = irq_domain_add_linear(NULL, chip->ngpio,
140 &irq_domain_simple_ops, cc);
141 if (!cc->irq_domain) {
142 err = -ENODEV;
143 goto err_irq_domain;
144 }
145 for (gpio = 0; gpio < chip->ngpio; gpio++) {
146 int irq = irq_create_mapping(cc->irq_domain, gpio);
147
148 irq_set_chip_data(irq, cc);
149 irq_set_chip_and_handler(irq, &bcma_gpio_irq_chip,
150 handle_simple_irq);
151 }
152
153 hwirq = bcma_core_irq(cc->core);
154 err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio",
155 cc);
156 if (err)
157 goto err_req_irq;
158
159 bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO);
160
161 return 0;
162
163err_req_irq:
164 for (gpio = 0; gpio < chip->ngpio; gpio++) {
165 int irq = irq_find_mapping(cc->irq_domain, gpio);
166
167 irq_dispose_mapping(irq);
168 }
169 irq_domain_remove(cc->irq_domain);
170err_irq_domain:
171 return err;
172}
173
174static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc)
175{
176 struct gpio_chip *chip = &cc->gpio;
177 int gpio;
178
179 if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC)
180 return;
181
182 bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO);
183 free_irq(bcma_core_irq(cc->core), cc);
184 for (gpio = 0; gpio < chip->ngpio; gpio++) {
185 int irq = irq_find_mapping(cc->irq_domain, gpio);
186
187 irq_dispose_mapping(irq);
188 }
189 irq_domain_remove(cc->irq_domain);
190}
191#else
192static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc)
193{
194 return 0;
195}
196
197static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc)
198{
199}
200#endif
201
86int bcma_gpio_init(struct bcma_drv_cc *cc) 202int bcma_gpio_init(struct bcma_drv_cc *cc)
87{ 203{
88 struct gpio_chip *chip = &cc->gpio; 204 struct gpio_chip *chip = &cc->gpio;
205 int err;
89 206
90 chip->label = "bcma_gpio"; 207 chip->label = "bcma_gpio";
91 chip->owner = THIS_MODULE; 208 chip->owner = THIS_MODULE;
@@ -95,7 +212,9 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
95 chip->set = bcma_gpio_set_value; 212 chip->set = bcma_gpio_set_value;
96 chip->direction_input = bcma_gpio_direction_input; 213 chip->direction_input = bcma_gpio_direction_input;
97 chip->direction_output = bcma_gpio_direction_output; 214 chip->direction_output = bcma_gpio_direction_output;
215#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
98 chip->to_irq = bcma_gpio_to_irq; 216 chip->to_irq = bcma_gpio_to_irq;
217#endif
99 chip->ngpio = 16; 218 chip->ngpio = 16;
100 /* There is just one SoC in one device and its GPIO addresses should be 219 /* There is just one SoC in one device and its GPIO addresses should be
101 * deterministic to address them more easily. The other buses could get 220 * deterministic to address them more easily. The other buses could get
@@ -105,10 +224,21 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
105 else 224 else
106 chip->base = -1; 225 chip->base = -1;
107 226
108 return gpiochip_add(chip); 227 err = bcma_gpio_irq_domain_init(cc);
228 if (err)
229 return err;
230
231 err = gpiochip_add(chip);
232 if (err) {
233 bcma_gpio_irq_domain_exit(cc);
234 return err;
235 }
236
237 return 0;
109} 238}
110 239
111int bcma_gpio_unregister(struct bcma_drv_cc *cc) 240int bcma_gpio_unregister(struct bcma_drv_cc *cc)
112{ 241{
242 bcma_gpio_irq_domain_exit(cc);
113 return gpiochip_remove(&cc->gpio); 243 return gpiochip_remove(&cc->gpio);
114} 244}