Commit 459773ae8dbbd480886d186181c6bc2e8556025f
Committed by
Linus Torvalds
1 parent
ead6db0843
Exists in
master
and in
7 other branches
gpio: adp5588-gpio: support interrupt controller
Implement irq_chip functionality on ADP5588/5587 GPIO expanders. Only level sensitive interrupts are supported. Interrupts provided by this irq_chip must be requested using request_threaded_irq(). Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 3 changed files with 278 additions and 21 deletions Side-by-side Diff
drivers/gpio/Kconfig
... | ... | @@ -272,6 +272,13 @@ |
272 | 272 | To compile this driver as a module, choose M here: the module will be |
273 | 273 | called adp5588-gpio. |
274 | 274 | |
275 | +config GPIO_ADP5588_IRQ | |
276 | + bool "Interrupt controller support for ADP5588" | |
277 | + depends on GPIO_ADP5588=y | |
278 | + help | |
279 | + Say yes here to enable the adp5588 to be used as an interrupt | |
280 | + controller. It requires the driver to be built in the kernel. | |
281 | + | |
275 | 282 | comment "PCI GPIO expanders:" |
276 | 283 | |
277 | 284 | config GPIO_CS5535 |
drivers/gpio/adp5588-gpio.c
1 | 1 | /* |
2 | 2 | * GPIO Chip driver for Analog Devices |
3 | - * ADP5588 I/O Expander and QWERTY Keypad Controller | |
3 | + * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller | |
4 | 4 | * |
5 | - * Copyright 2009 Analog Devices Inc. | |
5 | + * Copyright 2009-2010 Analog Devices Inc. | |
6 | 6 | * |
7 | 7 | * Licensed under the GPL-2 or later. |
8 | 8 | */ |
9 | 9 | |
10 | 10 | |
11 | 11 | |
12 | 12 | |
13 | 13 | |
... | ... | @@ -13,21 +13,34 @@ |
13 | 13 | #include <linux/init.h> |
14 | 14 | #include <linux/i2c.h> |
15 | 15 | #include <linux/gpio.h> |
16 | +#include <linux/interrupt.h> | |
17 | +#include <linux/irq.h> | |
16 | 18 | |
17 | 19 | #include <linux/i2c/adp5588.h> |
18 | 20 | |
19 | -#define DRV_NAME "adp5588-gpio" | |
20 | -#define MAXGPIO 18 | |
21 | -#define ADP_BANK(offs) ((offs) >> 3) | |
22 | -#define ADP_BIT(offs) (1u << ((offs) & 0x7)) | |
21 | +#define DRV_NAME "adp5588-gpio" | |
23 | 22 | |
23 | +/* | |
24 | + * Early pre 4.0 Silicon required to delay readout by at least 25ms, | |
25 | + * since the Event Counter Register updated 25ms after the interrupt | |
26 | + * asserted. | |
27 | + */ | |
28 | +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) | |
29 | + | |
24 | 30 | struct adp5588_gpio { |
25 | 31 | struct i2c_client *client; |
26 | 32 | struct gpio_chip gpio_chip; |
27 | 33 | struct mutex lock; /* protect cached dir, dat_out */ |
34 | + /* protect serialized access to the interrupt controller bus */ | |
35 | + struct mutex irq_lock; | |
28 | 36 | unsigned gpio_start; |
37 | + unsigned irq_base; | |
29 | 38 | uint8_t dat_out[3]; |
30 | 39 | uint8_t dir[3]; |
40 | + uint8_t int_lvl[3]; | |
41 | + uint8_t int_en[3]; | |
42 | + uint8_t irq_mask[3]; | |
43 | + uint8_t irq_stat[3]; | |
31 | 44 | }; |
32 | 45 | |
33 | 46 | static int adp5588_gpio_read(struct i2c_client *client, u8 reg) |
... | ... | @@ -55,8 +68,8 @@ |
55 | 68 | struct adp5588_gpio *dev = |
56 | 69 | container_of(chip, struct adp5588_gpio, gpio_chip); |
57 | 70 | |
58 | - return !!(adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + ADP_BANK(off)) | |
59 | - & ADP_BIT(off)); | |
71 | + return !!(adp5588_gpio_read(dev->client, | |
72 | + GPIO_DAT_STAT1 + ADP5588_BANK(off)) & ADP5588_BIT(off)); | |
60 | 73 | } |
61 | 74 | |
62 | 75 | static void adp5588_gpio_set_value(struct gpio_chip *chip, |
... | ... | @@ -66,8 +79,8 @@ |
66 | 79 | struct adp5588_gpio *dev = |
67 | 80 | container_of(chip, struct adp5588_gpio, gpio_chip); |
68 | 81 | |
69 | - bank = ADP_BANK(off); | |
70 | - bit = ADP_BIT(off); | |
82 | + bank = ADP5588_BANK(off); | |
83 | + bit = ADP5588_BIT(off); | |
71 | 84 | |
72 | 85 | mutex_lock(&dev->lock); |
73 | 86 | if (val) |
74 | 87 | |
... | ... | @@ -87,10 +100,10 @@ |
87 | 100 | struct adp5588_gpio *dev = |
88 | 101 | container_of(chip, struct adp5588_gpio, gpio_chip); |
89 | 102 | |
90 | - bank = ADP_BANK(off); | |
103 | + bank = ADP5588_BANK(off); | |
91 | 104 | |
92 | 105 | mutex_lock(&dev->lock); |
93 | - dev->dir[bank] &= ~ADP_BIT(off); | |
106 | + dev->dir[bank] &= ~ADP5588_BIT(off); | |
94 | 107 | ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); |
95 | 108 | mutex_unlock(&dev->lock); |
96 | 109 | |
... | ... | @@ -105,8 +118,8 @@ |
105 | 118 | struct adp5588_gpio *dev = |
106 | 119 | container_of(chip, struct adp5588_gpio, gpio_chip); |
107 | 120 | |
108 | - bank = ADP_BANK(off); | |
109 | - bit = ADP_BIT(off); | |
121 | + bank = ADP5588_BANK(off); | |
122 | + bit = ADP5588_BIT(off); | |
110 | 123 | |
111 | 124 | mutex_lock(&dev->lock); |
112 | 125 | dev->dir[bank] |= bit; |
... | ... | @@ -125,6 +138,213 @@ |
125 | 138 | return ret; |
126 | 139 | } |
127 | 140 | |
141 | +#ifdef CONFIG_GPIO_ADP5588_IRQ | |
142 | +static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) | |
143 | +{ | |
144 | + struct adp5588_gpio *dev = | |
145 | + container_of(chip, struct adp5588_gpio, gpio_chip); | |
146 | + return dev->irq_base + off; | |
147 | +} | |
148 | + | |
149 | +static void adp5588_irq_bus_lock(unsigned int irq) | |
150 | +{ | |
151 | + struct adp5588_gpio *dev = get_irq_chip_data(irq); | |
152 | + mutex_lock(&dev->irq_lock); | |
153 | +} | |
154 | + | |
155 | + /* | |
156 | + * genirq core code can issue chip->mask/unmask from atomic context. | |
157 | + * This doesn't work for slow busses where an access needs to sleep. | |
158 | + * bus_sync_unlock() is therefore called outside the atomic context, | |
159 | + * syncs the current irq mask state with the slow external controller | |
160 | + * and unlocks the bus. | |
161 | + */ | |
162 | + | |
163 | +static void adp5588_irq_bus_sync_unlock(unsigned int irq) | |
164 | +{ | |
165 | + struct adp5588_gpio *dev = get_irq_chip_data(irq); | |
166 | + int i; | |
167 | + | |
168 | + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) | |
169 | + if (dev->int_en[i] ^ dev->irq_mask[i]) { | |
170 | + dev->int_en[i] = dev->irq_mask[i]; | |
171 | + adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, | |
172 | + dev->int_en[i]); | |
173 | + } | |
174 | + | |
175 | + mutex_unlock(&dev->irq_lock); | |
176 | +} | |
177 | + | |
178 | +static void adp5588_irq_mask(unsigned int irq) | |
179 | +{ | |
180 | + struct adp5588_gpio *dev = get_irq_chip_data(irq); | |
181 | + unsigned gpio = irq - dev->irq_base; | |
182 | + | |
183 | + dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); | |
184 | +} | |
185 | + | |
186 | +static void adp5588_irq_unmask(unsigned int irq) | |
187 | +{ | |
188 | + struct adp5588_gpio *dev = get_irq_chip_data(irq); | |
189 | + unsigned gpio = irq - dev->irq_base; | |
190 | + | |
191 | + dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); | |
192 | +} | |
193 | + | |
194 | +static int adp5588_irq_set_type(unsigned int irq, unsigned int type) | |
195 | +{ | |
196 | + struct adp5588_gpio *dev = get_irq_chip_data(irq); | |
197 | + uint16_t gpio = irq - dev->irq_base; | |
198 | + unsigned bank, bit; | |
199 | + | |
200 | + if ((type & IRQ_TYPE_EDGE_BOTH)) { | |
201 | + dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", | |
202 | + irq, type); | |
203 | + return -EINVAL; | |
204 | + } | |
205 | + | |
206 | + bank = ADP5588_BANK(gpio); | |
207 | + bit = ADP5588_BIT(gpio); | |
208 | + | |
209 | + if (type & IRQ_TYPE_LEVEL_HIGH) | |
210 | + dev->int_lvl[bank] |= bit; | |
211 | + else if (type & IRQ_TYPE_LEVEL_LOW) | |
212 | + dev->int_lvl[bank] &= ~bit; | |
213 | + else | |
214 | + return -EINVAL; | |
215 | + | |
216 | + adp5588_gpio_direction_input(&dev->gpio_chip, gpio); | |
217 | + adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank, | |
218 | + dev->int_lvl[bank]); | |
219 | + | |
220 | + return 0; | |
221 | +} | |
222 | + | |
223 | +static struct irq_chip adp5588_irq_chip = { | |
224 | + .name = "adp5588", | |
225 | + .mask = adp5588_irq_mask, | |
226 | + .unmask = adp5588_irq_unmask, | |
227 | + .bus_lock = adp5588_irq_bus_lock, | |
228 | + .bus_sync_unlock = adp5588_irq_bus_sync_unlock, | |
229 | + .set_type = adp5588_irq_set_type, | |
230 | +}; | |
231 | + | |
232 | +static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) | |
233 | +{ | |
234 | + int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); | |
235 | + | |
236 | + if (ret < 0) | |
237 | + dev_err(&client->dev, "Read INT_STAT Error\n"); | |
238 | + | |
239 | + return ret; | |
240 | +} | |
241 | + | |
242 | +static irqreturn_t adp5588_irq_handler(int irq, void *devid) | |
243 | +{ | |
244 | + struct adp5588_gpio *dev = devid; | |
245 | + unsigned status, bank, bit, pending; | |
246 | + int ret; | |
247 | + status = adp5588_gpio_read(dev->client, INT_STAT); | |
248 | + | |
249 | + if (status & ADP5588_GPI_INT) { | |
250 | + ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); | |
251 | + if (ret < 0) | |
252 | + memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); | |
253 | + | |
254 | + for (bank = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); | |
255 | + bank++, bit = 0) { | |
256 | + pending = dev->irq_stat[bank] & dev->irq_mask[bank]; | |
257 | + | |
258 | + while (pending) { | |
259 | + if (pending & (1 << bit)) { | |
260 | + handle_nested_irq(dev->irq_base + | |
261 | + (bank << 3) + bit); | |
262 | + pending &= ~(1 << bit); | |
263 | + | |
264 | + } | |
265 | + bit++; | |
266 | + } | |
267 | + } | |
268 | + } | |
269 | + | |
270 | + adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */ | |
271 | + | |
272 | + return IRQ_HANDLED; | |
273 | +} | |
274 | + | |
275 | +static int adp5588_irq_setup(struct adp5588_gpio *dev) | |
276 | +{ | |
277 | + struct i2c_client *client = dev->client; | |
278 | + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; | |
279 | + unsigned gpio; | |
280 | + int ret; | |
281 | + | |
282 | + adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); | |
283 | + adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ | |
284 | + adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ | |
285 | + | |
286 | + dev->irq_base = pdata->irq_base; | |
287 | + mutex_init(&dev->irq_lock); | |
288 | + | |
289 | + for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { | |
290 | + int irq = gpio + dev->irq_base; | |
291 | + set_irq_chip_data(irq, dev); | |
292 | + set_irq_chip_and_handler(irq, &adp5588_irq_chip, | |
293 | + handle_level_irq); | |
294 | + set_irq_nested_thread(irq, 1); | |
295 | +#ifdef CONFIG_ARM | |
296 | + /* | |
297 | + * ARM needs us to explicitly flag the IRQ as VALID, | |
298 | + * once we do so, it will also set the noprobe. | |
299 | + */ | |
300 | + set_irq_flags(irq, IRQF_VALID); | |
301 | +#else | |
302 | + set_irq_noprobe(irq); | |
303 | +#endif | |
304 | + } | |
305 | + | |
306 | + ret = request_threaded_irq(client->irq, | |
307 | + NULL, | |
308 | + adp5588_irq_handler, | |
309 | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
310 | + dev_name(&client->dev), dev); | |
311 | + if (ret) { | |
312 | + dev_err(&client->dev, "failed to request irq %d\n", | |
313 | + client->irq); | |
314 | + goto out; | |
315 | + } | |
316 | + | |
317 | + dev->gpio_chip.to_irq = adp5588_gpio_to_irq; | |
318 | + adp5588_gpio_write(client, CFG, | |
319 | + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); | |
320 | + | |
321 | + return 0; | |
322 | + | |
323 | +out: | |
324 | + dev->irq_base = 0; | |
325 | + return ret; | |
326 | +} | |
327 | + | |
328 | +static void adp5588_irq_teardown(struct adp5588_gpio *dev) | |
329 | +{ | |
330 | + if (dev->irq_base) | |
331 | + free_irq(dev->client->irq, dev); | |
332 | +} | |
333 | + | |
334 | +#else | |
335 | +static int adp5588_irq_setup(struct adp5588_gpio *dev) | |
336 | +{ | |
337 | + struct i2c_client *client = dev->client; | |
338 | + dev_warn(&client->dev, "interrupt support not compiled in\n"); | |
339 | + | |
340 | + return 0; | |
341 | +} | |
342 | + | |
343 | +static void adp5588_irq_teardown(struct adp5588_gpio *dev) | |
344 | +{ | |
345 | +} | |
346 | +#endif /* CONFIG_GPIO_ADP5588_IRQ */ | |
347 | + | |
128 | 348 | static int __devinit adp5588_gpio_probe(struct i2c_client *client, |
129 | 349 | const struct i2c_device_id *id) |
130 | 350 | { |
131 | 351 | |
132 | 352 | |
133 | 353 | |
134 | 354 | |
135 | 355 | |
136 | 356 | |
137 | 357 | |
... | ... | @@ -160,37 +380,46 @@ |
160 | 380 | gc->can_sleep = 1; |
161 | 381 | |
162 | 382 | gc->base = pdata->gpio_start; |
163 | - gc->ngpio = MAXGPIO; | |
383 | + gc->ngpio = ADP5588_MAXGPIO; | |
164 | 384 | gc->label = client->name; |
165 | 385 | gc->owner = THIS_MODULE; |
166 | 386 | |
167 | 387 | mutex_init(&dev->lock); |
168 | 388 | |
169 | - | |
170 | 389 | ret = adp5588_gpio_read(dev->client, DEV_ID); |
171 | 390 | if (ret < 0) |
172 | 391 | goto err; |
173 | 392 | |
174 | 393 | revid = ret & ADP5588_DEVICE_ID_MASK; |
175 | 394 | |
176 | - for (i = 0, ret = 0; i <= ADP_BANK(MAXGPIO); i++) { | |
395 | + for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { | |
177 | 396 | dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); |
178 | 397 | dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); |
179 | 398 | ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); |
180 | 399 | ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, |
181 | 400 | (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); |
182 | - | |
401 | + ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); | |
183 | 402 | if (ret) |
184 | 403 | goto err; |
185 | 404 | } |
186 | 405 | |
406 | + if (pdata->irq_base) { | |
407 | + if (WA_DELAYED_READOUT_REVID(revid)) { | |
408 | + dev_warn(&client->dev, "GPIO int not supported\n"); | |
409 | + } else { | |
410 | + ret = adp5588_irq_setup(dev); | |
411 | + if (ret) | |
412 | + goto err; | |
413 | + } | |
414 | + } | |
415 | + | |
187 | 416 | ret = gpiochip_add(&dev->gpio_chip); |
188 | 417 | if (ret) |
189 | - goto err; | |
418 | + goto err_irq; | |
190 | 419 | |
191 | - dev_info(&client->dev, "gpios %d..%d on a %s Rev. %d\n", | |
420 | + dev_info(&client->dev, "gpios %d..%d (IRQ Base %d) on a %s Rev. %d\n", | |
192 | 421 | gc->base, gc->base + gc->ngpio - 1, |
193 | - client->name, revid); | |
422 | + pdata->irq_base, client->name, revid); | |
194 | 423 | |
195 | 424 | if (pdata->setup) { |
196 | 425 | ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); |
197 | 426 | |
... | ... | @@ -199,8 +428,11 @@ |
199 | 428 | } |
200 | 429 | |
201 | 430 | i2c_set_clientdata(client, dev); |
431 | + | |
202 | 432 | return 0; |
203 | 433 | |
434 | +err_irq: | |
435 | + adp5588_irq_teardown(dev); | |
204 | 436 | err: |
205 | 437 | kfree(dev); |
206 | 438 | return ret; |
... | ... | @@ -221,6 +453,9 @@ |
221 | 453 | return ret; |
222 | 454 | } |
223 | 455 | } |
456 | + | |
457 | + if (dev->irq_base) | |
458 | + free_irq(dev->client->irq, dev); | |
224 | 459 | |
225 | 460 | ret = gpiochip_remove(&dev->gpio_chip); |
226 | 461 | if (ret) { |
include/linux/i2c/adp5588.h
... | ... | @@ -74,6 +74,20 @@ |
74 | 74 | |
75 | 75 | #define ADP5588_DEVICE_ID_MASK 0xF |
76 | 76 | |
77 | + /* Configuration Register1 */ | |
78 | +#define ADP5588_AUTO_INC (1 << 7) | |
79 | +#define ADP5588_GPIEM_CFG (1 << 6) | |
80 | +#define ADP5588_INT_CFG (1 << 4) | |
81 | +#define ADP5588_GPI_IEN (1 << 1) | |
82 | + | |
83 | +/* Interrupt Status Register */ | |
84 | +#define ADP5588_GPI_INT (1 << 1) | |
85 | +#define ADP5588_KE_INT (1 << 0) | |
86 | + | |
87 | +#define ADP5588_MAXGPIO 18 | |
88 | +#define ADP5588_BANK(offs) ((offs) >> 3) | |
89 | +#define ADP5588_BIT(offs) (1u << ((offs) & 0x7)) | |
90 | + | |
77 | 91 | /* Put one of these structures in i2c_board_info platform_data */ |
78 | 92 | |
79 | 93 | #define ADP5588_KEYMAPSIZE 80 |
... | ... | @@ -128,6 +142,7 @@ |
128 | 142 | |
129 | 143 | struct adp5588_gpio_platform_data { |
130 | 144 | unsigned gpio_start; /* GPIO Chip base # */ |
145 | + unsigned irq_base; /* interrupt base # */ | |
131 | 146 | unsigned pullup_dis_mask; /* Pull-Up Disable Mask */ |
132 | 147 | int (*setup)(struct i2c_client *client, |
133 | 148 | int gpio, unsigned ngpio, |