// SPDX-License-Identifier: GPL-2.0+ /* * 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.freenix@gmail.com> * */ #include <common.h> #include <errno.h> #include <dm.h> #include <fdtdec.h> #include <malloc.h> #include <asm/global_data.h> #include <asm/gpio.h> #include <asm/io.h> #include <dm/device_compat.h> #include <dt-bindings/gpio/gpio.h> #include <spi.h> DECLARE_GLOBAL_DATA_PTR; /* * struct gen_74x164_chip - Data for 74Hx164 * * @oe: OE pin * @nregs: number of registers * @buffer: buffer for chained chips */ #define GEN_74X164_NUMBER_GPIOS 8 struct gen_74x164_priv { 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_priv *priv = dev_get_priv(dev); int ret; ret = dm_spi_claim_bus(dev); if (ret) return ret; ret = dm_spi_xfer(dev, priv->nregs * 8, priv->buffer, NULL, SPI_XFER_BEGIN | SPI_XFER_END); dm_spi_release_bus(dev); return ret; } static int gen_74x164_get_value(struct udevice *dev, unsigned offset) { struct gen_74x164_priv *priv = dev_get_priv(dev); uint bank = priv->nregs - 1 - offset / 8; uint pin = offset % 8; return (priv->buffer[bank] >> pin) & 0x1; } static int gen_74x164_set_value(struct udevice *dev, unsigned offset, int value) { struct gen_74x164_priv *priv = dev_get_priv(dev); uint bank = priv->nregs - 1 - offset / 8; uint pin = offset % 8; int ret; if (value) priv->buffer[bank] |= 1 << pin; else priv->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 -ENOSYS; } 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 ofnode_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_priv *priv = dev_get_priv(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); char *str, name[32]; int ret; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); snprintf(name, sizeof(name), "%s_", dev->name); str = strdup(name); if (!str) return -ENOMEM; /* * See Linux kernel: * Documentation/devicetree/bindings/gpio/gpio-74x164.txt */ priv->nregs = fdtdec_get_int(fdt, node, "registers-number", 1); priv->buffer = calloc(priv->nregs, sizeof(u8)); if (!priv->buffer) { ret = -ENOMEM; goto free_str; } ret = fdtdec_get_byte_array(fdt, node, "registers-default", priv->buffer, priv->nregs); if (ret) dev_dbg(dev, "No registers-default property\n"); ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); if (ret) { dev_dbg(dev, "No oe-pins property\n"); } uc_priv->bank_name = str; uc_priv->gpio_count = priv->nregs * 8; ret = gen_74x164_write_conf(dev); if (ret) goto free_buf; dev_dbg(dev, "%s is ready\n", dev->name); return 0; free_buf: free(priv->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, .priv_auto = sizeof(struct gen_74x164_priv), .of_match = gen_74x164_ids, };