Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Sonic Zhang and committed by
Linus Walleij
3af0dbd5 59e22114

+52 -31
-1
drivers/gpio/Kconfig
··· 806 806 807 807 config GPIO_MCP23S08 808 808 tristate "Microchip MCP23xxx I/O expander" 809 - depends on OF_GPIO 810 809 depends on (SPI_MASTER && !I2C) || I2C 811 810 help 812 811 SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
+34 -30
drivers/gpio/gpio-mcp23s08.c
··· 479 479 480 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 483 &irq_domain_simple_ops, mcp); 484 484 if (!mcp->irq_domain) 485 485 return -ENODEV; ··· 581 581 582 582 static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, 583 583 void *data, unsigned addr, unsigned type, 584 - unsigned base, unsigned pullups) 584 + struct mcp23s08_platform_data *pdata, int cs) 585 585 { 586 586 int status; 587 587 bool mirror = false; ··· 635 635 return -EINVAL; 636 636 } 637 637 638 - mcp->chip.base = base; 638 + mcp->chip.base = pdata->base; 639 639 mcp->chip.can_sleep = true; 640 640 mcp->chip.dev = dev; 641 641 mcp->chip.owner = THIS_MODULE; ··· 648 648 if (status < 0) 649 649 goto fail; 650 650 651 - mcp->irq_controller = of_property_read_bool(mcp->chip.of_node, 652 - "interrupt-controller"); 651 + mcp->irq_controller = pdata->irq_controller; 653 652 if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017)) 654 - mirror = of_property_read_bool(mcp->chip.of_node, 655 - "microchip,irq-mirror"); 653 + mirror = pdata->mirror; 656 654 657 655 if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) { 658 656 /* mcp23s17 has IOCON twice, make sure they are in sync */ ··· 666 668 } 667 669 668 670 /* configure ~100K pullups */ 669 - status = mcp->ops->write(mcp, MCP_GPPU, pullups); 671 + status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups); 670 672 if (status < 0) 671 673 goto fail; 672 674 ··· 766 768 static int mcp230xx_probe(struct i2c_client *client, 767 769 const struct i2c_device_id *id) 768 770 { 769 - struct mcp23s08_platform_data *pdata; 771 + struct mcp23s08_platform_data *pdata, local_pdata; 770 772 struct mcp23s08 *mcp; 771 - int status, base, pullups; 773 + int status; 772 774 const struct of_device_id *match; 773 775 774 776 match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), 775 777 &client->dev); 776 - pdata = dev_get_platdata(&client->dev); 777 - if (match || !pdata) { 778 - base = -1; 779 - pullups = 0; 778 + if (match) { 779 + pdata = &local_pdata; 780 + pdata->base = -1; 781 + pdata->chip[0].pullups = 0; 782 + pdata->irq_controller = of_property_read_bool( 783 + client->dev.of_node, 784 + "interrupt-controller"); 785 + pdata->mirror = of_property_read_bool(client->dev.of_node, 786 + "microchip,irq-mirror"); 780 787 client->irq = irq_of_parse_and_map(client->dev.of_node, 0); 781 788 } else { 782 - if (!gpio_is_valid(pdata->base)) { 789 + pdata = dev_get_platdata(&client->dev); 790 + if (!pdata || !gpio_is_valid(pdata->base)) { 783 791 dev_dbg(&client->dev, "invalid platform data\n"); 784 792 return -EINVAL; 785 793 } 786 - base = pdata->base; 787 - pullups = pdata->chip[0].pullups; 788 794 } 789 795 790 796 mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); ··· 797 795 798 796 mcp->irq = client->irq; 799 797 status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, 800 - id->driver_data, base, pullups); 798 + id->driver_data, pdata, 0); 801 799 if (status) 802 800 goto fail; 803 801 ··· 865 863 866 864 static int mcp23s08_probe(struct spi_device *spi) 867 865 { 868 - struct mcp23s08_platform_data *pdata; 866 + struct mcp23s08_platform_data *pdata, local_pdata; 869 867 unsigned addr; 870 868 int chips = 0; 871 869 struct mcp23s08_driver_data *data; 872 870 int status, type; 873 - unsigned base = -1, 874 - ngpio = 0, 875 - pullups[ARRAY_SIZE(pdata->chip)]; 871 + unsigned ngpio = 0; 876 872 const struct of_device_id *match; 877 873 u32 spi_present_mask = 0; 878 874 ··· 893 893 return -ENODEV; 894 894 } 895 895 896 + pdata = &local_pdata; 897 + pdata->base = -1; 896 898 for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { 897 - pullups[addr] = 0; 899 + pdata->chip[addr].pullups = 0; 898 900 if (spi_present_mask & (1 << addr)) 899 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 908 } else { 902 909 type = spi_get_device_id(spi)->driver_data; 903 910 pdata = dev_get_platdata(&spi->dev); ··· 924 917 return -EINVAL; 925 918 } 926 919 spi_present_mask |= 1 << addr; 927 - pullups[addr] = pdata->chip[addr].pullups; 928 920 } 929 - 930 - base = pdata->base; 931 921 } 932 922 933 923 if (!chips) ··· 942 938 chips--; 943 939 data->mcp[addr] = &data->chip[chips]; 944 940 status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, 945 - 0x40 | (addr << 1), type, base, 946 - pullups[addr]); 941 + 0x40 | (addr << 1), type, pdata, 942 + addr); 947 943 if (status < 0) 948 944 goto fail; 949 945 950 - if (base != -1) 951 - base += (type == MCP_TYPE_S17) ? 16 : 8; 946 + if (pdata->base != -1) 947 + pdata->base += (type == MCP_TYPE_S17) ? 16 : 8; 952 948 ngpio += (type == MCP_TYPE_S17) ? 16 : 8; 953 949 } 954 950 data->ngpio = ngpio;
+18
include/linux/spi/mcp23s08.h
··· 22 22 * base to base+15 (or base+31 for s17 variant). 23 23 */ 24 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 };