diff options
Diffstat (limited to 'drivers/gpio/gpio-mcp23s08.c')
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 137 |
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 | ||
484 | static 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 | }; | ||
493 | MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match); | ||
494 | #endif | ||
495 | |||
496 | #if IS_ENABLED(CONFIG_I2C) | ||
497 | static 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 | }; | ||
506 | MODULE_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 | ||
478 | static int mcp230xx_probe(struct i2c_client *client, | 513 | static 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 | ||