[PATCH 4/4] gpio: Add gpio latch driver

Sascha Hauer s.hauer at pengutronix.de
Mon Nov 7 06:45:24 PST 2022


This driver implements a GPIO multiplexer based on latches connected to
other GPIOs. A set of data GPIOs is connected to the data input of
multiple latches. The clock input of each latch is driven by another
set of GPIOs. With two 8-bit latches 10 GPIOs can be multiplexed into
16 GPIOs. GPOs might be a better term as in fact the multiplexed pins
are output only.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/gpio/Kconfig      |   6 ++
 drivers/gpio/Makefile     |   1 +
 drivers/gpio/gpio-latch.c | 195 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+)
 create mode 100644 drivers/gpio/gpio-latch.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ab75fe4ed9..dd2b56d256 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -205,6 +205,12 @@ config GPIO_ZYNQ
 	help
 	  Say yes here to support Xilinx Zynq GPIO controller.
 
+config GPIO_LATCH
+	tristate "GPIO latch driver"
+	help
+	  Say yes here to enable a driver for GPIO multiplexers based on latches
+	  connected to other GPIOs.
+
 endmenu
 
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fcb6a232e0..90ab0a8b28 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
 obj-$(CONFIG_GPIO_SIFIVE)	+= gpio-sifive.o
 obj-$(CONFIG_GPIO_STARFIVE)	+= gpio-starfive-vic.o
 obj-$(CONFIG_GPIO_ZYNQ)		+= gpio-zynq.o
+obj-$(CONFIG_GPIO_LATCH)	+= gpio-latch.o
diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
new file mode 100644
index 0000000000..2a89f22401
--- /dev/null
+++ b/drivers/gpio/gpio-latch.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO latch driver
+ *
+ *  Copyright (C) 2022 Sascha Hauer <s.hauer at pengutronix.de>
+ *
+ * This driver implements a GPIO (or better GPO as there is no input)
+ * multiplexer based on latches like this:
+ *
+ * CLK0 ----------------------.        ,--------.
+ * CLK1 -------------------.  `--------|>    #0 |
+ *                         |           |        |
+ * OUT0 ----------------+--|-----------|D0    Q0|-----|<
+ * OUT1 --------------+-|--|-----------|D1    Q1|-----|<
+ * OUT2 ------------+-|-|--|-----------|D2    Q2|-----|<
+ * OUT3 ----------+-|-|-|--|-----------|D3    Q3|-----|<
+ * OUT4 --------+-|-|-|-|--|-----------|D4    Q4|-----|<
+ * OUT5 ------+-|-|-|-|-|--|-----------|D5    Q5|-----|<
+ * OUT6 ----+-|-|-|-|-|-|--|-----------|D6    Q6|-----|<
+ * OUT7 --+-|-|-|-|-|-|-|--|-----------|D7    Q7|-----|<
+ *        | | | | | | | |  |           `--------'
+ *        | | | | | | | |  |
+ *        | | | | | | | |  |           ,--------.
+ *        | | | | | | | |  `-----------|>    #1 |
+ *        | | | | | | | |              |        |
+ *        | | | | | | | `--------------|D0    Q0|-----|<
+ *        | | | | | | `----------------|D1    Q1|-----|<
+ *        | | | | | `------------------|D2    Q2|-----|<
+ *        | | | | `--------------------|D3    Q3|-----|<
+ *        | | | `----------------------|D4    Q4|-----|<
+ *        | | `------------------------|D5    Q5|-----|<
+ *        | `--------------------------|D6    Q6|-----|<
+ *        `----------------------------|D7    Q7|-----|<
+ *                                     `--------'
+ *
+ * The above is just an example. The actual number of number of latches and
+ * the number of inputs per latch is derived from the number of GPIOs given
+ * in the corresponding device tree properties.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <io.h>
+#include <of.h>
+#include <gpio.h>
+#include <init.h>
+#include <of_gpio.h>
+#include <linux/bitmap.h>
+
+struct gpio_latch_priv {
+	struct gpio_chip gc;
+	int *clk_gpios;
+	int *latched_gpios;
+	int n_latched_gpios;
+	unsigned int setup_duration_ns;
+	unsigned int clock_duration_ns;
+	unsigned long *shadow;
+};
+
+static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+	return 0;
+}
+
+static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+	struct gpio_latch_priv *priv = container_of(gc, struct gpio_latch_priv, gc);
+	int latch = offset / priv->n_latched_gpios;
+	int i;
+
+	assign_bit(offset, priv->shadow, val);
+
+	for (i = 0; i < priv->n_latched_gpios; i++)
+		gpio_set_value(priv->latched_gpios[i],
+			       test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
+
+	ndelay(priv->setup_duration_ns);
+	gpio_set_value(priv->clk_gpios[latch], 1);
+	ndelay(priv->clock_duration_ns);
+	gpio_set_value(priv->clk_gpios[latch], 0);
+}
+static int gpio_latch_direction_output(struct gpio_chip *gc, unsigned gpio, int val)
+{
+	gpio_latch_set(gc, gpio, val);
+
+	return 0;
+}
+
+#define DURATION_NS_MAX 5000
+
+static struct gpio_ops gpio_latch_gpio_ops = {
+	.direction_output = gpio_latch_direction_output,
+	.set = gpio_latch_set,
+	.get_direction = gpio_latch_get_direction,
+};
+
+static int gpio_latch_probe(struct device_d *dev)
+{
+	struct gpio_latch_priv *priv;
+	int n_latches, i, ret;
+	struct device_node *np = dev->device_node;
+	enum of_gpio_flags flags;
+
+	priv = xzalloc(sizeof(*priv));
+
+	n_latches = of_gpio_named_count(np, "clk-gpios");
+	if (n_latches < 0) {
+		dev_err(dev, "invalid or missing clk-gpios");
+		ret = -EINVAL;
+		goto err_gpio;
+	}
+
+	priv->n_latched_gpios = of_gpio_named_count(np, "latched-gpios");
+	if (priv->n_latched_gpios < 0) {
+		dev_err(dev, "invalid or missing latched-gpios");
+		ret = -EINVAL;
+		goto err_gpio;
+	}
+
+	priv->clk_gpios = xzalloc(sizeof(int) * n_latches);
+	priv->latched_gpios = xzalloc(sizeof(int) * priv->n_latched_gpios);
+
+	for (i = 0; i < n_latches; i++) {
+		priv->clk_gpios[i] = of_get_named_gpio_flags(np, "clk-gpios",
+								i, &flags);
+		ret = gpio_request_one(priv->clk_gpios[i],
+				       flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+				       dev_name(dev));
+		if (ret) {
+			dev_err(dev, "Cannot request gpio %d: %s\n", priv->clk_gpios[i],
+				strerror(-ret));
+			goto err_gpio;
+		}
+
+		gpio_direction_output(priv->clk_gpios[i], 0);
+	}
+
+	for (i = 0; i < priv->n_latched_gpios; i++) {
+		priv->latched_gpios[i] = of_get_named_gpio_flags(np, "latched-gpios",
+							      i, &flags);
+		ret = gpio_request_one(priv->latched_gpios[i],
+				       flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+				       dev_name(dev));
+		if (ret) {
+			dev_err(dev, "Cannot request gpio %d: %s\n", priv->latched_gpios[i],
+				strerror(-ret));
+			goto err_gpio;
+		}
+	}
+
+	priv->shadow = bitmap_zalloc(n_latches * priv->n_latched_gpios);
+
+	of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
+	if (priv->setup_duration_ns > DURATION_NS_MAX) {
+		dev_warn(dev, "setup-duration-ns too high, limit to %d\n",
+			 DURATION_NS_MAX);
+		priv->setup_duration_ns = DURATION_NS_MAX;
+	}
+
+	of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns);
+	if (priv->clock_duration_ns > DURATION_NS_MAX) {
+		dev_warn(dev, "clock-duration-ns too high, limit to %d\n",
+			 DURATION_NS_MAX);
+		priv->clock_duration_ns = DURATION_NS_MAX;
+	}
+
+	priv->gc.ops = &gpio_latch_gpio_ops;
+	priv->gc.ngpio = n_latches * priv->n_latched_gpios;
+	priv->gc.base = -1;
+	priv->gc.dev = dev;
+
+	return gpiochip_add(&priv->gc);
+
+err_gpio:
+	return ret;
+}
+
+static const struct of_device_id gpio_latch_ids[] = {
+	{
+		.compatible	= "gpio-latch",
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver_d gpio_latch_driver = {
+	.name = "gpio-latch",
+	.probe = gpio_latch_probe,
+	.of_compatible = DRV_OF_COMPAT(gpio_latch_ids),
+};
+device_platform_driver(gpio_latch_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer at pengutronix.de>");
+MODULE_DESCRIPTION("GPIO latch driver");
-- 
2.30.2




More information about the barebox mailing list