Message ID | 20170515092438.13076-3-sebastian.reichel@collabora.co.uk |
---|---|
State | New |
Headers | show |
Hi, On Mon, May 15, 2017 at 11:24:26AM +0200, Sebastian Reichel wrote: > mcp23xxx device have configurable 100k pullup resistors. This adds > support for enabling them using pinctrl's pinconf interface. > > Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk> I just noticed, that I forgot to add: Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com> -- Sebastian > --- > drivers/pinctrl/Kconfig | 1 + > drivers/pinctrl/pinctrl-mcp23s08.c | 199 ++++++++++++++++++++++++++++++++----- > 2 files changed, 176 insertions(+), 24 deletions(-) > > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig > index b5aa50c51633..1dabd1d79c1d 100644 > --- a/drivers/pinctrl/Kconfig > +++ b/drivers/pinctrl/Kconfig > @@ -153,6 +153,7 @@ config PINCTRL_MCP23S08 > select GPIOLIB_IRQCHIP > select REGMAP_I2C if I2C > select REGMAP_SPI if SPI_MASTER > + select GENERIC_PINCONF > help > SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 > I/O expanders. > diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c > index 2a57d024481d..d957c4bbc8c1 100644 > --- a/drivers/pinctrl/pinctrl-mcp23s08.c > +++ b/drivers/pinctrl/pinctrl-mcp23s08.c > @@ -24,6 +24,9 @@ > #include <linux/of_irq.h> > #include <linux/of_device.h> > #include <linux/regmap.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/pinctrl/pinconf.h> > +#include <linux/pinctrl/pinconf-generic.h> > > /** > * MCP types supported by driver > @@ -77,6 +80,9 @@ struct mcp23s08 { > > struct regmap *regmap; > struct device *dev; > + > + struct pinctrl_dev *pctldev; > + struct pinctrl_desc pinctrl_desc; > }; > > static const struct regmap_config mcp23x08_regmap = { > @@ -96,6 +102,158 @@ static const struct regmap_config mcp23x17_regmap = { > .val_format_endian = REGMAP_ENDIAN_LITTLE, > }; > > +static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) > +{ > + return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); > +} > + > +static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) > +{ > + return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); > +} > + > +static int mcp_set_bit(struct mcp23s08 *mcp, unsigned int reg, > + unsigned int pin, bool enabled) > +{ > + u16 val = enabled ? 0xffff : 0x0000; > + u16 mask = BIT(pin); > + return regmap_update_bits(mcp->regmap, reg << mcp->reg_shift, > + mask, val); > +} > + > +static int mcp_update_cache(struct mcp23s08 *mcp) > +{ > + int ret, reg, i; > + > + for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { > + ret = mcp_read(mcp, i, ®); > + if (ret < 0) > + return ret; > + mcp->cache[i] = reg; > + } > + > + return 0; > +} > + > +static const struct pinctrl_pin_desc mcp23x08_pins[] = { > + PINCTRL_PIN(0, "gpio0"), > + PINCTRL_PIN(1, "gpio1"), > + PINCTRL_PIN(2, "gpio2"), > + PINCTRL_PIN(3, "gpio3"), > + PINCTRL_PIN(4, "gpio4"), > + PINCTRL_PIN(5, "gpio5"), > + PINCTRL_PIN(6, "gpio6"), > + PINCTRL_PIN(7, "gpio7"), > +}; > + > +static const struct pinctrl_pin_desc mcp23x17_pins[] = { > + PINCTRL_PIN(0, "gpio0"), > + PINCTRL_PIN(1, "gpio1"), > + PINCTRL_PIN(2, "gpio2"), > + PINCTRL_PIN(3, "gpio3"), > + PINCTRL_PIN(4, "gpio4"), > + PINCTRL_PIN(5, "gpio5"), > + PINCTRL_PIN(6, "gpio6"), > + PINCTRL_PIN(7, "gpio7"), > + PINCTRL_PIN(8, "gpio8"), > + PINCTRL_PIN(9, "gpio9"), > + PINCTRL_PIN(10, "gpio10"), > + PINCTRL_PIN(11, "gpio11"), > + PINCTRL_PIN(12, "gpio12"), > + PINCTRL_PIN(13, "gpio13"), > + PINCTRL_PIN(14, "gpio14"), > + PINCTRL_PIN(15, "gpio15"), > +}; > + > +static int mcp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) > +{ > + return 0; > +} > + > +static const char *mcp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, > + unsigned int group) > +{ > + return NULL; > +} > + > +static int mcp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, > + unsigned int group, > + const unsigned int **pins, > + unsigned int *num_pins) > +{ > + return -ENOTSUPP; > +} > + > +static const struct pinctrl_ops mcp_pinctrl_ops = { > + .get_groups_count = mcp_pinctrl_get_groups_count, > + .get_group_name = mcp_pinctrl_get_group_name, > + .get_group_pins = mcp_pinctrl_get_group_pins, > +#ifdef CONFIG_OF > + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, > + .dt_free_map = pinconf_generic_dt_free_map, > +#endif > +}; > + > +static int mcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, > + unsigned long *config) > +{ > + struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); > + enum pin_config_param param = pinconf_to_config_param(*config); > + unsigned int data, status; > + int ret; > + > + switch (param) { > + case PIN_CONFIG_BIAS_PULL_UP: > + ret = mcp_read(mcp, MCP_GPPU, &data); > + if (ret < 0) > + return ret; > + status = (data & BIT(pin)) ? 1 : 0; > + break; > + default: > + dev_err(mcp->dev, "Invalid config param %04x\n", param); > + return -ENOTSUPP; > + } > + > + *config = 0; > + > + return status ? 0 : -EINVAL; > +} > + > +static int mcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, > + unsigned long *configs, unsigned int num_configs) > +{ > + struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); > + enum pin_config_param param; > + u32 arg, mask; > + u16 val; > + int ret = 0; > + int i; > + > + for (i = 0; i < num_configs; i++) { > + param = pinconf_to_config_param(configs[i]); > + arg = pinconf_to_config_argument(configs[i]); > + > + switch (param) { > + case PIN_CONFIG_BIAS_PULL_UP: > + val = arg ? 0xFFFF : 0x0000; > + mask = BIT(pin); > + ret = mcp_set_bit(mcp, MCP_GPPU, pin, arg); > + break; > + default: > + dev_err(mcp->dev, "Invalid config param %04x\n", param); > + return -ENOTSUPP; > + } > + } > + > + return ret; > +} > + > +static const struct pinconf_ops mcp_pinconf_ops = { > + .pin_config_get = mcp_pinconf_get, > + .pin_config_set = mcp_pinconf_set, > + .is_generic = true, > +}; > + > /*----------------------------------------------------------------------*/ > > #ifdef CONFIG_SPI_MASTER > @@ -158,30 +316,6 @@ static const struct regmap_bus mcp23sxx_spi_regmap = { > > #endif /* CONFIG_SPI_MASTER */ > > -static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) > -{ > - return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); > -} > - > -static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) > -{ > - return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); > -} > - > -static int mcp_update_cache(struct mcp23s08 *mcp) > -{ > - int ret, reg, i; > - > - for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { > - ret = mcp_read(mcp, i, ®); > - if (ret < 0) > - return ret; > - mcp->cache[i] = reg; > - } > - > - return 0; > -} > - > /*----------------------------------------------------------------------*/ > > /* A given spi_device can represent up to eight mcp23sxx chips > @@ -682,6 +816,23 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, > if (ret) > goto fail; > } > + > + mcp->pinctrl_desc.name = "mcp23xxx-pinctrl"; > + mcp->pinctrl_desc.pctlops = &mcp_pinctrl_ops; > + mcp->pinctrl_desc.confops = &mcp_pinconf_ops; > + mcp->pinctrl_desc.npins = mcp->chip.ngpio; > + if (mcp->pinctrl_desc.npins == 8) > + mcp->pinctrl_desc.pins = mcp23x08_pins; > + else if (mcp->pinctrl_desc.npins == 16) > + mcp->pinctrl_desc.pins = mcp23x17_pins; > + mcp->pinctrl_desc.owner = THIS_MODULE; > + > + mcp->pctldev = devm_pinctrl_register(dev, &mcp->pinctrl_desc, mcp); > + if (IS_ERR(mcp->pctldev)) { > + ret = PTR_ERR(mcp->pctldev); > + goto fail; > + } > + > fail: > if (ret < 0) > dev_dbg(dev, "can't setup chip %d, --> %d\n", addr, ret); > -- > 2.11.0 >
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index b5aa50c51633..1dabd1d79c1d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -153,6 +153,7 @@ config PINCTRL_MCP23S08 select GPIOLIB_IRQCHIP select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER + select GENERIC_PINCONF help SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 I/O expanders. diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 2a57d024481d..d957c4bbc8c1 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -24,6 +24,9 @@ #include <linux/of_irq.h> #include <linux/of_device.h> #include <linux/regmap.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> /** * MCP types supported by driver @@ -77,6 +80,9 @@ struct mcp23s08 { struct regmap *regmap; struct device *dev; + + struct pinctrl_dev *pctldev; + struct pinctrl_desc pinctrl_desc; }; static const struct regmap_config mcp23x08_regmap = { @@ -96,6 +102,158 @@ static const struct regmap_config mcp23x17_regmap = { .val_format_endian = REGMAP_ENDIAN_LITTLE, }; +static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) +{ + return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); +} + +static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) +{ + return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); +} + +static int mcp_set_bit(struct mcp23s08 *mcp, unsigned int reg, + unsigned int pin, bool enabled) +{ + u16 val = enabled ? 0xffff : 0x0000; + u16 mask = BIT(pin); + return regmap_update_bits(mcp->regmap, reg << mcp->reg_shift, + mask, val); +} + +static int mcp_update_cache(struct mcp23s08 *mcp) +{ + int ret, reg, i; + + for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { + ret = mcp_read(mcp, i, ®); + if (ret < 0) + return ret; + mcp->cache[i] = reg; + } + + return 0; +} + +static const struct pinctrl_pin_desc mcp23x08_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), +}; + +static const struct pinctrl_pin_desc mcp23x17_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), +}; + +static int mcp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 0; +} + +static const char *mcp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + return NULL; +} + +static int mcp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + return -ENOTSUPP; +} + +static const struct pinctrl_ops mcp_pinctrl_ops = { + .get_groups_count = mcp_pinctrl_get_groups_count, + .get_group_name = mcp_pinctrl_get_group_name, + .get_group_pins = mcp_pinctrl_get_group_pins, +#ifdef CONFIG_OF + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +#endif +}; + +static int mcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int data, status; + int ret; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + ret = mcp_read(mcp, MCP_GPPU, &data); + if (ret < 0) + return ret; + status = (data & BIT(pin)) ? 1 : 0; + break; + default: + dev_err(mcp->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; + } + + *config = 0; + + return status ? 0 : -EINVAL; +} + +static int mcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u32 arg, mask; + u16 val; + int ret = 0; + int i; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + val = arg ? 0xFFFF : 0x0000; + mask = BIT(pin); + ret = mcp_set_bit(mcp, MCP_GPPU, pin, arg); + break; + default: + dev_err(mcp->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; + } + } + + return ret; +} + +static const struct pinconf_ops mcp_pinconf_ops = { + .pin_config_get = mcp_pinconf_get, + .pin_config_set = mcp_pinconf_set, + .is_generic = true, +}; + /*----------------------------------------------------------------------*/ #ifdef CONFIG_SPI_MASTER @@ -158,30 +316,6 @@ static const struct regmap_bus mcp23sxx_spi_regmap = { #endif /* CONFIG_SPI_MASTER */ -static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) -{ - return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); -} - -static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) -{ - return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); -} - -static int mcp_update_cache(struct mcp23s08 *mcp) -{ - int ret, reg, i; - - for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { - ret = mcp_read(mcp, i, ®); - if (ret < 0) - return ret; - mcp->cache[i] = reg; - } - - return 0; -} - /*----------------------------------------------------------------------*/ /* A given spi_device can represent up to eight mcp23sxx chips @@ -682,6 +816,23 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, if (ret) goto fail; } + + mcp->pinctrl_desc.name = "mcp23xxx-pinctrl"; + mcp->pinctrl_desc.pctlops = &mcp_pinctrl_ops; + mcp->pinctrl_desc.confops = &mcp_pinconf_ops; + mcp->pinctrl_desc.npins = mcp->chip.ngpio; + if (mcp->pinctrl_desc.npins == 8) + mcp->pinctrl_desc.pins = mcp23x08_pins; + else if (mcp->pinctrl_desc.npins == 16) + mcp->pinctrl_desc.pins = mcp23x17_pins; + mcp->pinctrl_desc.owner = THIS_MODULE; + + mcp->pctldev = devm_pinctrl_register(dev, &mcp->pinctrl_desc, mcp); + if (IS_ERR(mcp->pctldev)) { + ret = PTR_ERR(mcp->pctldev); + goto fail; + } + fail: if (ret < 0) dev_dbg(dev, "can't setup chip %d, --> %d\n", addr, ret);
mcp23xxx device have configurable 100k pullup resistors. This adds support for enabling them using pinctrl's pinconf interface. Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk> --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/pinctrl-mcp23s08.c | 199 ++++++++++++++++++++++++++++++++----- 2 files changed, 176 insertions(+), 24 deletions(-)