[PATCH] gpio: add driver for SN74273 output expander

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The SN74273 is an octal D-Type Flip-Flop. When used as an output expander,
N existing output signals can be turned into (N-8)*8 outputs using
N-8 chips. Add driver to facilitate this.

Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
 .../bindings/gpio/ti,74273-gpio.rst           |  34 ++++
 drivers/gpio/Kconfig                          |   5 +
 drivers/gpio/Makefile                         |   1 +
 drivers/gpio/gpio-74273.c                     | 183 ++++++++++++++++++
 4 files changed, 223 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/ti,74273-gpio.rst
 create mode 100644 drivers/gpio/gpio-74273.c

diff --git a/Documentation/devicetree/bindings/gpio/ti,74273-gpio.rst b/Documentation/devicetree/bindings/gpio/ti,74273-gpio.rst
new file mode 100644
index 000000000000..6fc001451c2f
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/ti,74273-gpio.rst
@@ -0,0 +1,34 @@
+74273 output expander driver
+============================
+
+Required properties:
+
+- ``compatible``: Should be ``"ti,74273-gpio"``
+- ``gpio-controller``: Marks the device node as a gpio controller.
+- ``#gpio-cells``: Should be two. The first cell is the pin number and
+   the second cell is used to specify the GPIO polarity:
+    ``0`` = Active High,
+    ``1`` = Active Low.
+- ``clk-gpios``: One GPIO per connected 74273 chip
+- ``data-gpios``: Eight GPIOs per connected 74273 (D-Pins)
+
+Optional properties:
+
+- ``ti,clk-pulse-width-ns``: minimal width of clock pulse for each chip.
+			     If unspecified, ``<20>`` is used as default.
+
+Example::
+
+	sn74273-gpio {
+		compatible = "ti,74273-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		clk-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>, <&gpio3 8 GPIO_ACTIVE_HIGH>;
+		ti,clk-pulse-width-ns = <30>, <30>;
+		data-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>, <&gpio3 22 GPIO_ACTIVE_LOW>,
+			     <&gpio3 23 GPIO_ACTIVE_LOW>, <&gpio3 24 GPIO_ACTIVE_LOW>,
+			     <&gpio3 25 GPIO_ACTIVE_LOW>, <&gpio3 26 GPIO_ACTIVE_LOW>,
+			     <&gpio3 27 GPIO_ACTIVE_LOW>, <&gpio3 28 GPIO_ACTIVE_LOW>;
+	};
+
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7a1503198b49..dd6e6dd0c1c8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -145,6 +145,11 @@ config GPIO_SX150X
 	  Say Y here to build support for the Semtec Sx150x I2C GPIO
 	  expander chip.
 
+config GPIO_74273
+	bool "SN74273 output ports"
+	help
+	  Say Y here to include a driver for the SN74273 as output expander
+
 config GPIO_LIBFTDI1
 	bool "libftdi1 driver"
 	depends on SANDBOX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 990df01788bc..ba818c7b94c0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_GPIO_TEGRA)	+= gpio-tegra.o
 obj-$(CONFIG_GPIO_DESIGNWARE)	+= gpio-dw.o
 obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.o
 obj-$(CONFIG_GPIO_VF610)	+= gpio-vf610.o
+obj-$(CONFIG_GPIO_74273)	+= gpio-74273.o
diff --git a/drivers/gpio/gpio-74273.c b/drivers/gpio/gpio-74273.c
new file mode 100644
index 000000000000..873f523c1b0b
--- /dev/null
+++ b/drivers/gpio/gpio-74273.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Sascha Hauer, Pengutronix
+ * Copyright (c) 2019 Ahmad Fatoum, Pengutronix
+ */
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <of.h>
+#include <clock.h>
+
+#define DEFAULT_DELAY_NS	20
+
+struct clk_gpio {
+	int desc;
+	u32 delay_ns;
+};
+
+struct sn74273 {
+	struct gpio_chip chip;
+	struct clk_gpio *clk_gpios;
+	int *data_gpios;
+	u8 *shadow;
+	unsigned n_ports;
+	unsigned n_pins;
+};
+
+static inline struct sn74273 *to_sn74273(struct gpio_chip *chip)
+{
+	return container_of(chip, struct sn74273, chip);
+}
+
+static void sn74273_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+	struct sn74273 *sn74273 = to_sn74273(chip);
+	unsigned port = gpio / 8;
+	unsigned pin = gpio % 8;
+	int i, j;
+	u8 val;
+
+	val = sn74273->shadow[port];
+	if (value)
+		val |= 1 << pin;
+	else
+		val &= ~(1 << pin);
+	sn74273->shadow[port] = val;
+
+	for (i = 0; i < sn74273->n_ports; i++) {
+		for (j = 0; j < sn74273->n_pins; j++) {
+			gpio_set_active(sn74273->data_gpios[j],
+				sn74273->shadow[i] & (1 << j));
+		}
+
+		ndelay(sn74273->clk_gpios[i].delay_ns);
+		gpio_set_active(sn74273->clk_gpios[i].desc, 1);
+		ndelay(sn74273->clk_gpios[i].delay_ns);
+		gpio_set_active(sn74273->clk_gpios[i].desc, 0);
+	}
+}
+
+static int sn74273_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+	struct sn74273 *sn74273 = to_sn74273(chip);
+	unsigned port = gpio / 8;
+	unsigned pin = gpio % 8;
+
+	return sn74273->shadow[port] & pin;
+}
+
+static int sn74273_gpio_direction_output(struct gpio_chip *chip,
+					 unsigned offset, int value)
+{
+	sn74273_gpio_set_value(chip, offset, value);
+	return 0;
+}
+
+static int sn74273_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+	return GPIOF_DIR_OUT;
+}
+
+static struct gpio_ops sn74273_gpio_ops = {
+	.get			= sn74273_gpio_get_value,
+	.set			= sn74273_gpio_set_value,
+	.direction_output	= sn74273_gpio_direction_output,
+	.get_direction		= sn74273_gpio_get_direction,
+};
+
+static int sn74273_gpio_probe(struct device_d *dev)
+{
+	struct device_node *np = dev->device_node;
+	enum of_gpio_flags flags;
+	struct sn74273 *sn74273;
+	int i, ret;
+
+	sn74273 = xzalloc(sizeof(*sn74273));
+
+	sn74273->n_ports = of_gpio_named_count(np, "clk-gpios");
+	if (sn74273->n_ports < 0) {
+		dev_err(dev, "invalid or missing clk-gpios\n");
+		return -EINVAL;
+	}
+
+	sn74273->n_pins = of_gpio_named_count(np, "data-gpios");
+	if (sn74273->n_pins < 0) {
+		dev_err(dev, "invalid or missing data-gpios\n");
+		return -EINVAL;
+	}
+
+	sn74273->chip.base = -1;
+	sn74273->chip.ngpio = sn74273->n_ports * sn74273->n_pins;
+	sn74273->chip.dev = dev;
+	sn74273->chip.ops = &sn74273_gpio_ops;
+
+	sn74273->clk_gpios = xzalloc(sizeof(struct clk_gpio) * sn74273->n_ports);
+	sn74273->data_gpios = xzalloc(sizeof(int) * sn74273->n_pins);
+
+	for (i = 0; i < sn74273->n_ports; i++) {
+		struct clk_gpio *clk_gpio = &sn74273->clk_gpios[i];
+		clk_gpio->desc = of_get_named_gpio_flags(np, "clk-gpios",
+							 i, &flags);
+		ret = gpio_request_one(clk_gpio->desc,
+				 flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+				 dev_name(dev));
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "Cannot request gpio %d: %s\n", clk_gpio->desc,
+					strerror(-ret));
+			return ret;
+		}
+
+		gpio_direction_output(clk_gpio->desc, 0);
+
+		clk_gpio->delay_ns = DEFAULT_DELAY_NS;
+		of_property_read_u32_index(np, "ti,clk-pulse-width-ns",
+					   i, &clk_gpio->delay_ns);
+	}
+
+	for (i = 0; i < sn74273->n_pins; i++) {
+		sn74273->data_gpios[i] = of_get_named_gpio_flags(np, "data-gpios",
+								 i, &flags);
+		ret = gpio_request_one(sn74273->data_gpios[i],
+				 flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0,
+				 dev_name(dev));
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "Cannot request gpio %d: %s\n", sn74273->data_gpios[i],
+					strerror(-ret));
+			return ret;
+		}
+
+		gpio_direction_output(sn74273->data_gpios[i], 0);
+	}
+
+	sn74273->shadow = xzalloc(DIV_ROUND_UP(sn74273->n_pins, 8) * sn74273->n_ports);
+
+	ret = gpiochip_add(&sn74273->chip);
+	if (ret)
+		dev_err(dev, "cannot add 74273 GPIO chip: %s\n", strerror(-ret));
+
+	return ret;
+}
+
+static const struct of_device_id of_sn74273_gpio_leds_match[] = {
+	{ .compatible = "ti,74273-gpio" },
+	{ /* sentinel */ },
+};
+
+static struct driver_d sn74273_gpio_driver = {
+	.name = "74273-gpio",
+	.probe = sn74273_gpio_probe,
+	.of_compatible = DRV_OF_COMPAT(of_sn74273_gpio_leds_match),
+};
+
+static int sn74273_gpio_add(void)
+{
+	platform_driver_register(&sn74273_gpio_driver);
+	return 0;
+}
+postcore_initcall(sn74273_gpio_add);
-- 
2.23.0


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux