On Mon, 11 Feb 2013 12:52:42 +0100, Lars Poeschel <larsi@xxxxxxxxxxxxxxxxx> wrote: > From: Lars Poeschel <poeschel@xxxxxxxxxxx> > > This converts the mcp23s08 driver to be able to be used with device > tree. > Explicitly allow -1 as a legal value for the > mcp23s08_platform_data->base. This is the special value lets the > kernel choose a valid global gpio base number. > There is a "mcp,chips" device tree property, that correspond to the > chips member of the struct mcp23s08_platform_data. It can be used for > multiple mcp23s08/mc23s17 on the same spi chipselect. > > Signed-off-by: Lars Poeschel <poeschel@xxxxxxxxxxx> > --- > v2: > - squashed booth patches together > - fixed build warning, when CONFIG_OF is not defined > - use of_match_ptr macro for of_match_table > > .../devicetree/bindings/gpio/gpio-mcp23s08.txt | 36 ++++++++ > drivers/gpio/gpio-mcp23s08.c | 95 ++++++++++++++++++-- > include/linux/spi/mcp23s08.h | 1 + > 3 files changed, 126 insertions(+), 6 deletions(-) > create mode 100644 Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt > > diff --git a/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt > new file mode 100644 > index 0000000..17eb669 > --- /dev/null > +++ b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt > @@ -0,0 +1,36 @@ > +Microchip MCP2308/MCP23S08/MCP23017/MCP23S17 driver for > +8-/16-bit I/O expander with serial interface (I2C/SPI) > + > +Required properties: > +- compatible : Should be > + - "mcp,mcp23s08" for 8 GPIO SPI version > + - "mcp,mcp23s17" for 16 GPIO SPI version > + - "mcp,mcp23008" for 8 GPIO I2C version or > + - "mcp,mcp23017" for 16 GPIO I2C version of the chip > +- #gpio-cells : Should be two. > + - first cell is the pin number > + - second cell is used to specify optional parameters (currently unused) > +- gpio-controller : Marks the device node as a GPIO controller. > +- reg : For an address on its bus > + > +Optional device specific properties: > +- mcp,chips : This is a table with 2 columns and up to 8 entries. The first column > + is a is_present flag, that makes only sense for SPI chips. Multiple > + chips can share the same chipselect. This flag can be 0 or 1 depending > + if there is a chip at this address or not. > + The second column is written to the GPPU register, selecting a 100k > + pullup resistor or not. Setting a 1 is activating the pullup. > + For I2C chips only the first line in this table is used. Further chips > + are registered at different addresses at the I2C bus. Since these are two separate things, I would put them into separate properties. Perhaps something like: - mcp,spi-present-mask = < mask >; /* one bit per chip */ - mcp,pullup-enable-mask = < enable-value ... >; However, is the pullup selection per-gpio line? If so, then why not encode it into the flags field of the gpio specifier? > + > +Example: > +gpiom1: gpio@20 { > + compatible = "mcp,mcp23017"; > + gpio-controller; > + #gpio-cells = <2>; > + reg = <0x20>; > + chips = < > + /* is_present pullups */ > + 1 0x07 /* chip address 0 */ > + >; > +}; > diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c > index 3cea0ea..ad08a5a 100644 > --- a/drivers/gpio/gpio-mcp23s08.c > +++ b/drivers/gpio/gpio-mcp23s08.c > @@ -12,6 +12,8 @@ > #include <linux/spi/mcp23s08.h> > #include <linux/slab.h> > #include <asm/byteorder.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > > /** > * MCP types supported by driver > @@ -473,17 +475,88 @@ fail: > > /*----------------------------------------------------------------------*/ > > +#ifdef CONFIG_OF > +static struct of_device_id mcp23s08_of_match[] = { > +#ifdef CONFIG_SPI_MASTER > + { > + .compatible = "mcp,mcp23s08", > + }, > + { > + .compatible = "mcp,mcp23s17", > + }, > +#endif > +#if IS_ENABLED(CONFIG_I2C) > + { > + .compatible = "mcp,mcp23008", > + }, > + { > + .compatible = "mcp,mcp23017", > + }, > +#endif > + { }, > +}; > +MODULE_DEVICE_TABLE(of, mcp23s08_of_match); I don't think this is actually what you want. You should use separate match tables; one for I2C and one for SPI. > + > +static struct mcp23s08_platform_data * > + of_get_mcp23s08_pdata(struct device *dev) > +{ > + struct mcp23s08_platform_data *pdata; > + struct device_node *np = dev->of_node; > + u32 chips[sizeof(pdata->chip)]; > + int ret, i, j; > + > + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return NULL; > + > + pdata->base = -1; > + > + for (i = ARRAY_SIZE(pdata->chip) * MCP23S08_CHIP_INFO_MEMBERS; > + i > 0; i -= MCP23S08_CHIP_INFO_MEMBERS) { > + ret = of_property_read_u32_array(np, "mcp,chips", chips, i); > + if (ret == -EOVERFLOW) > + continue; > + break; > + } > + if (!ret) { > + for (j = 0; j < i / MCP23S08_CHIP_INFO_MEMBERS ; j++) { > + pdata->chip[j].is_present = > + chips[j * MCP23S08_CHIP_INFO_MEMBERS]; > + pdata->chip[j].pullups = > + chips[j * MCP23S08_CHIP_INFO_MEMBERS + 1]; > + } > + } > + > + return pdata; Using devm is probably overkill since the pdata structure is not touched again after probe() exits. You could instead just put the mcp23s08_platform_data structure into the stack of the probe hook. > +} > +#else > +static struct mcp23s08_platform_data * > + of_get_mcp23s08_pdata(struct device *dev) > +{ > + return NULL; > +} > +#endif /* CONFIG_OF */ > + > + > #if IS_ENABLED(CONFIG_I2C) > > static int mcp230xx_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > - struct mcp23s08_platform_data *pdata; > + struct mcp23s08_platform_data *pdata = NULL; > struct mcp23s08 *mcp; > int status; > + const struct of_device_id *match; > > - pdata = client->dev.platform_data; > - if (!pdata || !gpio_is_valid(pdata->base)) { > + match = of_match_device(of_match_ptr(mcp23s08_of_match), &client->dev); > + if (match) > + pdata = of_get_mcp23s08_pdata(&client->dev); > + > + /* if there was no pdata in DT, take it the legacy way */ > + if (!pdata) > + pdata = client->dev.platform_data; > + > + if ((!pdata || !gpio_is_valid(pdata->base)) && pdata->base != -1) { > dev_dbg(&client->dev, "invalid or missing platform data\n"); > return -EINVAL; > } > @@ -531,6 +604,7 @@ static struct i2c_driver mcp230xx_driver = { > .driver = { > .name = "mcp230xx", > .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(mcp23s08_of_match), > }, > .probe = mcp230xx_probe, > .remove = mcp230xx_remove, > @@ -560,17 +634,25 @@ static void mcp23s08_i2c_exit(void) { } > > static int mcp23s08_probe(struct spi_device *spi) > { > - struct mcp23s08_platform_data *pdata; > + struct mcp23s08_platform_data *pdata = NULL; > unsigned addr; > unsigned chips = 0; > struct mcp23s08_driver_data *data; > int status, type; > unsigned base; > + const struct of_device_id *match; > > type = spi_get_device_id(spi)->driver_data; > > - pdata = spi->dev.platform_data; > - if (!pdata || !gpio_is_valid(pdata->base)) { > + match = of_match_device(of_match_ptr(mcp23s08_of_match), &spi->dev); > + if (match) > + pdata = of_get_mcp23s08_pdata(&spi->dev); > + > + /* if there was no pdata in DT, take it the legacy way */ > + if (!pdata) > + pdata = spi->dev.platform_data; > + > + if ((!pdata || !gpio_is_valid(pdata->base)) && pdata->base != -1) { > dev_dbg(&spi->dev, "invalid or missing platform data\n"); > return -EINVAL; > } > @@ -668,6 +750,7 @@ static struct spi_driver mcp23s08_driver = { > .driver = { > .name = "mcp23s08", > .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(mcp23s08_of_match), > }, > }; > > diff --git a/include/linux/spi/mcp23s08.h b/include/linux/spi/mcp23s08.h > index 2d676d5..3969e12 100644 > --- a/include/linux/spi/mcp23s08.h > +++ b/include/linux/spi/mcp23s08.h > @@ -1,3 +1,4 @@ > +#define MCP23S08_CHIP_INFO_MEMBERS 2 > > /* FIXME driver should be able to handle IRQs... */ > > -- > 1.7.10.4 > -- Grant Likely, B.Sc, P.Eng. Secret Lab Technologies, Ltd. -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html