linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/2] gpio: Add gpio-latch driver
@ 2022-10-06  8:30 Sascha Hauer
  2022-10-06  8:30 ` [PATCH v4 1/2] gpio: Add gpio latch driver Sascha Hauer
  2022-10-06  8:30 ` [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document Sascha Hauer
  0 siblings, 2 replies; 7+ messages in thread
From: Sascha Hauer @ 2022-10-06  8:30 UTC (permalink / raw)
  To: linux-gpio
  Cc: devicetree, linux-kernel, Linus Walleij, Bartosz Golaszewski,
	kernel, Serge Semin, Sascha Hauer

This round includes the changes suggested by Serge: Instead of hammering
the GPIOs as fast as we can we now have device tree properties to limit
the speed so that slower hardware can work with the driver.

Sascha

Sascha Hauer (2):
  gpio: Add gpio latch driver
  dt-bindings: gpio: Add gpio-latch binding document

 .../devicetree/bindings/gpio/gpio-latch.yaml  |  94 ++++++++
 drivers/gpio/Kconfig                          |   6 +
 drivers/gpio/Makefile                         |   1 +
 drivers/gpio/gpio-latch.c                     | 220 ++++++++++++++++++
 4 files changed, 321 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-latch.yaml
 create mode 100644 drivers/gpio/gpio-latch.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 1/2] gpio: Add gpio latch driver
  2022-10-06  8:30 [PATCH v4 0/2] gpio: Add gpio-latch driver Sascha Hauer
@ 2022-10-06  8:30 ` Sascha Hauer
  2022-10-06  9:15   ` Serge Semin
  2022-10-06  8:30 ` [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document Sascha Hauer
  1 sibling, 1 reply; 7+ messages in thread
From: Sascha Hauer @ 2022-10-06  8:30 UTC (permalink / raw)
  To: linux-gpio
  Cc: devicetree, linux-kernel, Linus Walleij, Bartosz Golaszewski,
	kernel, Serge Semin, Sascha Hauer

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@pengutronix.de>
---

Notes:
    Changes since v3:
    - Introduce delays between GPIO toggles as suggested by Serge Semin
    
    Changes since v2:
    - Fix inconsistent licensing
    - document locks
    - use regular bit operations
    - include linux/mod_devicetable.h rather than linux/of_device.h
    - Put spinlock and mutex into a union to make clear that only one of them is used
    - rename __gpio_latch_set to gpio_latch_set_unlocked
    
    Changes since v1:
    - Use gpiod_set_value_cansleep when the underlying GPIOs might sleep
    - Move MODULE_DEVICE_TABLE near to the end

 drivers/gpio/Kconfig      |   6 ++
 drivers/gpio/Makefile     |   1 +
 drivers/gpio/gpio-latch.c | 220 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+)
 create mode 100644 drivers/gpio/gpio-latch.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0642f579196f2..e4603810ec910 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1690,6 +1690,12 @@ config GPIO_AGGREGATOR
 	      industrial control context, to be operated from userspace using
 	      the GPIO chardev interface.
 
+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.
+
 config GPIO_MOCKUP
 	tristate "GPIO Testing Driver"
 	select IRQ_SIM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a0985d30f51bb..310fa08decc69 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
 obj-$(CONFIG_GPIO_IXP4XX)		+= gpio-ixp4xx.o
 obj-$(CONFIG_GPIO_JANZ_TTL)		+= gpio-janz-ttl.o
 obj-$(CONFIG_GPIO_KEMPLD)		+= gpio-kempld.o
+obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o
 obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
 obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
 obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
new file mode 100644
index 0000000000000..a4ab8f1240450
--- /dev/null
+++ b/drivers/gpio/gpio-latch.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO latch driver
+ *
+ *  Copyright (C) 2022 Sascha Hauer <s.hauer@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 <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include "gpiolib.h"
+
+struct gpio_latch_priv {
+	struct gpio_chip gc;
+	struct gpio_descs *clk_gpios;
+	struct gpio_descs *latched_gpios;
+	int n_latched_gpios;
+	unsigned int setup_duration_ns;
+	unsigned int clock_duration_ns;
+	unsigned long *shadow;
+	/*
+	 * Depending on whether any of the underlying GPIOs may sleep we either
+	 * use a mutex or a spinlock to protect our shadow map.
+	 */
+	union {
+		struct mutex mutex; /* protects @shadow */
+		spinlock_t spinlock; /* protects @shadow */
+	};
+};
+
+static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+	return GPIO_LINE_DIRECTION_OUT;
+}
+
+static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
+				    void (*set)(struct gpio_desc *desc, int value),
+				    unsigned int offset, bool val)
+{
+	int latch = offset / priv->n_latched_gpios;
+	int i;
+
+	assign_bit(offset, priv->shadow, val);
+
+	for (i = 0; i < priv->n_latched_gpios; i++)
+		set(priv->latched_gpios->desc[i],
+		    test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
+
+	ndelay(priv->setup_duration_ns);
+	set(priv->clk_gpios->desc[latch], 1);
+	udelay(priv->clock_duration_ns);
+	set(priv->clk_gpios->desc[latch], 0);
+	udelay(priv->clock_duration_ns);
+}
+
+static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->spinlock, flags);
+
+	gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val);
+
+	spin_unlock_irqrestore(&priv->spinlock, flags);
+}
+
+static void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val)
+{
+	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
+
+	mutex_lock(&priv->mutex);
+
+	gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches)
+{
+	int i;
+
+	for (i = 0; i < n_latches; i++)
+		if (gpiod_cansleep(priv->clk_gpios->desc[i]))
+			return true;
+
+	for (i = 0; i < priv->n_latched_gpios; i++)
+		if (gpiod_cansleep(priv->latched_gpios->desc[i]))
+			return true;
+
+	return false;
+}
+
+static int gpio_latch_probe(struct platform_device *pdev)
+{
+	struct gpio_latch_priv *priv;
+	unsigned int n_latches;
+	struct device_node *np = pdev->dev.of_node;
+	/*
+	 * Some value which is still acceptable to delay in atomic context.
+	 * If we need to go higher we might have to switch to usleep_range(),
+	 * but that cannot ne used in atomic context and the driver would have
+	 * to be adjusted to support that.
+	 */
+	unsigned int duration_ns_max = 5000;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->clk_gpios = devm_gpiod_get_array(&pdev->dev, "clk", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->clk_gpios))
+		return PTR_ERR(priv->clk_gpios);
+
+	priv->latched_gpios = devm_gpiod_get_array(&pdev->dev, "latched", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->latched_gpios))
+		return PTR_ERR(priv->latched_gpios);
+
+	n_latches = priv->clk_gpios->ndescs;
+	priv->n_latched_gpios = priv->latched_gpios->ndescs;
+
+	priv->shadow = devm_bitmap_zalloc(&pdev->dev, n_latches * priv->n_latched_gpios,
+					  GFP_KERNEL);
+	if (!priv->shadow)
+		return -ENOMEM;
+
+	if (gpio_latch_can_sleep(priv, n_latches)) {
+		priv->gc.can_sleep = true;
+		priv->gc.set = gpio_latch_set_can_sleep;
+		mutex_init(&priv->mutex);
+	} else {
+		priv->gc.can_sleep = false;
+		priv->gc.set = gpio_latch_set;
+		spin_lock_init(&priv->spinlock);
+	}
+
+	of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
+	if (priv->setup_duration_ns > duration_ns_max) {
+		dev_warn(&pdev->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(&pdev->dev, "clock-duration-ns too high, limit to %d\n",
+			 duration_ns_max);
+		priv->clock_duration_ns = duration_ns_max;
+	}
+
+	priv->gc.get_direction = gpio_latch_get_direction;
+	priv->gc.ngpio = n_latches * priv->n_latched_gpios;
+	priv->gc.owner = THIS_MODULE;
+	priv->gc.base = -1;
+	priv->gc.parent = &pdev->dev;
+
+	platform_set_drvdata(pdev, priv);
+
+	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
+}
+
+static const struct of_device_id gpio_latch_ids[] = {
+	{
+		.compatible	= "gpio-latch",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, gpio_latch_ids);
+
+static struct platform_driver gpio_latch_driver = {
+	.driver	= {
+		.name		= "gpio-latch",
+		.of_match_table	= gpio_latch_ids,
+	},
+	.probe	= gpio_latch_probe,
+};
+module_platform_driver(gpio_latch_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("GPIO latch driver");
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document
  2022-10-06  8:30 [PATCH v4 0/2] gpio: Add gpio-latch driver Sascha Hauer
  2022-10-06  8:30 ` [PATCH v4 1/2] gpio: Add gpio latch driver Sascha Hauer
@ 2022-10-06  8:30 ` Sascha Hauer
  2022-10-06 18:53   ` Rob Herring
  1 sibling, 1 reply; 7+ messages in thread
From: Sascha Hauer @ 2022-10-06  8:30 UTC (permalink / raw)
  To: linux-gpio
  Cc: devicetree, linux-kernel, Linus Walleij, Bartosz Golaszewski,
	kernel, Serge Semin, Sascha Hauer

This adds a binding for a GPIO multiplexer driver based on latches
connected to other GPIOs.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

Notes:
    Changes since v3:
    - Introduce delays between GPIO toggles as suggested by Serge Semin
    
    Changes since v1:
    - Add license to binding file

 .../devicetree/bindings/gpio/gpio-latch.yaml  | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-latch.yaml

diff --git a/Documentation/devicetree/bindings/gpio/gpio-latch.yaml b/Documentation/devicetree/bindings/gpio/gpio-latch.yaml
new file mode 100644
index 0000000000000..1ed82a2cebdaa
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-latch.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/gpio-latch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO latch controller
+
+maintainers:
+  - Sascha Hauer <s.hauer@pengutronix.de>
+
+description: |
+  This binding describes a GPIO multiplexer based on latches connected to
+  other GPIOs, 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 number of clk-gpios and latched-gpios is not fixed. 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.
+
+properties:
+  compatible:
+    const: gpio-latch
+  "#gpio-cells":
+    const: 2
+
+  clk-gpios:
+    description: Array of GPIOs to be used to clock a latch
+
+  latched-gpios:
+    description: Array of GPIOs to be used as inputs per latch
+
+  setup-duration-ns:
+    description: Delay in nanoseconds to wait after the latch inputs have been
+      set up
+
+  clock-duration-ns:
+    description: Delay in nanoseconds to wait between clock output changes
+
+  gpio-controller: true
+
+  gpio-line-names: true
+
+required:
+  - compatible
+  - "#gpio-cells"
+  - gpio-controller
+  - clk-gpios
+  - latched-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    gpio-latch {
+        #gpio-cells = <2>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&pinctrl_di_do_leds>;
+        compatible = "gpio-latch";
+        gpio-controller;
+        setup-duration-ns = <100>;
+        clock-duration-ns = <100>;
+
+        clk-gpios = <&gpio3 7 0>, <&gpio3 8 0>;
+        latched-gpios = <&gpio3 21 0>, <&gpio3 22 0>,
+                       <&gpio3 23 0>, <&gpio3 24 0>,
+                       <&gpio3 25 0>, <&gpio3 26 0>,
+                       <&gpio3 27 0>, <&gpio3 28 0>;
+    };
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/2] gpio: Add gpio latch driver
  2022-10-06  8:30 ` [PATCH v4 1/2] gpio: Add gpio latch driver Sascha Hauer
@ 2022-10-06  9:15   ` Serge Semin
  2022-10-06 12:26     ` Sascha Hauer
  0 siblings, 1 reply; 7+ messages in thread
From: Serge Semin @ 2022-10-06  9:15 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-gpio, devicetree, linux-kernel, Linus Walleij,
	Bartosz Golaszewski, kernel

On Thu, Oct 06, 2022 at 10:30:30AM +0200, Sascha Hauer wrote:
> 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@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v3:
>     - Introduce delays between GPIO toggles as suggested by Serge Semin
>     
>     Changes since v2:
>     - Fix inconsistent licensing
>     - document locks
>     - use regular bit operations
>     - include linux/mod_devicetable.h rather than linux/of_device.h
>     - Put spinlock and mutex into a union to make clear that only one of them is used
>     - rename __gpio_latch_set to gpio_latch_set_unlocked
>     
>     Changes since v1:
>     - Use gpiod_set_value_cansleep when the underlying GPIOs might sleep
>     - Move MODULE_DEVICE_TABLE near to the end
> 
>  drivers/gpio/Kconfig      |   6 ++
>  drivers/gpio/Makefile     |   1 +
>  drivers/gpio/gpio-latch.c | 220 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 227 insertions(+)
>  create mode 100644 drivers/gpio/gpio-latch.c
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 0642f579196f2..e4603810ec910 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1690,6 +1690,12 @@ config GPIO_AGGREGATOR
>  	      industrial control context, to be operated from userspace using
>  	      the GPIO chardev interface.
>  
> +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.
> +
>  config GPIO_MOCKUP
>  	tristate "GPIO Testing Driver"
>  	select IRQ_SIM
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index a0985d30f51bb..310fa08decc69 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
>  obj-$(CONFIG_GPIO_IXP4XX)		+= gpio-ixp4xx.o
>  obj-$(CONFIG_GPIO_JANZ_TTL)		+= gpio-janz-ttl.o
>  obj-$(CONFIG_GPIO_KEMPLD)		+= gpio-kempld.o
> +obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o
>  obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
>  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
>  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
> diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
> new file mode 100644
> index 0000000000000..a4ab8f1240450
> --- /dev/null
> +++ b/drivers/gpio/gpio-latch.c
> @@ -0,0 +1,220 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * GPIO latch driver
> + *
> + *  Copyright (C) 2022 Sascha Hauer <s.hauer@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 <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +
> +#include "gpiolib.h"
> +
> +struct gpio_latch_priv {
> +	struct gpio_chip gc;
> +	struct gpio_descs *clk_gpios;
> +	struct gpio_descs *latched_gpios;
> +	int n_latched_gpios;
> +	unsigned int setup_duration_ns;
> +	unsigned int clock_duration_ns;
> +	unsigned long *shadow;
> +	/*
> +	 * Depending on whether any of the underlying GPIOs may sleep we either
> +	 * use a mutex or a spinlock to protect our shadow map.
> +	 */
> +	union {
> +		struct mutex mutex; /* protects @shadow */
> +		spinlock_t spinlock; /* protects @shadow */
> +	};
> +};
> +
> +static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> +	return GPIO_LINE_DIRECTION_OUT;
> +}
> +
> +static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
> +				    void (*set)(struct gpio_desc *desc, int value),
> +				    unsigned int offset, bool val)
> +{
> +	int latch = offset / priv->n_latched_gpios;
> +	int i;
> +
> +	assign_bit(offset, priv->shadow, val);
> +
> +	for (i = 0; i < priv->n_latched_gpios; i++)
> +		set(priv->latched_gpios->desc[i],
> +		    test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
> +
> +	ndelay(priv->setup_duration_ns);
> +	set(priv->clk_gpios->desc[latch], 1);

> +	udelay(priv->clock_duration_ns);

These are supposed to be !n!delay()'s. Aren't they?

> +	set(priv->clk_gpios->desc[latch], 0);

> +	udelay(priv->clock_duration_ns);

Why do you need the second clock-duration? AFAICS at least from the
TI SNx4LV74A specification the outputs get updated on the positive
edge of the pulse. So the clock-duration value shall contain both
the "CLK pulse duration" and "CLK hold time", which should be enough
for the latches states being perceived by the device.

> +}
> +
> +static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
> +{
> +	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->spinlock, flags);
> +
> +	gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val);
> +
> +	spin_unlock_irqrestore(&priv->spinlock, flags);
> +}
> +
> +static void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val)
> +{
> +	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
> +
> +	mutex_lock(&priv->mutex);
> +
> +	gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val);
> +
> +	mutex_unlock(&priv->mutex);
> +}
> +
> +static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches)
> +{
> +	int i;
> +
> +	for (i = 0; i < n_latches; i++)
> +		if (gpiod_cansleep(priv->clk_gpios->desc[i]))
> +			return true;
> +
> +	for (i = 0; i < priv->n_latched_gpios; i++)
> +		if (gpiod_cansleep(priv->latched_gpios->desc[i]))
> +			return true;
> +
> +	return false;
> +}
> +
> +static int gpio_latch_probe(struct platform_device *pdev)
> +{
> +	struct gpio_latch_priv *priv;
> +	unsigned int n_latches;
> +	struct device_node *np = pdev->dev.of_node;

> +	/*
> +	 * Some value which is still acceptable to delay in atomic context.
> +	 * If we need to go higher we might have to switch to usleep_range(),
> +	 * but that cannot ne used in atomic context and the driver would have
> +	 * to be adjusted to support that.
> +	 */
> +	unsigned int duration_ns_max = 5000;

I don't see you changing this parameter. So it's better to be a macro.

-Sergey

> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->clk_gpios = devm_gpiod_get_array(&pdev->dev, "clk", GPIOD_OUT_LOW);
> +	if (IS_ERR(priv->clk_gpios))
> +		return PTR_ERR(priv->clk_gpios);
> +
> +	priv->latched_gpios = devm_gpiod_get_array(&pdev->dev, "latched", GPIOD_OUT_LOW);
> +	if (IS_ERR(priv->latched_gpios))
> +		return PTR_ERR(priv->latched_gpios);
> +
> +	n_latches = priv->clk_gpios->ndescs;
> +	priv->n_latched_gpios = priv->latched_gpios->ndescs;
> +
> +	priv->shadow = devm_bitmap_zalloc(&pdev->dev, n_latches * priv->n_latched_gpios,
> +					  GFP_KERNEL);
> +	if (!priv->shadow)
> +		return -ENOMEM;
> +
> +	if (gpio_latch_can_sleep(priv, n_latches)) {
> +		priv->gc.can_sleep = true;
> +		priv->gc.set = gpio_latch_set_can_sleep;
> +		mutex_init(&priv->mutex);
> +	} else {
> +		priv->gc.can_sleep = false;
> +		priv->gc.set = gpio_latch_set;
> +		spin_lock_init(&priv->spinlock);
> +	}
> +
> +	of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
> +	if (priv->setup_duration_ns > duration_ns_max) {
> +		dev_warn(&pdev->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(&pdev->dev, "clock-duration-ns too high, limit to %d\n",
> +			 duration_ns_max);
> +		priv->clock_duration_ns = duration_ns_max;
> +	}
> +
> +	priv->gc.get_direction = gpio_latch_get_direction;
> +	priv->gc.ngpio = n_latches * priv->n_latched_gpios;
> +	priv->gc.owner = THIS_MODULE;
> +	priv->gc.base = -1;
> +	priv->gc.parent = &pdev->dev;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
> +}
> +
> +static const struct of_device_id gpio_latch_ids[] = {
> +	{
> +		.compatible	= "gpio-latch",
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +MODULE_DEVICE_TABLE(of, gpio_latch_ids);
> +
> +static struct platform_driver gpio_latch_driver = {
> +	.driver	= {
> +		.name		= "gpio-latch",
> +		.of_match_table	= gpio_latch_ids,
> +	},
> +	.probe	= gpio_latch_probe,
> +};
> +module_platform_driver(gpio_latch_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
> +MODULE_DESCRIPTION("GPIO latch driver");
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/2] gpio: Add gpio latch driver
  2022-10-06  9:15   ` Serge Semin
@ 2022-10-06 12:26     ` Sascha Hauer
  2022-10-06 12:45       ` Serge Semin
  0 siblings, 1 reply; 7+ messages in thread
From: Sascha Hauer @ 2022-10-06 12:26 UTC (permalink / raw)
  To: Serge Semin
  Cc: linux-gpio, devicetree, linux-kernel, Linus Walleij,
	Bartosz Golaszewski, kernel

On Thu, Oct 06, 2022 at 12:15:06PM +0300, Serge Semin wrote:
> On Thu, Oct 06, 2022 at 10:30:30AM +0200, Sascha Hauer wrote:
> > 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@pengutronix.de>
> > ---
> > 
> > Notes:
> >     Changes since v3:
> >     - Introduce delays between GPIO toggles as suggested by Serge Semin
> >     
> >     Changes since v2:
> >     - Fix inconsistent licensing
> >     - document locks
> >     - use regular bit operations
> >     - include linux/mod_devicetable.h rather than linux/of_device.h
> >     - Put spinlock and mutex into a union to make clear that only one of them is used
> >     - rename __gpio_latch_set to gpio_latch_set_unlocked
> >     
> >     Changes since v1:
> >     - Use gpiod_set_value_cansleep when the underlying GPIOs might sleep
> >     - Move MODULE_DEVICE_TABLE near to the end
> > 
> >  drivers/gpio/Kconfig      |   6 ++
> >  drivers/gpio/Makefile     |   1 +
> >  drivers/gpio/gpio-latch.c | 220 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 227 insertions(+)
> >  create mode 100644 drivers/gpio/gpio-latch.c
> > 
> > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> > index 0642f579196f2..e4603810ec910 100644
> > --- a/drivers/gpio/Kconfig
> > +++ b/drivers/gpio/Kconfig
> > @@ -1690,6 +1690,12 @@ config GPIO_AGGREGATOR
> >  	      industrial control context, to be operated from userspace using
> >  	      the GPIO chardev interface.
> >  
> > +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.
> > +
> >  config GPIO_MOCKUP
> >  	tristate "GPIO Testing Driver"
> >  	select IRQ_SIM
> > diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> > index a0985d30f51bb..310fa08decc69 100644
> > --- a/drivers/gpio/Makefile
> > +++ b/drivers/gpio/Makefile
> > @@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
> >  obj-$(CONFIG_GPIO_IXP4XX)		+= gpio-ixp4xx.o
> >  obj-$(CONFIG_GPIO_JANZ_TTL)		+= gpio-janz-ttl.o
> >  obj-$(CONFIG_GPIO_KEMPLD)		+= gpio-kempld.o
> > +obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o
> >  obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
> >  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
> >  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
> > diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
> > new file mode 100644
> > index 0000000000000..a4ab8f1240450
> > --- /dev/null
> > +++ b/drivers/gpio/gpio-latch.c
> > @@ -0,0 +1,220 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * GPIO latch driver
> > + *
> > + *  Copyright (C) 2022 Sascha Hauer <s.hauer@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 <linux/err.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/gpio/driver.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/delay.h>
> > +
> > +#include "gpiolib.h"
> > +
> > +struct gpio_latch_priv {
> > +	struct gpio_chip gc;
> > +	struct gpio_descs *clk_gpios;
> > +	struct gpio_descs *latched_gpios;
> > +	int n_latched_gpios;
> > +	unsigned int setup_duration_ns;
> > +	unsigned int clock_duration_ns;
> > +	unsigned long *shadow;
> > +	/*
> > +	 * Depending on whether any of the underlying GPIOs may sleep we either
> > +	 * use a mutex or a spinlock to protect our shadow map.
> > +	 */
> > +	union {
> > +		struct mutex mutex; /* protects @shadow */
> > +		spinlock_t spinlock; /* protects @shadow */
> > +	};
> > +};
> > +
> > +static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
> > +{
> > +	return GPIO_LINE_DIRECTION_OUT;
> > +}
> > +
> > +static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
> > +				    void (*set)(struct gpio_desc *desc, int value),
> > +				    unsigned int offset, bool val)
> > +{
> > +	int latch = offset / priv->n_latched_gpios;
> > +	int i;
> > +
> > +	assign_bit(offset, priv->shadow, val);
> > +
> > +	for (i = 0; i < priv->n_latched_gpios; i++)
> > +		set(priv->latched_gpios->desc[i],
> > +		    test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
> > +
> > +	ndelay(priv->setup_duration_ns);
> > +	set(priv->clk_gpios->desc[latch], 1);
> 
> > +	udelay(priv->clock_duration_ns);
> 
> These are supposed to be !n!delay()'s. Aren't they?

Yes, indeed.

> 
> > +	set(priv->clk_gpios->desc[latch], 0);
> 
> > +	udelay(priv->clock_duration_ns);
> 
> Why do you need the second clock-duration? AFAICS at least from the
> TI SNx4LV74A specification the outputs get updated on the positive
> edge of the pulse. So the clock-duration value shall contain both
> the "CLK pulse duration" and "CLK hold time", which should be enough
> for the latches states being perceived by the device.

The rationale was that there should be some delay between two subsequent
calls to gpio_latch_set_unlocked(). You are right though, on my latch
the update happens on the positive edge as well, so this shouldn't be
needed.

Sascha

> > +	/*
> > +	 * Some value which is still acceptable to delay in atomic context.
> > +	 * If we need to go higher we might have to switch to usleep_range(),
> > +	 * but that cannot ne used in atomic context and the driver would have
> > +	 * to be adjusted to support that.
> > +	 */
> > +	unsigned int duration_ns_max = 5000;
> 
> I don't see you changing this parameter. So it's better to be a macro.

Why should it?

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/2] gpio: Add gpio latch driver
  2022-10-06 12:26     ` Sascha Hauer
@ 2022-10-06 12:45       ` Serge Semin
  0 siblings, 0 replies; 7+ messages in thread
From: Serge Semin @ 2022-10-06 12:45 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-gpio, devicetree, linux-kernel, Linus Walleij,
	Bartosz Golaszewski, kernel

On Thu, Oct 06, 2022 at 02:26:34PM +0200, Sascha Hauer wrote:
> On Thu, Oct 06, 2022 at 12:15:06PM +0300, Serge Semin wrote:
> > On Thu, Oct 06, 2022 at 10:30:30AM +0200, Sascha Hauer wrote:
> > > 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@pengutronix.de>
> > > ---
> > > 
> > > Notes:
> > >     Changes since v3:
> > >     - Introduce delays between GPIO toggles as suggested by Serge Semin
> > >     
> > >     Changes since v2:
> > >     - Fix inconsistent licensing
> > >     - document locks
> > >     - use regular bit operations
> > >     - include linux/mod_devicetable.h rather than linux/of_device.h
> > >     - Put spinlock and mutex into a union to make clear that only one of them is used
> > >     - rename __gpio_latch_set to gpio_latch_set_unlocked
> > >     
> > >     Changes since v1:
> > >     - Use gpiod_set_value_cansleep when the underlying GPIOs might sleep
> > >     - Move MODULE_DEVICE_TABLE near to the end
> > > 
> > >  drivers/gpio/Kconfig      |   6 ++
> > >  drivers/gpio/Makefile     |   1 +
> > >  drivers/gpio/gpio-latch.c | 220 ++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 227 insertions(+)
> > >  create mode 100644 drivers/gpio/gpio-latch.c
> > > 
> > > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> > > index 0642f579196f2..e4603810ec910 100644
> > > --- a/drivers/gpio/Kconfig
> > > +++ b/drivers/gpio/Kconfig
> > > @@ -1690,6 +1690,12 @@ config GPIO_AGGREGATOR
> > >  	      industrial control context, to be operated from userspace using
> > >  	      the GPIO chardev interface.
> > >  
> > > +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.
> > > +
> > >  config GPIO_MOCKUP
> > >  	tristate "GPIO Testing Driver"
> > >  	select IRQ_SIM
> > > diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> > > index a0985d30f51bb..310fa08decc69 100644
> > > --- a/drivers/gpio/Makefile
> > > +++ b/drivers/gpio/Makefile
> > > @@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
> > >  obj-$(CONFIG_GPIO_IXP4XX)		+= gpio-ixp4xx.o
> > >  obj-$(CONFIG_GPIO_JANZ_TTL)		+= gpio-janz-ttl.o
> > >  obj-$(CONFIG_GPIO_KEMPLD)		+= gpio-kempld.o
> > > +obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o
> > >  obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
> > >  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
> > >  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
> > > diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
> > > new file mode 100644
> > > index 0000000000000..a4ab8f1240450
> > > --- /dev/null
> > > +++ b/drivers/gpio/gpio-latch.c
> > > @@ -0,0 +1,220 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * GPIO latch driver
> > > + *
> > > + *  Copyright (C) 2022 Sascha Hauer <s.hauer@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 <linux/err.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/gpio/driver.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mod_devicetable.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/delay.h>
> > > +
> > > +#include "gpiolib.h"
> > > +
> > > +struct gpio_latch_priv {
> > > +	struct gpio_chip gc;
> > > +	struct gpio_descs *clk_gpios;
> > > +	struct gpio_descs *latched_gpios;
> > > +	int n_latched_gpios;
> > > +	unsigned int setup_duration_ns;
> > > +	unsigned int clock_duration_ns;
> > > +	unsigned long *shadow;
> > > +	/*
> > > +	 * Depending on whether any of the underlying GPIOs may sleep we either
> > > +	 * use a mutex or a spinlock to protect our shadow map.
> > > +	 */
> > > +	union {
> > > +		struct mutex mutex; /* protects @shadow */
> > > +		spinlock_t spinlock; /* protects @shadow */
> > > +	};
> > > +};
> > > +
> > > +static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
> > > +{
> > > +	return GPIO_LINE_DIRECTION_OUT;
> > > +}
> > > +
> > > +static void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
> > > +				    void (*set)(struct gpio_desc *desc, int value),
> > > +				    unsigned int offset, bool val)
> > > +{
> > > +	int latch = offset / priv->n_latched_gpios;
> > > +	int i;
> > > +
> > > +	assign_bit(offset, priv->shadow, val);
> > > +
> > > +	for (i = 0; i < priv->n_latched_gpios; i++)
> > > +		set(priv->latched_gpios->desc[i],
> > > +		    test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
> > > +
> > > +	ndelay(priv->setup_duration_ns);
> > > +	set(priv->clk_gpios->desc[latch], 1);
> > 
> > > +	udelay(priv->clock_duration_ns);
> > 
> > These are supposed to be !n!delay()'s. Aren't they?
> 
> Yes, indeed.
> 
> > 
> > > +	set(priv->clk_gpios->desc[latch], 0);
> > 
> > > +	udelay(priv->clock_duration_ns);
> > 
> > Why do you need the second clock-duration? AFAICS at least from the
> > TI SNx4LV74A specification the outputs get updated on the positive
> > edge of the pulse. So the clock-duration value shall contain both
> > the "CLK pulse duration" and "CLK hold time", which should be enough
> > for the latches states being perceived by the device.
> 
> The rationale was that there should be some delay between two subsequent
> calls to gpio_latch_set_unlocked(). You are right though, on my latch
> the update happens on the positive edge as well, so this shouldn't be
> needed.
> 
> Sascha
> 
> > > +	/*
> > > +	 * Some value which is still acceptable to delay in atomic context.
> > > +	 * If we need to go higher we might have to switch to usleep_range(),
> > > +	 * but that cannot ne used in atomic context and the driver would have
> > > +	 * to be adjusted to support that.
> > > +	 */
> > > +	unsigned int duration_ns_max = 5000;
> > 

> > I don't see you changing this parameter. So it's better to be a macro.
> 
> Why should it?

Well because of its semantics. It's a constant and is definitely a
common parameter for any device. So const-macro-based parametrization
like

/*
...
 */
#define GPIO_LATCH_DURATION_NS_MAX		5000

placed above the struct gpio_latch_priv declaration with the same
comment would be more appropriate than a pre-initialized local
variable. Thus the capitalized name could be right away read that the
parameter is a constant and common for all the devices. In your
implementation it isn't obvious from the variable usage context. I had
to parse the whole gpio_latch_probe() method to understand that.

-Sergey

> 
> Sascha
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document
  2022-10-06  8:30 ` [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document Sascha Hauer
@ 2022-10-06 18:53   ` Rob Herring
  0 siblings, 0 replies; 7+ messages in thread
From: Rob Herring @ 2022-10-06 18:53 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, devicetree, Bartosz Golaszewski, Serge Semin,
	Linus Walleij, linux-gpio, kernel

On Thu, 06 Oct 2022 10:30:31 +0200, Sascha Hauer wrote:
> This adds a binding for a GPIO multiplexer driver based on latches
> connected to other GPIOs.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> 
> Notes:
>     Changes since v3:
>     - Introduce delays between GPIO toggles as suggested by Serge Semin
> 
>     Changes since v1:
>     - Add license to binding file
> 
>  .../devicetree/bindings/gpio/gpio-latch.yaml  | 94 +++++++++++++++++++
>  1 file changed, 94 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/gpio-latch.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-10-06 18:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-06  8:30 [PATCH v4 0/2] gpio: Add gpio-latch driver Sascha Hauer
2022-10-06  8:30 ` [PATCH v4 1/2] gpio: Add gpio latch driver Sascha Hauer
2022-10-06  9:15   ` Serge Semin
2022-10-06 12:26     ` Sascha Hauer
2022-10-06 12:45       ` Serge Semin
2022-10-06  8:30 ` [PATCH v4 2/2] dt-bindings: gpio: Add gpio-latch binding document Sascha Hauer
2022-10-06 18:53   ` Rob Herring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).