summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Reichel <sre@kernel.org>2017-02-28 08:34:03 +0100
committerSebastian Reichel <sre@kernel.org>2017-02-28 08:34:06 +0100
commitd0475a1c30ac1733ff09c5c1d044d93a0cf9800b (patch)
tree97055b573082d27092661a7a358eeebccdbfa1fc
parentcea17575cf74671f09440e5e58f4da96a2952a14 (diff)
downloadlinux-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/Kconfig10
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-sc18is600.c134
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");