Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 82039d24 authored by Sebastian Reichel's avatar Sebastian Reichel Committed by Linus Walleij
Browse files

pinctrl: mcp23s08: add pinconf support



mcp23xxx device have configurable 100k pullup resistors. This adds
support for enabling them using pinctrl's pinconf interface.

Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.co.uk>
Tested-by: default avatarEnric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 64ac43e6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+175 −24
Original line number Diff line number Diff line
@@ -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, &reg);
		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, &reg);
		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);