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>
This commit is contained in:
parent
a553a4dc73
commit
43ba8232ab
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue