aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2012-09-18 04:57:10 -0400
committerLinus Walleij <linus.walleij@linaro.org>2012-09-18 17:27:25 -0400
commit5e969a401a0126806cc2a358800b5b52d6c0a246 (patch)
treebf1abac453b63d37222331cb414e2a783ae9887b /drivers/gpio
parentd724f1c9c3c7dee420b8d778ee53207ef3c17120 (diff)
gpio: Add Avionic Design N-bit GPIO expander support
This commit adds a driver for the Avionic Design N-bit GPIO expander. The expander provides a variable number of GPIO pins with interrupt support. Changes in v2: - allow building the driver as a module - assign of_node unconditionally - use linear mapping IRQ domain - properly cleanup IRQ domain - add OF device table and annotate device tables - emulate rising and falling edge triggers - increase #gpio-cells to 2 - drop support for !OF - use IS_ENABLED to conditionalize DEBUG_FS code Changes in v3: - make IRQ support runtime configurable (interrupt-controller property) - drop interrupt-controller and #interrupt-cells from DT binding - add inline to_adnp() function to wrap container_of() macro - consistently use adnp as name for struct adnp variables - remove irq_mask_cur and rename irq_mask to irq_enable - fix a subtle deadlock in adnp_gpio_direction_output() - remove dynamic allocations from debugfs code - rename regs to num_regs to avoid confusion - annotate non-trivial code with comments - don't acquire mutex in adnp_gpio_get() - assume NO_IRQ == 0 Cc: Grant Likely <grant.likely@secretlab.ca> Cc: devicetree-discuss@lists.ozlabs.org Cc: Linus Walleij <linus.walleij@stericsson.com> Cc: linux-kernel@vger.kernel.org Acked-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig11
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-adnp.c611
3 files changed, 623 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2f2a3b86b985..bf48c74ba4a0 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -444,6 +444,17 @@ config GPIO_ADP5588_IRQ
444 Say yes here to enable the adp5588 to be used as an interrupt 444 Say yes here to enable the adp5588 to be used as an interrupt
445 controller. It requires the driver to be built in the kernel. 445 controller. It requires the driver to be built in the kernel.
446 446
447config GPIO_ADNP
448 tristate "Avionic Design N-bit GPIO expander"
449 depends on I2C && OF
450 help
451 This option enables support for N GPIOs found on Avionic Design
452 I2C GPIO expanders. The register space will be extended by powers
453 of two, so the controller will need to accomodate for that. For
454 example: if a controller provides 48 pins, 6 registers will be
455 enough to represent all pins, but the driver will assume a
456 register layout for 64 pins (8 registers).
457
447comment "PCI GPIO expanders:" 458comment "PCI GPIO expanders:"
448 459
449config GPIO_CS5535 460config GPIO_CS5535
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 153caceeb053..710f2b6f6f04 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
10 10
11obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o 11obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
12obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o 12obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
13obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
13obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o 14obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
14obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o 15obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
15obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o 16obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
new file mode 100644
index 000000000000..3df88336415e
--- /dev/null
+++ b/drivers/gpio/gpio-adnp.c
@@ -0,0 +1,611 @@
1/*
2 * Copyright (C) 2011-2012 Avionic Design GmbH
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/gpio.h>
10#include <linux/i2c.h>
11#include <linux/interrupt.h>
12#include <linux/irqdomain.h>
13#include <linux/module.h>
14#include <linux/of_irq.h>
15#include <linux/seq_file.h>
16#include <linux/slab.h>
17
18#define GPIO_DDR(gpio) (0x00 << (gpio)->reg_shift)
19#define GPIO_PLR(gpio) (0x01 << (gpio)->reg_shift)
20#define GPIO_IER(gpio) (0x02 << (gpio)->reg_shift)
21#define GPIO_ISR(gpio) (0x03 << (gpio)->reg_shift)
22#define GPIO_PTR(gpio) (0x04 << (gpio)->reg_shift)
23
24struct adnp {
25 struct i2c_client *client;
26 struct gpio_chip gpio;
27 unsigned int reg_shift;
28
29 struct mutex i2c_lock;
30
31 struct irq_domain *domain;
32 struct mutex irq_lock;
33
34 u8 *irq_enable;
35 u8 *irq_level;
36 u8 *irq_rise;
37 u8 *irq_fall;
38 u8 *irq_high;
39 u8 *irq_low;
40};
41
42static inline struct adnp *to_adnp(struct gpio_chip *chip)
43{
44 return container_of(chip, struct adnp, gpio);
45}
46
47static int adnp_read(struct adnp *adnp, unsigned offset, uint8_t *value)
48{
49 int err;
50
51 err = i2c_smbus_read_byte_data(adnp->client, offset);
52 if (err < 0) {
53 dev_err(adnp->gpio.dev, "%s failed: %d\n",
54 "i2c_smbus_read_byte_data()", err);
55 return err;
56 }
57
58 *value = err;
59 return 0;
60}
61
62static int adnp_write(struct adnp *adnp, unsigned offset, uint8_t value)
63{
64 int err;
65
66 err = i2c_smbus_write_byte_data(adnp->client, offset, value);
67 if (err < 0) {
68 dev_err(adnp->gpio.dev, "%s failed: %d\n",
69 "i2c_smbus_write_byte_data()", err);
70 return err;
71 }
72
73 return 0;
74}
75
76static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset)
77{
78 struct adnp *adnp = to_adnp(chip);
79 unsigned int reg = offset >> adnp->reg_shift;
80 unsigned int pos = offset & 7;
81 u8 value;
82 int err;
83
84 err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &value);
85 if (err < 0)
86 return err;
87
88 return (value & BIT(pos)) ? 1 : 0;
89}
90
91static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value)
92{
93 unsigned int reg = offset >> adnp->reg_shift;
94 unsigned int pos = offset & 7;
95 int err;
96 u8 val;
97
98 err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val);
99 if (err < 0)
100 return;
101
102 if (value)
103 val |= BIT(pos);
104 else
105 val &= ~BIT(pos);
106
107 adnp_write(adnp, GPIO_PLR(adnp) + reg, val);
108}
109
110static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
111{
112 struct adnp *adnp = to_adnp(chip);
113
114 mutex_lock(&adnp->i2c_lock);
115 __adnp_gpio_set(adnp, offset, value);
116 mutex_unlock(&adnp->i2c_lock);
117}
118
119static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
120{
121 struct adnp *adnp = to_adnp(chip);
122 unsigned int reg = offset >> adnp->reg_shift;
123 unsigned int pos = offset & 7;
124 u8 value;
125 int err;
126
127 mutex_lock(&adnp->i2c_lock);
128
129 err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
130 if (err < 0)
131 goto out;
132
133 value &= ~BIT(pos);
134
135 err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value);
136 if (err < 0)
137 goto out;
138
139 err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
140 if (err < 0)
141 goto out;
142
143 if (err & BIT(pos))
144 err = -EACCES;
145
146 err = 0;
147
148out:
149 mutex_unlock(&adnp->i2c_lock);
150 return err;
151}
152
153static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
154 int value)
155{
156 struct adnp *adnp = to_adnp(chip);
157 unsigned int reg = offset >> adnp->reg_shift;
158 unsigned int pos = offset & 7;
159 int err;
160 u8 val;
161
162 mutex_lock(&adnp->i2c_lock);
163
164 err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
165 if (err < 0)
166 goto out;
167
168 val |= BIT(pos);
169
170 err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val);
171 if (err < 0)
172 goto out;
173
174 err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
175 if (err < 0)
176 goto out;
177
178 if (!(val & BIT(pos))) {
179 err = -EPERM;
180 goto out;
181 }
182
183 __adnp_gpio_set(adnp, offset, value);
184 err = 0;
185
186out:
187 mutex_unlock(&adnp->i2c_lock);
188 return err;
189}
190
191static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
192{
193 struct adnp *adnp = to_adnp(chip);
194 unsigned int num_regs = 1 << adnp->reg_shift, i, j;
195 int err;
196
197 for (i = 0; i < num_regs; i++) {
198 u8 ddr, plr, ier, isr;
199
200 mutex_lock(&adnp->i2c_lock);
201
202 err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr);
203 if (err < 0) {
204 mutex_unlock(&adnp->i2c_lock);
205 return;
206 }
207
208 err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr);
209 if (err < 0) {
210 mutex_unlock(&adnp->i2c_lock);
211 return;
212 }
213
214 err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
215 if (err < 0) {
216 mutex_unlock(&adnp->i2c_lock);
217 return;
218 }
219
220 err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
221 if (err < 0) {
222 mutex_unlock(&adnp->i2c_lock);
223 return;
224 }
225
226 mutex_unlock(&adnp->i2c_lock);
227
228 for (j = 0; j < 8; j++) {
229 unsigned int bit = (i << adnp->reg_shift) + j;
230 const char *direction = "input ";
231 const char *level = "low ";
232 const char *interrupt = "disabled";
233 const char *pending = "";
234
235 if (ddr & BIT(j))
236 direction = "output";
237
238 if (plr & BIT(j))
239 level = "high";
240
241 if (ier & BIT(j))
242 interrupt = "enabled ";
243
244 if (isr & BIT(j))
245 pending = "pending";
246
247 seq_printf(s, "%2u: %s %s IRQ %s %s\n", bit,
248 direction, level, interrupt, pending);
249 }
250 }
251}
252
253static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
254{
255 struct gpio_chip *chip = &adnp->gpio;
256
257 adnp->reg_shift = get_count_order(num_gpios) - 3;
258
259 chip->direction_input = adnp_gpio_direction_input;
260 chip->direction_output = adnp_gpio_direction_output;
261 chip->get = adnp_gpio_get;
262 chip->set = adnp_gpio_set;
263 chip->can_sleep = 1;
264
265 if (IS_ENABLED(CONFIG_DEBUG_FS))
266 chip->dbg_show = adnp_gpio_dbg_show;
267
268 chip->base = -1;
269 chip->ngpio = num_gpios;
270 chip->label = adnp->client->name;
271 chip->dev = &adnp->client->dev;
272 chip->of_node = chip->dev->of_node;
273 chip->owner = THIS_MODULE;
274
275 return 0;
276}
277
278static irqreturn_t adnp_irq(int irq, void *data)
279{
280 struct adnp *adnp = data;
281 unsigned int num_regs, i;
282
283 num_regs = 1 << adnp->reg_shift;
284
285 for (i = 0; i < num_regs; i++) {
286 unsigned int base = i << adnp->reg_shift, bit;
287 u8 changed, level, isr, ier;
288 unsigned long pending;
289 int err;
290
291 mutex_lock(&adnp->i2c_lock);
292
293 err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level);
294 if (err < 0) {
295 mutex_unlock(&adnp->i2c_lock);
296 continue;
297 }
298
299 err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
300 if (err < 0) {
301 mutex_unlock(&adnp->i2c_lock);
302 continue;
303 }
304
305 err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
306 if (err < 0) {
307 mutex_unlock(&adnp->i2c_lock);
308 continue;
309 }
310
311 mutex_unlock(&adnp->i2c_lock);
312
313 /* determine pins that changed levels */
314 changed = level ^ adnp->irq_level[i];
315
316 /* compute edge-triggered interrupts */
317 pending = changed & ((adnp->irq_fall[i] & ~level) |
318 (adnp->irq_rise[i] & level));
319
320 /* add in level-triggered interrupts */
321 pending |= (adnp->irq_high[i] & level) |
322 (adnp->irq_low[i] & ~level);
323
324 /* mask out non-pending and disabled interrupts */
325 pending &= isr & ier;
326
327 for_each_set_bit(bit, &pending, 8) {
328 unsigned int virq;
329 virq = irq_find_mapping(adnp->domain, base + bit);
330 handle_nested_irq(virq);
331 }
332 }
333
334 return IRQ_HANDLED;
335}
336
337static int adnp_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
338{
339 struct adnp *adnp = to_adnp(chip);
340 return irq_create_mapping(adnp->domain, offset);
341}
342
343static void adnp_irq_mask(struct irq_data *data)
344{
345 struct adnp *adnp = irq_data_get_irq_chip_data(data);
346 unsigned int reg = data->hwirq >> adnp->reg_shift;
347 unsigned int pos = data->hwirq & 7;
348
349 adnp->irq_enable[reg] &= ~BIT(pos);
350}
351
352static void adnp_irq_unmask(struct irq_data *data)
353{
354 struct adnp *adnp = irq_data_get_irq_chip_data(data);
355 unsigned int reg = data->hwirq >> adnp->reg_shift;
356 unsigned int pos = data->hwirq & 7;
357
358 adnp->irq_enable[reg] |= BIT(pos);
359}
360
361static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
362{
363 struct adnp *adnp = irq_data_get_irq_chip_data(data);
364 unsigned int reg = data->hwirq >> adnp->reg_shift;
365 unsigned int pos = data->hwirq & 7;
366
367 if (type & IRQ_TYPE_EDGE_RISING)
368 adnp->irq_rise[reg] |= BIT(pos);
369 else
370 adnp->irq_rise[reg] &= ~BIT(pos);
371
372 if (type & IRQ_TYPE_EDGE_FALLING)
373 adnp->irq_fall[reg] |= BIT(pos);
374 else
375 adnp->irq_fall[reg] &= ~BIT(pos);
376
377 if (type & IRQ_TYPE_LEVEL_HIGH)
378 adnp->irq_high[reg] |= BIT(pos);
379 else
380 adnp->irq_high[reg] &= ~BIT(pos);
381
382 if (type & IRQ_TYPE_LEVEL_LOW)
383 adnp->irq_low[reg] |= BIT(pos);
384 else
385 adnp->irq_low[reg] &= ~BIT(pos);
386
387 return 0;
388}
389
390static void adnp_irq_bus_lock(struct irq_data *data)
391{
392 struct adnp *adnp = irq_data_get_irq_chip_data(data);
393
394 mutex_lock(&adnp->irq_lock);
395}
396
397static void adnp_irq_bus_unlock(struct irq_data *data)
398{
399 struct adnp *adnp = irq_data_get_irq_chip_data(data);
400 unsigned int num_regs = 1 << adnp->reg_shift, i;
401
402 mutex_lock(&adnp->i2c_lock);
403
404 for (i = 0; i < num_regs; i++)
405 adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]);
406
407 mutex_unlock(&adnp->i2c_lock);
408 mutex_unlock(&adnp->irq_lock);
409}
410
411static struct irq_chip adnp_irq_chip = {
412 .name = "gpio-adnp",
413 .irq_mask = adnp_irq_mask,
414 .irq_unmask = adnp_irq_unmask,
415 .irq_set_type = adnp_irq_set_type,
416 .irq_bus_lock = adnp_irq_bus_lock,
417 .irq_bus_sync_unlock = adnp_irq_bus_unlock,
418};
419
420static int adnp_irq_map(struct irq_domain *domain, unsigned int irq,
421 irq_hw_number_t hwirq)
422{
423 irq_set_chip_data(irq, domain->host_data);
424 irq_set_chip(irq, &adnp_irq_chip);
425 irq_set_nested_thread(irq, true);
426
427#ifdef CONFIG_ARM
428 set_irq_flags(irq, IRQF_VALID);
429#else
430 irq_set_noprobe(irq);
431#endif
432
433 return 0;
434}
435
436static const struct irq_domain_ops adnp_irq_domain_ops = {
437 .map = adnp_irq_map,
438 .xlate = irq_domain_xlate_twocell,
439};
440
441static int adnp_irq_setup(struct adnp *adnp)
442{
443 unsigned int num_regs = 1 << adnp->reg_shift, i;
444 struct gpio_chip *chip = &adnp->gpio;
445 int err;
446
447 mutex_init(&adnp->irq_lock);
448
449 /*
450 * Allocate memory to keep track of the current level and trigger
451 * modes of the interrupts. To avoid multiple allocations, a single
452 * large buffer is allocated and pointers are setup to point at the
453 * corresponding offsets. For consistency, the layout of the buffer
454 * is chosen to match the register layout of the hardware in that
455 * each segment contains the corresponding bits for all interrupts.
456 */
457 adnp->irq_enable = devm_kzalloc(chip->dev, num_regs * 6, GFP_KERNEL);
458 if (!adnp->irq_enable)
459 return -ENOMEM;
460
461 adnp->irq_level = adnp->irq_enable + (num_regs * 1);
462 adnp->irq_rise = adnp->irq_enable + (num_regs * 2);
463 adnp->irq_fall = adnp->irq_enable + (num_regs * 3);
464 adnp->irq_high = adnp->irq_enable + (num_regs * 4);
465 adnp->irq_low = adnp->irq_enable + (num_regs * 5);
466
467 for (i = 0; i < num_regs; i++) {
468 /*
469 * Read the initial level of all pins to allow the emulation
470 * of edge triggered interrupts.
471 */
472 err = adnp_read(adnp, GPIO_PLR(adnp) + i, &adnp->irq_level[i]);
473 if (err < 0)
474 return err;
475
476 /* disable all interrupts */
477 err = adnp_write(adnp, GPIO_IER(adnp) + i, 0);
478 if (err < 0)
479 return err;
480
481 adnp->irq_enable[i] = 0x00;
482 }
483
484 adnp->domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
485 &adnp_irq_domain_ops, adnp);
486
487 err = request_threaded_irq(adnp->client->irq, NULL, adnp_irq,
488 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
489 dev_name(chip->dev), adnp);
490 if (err != 0) {
491 dev_err(chip->dev, "can't request IRQ#%d: %d\n",
492 adnp->client->irq, err);
493 goto error;
494 }
495
496 chip->to_irq = adnp_gpio_to_irq;
497 return 0;
498
499error:
500 irq_domain_remove(adnp->domain);
501 return err;
502}
503
504static void adnp_irq_teardown(struct adnp *adnp)
505{
506 unsigned int irq, i;
507
508 free_irq(adnp->client->irq, adnp);
509
510 for (i = 0; i < adnp->gpio.ngpio; i++) {
511 irq = irq_find_mapping(adnp->domain, i);
512 if (irq > 0)
513 irq_dispose_mapping(irq);
514 }
515
516 irq_domain_remove(adnp->domain);
517}
518
519static __devinit int adnp_i2c_probe(struct i2c_client *client,
520 const struct i2c_device_id *id)
521{
522 struct device_node *np = client->dev.of_node;
523 struct adnp *adnp;
524 u32 num_gpios;
525 int err;
526
527 err = of_property_read_u32(np, "nr-gpios", &num_gpios);
528 if (err < 0)
529 return err;
530
531 client->irq = irq_of_parse_and_map(np, 0);
532 if (!client->irq)
533 return -EPROBE_DEFER;
534
535 adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL);
536 if (!adnp)
537 return -ENOMEM;
538
539 mutex_init(&adnp->i2c_lock);
540 adnp->client = client;
541
542 err = adnp_gpio_setup(adnp, num_gpios);
543 if (err < 0)
544 return err;
545
546 if (of_find_property(np, "interrupt-controller", NULL)) {
547 err = adnp_irq_setup(adnp);
548 if (err < 0)
549 goto teardown;
550 }
551
552 err = gpiochip_add(&adnp->gpio);
553 if (err < 0)
554 goto teardown;
555
556 i2c_set_clientdata(client, adnp);
557 return 0;
558
559teardown:
560 if (of_find_property(np, "interrupt-controller", NULL))
561 adnp_irq_teardown(adnp);
562
563 return err;
564}
565
566static __devexit int adnp_i2c_remove(struct i2c_client *client)
567{
568 struct adnp *adnp = i2c_get_clientdata(client);
569 struct device_node *np = client->dev.of_node;
570 int err;
571
572 err = gpiochip_remove(&adnp->gpio);
573 if (err < 0) {
574 dev_err(&client->dev, "%s failed: %d\n", "gpiochip_remove()",
575 err);
576 return err;
577 }
578
579 if (of_find_property(np, "interrupt-controller", NULL))
580 adnp_irq_teardown(adnp);
581
582 return 0;
583}
584
585static const struct i2c_device_id adnp_i2c_id[] __devinitconst = {
586 { "gpio-adnp" },
587 { },
588};
589MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);
590
591static const struct of_device_id adnp_of_match[] __devinitconst = {
592 { .compatible = "ad,gpio-adnp", },
593 { },
594};
595MODULE_DEVICE_TABLE(of, adnp_of_match);
596
597static struct i2c_driver adnp_i2c_driver = {
598 .driver = {
599 .name = "gpio-adnp",
600 .owner = THIS_MODULE,
601 .of_match_table = of_match_ptr(adnp_of_match),
602 },
603 .probe = adnp_i2c_probe,
604 .remove = __devexit_p(adnp_i2c_remove),
605 .id_table = adnp_i2c_id,
606};
607module_i2c_driver(adnp_i2c_driver);
608
609MODULE_DESCRIPTION("Avionic Design N-bit GPIO expander");
610MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
611MODULE_LICENSE("GPL");