summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorSebastian Hesselbarth <sebastian.hesselbarth@gmail.com>2015-04-20 22:11:14 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-04-27 08:09:34 +0200
commit43ba8232ab75133227cef3a7c4b7ae22703c53c1 (patch)
tree29702c4e8adeebb168b57f430068505b3d87d7dc /drivers/gpio
parenta553a4dc731e3f4ce0936361ca508939e76a9dce (diff)
downloadbarebox-43ba8232ab75133227cef3a7c4b7ae22703c53c1.tar.gz
barebox-43ba8232ab75133227cef3a7c4b7ae22703c53c1.tar.xz
gpio: Add driver for 74x164 compatible shift-registers
A 74x164 shift register can be seen as a SPI attached GPIO expander. This adds a driver for those poor-man expanders based on the Linux driver. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig8
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-74164.c131
3 files changed, 140 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index c8b1efb1f2..9cb22613dd 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -13,6 +13,14 @@ config GPIO_DIGIC
bool "GPIO support for Canon DIGIC"
depends on ARCH_DIGIC
+config GPIO_74164
+ bool "Generic SPI attached shift register"
+ depends on SPI
+ help
+ Driver for 74x164 compatible serial-in/parallel-out 8-outputs
+ shift registers. This driver can be used to provide access
+ to more gpio outputs.
+
config GPIO_BCM2835
bool "GPIO support for BCM2835"
depends on ARCH_BCM2835
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 508e228bac..1d946614e3 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_GPIOLIB) += gpiolib.o
+obj-$(CONFIG_GPIO_74164) += gpio-74164.o
obj-$(CONFIG_GPIO_BCM2835) += gpio-bcm2835.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
diff --git a/drivers/gpio/gpio-74164.c b/drivers/gpio/gpio-74164.c
new file mode 100644
index 0000000000..926207aaf7
--- /dev/null
+++ b/drivers/gpio/gpio-74164.c
@@ -0,0 +1,131 @@
+/*
+ * Generic serial-in/parallel-out 8-bits shift register GPIO driver
+ * e.g. for 74x164
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * Based on Linux driver
+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <gpio.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <spi/spi.h>
+
+#define MAX_REGS 4
+
+struct gpio_74164 {
+ struct gpio_chip chip;
+ struct spi_device *spi;
+ u8 buffer[MAX_REGS];
+ u8 num_regs;
+};
+
+#define gc_to_gpio_74164(c) container_of(c, struct gpio_74164, chip)
+
+/*
+ * Since the registers are chained, every byte sent will make
+ * the previous byte shift to the next register in the
+ * chain. Thus, the first byte send will end up in the last
+ * register at the end of the transfer. So, to have a logical
+ * numbering, send the bytes in reverse order so that the last
+ * byte of the buffer will end up in the last register.
+ */
+static int gpio_74164_update_buffers(struct gpio_74164 *priv)
+{
+ u8 b[MAX_REGS];
+ int n;
+
+ for (n = 0; n < priv->num_regs; n++)
+ b[priv->num_regs - n - 1] = priv->buffer[n];
+ spi_write(priv->spi, b, priv->num_regs);
+
+ return 0;
+}
+
+static int gpio_74164_get_value(struct gpio_chip *chip, unsigned off)
+{
+ struct gpio_74164 *priv = gc_to_gpio_74164(chip);
+ u8 bank = off / 8;
+ u8 pin = off % 8;
+
+ return (priv->buffer[bank] >> pin) & 1;
+}
+
+static void gpio_74164_set_value(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ struct gpio_74164 *priv = gc_to_gpio_74164(chip);
+ u8 bank = off / 8;
+ u8 pin = off % 8;
+
+ if (val)
+ priv->buffer[bank] |= BIT(pin);
+ else
+ priv->buffer[bank] &= ~BIT(pin);
+
+ gpio_74164_update_buffers(priv);
+}
+
+static int gpio_74164_direction_output(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ gpio_74164_set_value(chip, off, val);
+ return 0;
+}
+
+static struct gpio_ops gpio_74164_ops = {
+ .direction_output = gpio_74164_direction_output,
+ .get = gpio_74164_get_value,
+ .set = gpio_74164_set_value,
+};
+
+static struct platform_device_id gpio_74164_ids[] = {
+ { "74hc164" },
+ { "74hc595" },
+ { }
+};
+
+static int gpio_74164_probe(struct device_d *dev)
+{
+ struct spi_device *spi = (struct spi_device *)dev->type_data;
+ struct gpio_74164 *priv;
+ u32 num_regs = 1;
+
+ dev->id = DEVICE_ID_DYNAMIC;
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
+ dev->id = of_alias_get_id(dev->device_node, "gpio");
+ of_property_read_u32(dev->device_node, "registers-number",
+ &num_regs);
+ }
+
+ if (num_regs > MAX_REGS)
+ num_regs = MAX_REGS;
+
+ priv = xzalloc(sizeof(*priv));
+ priv->spi = spi;
+ priv->num_regs = num_regs;
+ priv->chip.dev = dev;
+ priv->chip.ops = &gpio_74164_ops;
+ priv->chip.base = dev->id * 32;
+ priv->chip.ngpio = num_regs * 8;
+
+ return gpiochip_add(&priv->chip);
+}
+
+static struct driver_d gpio_74164_driver = {
+ .name = "gpio-74164",
+ .probe = gpio_74164_probe,
+ .id_table = gpio_74164_ids,
+};
+device_spi_driver(gpio_74164_driver);