aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Poeschel <poeschel@lemonage.de>2014-01-16 05:44:15 -0500
committerLinus Walleij <linus.walleij@linaro.org>2014-01-17 09:30:14 -0500
commit4e47f91bf741e011a90ceb6241b8d78141709733 (patch)
tree1febf3cbbf59a1202dc5d944d392ce28b6b851fe
parent785acec3eecf4c21bab9e24afb5d354b57a72e03 (diff)
gpio: mcp23s08: Add irq functionality for i2c chips
This adds interrupt functionality for i2c chips to the driver. They can act as a interrupt-controller and generate interrupts, if the inputs change. This is tested with a mcp23017 chip on an arm based platform. v3: - be a bit more clear that the irq functionality is also available on spi versions of the chips, but the linux driver does not support this yet v2: - some more word about irq-mirror property in binding doc - use of_read_bool instead of of_find_property for "interrupt-contrller" and "irq-mirror" - cache the "interrupt-controller" for remove function - do set the irq-mirror bit only if device is marked as interrupt-controller - do create the irq mapping and setup of irq_desc of all possible interrupts in probe path instead of in gpio_to_irq - mark gpios as in use as interrupts in irq in irq_startup and unlock it in irq_shutdown - rename virq to child_irq - remove dev argument from mcp23s08_irq_setup function - move gpiochip_add before mcp23s08_irq_setup in probe path Signed-off-by: Lars Poeschel <poeschel@lemonage.de> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt28
-rw-r--r--drivers/gpio/Kconfig1
-rw-r--r--drivers/gpio/gpio-mcp23s08.c248
3 files changed, 271 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
index daa30174bcc1..3ddc7ccfe5f3 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
@@ -38,12 +38,38 @@ Required device specific properties (only for SPI chips):
38 removed. 38 removed.
39- spi-max-frequency = The maximum frequency this chip is able to handle 39- spi-max-frequency = The maximum frequency this chip is able to handle
40 40
41Example I2C: 41Optional properties:
42- #interrupt-cells : Should be two.
43 - first cell is the pin number
44 - second cell is used to specify flags.
45- interrupt-controller: Marks the device node as a interrupt controller.
46NOTE: The interrupt functionality is only supported for i2c versions of the
47chips. The spi chips can also do the interrupts, but this is not supported by
48the linux driver yet.
49
50Optional device specific properties:
51- microchip,irq-mirror: Sets the mirror flag in the IOCON register. Devices
52 with two interrupt outputs (these are the devices ending with 17 and
53 those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and
54 IO 8-15 are bank 2. These chips have two different interrupt outputs:
55 One for bank 1 and another for bank 2. If irq-mirror is set, both
56 interrupts are generated regardless of the bank that an input change
57 occured on. If it is not set, the interrupt are only generated for the
58 bank they belong to.
59 On devices with only one interrupt output this property is useless.
60
61Example I2C (with interrupt):
42gpiom1: gpio@20 { 62gpiom1: gpio@20 {
43 compatible = "microchip,mcp23017"; 63 compatible = "microchip,mcp23017";
44 gpio-controller; 64 gpio-controller;
45 #gpio-cells = <2>; 65 #gpio-cells = <2>;
46 reg = <0x20>; 66 reg = <0x20>;
67
68 interrupt-parent = <&gpio1>;
69 interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
70 interrupt-controller;
71 #interrupt-cells=<2>;
72 microchip,irq-mirror;
47}; 73};
48 74
49Example SPI: 75Example SPI:
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 858d489f6bae..2d49784109b5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -722,6 +722,7 @@ config GPIO_MCP23S08
722 SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 722 SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
723 I/O expanders. 723 I/O expanders.
724 This provides a GPIO interface supporting inputs and outputs. 724 This provides a GPIO interface supporting inputs and outputs.
725 The I2C versions of the chips can be used as interrupt-controller.
725 726
726config GPIO_MC33880 727config GPIO_MC33880
727 tristate "Freescale MC33880 high-side/low-side switch" 728 tristate "Freescale MC33880 high-side/low-side switch"
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index b16401ee4766..ef3fd48aca3b 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -1,5 +1,13 @@
1/* 1/*
2 * MCP23S08 SPI/GPIO gpio expander driver 2 * MCP23S08 SPI/I2C GPIO gpio expander driver
3 *
4 * The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are
5 * supported.
6 * For the I2C versions of the chips (mcp23008 and mcp23017) generation of
7 * interrupts is also supported.
8 * The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is
9 * also capable of generating interrupts, but the linux driver does not
10 * support that yet.
3 */ 11 */
4 12
5#include <linux/kernel.h> 13#include <linux/kernel.h>
@@ -12,7 +20,8 @@
12#include <linux/spi/mcp23s08.h> 20#include <linux/spi/mcp23s08.h>
13#include <linux/slab.h> 21#include <linux/slab.h>
14#include <asm/byteorder.h> 22#include <asm/byteorder.h>
15#include <linux/of.h> 23#include <linux/interrupt.h>
24#include <linux/of_irq.h>
16#include <linux/of_device.h> 25#include <linux/of_device.h>
17 26
18/** 27/**
@@ -34,6 +43,7 @@
34#define MCP_DEFVAL 0x03 43#define MCP_DEFVAL 0x03
35#define MCP_INTCON 0x04 44#define MCP_INTCON 0x04
36#define MCP_IOCON 0x05 45#define MCP_IOCON 0x05
46# define IOCON_MIRROR (1 << 6)
37# define IOCON_SEQOP (1 << 5) 47# define IOCON_SEQOP (1 << 5)
38# define IOCON_HAEN (1 << 3) 48# define IOCON_HAEN (1 << 3)
39# define IOCON_ODR (1 << 2) 49# define IOCON_ODR (1 << 2)
@@ -57,8 +67,14 @@ struct mcp23s08 {
57 u8 addr; 67 u8 addr;
58 68
59 u16 cache[11]; 69 u16 cache[11];
70 u16 irq_rise;
71 u16 irq_fall;
72 int irq;
73 bool irq_controller;
60 /* lock protects the cached values */ 74 /* lock protects the cached values */
61 struct mutex lock; 75 struct mutex lock;
76 struct mutex irq_lock;
77 struct irq_domain *irq_domain;
62 78
63 struct gpio_chip chip; 79 struct gpio_chip chip;
64 80
@@ -77,6 +93,11 @@ struct mcp23s08_driver_data {
77 struct mcp23s08 chip[]; 93 struct mcp23s08 chip[];
78}; 94};
79 95
96/* This lock class tells lockdep that GPIO irqs are in a different
97 * category than their parents, so it won't report false recursion.
98 */
99static struct lock_class_key gpio_lock_class;
100
80/*----------------------------------------------------------------------*/ 101/*----------------------------------------------------------------------*/
81 102
82#if IS_ENABLED(CONFIG_I2C) 103#if IS_ENABLED(CONFIG_I2C)
@@ -316,6 +337,195 @@ mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
316} 337}
317 338
318/*----------------------------------------------------------------------*/ 339/*----------------------------------------------------------------------*/
340static irqreturn_t mcp23s08_irq(int irq, void *data)
341{
342 struct mcp23s08 *mcp = data;
343 int intcap, intf, i;
344 unsigned int child_irq;
345
346 mutex_lock(&mcp->lock);
347 intf = mcp->ops->read(mcp, MCP_INTF);
348 if (intf < 0) {
349 mutex_unlock(&mcp->lock);
350 return IRQ_HANDLED;
351 }
352
353 mcp->cache[MCP_INTF] = intf;
354
355 intcap = mcp->ops->read(mcp, MCP_INTCAP);
356 if (intcap < 0) {
357 mutex_unlock(&mcp->lock);
358 return IRQ_HANDLED;
359 }
360
361 mcp->cache[MCP_INTCAP] = intcap;
362 mutex_unlock(&mcp->lock);
363
364
365 for (i = 0; i < mcp->chip.ngpio; i++) {
366 if ((BIT(i) & mcp->cache[MCP_INTF]) &&
367 ((BIT(i) & intcap & mcp->irq_rise) ||
368 (mcp->irq_fall & ~intcap & BIT(i)))) {
369 child_irq = irq_find_mapping(mcp->irq_domain, i);
370 handle_nested_irq(child_irq);
371 }
372 }
373
374 return IRQ_HANDLED;
375}
376
377static int mcp23s08_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
378{
379 struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
380
381 return irq_find_mapping(mcp->irq_domain, offset);
382}
383
384static void mcp23s08_irq_mask(struct irq_data *data)
385{
386 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
387 unsigned int pos = data->hwirq;
388
389 mcp->cache[MCP_GPINTEN] &= ~BIT(pos);
390}
391
392static void mcp23s08_irq_unmask(struct irq_data *data)
393{
394 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
395 unsigned int pos = data->hwirq;
396
397 mcp->cache[MCP_GPINTEN] |= BIT(pos);
398}
399
400static int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type)
401{
402 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
403 unsigned int pos = data->hwirq;
404 int status = 0;
405
406 if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
407 mcp->cache[MCP_INTCON] &= ~BIT(pos);
408 mcp->irq_rise |= BIT(pos);
409 mcp->irq_fall |= BIT(pos);
410 } else if (type & IRQ_TYPE_EDGE_RISING) {
411 mcp->cache[MCP_INTCON] &= ~BIT(pos);
412 mcp->irq_rise |= BIT(pos);
413 mcp->irq_fall &= ~BIT(pos);
414 } else if (type & IRQ_TYPE_EDGE_FALLING) {
415 mcp->cache[MCP_INTCON] &= ~BIT(pos);
416 mcp->irq_rise &= ~BIT(pos);
417 mcp->irq_fall |= BIT(pos);
418 } else
419 return -EINVAL;
420
421 return status;
422}
423
424static void mcp23s08_irq_bus_lock(struct irq_data *data)
425{
426 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
427
428 mutex_lock(&mcp->irq_lock);
429}
430
431static void mcp23s08_irq_bus_unlock(struct irq_data *data)
432{
433 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
434
435 mutex_lock(&mcp->lock);
436 mcp->ops->write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]);
437 mcp->ops->write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]);
438 mcp->ops->write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]);
439 mutex_unlock(&mcp->lock);
440 mutex_unlock(&mcp->irq_lock);
441}
442
443static unsigned int mcp23s08_irq_startup(struct irq_data *data)
444{
445 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
446
447 if (gpio_lock_as_irq(&mcp->chip, data->hwirq))
448 dev_err(mcp->chip.dev,
449 "unable to lock HW IRQ %lu for IRQ usage\n",
450 data->hwirq);
451
452 mcp23s08_irq_unmask(data);
453 return 0;
454}
455
456static void mcp23s08_irq_shutdown(struct irq_data *data)
457{
458 struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
459
460 mcp23s08_irq_mask(data);
461 gpio_unlock_as_irq(&mcp->chip, data->hwirq);
462}
463
464static struct irq_chip mcp23s08_irq_chip = {
465 .name = "gpio-mcp23xxx",
466 .irq_mask = mcp23s08_irq_mask,
467 .irq_unmask = mcp23s08_irq_unmask,
468 .irq_set_type = mcp23s08_irq_set_type,
469 .irq_bus_lock = mcp23s08_irq_bus_lock,
470 .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock,
471 .irq_startup = mcp23s08_irq_startup,
472 .irq_shutdown = mcp23s08_irq_shutdown,
473};
474
475static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
476{
477 struct gpio_chip *chip = &mcp->chip;
478 int err, irq, j;
479
480 mutex_init(&mcp->irq_lock);
481
482 mcp->irq_domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
483 &irq_domain_simple_ops, mcp);
484 if (!mcp->irq_domain)
485 return -ENODEV;
486
487 err = devm_request_threaded_irq(chip->dev, mcp->irq, NULL, mcp23s08_irq,
488 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
489 dev_name(chip->dev), mcp);
490 if (err != 0) {
491 dev_err(chip->dev, "unable to request IRQ#%d: %d\n",
492 mcp->irq, err);
493 return err;
494 }
495
496 chip->to_irq = mcp23s08_gpio_to_irq;
497
498 for (j = 0; j < mcp->chip.ngpio; j++) {
499 irq = irq_create_mapping(mcp->irq_domain, j);
500 irq_set_lockdep_class(irq, &gpio_lock_class);
501 irq_set_chip_data(irq, mcp);
502 irq_set_chip(irq, &mcp23s08_irq_chip);
503 irq_set_nested_thread(irq, true);
504#ifdef CONFIG_ARM
505 set_irq_flags(irq, IRQF_VALID);
506#else
507 irq_set_noprobe(irq);
508#endif
509 }
510 return 0;
511}
512
513static void mcp23s08_irq_teardown(struct mcp23s08 *mcp)
514{
515 unsigned int irq, i;
516
517 free_irq(mcp->irq, mcp);
518
519 for (i = 0; i < mcp->chip.ngpio; i++) {
520 irq = irq_find_mapping(mcp->irq_domain, i);
521 if (irq > 0)
522 irq_dispose_mapping(irq);
523 }
524
525 irq_domain_remove(mcp->irq_domain);
526}
527
528/*----------------------------------------------------------------------*/
319 529
320#ifdef CONFIG_DEBUG_FS 530#ifdef CONFIG_DEBUG_FS
321 531
@@ -370,10 +580,11 @@ done:
370/*----------------------------------------------------------------------*/ 580/*----------------------------------------------------------------------*/
371 581
372static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, 582static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
373 void *data, unsigned addr, 583 void *data, unsigned addr, unsigned type,
374 unsigned type, unsigned base, unsigned pullups) 584 unsigned base, unsigned pullups)
375{ 585{
376 int status; 586 int status;
587 bool mirror = false;
377 588
378 mutex_init(&mcp->lock); 589 mutex_init(&mcp->lock);
379 590
@@ -432,13 +643,25 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
432 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, 643 /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
433 * and MCP_IOCON.HAEN = 1, so we work with all chips. 644 * and MCP_IOCON.HAEN = 1, so we work with all chips.
434 */ 645 */
646
435 status = mcp->ops->read(mcp, MCP_IOCON); 647 status = mcp->ops->read(mcp, MCP_IOCON);
436 if (status < 0) 648 if (status < 0)
437 goto fail; 649 goto fail;
438 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { 650
651 mcp->irq_controller = of_property_read_bool(mcp->chip.of_node,
652 "interrupt-controller");
653 if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017))
654 mirror = of_property_read_bool(mcp->chip.of_node,
655 "microchip,irq-mirror");
656
657 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) {
439 /* mcp23s17 has IOCON twice, make sure they are in sync */ 658 /* mcp23s17 has IOCON twice, make sure they are in sync */
440 status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); 659 status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
441 status |= IOCON_HAEN | (IOCON_HAEN << 8); 660 status |= IOCON_HAEN | (IOCON_HAEN << 8);
661 status &= ~(IOCON_INTPOL | (IOCON_INTPOL << 8));
662 if (mirror)
663 status |= IOCON_MIRROR | (IOCON_MIRROR << 8);
664
442 status = mcp->ops->write(mcp, MCP_IOCON, status); 665 status = mcp->ops->write(mcp, MCP_IOCON, status);
443 if (status < 0) 666 if (status < 0)
444 goto fail; 667 goto fail;
@@ -470,6 +693,16 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
470 } 693 }
471 694
472 status = gpiochip_add(&mcp->chip); 695 status = gpiochip_add(&mcp->chip);
696 if (status < 0)
697 goto fail;
698
699 if (mcp->irq && mcp->irq_controller) {
700 status = mcp23s08_irq_setup(mcp);
701 if (status) {
702 mcp23s08_irq_teardown(mcp);
703 goto fail;
704 }
705 }
473fail: 706fail:
474 if (status < 0) 707 if (status < 0)
475 dev_dbg(dev, "can't setup chip %d, --> %d\n", 708 dev_dbg(dev, "can't setup chip %d, --> %d\n",
@@ -546,6 +779,7 @@ static int mcp230xx_probe(struct i2c_client *client,
546 if (match || !pdata) { 779 if (match || !pdata) {
547 base = -1; 780 base = -1;
548 pullups = 0; 781 pullups = 0;
782 client->irq = irq_of_parse_and_map(client->dev.of_node, 0);
549 } else { 783 } else {
550 if (!gpio_is_valid(pdata->base)) { 784 if (!gpio_is_valid(pdata->base)) {
551 dev_dbg(&client->dev, "invalid platform data\n"); 785 dev_dbg(&client->dev, "invalid platform data\n");
@@ -559,6 +793,7 @@ static int mcp230xx_probe(struct i2c_client *client,
559 if (!mcp) 793 if (!mcp)
560 return -ENOMEM; 794 return -ENOMEM;
561 795
796 mcp->irq = client->irq;
562 status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, 797 status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
563 id->driver_data, base, pullups); 798 id->driver_data, base, pullups);
564 if (status) 799 if (status)
@@ -579,6 +814,9 @@ static int mcp230xx_remove(struct i2c_client *client)
579 struct mcp23s08 *mcp = i2c_get_clientdata(client); 814 struct mcp23s08 *mcp = i2c_get_clientdata(client);
580 int status; 815 int status;
581 816
817 if (client->irq && mcp->irq_controller)
818 mcp23s08_irq_teardown(mcp);
819
582 status = gpiochip_remove(&mcp->chip); 820 status = gpiochip_remove(&mcp->chip);
583 if (status == 0) 821 if (status == 0)
584 kfree(mcp); 822 kfree(mcp);