diff options
author | Sebastian Reichel <sre@kernel.org> | 2017-02-28 08:34:03 +0100 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2017-02-28 08:34:06 +0100 |
commit | d0475a1c30ac1733ff09c5c1d044d93a0cf9800b (patch) | |
tree | 97055b573082d27092661a7a358eeebccdbfa1fc | |
parent | cea17575cf74671f09440e5e58f4da96a2952a14 (diff) | |
download | linux-d0475a1c30ac1733ff09c5c1d044d93a0cf9800b.tar.bz2 |
gpio: add sc18is600 driversc18is600
SC18IS600 and CP2120 are SPI -> I²C bus bridges, that also
have 4/8 GPIO lines. This driver adds simple support for
the (IRQ incapable) GPIO lines.
Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r-- | drivers/gpio/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-sc18is600.c | 134 |
3 files changed, 145 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d5d36549ecc1..55405c0211f2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -956,6 +956,16 @@ config GPIO_RC5T583 This driver provides the support for driving/reading the gpio pins of RC5T583 device through standard gpio library. +config GPIO_SC18IS600 + tristate "SC18IS600 GPIO support" + depends on I2C_SC18IS600 + help + Say yes here to support GPIO interface of SC18IS600 and CP2120 + SPI to I2C bus translation chip. + + To compile this driver as a module, choose M here: the module + will be called gpio-sc18is600. + config GPIO_STMPE bool "STMPE GPIOs" depends on MFD_STMPE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a7676b82de6f..2ffd02c6a3bb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o +obj-$(CONFIG_GPIO_SC18IS600) += gpio-sc18is600.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o diff --git a/drivers/gpio/gpio-sc18is600.c b/drivers/gpio/gpio-sc18is600.c new file mode 100644 index 000000000000..cfc3398834a1 --- /dev/null +++ b/drivers/gpio/gpio-sc18is600.c @@ -0,0 +1,134 @@ +#include <linux/gpio.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/module.h> + +#define SC18IS600_REG_IO_CONFIG 0x00 +#define SC18IS600_REG_IO_STATE 0x01 +#define SC18IS600_REG_IO_CONFIG2 0x07 /* only cp2120 */ + +struct gpio_sc18is600 { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static int sc18is600_mode(struct gpio_sc18is600 *sc18is600, u8 gpio, u8 mode) +{ + u8 reg = (gpio <= 4) ? + SC18IS600_REG_IO_CONFIG : SC18IS600_REG_IO_CONFIG2; + + u8 mask = (0x03 << (gpio % 4)); + u8 val = (mode << (gpio % 4)); + + return regmap_update_bits(sc18is600->regmap, reg, mask, val); +} + +void sc18is600_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gpio_sc18is600 *sc18is600 = gpiochip_get_data(chip); + u8 reg = SC18IS600_REG_IO_STATE; + u8 mask = (1 << offset); + u8 val = value ? 0xff : 0x00; + int err; + + err = regmap_update_bits(sc18is600->regmap, reg, mask, val); + if (err) + dev_err(chip->parent, "regmap failed: %d", err); +} + +static int sc18is600_get(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_sc18is600 *sc18is600 = gpiochip_get_data(chip); + u8 reg = SC18IS600_REG_IO_STATE; + u8 mask = (1 << offset); + int val, err; + + err = regmap_read(sc18is600->regmap, reg, &val); + if (err) { + dev_err(chip->parent, "regmap failed: %d", err); + return err; + } + + return val & mask; +} + +static int sc18is600_input(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_sc18is600 *sc18is600 = gpiochip_get_data(chip); + + /* input only mode */ + return sc18is600_mode(sc18is600, offset, 0x01); +} + +static int sc18is600_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gpio_sc18is600 *sc18is600 = gpiochip_get_data(chip); + int err; + + /* push-pull mode */ + err = sc18is600_mode(sc18is600, offset, 0x02); + if (err) + return err; + + sc18is600_set(chip, offset, value); + + return 0; +} + +static int gpio_sc18is600_probe(struct platform_device *pdev) +{ + struct gpio_sc18is600 *sc18is600; + u8 *chiptype = dev_get_platdata(&pdev->dev); + int err; + + sc18is600 = devm_kzalloc(&pdev->dev, sizeof(*sc18is600), GFP_KERNEL); + if (!sc18is600) + return -ENOMEM; + + sc18is600->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!sc18is600->regmap) { + dev_err(&pdev->dev, "missing regmap!"); + return -ENODEV; + } + + if (!chiptype || *chiptype > 0x02) { + dev_err(&pdev->dev, "missing chiptype!"); + return -ENXIO; + } + + sc18is600->chip.base = -1; + sc18is600->chip.direction_input = sc18is600_input; + sc18is600->chip.get = sc18is600_get; + sc18is600->chip.direction_output = sc18is600_output; + sc18is600->chip.set = sc18is600_set; + sc18is600->chip.can_sleep = true; + sc18is600->chip.parent = &pdev->dev; + sc18is600->chip.owner = THIS_MODULE; + + if (*chiptype == 0x02) { + sc18is600->chip.ngpio = 8; + sc18is600->chip.label = "cp2120"; + } else { + sc18is600->chip.ngpio = 4; + sc18is600->chip.label = "sc18is600"; + } + + err = devm_gpiochip_add_data(&pdev->dev, &sc18is600->chip, sc18is600); + if (err) + dev_err(&pdev->dev, "failed to add gpiochip: %d", err); + + return err; +} + +static struct platform_driver sc18is600_driver = { + .driver = { + .name = "gpio-sc18is600", + }, + .probe = gpio_sc18is600_probe, +}; +module_platform_driver(sc18is600_driver); + +MODULE_ALIAS("platform:gpio-sc18is600"); +MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); +MODULE_DESCRIPTION("NXP SC18IS600 GPIO interface"); +MODULE_LICENSE("GPL"); |