diff options
Diffstat (limited to 'drivers/gpio/gpio-mcp23s08.c')
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 252 |
1 files changed, 245 insertions, 7 deletions
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 2deb0c5e54a4..1ac288ea810d 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 | */ | ||
99 | static 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 | /*----------------------------------------------------------------------*/ |
340 | static 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 | |||
377 | static 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 | |||
384 | static 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 | |||
392 | static 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 | |||
400 | static 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 | |||
424 | static 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 | |||
431 | static 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 | |||
443 | static 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 | |||
456 | static 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 | |||
464 | static 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 | |||
475 | static 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 | |||
513 | static 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 | ||
372 | static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | 582 | static 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 | ||
@@ -425,20 +636,32 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | |||
425 | } | 636 | } |
426 | 637 | ||
427 | mcp->chip.base = base; | 638 | mcp->chip.base = base; |
428 | mcp->chip.can_sleep = 1; | 639 | mcp->chip.can_sleep = true; |
429 | mcp->chip.dev = dev; | 640 | mcp->chip.dev = dev; |
430 | mcp->chip.owner = THIS_MODULE; | 641 | mcp->chip.owner = THIS_MODULE; |
431 | 642 | ||
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 | } | ||
473 | fail: | 706 | fail: |
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); |
@@ -640,7 +878,7 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
640 | 878 | ||
641 | match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev); | 879 | match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev); |
642 | if (match) { | 880 | if (match) { |
643 | type = (int)match->data; | 881 | type = (int)(uintptr_t)match->data; |
644 | status = of_property_read_u32(spi->dev.of_node, | 882 | status = of_property_read_u32(spi->dev.of_node, |
645 | "microchip,spi-present-mask", &spi_present_mask); | 883 | "microchip,spi-present-mask", &spi_present_mask); |
646 | if (status) { | 884 | if (status) { |