diff options
author | Sonic Zhang <sonic.zhang@analog.com> | 2014-08-31 23:19:52 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2014-09-04 12:59:15 -0400 |
commit | 3af0dbd592fe0a92002f16e341519ba03e92adf7 (patch) | |
tree | 2e9d28522004ab8a16283fc8cc33d6de13cbdbc6 | |
parent | 59e22114b253aaa7caf14221df4dcf924d067922 (diff) |
gpio: mcp23s08 to support both device tree and platform data
Device tree is not enabled in some architecture where gpio
driver mcp23s08 is still required.
v2-changes:
- Parse device tree properties into platform data other than
individual variables.
v3-changes:
- Use of_node in gpio_chip device structure, because the
struct device * always has an of_node which is NULL when
OF is not used.
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/gpio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 64 | ||||
-rw-r--r-- | include/linux/spi/mcp23s08.h | 18 |
3 files changed, 52 insertions, 31 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ec27ec0be8c2..ec398bee7c60 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -806,7 +806,6 @@ config GPIO_MAX7301 | |||
806 | 806 | ||
807 | config GPIO_MCP23S08 | 807 | config GPIO_MCP23S08 |
808 | tristate "Microchip MCP23xxx I/O expander" | 808 | tristate "Microchip MCP23xxx I/O expander" |
809 | depends on OF_GPIO | ||
810 | depends on (SPI_MASTER && !I2C) || I2C | 809 | depends on (SPI_MASTER && !I2C) || I2C |
811 | help | 810 | help |
812 | SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 | 811 | SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 |
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 6f183d9b487e..8488e2fd307c 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c | |||
@@ -479,7 +479,7 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp) | |||
479 | 479 | ||
480 | mutex_init(&mcp->irq_lock); | 480 | mutex_init(&mcp->irq_lock); |
481 | 481 | ||
482 | mcp->irq_domain = irq_domain_add_linear(chip->of_node, chip->ngpio, | 482 | mcp->irq_domain = irq_domain_add_linear(chip->dev->of_node, chip->ngpio, |
483 | &irq_domain_simple_ops, mcp); | 483 | &irq_domain_simple_ops, mcp); |
484 | if (!mcp->irq_domain) | 484 | if (!mcp->irq_domain) |
485 | return -ENODEV; | 485 | return -ENODEV; |
@@ -581,7 +581,7 @@ done: | |||
581 | 581 | ||
582 | static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | 582 | static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, |
583 | void *data, unsigned addr, unsigned type, | 583 | void *data, unsigned addr, unsigned type, |
584 | unsigned base, unsigned pullups) | 584 | struct mcp23s08_platform_data *pdata, int cs) |
585 | { | 585 | { |
586 | int status; | 586 | int status; |
587 | bool mirror = false; | 587 | bool mirror = false; |
@@ -635,7 +635,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | |||
635 | return -EINVAL; | 635 | return -EINVAL; |
636 | } | 636 | } |
637 | 637 | ||
638 | mcp->chip.base = base; | 638 | mcp->chip.base = pdata->base; |
639 | mcp->chip.can_sleep = true; | 639 | mcp->chip.can_sleep = true; |
640 | mcp->chip.dev = dev; | 640 | mcp->chip.dev = dev; |
641 | mcp->chip.owner = THIS_MODULE; | 641 | mcp->chip.owner = THIS_MODULE; |
@@ -648,11 +648,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | |||
648 | if (status < 0) | 648 | if (status < 0) |
649 | goto fail; | 649 | goto fail; |
650 | 650 | ||
651 | mcp->irq_controller = of_property_read_bool(mcp->chip.of_node, | 651 | mcp->irq_controller = pdata->irq_controller; |
652 | "interrupt-controller"); | ||
653 | if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017)) | 652 | if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017)) |
654 | mirror = of_property_read_bool(mcp->chip.of_node, | 653 | mirror = pdata->mirror; |
655 | "microchip,irq-mirror"); | ||
656 | 654 | ||
657 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) { | 655 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) { |
658 | /* mcp23s17 has IOCON twice, make sure they are in sync */ | 656 | /* mcp23s17 has IOCON twice, make sure they are in sync */ |
@@ -668,7 +666,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, | |||
668 | } | 666 | } |
669 | 667 | ||
670 | /* configure ~100K pullups */ | 668 | /* configure ~100K pullups */ |
671 | status = mcp->ops->write(mcp, MCP_GPPU, pullups); | 669 | status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups); |
672 | if (status < 0) | 670 | if (status < 0) |
673 | goto fail; | 671 | goto fail; |
674 | 672 | ||
@@ -768,25 +766,29 @@ MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match); | |||
768 | static int mcp230xx_probe(struct i2c_client *client, | 766 | static int mcp230xx_probe(struct i2c_client *client, |
769 | const struct i2c_device_id *id) | 767 | const struct i2c_device_id *id) |
770 | { | 768 | { |
771 | struct mcp23s08_platform_data *pdata; | 769 | struct mcp23s08_platform_data *pdata, local_pdata; |
772 | struct mcp23s08 *mcp; | 770 | struct mcp23s08 *mcp; |
773 | int status, base, pullups; | 771 | int status; |
774 | const struct of_device_id *match; | 772 | const struct of_device_id *match; |
775 | 773 | ||
776 | match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), | 774 | match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), |
777 | &client->dev); | 775 | &client->dev); |
778 | pdata = dev_get_platdata(&client->dev); | 776 | if (match) { |
779 | if (match || !pdata) { | 777 | pdata = &local_pdata; |
780 | base = -1; | 778 | pdata->base = -1; |
781 | pullups = 0; | 779 | pdata->chip[0].pullups = 0; |
780 | pdata->irq_controller = of_property_read_bool( | ||
781 | client->dev.of_node, | ||
782 | "interrupt-controller"); | ||
783 | pdata->mirror = of_property_read_bool(client->dev.of_node, | ||
784 | "microchip,irq-mirror"); | ||
782 | client->irq = irq_of_parse_and_map(client->dev.of_node, 0); | 785 | client->irq = irq_of_parse_and_map(client->dev.of_node, 0); |
783 | } else { | 786 | } else { |
784 | if (!gpio_is_valid(pdata->base)) { | 787 | pdata = dev_get_platdata(&client->dev); |
788 | if (!pdata || !gpio_is_valid(pdata->base)) { | ||
785 | dev_dbg(&client->dev, "invalid platform data\n"); | 789 | dev_dbg(&client->dev, "invalid platform data\n"); |
786 | return -EINVAL; | 790 | return -EINVAL; |
787 | } | 791 | } |
788 | base = pdata->base; | ||
789 | pullups = pdata->chip[0].pullups; | ||
790 | } | 792 | } |
791 | 793 | ||
792 | mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); | 794 | mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); |
@@ -795,7 +797,7 @@ static int mcp230xx_probe(struct i2c_client *client, | |||
795 | 797 | ||
796 | mcp->irq = client->irq; | 798 | mcp->irq = client->irq; |
797 | status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, | 799 | status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, |
798 | id->driver_data, base, pullups); | 800 | id->driver_data, pdata, 0); |
799 | if (status) | 801 | if (status) |
800 | goto fail; | 802 | goto fail; |
801 | 803 | ||
@@ -863,14 +865,12 @@ static void mcp23s08_i2c_exit(void) { } | |||
863 | 865 | ||
864 | static int mcp23s08_probe(struct spi_device *spi) | 866 | static int mcp23s08_probe(struct spi_device *spi) |
865 | { | 867 | { |
866 | struct mcp23s08_platform_data *pdata; | 868 | struct mcp23s08_platform_data *pdata, local_pdata; |
867 | unsigned addr; | 869 | unsigned addr; |
868 | int chips = 0; | 870 | int chips = 0; |
869 | struct mcp23s08_driver_data *data; | 871 | struct mcp23s08_driver_data *data; |
870 | int status, type; | 872 | int status, type; |
871 | unsigned base = -1, | 873 | unsigned ngpio = 0; |
872 | ngpio = 0, | ||
873 | pullups[ARRAY_SIZE(pdata->chip)]; | ||
874 | const struct of_device_id *match; | 874 | const struct of_device_id *match; |
875 | u32 spi_present_mask = 0; | 875 | u32 spi_present_mask = 0; |
876 | 876 | ||
@@ -893,11 +893,18 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
893 | return -ENODEV; | 893 | return -ENODEV; |
894 | } | 894 | } |
895 | 895 | ||
896 | pdata = &local_pdata; | ||
897 | pdata->base = -1; | ||
896 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { | 898 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { |
897 | pullups[addr] = 0; | 899 | pdata->chip[addr].pullups = 0; |
898 | if (spi_present_mask & (1 << addr)) | 900 | if (spi_present_mask & (1 << addr)) |
899 | chips++; | 901 | chips++; |
900 | } | 902 | } |
903 | pdata->irq_controller = of_property_read_bool( | ||
904 | spi->dev.of_node, | ||
905 | "interrupt-controller"); | ||
906 | pdata->mirror = of_property_read_bool(spi->dev.of_node, | ||
907 | "microchip,irq-mirror"); | ||
901 | } else { | 908 | } else { |
902 | type = spi_get_device_id(spi)->driver_data; | 909 | type = spi_get_device_id(spi)->driver_data; |
903 | pdata = dev_get_platdata(&spi->dev); | 910 | pdata = dev_get_platdata(&spi->dev); |
@@ -917,10 +924,7 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
917 | return -EINVAL; | 924 | return -EINVAL; |
918 | } | 925 | } |
919 | spi_present_mask |= 1 << addr; | 926 | spi_present_mask |= 1 << addr; |
920 | pullups[addr] = pdata->chip[addr].pullups; | ||
921 | } | 927 | } |
922 | |||
923 | base = pdata->base; | ||
924 | } | 928 | } |
925 | 929 | ||
926 | if (!chips) | 930 | if (!chips) |
@@ -938,13 +942,13 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
938 | chips--; | 942 | chips--; |
939 | data->mcp[addr] = &data->chip[chips]; | 943 | data->mcp[addr] = &data->chip[chips]; |
940 | status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, | 944 | status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, |
941 | 0x40 | (addr << 1), type, base, | 945 | 0x40 | (addr << 1), type, pdata, |
942 | pullups[addr]); | 946 | addr); |
943 | if (status < 0) | 947 | if (status < 0) |
944 | goto fail; | 948 | goto fail; |
945 | 949 | ||
946 | if (base != -1) | 950 | if (pdata->base != -1) |
947 | base += (type == MCP_TYPE_S17) ? 16 : 8; | 951 | pdata->base += (type == MCP_TYPE_S17) ? 16 : 8; |
948 | ngpio += (type == MCP_TYPE_S17) ? 16 : 8; | 952 | ngpio += (type == MCP_TYPE_S17) ? 16 : 8; |
949 | } | 953 | } |
950 | data->ngpio = ngpio; | 954 | data->ngpio = ngpio; |
diff --git a/include/linux/spi/mcp23s08.h b/include/linux/spi/mcp23s08.h index 2d676d5aaa89..aa07d7b32568 100644 --- a/include/linux/spi/mcp23s08.h +++ b/include/linux/spi/mcp23s08.h | |||
@@ -22,4 +22,22 @@ struct mcp23s08_platform_data { | |||
22 | * base to base+15 (or base+31 for s17 variant). | 22 | * base to base+15 (or base+31 for s17 variant). |
23 | */ | 23 | */ |
24 | unsigned base; | 24 | unsigned base; |
25 | /* Marks the device as a interrupt controller. | ||
26 | * NOTE: The interrupt functionality is only supported for i2c | ||
27 | * versions of the chips. The spi chips can also do the interrupts, | ||
28 | * but this is not supported by the linux driver yet. | ||
29 | */ | ||
30 | bool irq_controller; | ||
31 | |||
32 | /* Sets the mirror flag in the IOCON register. Devices | ||
33 | * with two interrupt outputs (these are the devices ending with 17 and | ||
34 | * those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and | ||
35 | * IO 8-15 are bank 2. These chips have two different interrupt outputs: | ||
36 | * One for bank 1 and another for bank 2. If irq-mirror is set, both | ||
37 | * interrupts are generated regardless of the bank that an input change | ||
38 | * occurred on. If it is not set, the interrupt are only generated for | ||
39 | * the bank they belong to. | ||
40 | * On devices with only one interrupt output this property is useless. | ||
41 | */ | ||
42 | bool mirror; | ||
25 | }; | 43 | }; |