From d0475a1c30ac1733ff09c5c1d044d93a0cf9800b Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Feb 2017 08:34:03 +0100 Subject: gpio: add sc18is600 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- drivers/gpio/Kconfig | 10 ++++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-sc18is600.c | 134 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 drivers/gpio/gpio-sc18is600.c 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 +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("NXP SC18IS600 GPIO interface"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3