Introduce driver to support "fairchild,74hc595" devices. 1. Take linux drivers/drivers/gpio/gpio-74x164.c as reference. 2. Following the naming used in Linux driver with gen_7x164 as the prefix. 3. Enable CONFIG_DM_74X164 to use this driver. 4. Follow Documentation/devicetree/bindings/gpio/gpio-74x164.txt to add device nodes 5. Tested on i.MX6 UltraLite with 74LV595 using gpio command and oscillograph.
Signed-off-by: Peng Fan <van.free...@gmail.com> Cc: Simon Glass <s...@chromium.org> Cc: Masahiro Yamada <yamada.masah...@socionext.com> Cc: Chin Liang See <cl...@altera.com> Cc: Bhuvanchandra DV <bhuvanchandra...@toradex.com> Cc: Daniel Schwierzeck <daniel.schwierz...@gmail.com> Cc: Fabio Estevam <fabio.este...@nxp.com> Cc: Stefano Babic <sba...@denx.de> --- drivers/gpio/74x164_gpio.c | 220 +++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + 3 files changed, 229 insertions(+) create mode 100644 drivers/gpio/74x164_gpio.c diff --git a/drivers/gpio/74x164_gpio.c b/drivers/gpio/74x164_gpio.c new file mode 100644 index 0000000..5d12a9a --- /dev/null +++ b/drivers/gpio/74x164_gpio.c @@ -0,0 +1,220 @@ +/* + * Take drivers/gpio/gpio-74x164.c as reference. + * + * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver + * + * Copyright (C) 2016 Peng Fan <van.free...@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <dt-bindings/gpio/gpio.h> +#include <spi.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * struct gen_74x164_chip - Data for 74Hx164 + * + * @dev: udevice structure for the device + * @slave: spi slave + * @oe: OE pin + * @nregs: number of registers + * @buffer: buffer for chained chips + */ +#define GEN_74X164_NUMBER_GPIOS 8 + +struct gen_74x164_info { + struct udevice *dev; + struct spi_slave *slave; + struct gpio_desc oe; + u32 nregs; + /* + * Since the nregs are chained, every byte sent will make + * the previous byte shift to the next register in the + * chain. Thus, the first byte sent will end up in the last + * register at the end of the transfer. So, to have a logical + * numbering, store the bytes in reverse order. + */ + u8 *buffer; +}; + +static int gen_74x164_write_conf(struct udevice *dev) +{ + struct gen_74x164_info *info = dev_get_platdata(dev); + struct spi_slave *slave = info->slave; + int ret; + + ret = spi_claim_bus(slave); + if (ret) + return ret; + + ret = spi_xfer(slave, info->nregs * 8, info->buffer, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(slave); + + return ret; +} + +static int gen_74x164_get_value(struct udevice *dev, unsigned offset) +{ + struct gen_74x164_info *info = dev_get_platdata(dev); + u8 bank = info->nregs - 1 - offset / 8; + u8 pin = offset % 8; + + return (info->buffer[bank] >> pin) & 0x1; +} + +static int gen_74x164_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct gen_74x164_info *info = dev_get_platdata(dev); + u8 bank = info->nregs - 1 - offset / 8; + u8 pin = offset % 8; + int ret; + + if (value) + info->buffer[bank] |= 1 << pin; + else + info->buffer[bank] &= ~(1 << pin); + + ret = gen_74x164_write_conf(dev); + if (ret) + return ret; + + return 0; +} + +static int gen_74x164_direction_input(struct udevice *dev, unsigned offset) +{ + return -EINVAL; +} + +static int gen_74x164_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + return gen_74x164_set_value(dev, offset, value); +} + +static int gen_74x164_get_function(struct udevice *dev, unsigned offset) +{ + return GPIOF_OUTPUT; +} + +static int gen_74x164_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + +static const struct dm_gpio_ops gen_74x164_ops = { + .direction_input = gen_74x164_direction_input, + .direction_output = gen_74x164_direction_output, + .get_value = gen_74x164_get_value, + .set_value = gen_74x164_set_value, + .get_function = gen_74x164_get_function, + .xlate = gen_74x164_xlate, +}; + +static int gen_74x164_probe(struct udevice *dev) +{ + struct gen_74x164_info *info = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + char *str, *str1; + int ret; + const void *fdt = gd->fdt_blob; + int node = dev->of_offset; + struct udevice *bus; + int busnum; + char name[30]; + + str = strdup(dev->name); + if (!str) + return -ENOMEM; + + info->nregs = fdtdec_get_int(fdt, node, "registers-number", 1); + info->buffer = calloc(info->nregs, sizeof(u8)); + if (!info->buffer) { + ret = -ENOMEM; + goto free_str; + } + + ret = fdtdec_get_byte_array(fdt, node, "registers-default", + info->buffer, info->nregs); + if (ret) + dev_dbg(dev, "No registers-default property\n"); + + ret = gpio_request_by_name(dev, "oe-gpios", 0, &info->oe, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + dev_err(dev, "No oe-pins property\n"); + goto free_buf; + } + + /* output enable */ + ret = dm_gpio_set_value(&info->oe, 0); + if (ret) { + dev_err(dev, "Set oe-pins value failure\n"); + goto free_buf; + } + + uc_priv->bank_name = str; + uc_priv->gpio_count = info->nregs * 8; + + bus = dev_get_parent(dev); + busnum = bus->seq; + + snprintf(name, sizeof(name), "generic_%d:%d", busnum, 0); + str1 = strdup(name); + if (!str1) { + ret = -ENOMEM; + goto free_buf; + } + + ret = spi_get_bus_and_cs(busnum, 0, 1000000, SPI_MODE_0, + "spi_generic_drv", str, &bus, &info->slave); + if (ret) + goto free_str1; + + ret = gen_74x164_write_conf(dev); + if (ret) + goto free_str1; + + dev_dbg(dev, "%s is ready\n", dev->name); + + return 0; + +free_str1: + free(str1); +free_buf: + free(info->buffer); +free_str: + free(str); + return ret; +} + +static const struct udevice_id gen_74x164_ids[] = { + { .compatible = "fairchild,74hc595" }, + { } +}; + +U_BOOT_DRIVER(74x164) = { + .name = "74x164", + .id = UCLASS_GPIO, + .ops = &gen_74x164_ops, + .probe = gen_74x164_probe, + .platdata_auto_alloc_size = sizeof(struct gen_74x164_info), + .of_match = gen_74x164_ids, +}; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 51658f1..43fc9c4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -96,6 +96,14 @@ config PIC32_GPIO help Say yes here to support Microchip PIC32 GPIOs. +config DM_74X164 + bool "74x164 serial-in/parallel-out 8-bits shift register" + depends on DM_GPIO + 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 DM_PCA953X bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports" depends on DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index bfc67d2..393b148 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -12,6 +12,7 @@ endif obj-$(CONFIG_DM_GPIO) += gpio-uclass.o obj-$(CONFIG_DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_DM_74X164) += 74x164_gpio.o obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o -- 2.6.2 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot