]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
gpio: Add a Gateworks PLD GPIO driver
authorLinus Walleij <linus.walleij@linaro.org>
Fri, 1 Feb 2019 20:41:31 +0000 (21:41 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Sat, 9 Feb 2019 10:33:37 +0000 (11:33 +0100)
This adds a driver for Gateworks PLD GPIO, that exist in
two instances on the Gateworks Cambria GW2358-4 router
platform at least.

Cc: Imre Kaloz <kaloz@openwrt.org>
Cc: Tim Harvey <tharvey@gateworks.com>
Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-gw-pld.c [new file with mode: 0644]

index 7a934b40bdae2cecee82588f8e70c031d455f58f..486d9de2716ad13ab1118c1346bf18cbbe3f3c62 100644 (file)
@@ -831,6 +831,13 @@ config GPIO_ADNP
          enough to represent all pins, but the driver will assume a
          register layout for 64 pins (8 registers).
 
+config GPIO_GW_PLD
+       tristate "Gateworks PLD GPIO Expander"
+       depends on OF_GPIO
+       help
+         Say yes here to provide access to the Gateworks I2C PLD GPIO
+         Expander. This is used at least on the Cambria GW2358-4.
+
 config GPIO_MAX7300
        tristate "Maxim MAX7300 GPIO expander"
        select GPIO_MAX730X
index 89cfad015687fad5fc31f0169d83b8c6f7bf26ae..9655927a3dcfa928f09e80a3a3dca1791982eff9 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_FTGPIO010)  += gpio-ftgpio010.o
 obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o
 obj-$(CONFIG_GPIO_GPIO_MM)     += gpio-gpio-mm.o
 obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o
+obj-$(CONFIG_GPIO_GW_PLD)      += gpio-gw-pld.o
 obj-$(CONFIG_GPIO_HLWD)                += gpio-hlwd.o
 obj-$(CONFIG_HTC_EGPIO)                += gpio-htc-egpio.o
 obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o
diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c
new file mode 100644 (file)
index 0000000..242112f
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Gateworks I2C PLD GPIO expander
+//
+// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>
+//
+// Based on code and know-how from the OpenWrt driver:
+// Copyright (C) 2009 Gateworks Corporation
+// Authors: Chris Lang, Imre Kaloz
+
+#include <linux/bits.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+/**
+ * struct gw_pld - State container for Gateworks PLD
+ * @chip: GPIO chip instance
+ * @client: I2C client
+ * @out: shadow register for the output bute
+ */
+struct gw_pld {
+       struct gpio_chip chip;
+       struct i2c_client *client;
+       u8 out;
+};
+
+/*
+ * The Gateworks I2C PLD chip only expose one read and one write register.
+ * Writing a "one" bit (to match the reset state) lets that pin be used as an
+ * input. It is an open-drain model.
+ */
+static int gw_pld_input8(struct gpio_chip *gc, unsigned offset)
+{
+       struct gw_pld *gw = gpiochip_get_data(gc);
+
+       gw->out |= BIT(offset);
+       return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static int gw_pld_get8(struct gpio_chip *gc, unsigned offset)
+{
+       struct gw_pld *gw = gpiochip_get_data(gc);
+       s32 val;
+
+       val = i2c_smbus_read_byte(gw->client);
+
+       return (val < 0) ? 0 : !!(val & BIT(offset));
+}
+
+static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)
+{
+       struct gw_pld *gw = gpiochip_get_data(gc);
+
+       if (value)
+               gw->out |= BIT(offset);
+       else
+               gw->out &= ~BIT(offset);
+
+       return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)
+{
+       gw_pld_output8(gc, offset, value);
+}
+
+static int gw_pld_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct device_node *np = dev->of_node;
+       struct gw_pld *gw;
+       int ret;
+
+       gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL);
+       if (!gw)
+               return -ENOMEM;
+
+       gw->chip.base = -1;
+       gw->chip.can_sleep = true;
+       gw->chip.parent = dev;
+       gw->chip.of_node = np;
+       gw->chip.owner = THIS_MODULE;
+       gw->chip.label = dev_name(dev);
+       gw->chip.ngpio = 8;
+       gw->chip.direction_input = gw_pld_input8;
+       gw->chip.get = gw_pld_get8;
+       gw->chip.direction_output = gw_pld_output8;
+       gw->chip.set = gw_pld_set8;
+       gw->client = client;
+
+       /*
+        * The Gateworks I2C PLD chip does not properly send the acknowledge
+        * bit at all times, but we can still use the standard i2c_smbus
+        * functions by simply ignoring this bit.
+        */
+       client->flags |= I2C_M_IGNORE_NAK;
+       gw->out = 0xFF;
+
+       i2c_set_clientdata(client, gw);
+
+       ret = devm_gpiochip_add_data(dev, &gw->chip, gw);
+       if (ret)
+               return ret;
+
+       dev_info(dev, "registered Gateworks PLD GPIO device\n");
+
+       return 0;
+}
+
+static const struct i2c_device_id gw_pld_id[] = {
+       { "gw-pld", },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, gw_pld_id);
+
+static const struct of_device_id gw_pld_dt_ids[] = {
+       { .compatible = "gateworks,pld-gpio", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, gw_pld_dt_ids);
+
+static struct i2c_driver gw_pld_driver = {
+       .driver = {
+               .name = "gw_pld",
+               .of_match_table = gw_pld_dt_ids,
+       },
+       .probe = gw_pld_probe,
+       .id_table = gw_pld_id,
+};
+module_i2c_driver(gw_pld_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");