aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-mcp23s08.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-mcp23s08.c')
-rw-r--r--drivers/gpio/gpio-mcp23s08.c137
1 files changed, 106 insertions, 31 deletions
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 3cea0ea79e80..6a4470b84488 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -12,6 +12,8 @@
12#include <linux/spi/mcp23s08.h> 12#include <linux/spi/mcp23s08.h>
13#include <linux/slab.h> 13#include <linux/slab.h>
14#include <asm/byteorder.h> 14#include <asm/byteorder.h>
15#include <linux/of.h>
16#include <linux/of_device.h>
15 17
16/** 18/**
17 * MCP types supported by driver 19 * MCP types supported by driver
@@ -383,6 +385,10 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
383 mcp->chip.direction_output = mcp23s08_direction_output; 385 mcp->chip.direction_output = mcp23s08_direction_output;
384 mcp->chip.set = mcp23s08_set; 386 mcp->chip.set = mcp23s08_set;
385 mcp->chip.dbg_show = mcp23s08_dbg_show; 387 mcp->chip.dbg_show = mcp23s08_dbg_show;
388#ifdef CONFIG_OF
389 mcp->chip.of_gpio_n_cells = 2;
390 mcp->chip.of_node = dev->of_node;
391#endif
386 392
387 switch (type) { 393 switch (type) {
388#ifdef CONFIG_SPI_MASTER 394#ifdef CONFIG_SPI_MASTER
@@ -473,6 +479,35 @@ fail:
473 479
474/*----------------------------------------------------------------------*/ 480/*----------------------------------------------------------------------*/
475 481
482#ifdef CONFIG_OF
483#ifdef CONFIG_SPI_MASTER
484static struct of_device_id mcp23s08_spi_of_match[] = {
485 {
486 .compatible = "mcp,mcp23s08", .data = (void *) MCP_TYPE_S08,
487 },
488 {
489 .compatible = "mcp,mcp23s17", .data = (void *) MCP_TYPE_S17,
490 },
491 { },
492};
493MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
494#endif
495
496#if IS_ENABLED(CONFIG_I2C)
497static struct of_device_id mcp23s08_i2c_of_match[] = {
498 {
499 .compatible = "mcp,mcp23008", .data = (void *) MCP_TYPE_008,
500 },
501 {
502 .compatible = "mcp,mcp23017", .data = (void *) MCP_TYPE_017,
503 },
504 { },
505};
506MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
507#endif
508#endif /* CONFIG_OF */
509
510
476#if IS_ENABLED(CONFIG_I2C) 511#if IS_ENABLED(CONFIG_I2C)
477 512
478static int mcp230xx_probe(struct i2c_client *client, 513static int mcp230xx_probe(struct i2c_client *client,
@@ -480,12 +515,23 @@ static int mcp230xx_probe(struct i2c_client *client,
480{ 515{
481 struct mcp23s08_platform_data *pdata; 516 struct mcp23s08_platform_data *pdata;
482 struct mcp23s08 *mcp; 517 struct mcp23s08 *mcp;
483 int status; 518 int status, base, pullups;
484 519 const struct of_device_id *match;
485 pdata = client->dev.platform_data; 520
486 if (!pdata || !gpio_is_valid(pdata->base)) { 521 match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
487 dev_dbg(&client->dev, "invalid or missing platform data\n"); 522 &client->dev);
488 return -EINVAL; 523 if (match) {
524 base = -1;
525 pullups = 0;
526 } else {
527 pdata = client->dev.platform_data;
528 if (!pdata || !gpio_is_valid(pdata->base)) {
529 dev_dbg(&client->dev,
530 "invalid or missing platform data\n");
531 return -EINVAL;
532 }
533 base = pdata->base;
534 pullups = pdata->chip[0].pullups;
489 } 535 }
490 536
491 mcp = kzalloc(sizeof *mcp, GFP_KERNEL); 537 mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
@@ -493,8 +539,7 @@ static int mcp230xx_probe(struct i2c_client *client,
493 return -ENOMEM; 539 return -ENOMEM;
494 540
495 status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, 541 status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
496 id->driver_data, pdata->base, 542 id->driver_data, base, pullups);
497 pdata->chip[0].pullups);
498 if (status) 543 if (status)
499 goto fail; 544 goto fail;
500 545
@@ -531,6 +576,7 @@ static struct i2c_driver mcp230xx_driver = {
531 .driver = { 576 .driver = {
532 .name = "mcp230xx", 577 .name = "mcp230xx",
533 .owner = THIS_MODULE, 578 .owner = THIS_MODULE,
579 .of_match_table = of_match_ptr(mcp23s08_i2c_of_match),
534 }, 580 },
535 .probe = mcp230xx_probe, 581 .probe = mcp230xx_probe,
536 .remove = mcp230xx_remove, 582 .remove = mcp230xx_remove,
@@ -565,28 +611,55 @@ static int mcp23s08_probe(struct spi_device *spi)
565 unsigned chips = 0; 611 unsigned chips = 0;
566 struct mcp23s08_driver_data *data; 612 struct mcp23s08_driver_data *data;
567 int status, type; 613 int status, type;
568 unsigned base; 614 unsigned base = -1,
569 615 ngpio = 0,
570 type = spi_get_device_id(spi)->driver_data; 616 pullups[ARRAY_SIZE(pdata->chip)];
571 617 const struct of_device_id *match;
572 pdata = spi->dev.platform_data; 618 u32 spi_present_mask = 0;
573 if (!pdata || !gpio_is_valid(pdata->base)) { 619
574 dev_dbg(&spi->dev, "invalid or missing platform data\n"); 620 match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev);
575 return -EINVAL; 621 if (match) {
576 } 622 type = (int)match->data;
623 status = of_property_read_u32(spi->dev.of_node,
624 "mcp,spi-present-mask", &spi_present_mask);
625 if (status) {
626 dev_err(&spi->dev, "DT has no spi-present-mask\n");
627 return -ENODEV;
628 }
629 if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) {
630 dev_err(&spi->dev, "invalid spi-present-mask\n");
631 return -ENODEV;
632 }
577 633
578 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { 634 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++)
579 if (!pdata->chip[addr].is_present) 635 pullups[addr] = 0;
580 continue; 636 } else {
581 chips++; 637 type = spi_get_device_id(spi)->driver_data;
582 if ((type == MCP_TYPE_S08) && (addr > 3)) { 638 pdata = spi->dev.platform_data;
583 dev_err(&spi->dev, 639 if (!pdata || !gpio_is_valid(pdata->base)) {
584 "mcp23s08 only supports address 0..3\n"); 640 dev_dbg(&spi->dev,
641 "invalid or missing platform data\n");
585 return -EINVAL; 642 return -EINVAL;
586 } 643 }
644
645 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
646 if (!pdata->chip[addr].is_present)
647 continue;
648 chips++;
649 if ((type == MCP_TYPE_S08) && (addr > 3)) {
650 dev_err(&spi->dev,
651 "mcp23s08 only supports address 0..3\n");
652 return -EINVAL;
653 }
654 spi_present_mask |= 1 << addr;
655 pullups[addr] = pdata->chip[addr].pullups;
656 }
657
658 if (!chips)
659 return -ENODEV;
660
661 base = pdata->base;
587 } 662 }
588 if (!chips)
589 return -ENODEV;
590 663
591 data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), 664 data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
592 GFP_KERNEL); 665 GFP_KERNEL);
@@ -594,21 +667,22 @@ static int mcp23s08_probe(struct spi_device *spi)
594 return -ENOMEM; 667 return -ENOMEM;
595 spi_set_drvdata(spi, data); 668 spi_set_drvdata(spi, data);
596 669
597 base = pdata->base;
598 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { 670 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
599 if (!pdata->chip[addr].is_present) 671 if (!(spi_present_mask & (1 << addr)))
600 continue; 672 continue;
601 chips--; 673 chips--;
602 data->mcp[addr] = &data->chip[chips]; 674 data->mcp[addr] = &data->chip[chips];
603 status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, 675 status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
604 0x40 | (addr << 1), type, base, 676 0x40 | (addr << 1), type, base,
605 pdata->chip[addr].pullups); 677 pullups[addr]);
606 if (status < 0) 678 if (status < 0)
607 goto fail; 679 goto fail;
608 680
609 base += (type == MCP_TYPE_S17) ? 16 : 8; 681 if (base != -1)
682 base += (type == MCP_TYPE_S17) ? 16 : 8;
683 ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
610 } 684 }
611 data->ngpio = base - pdata->base; 685 data->ngpio = ngpio;
612 686
613 /* NOTE: these chips have a relatively sane IRQ framework, with 687 /* NOTE: these chips have a relatively sane IRQ framework, with
614 * per-signal masking and level/edge triggering. It's not yet 688 * per-signal masking and level/edge triggering. It's not yet
@@ -668,6 +742,7 @@ static struct spi_driver mcp23s08_driver = {
668 .driver = { 742 .driver = {
669 .name = "mcp23s08", 743 .name = "mcp23s08",
670 .owner = THIS_MODULE, 744 .owner = THIS_MODULE,
745 .of_match_table = of_match_ptr(mcp23s08_spi_of_match),
671 }, 746 },
672}; 747};
673 748