xref: /openbmc/u-boot/drivers/gpio/74x164_gpio.c (revision e160f7d4)
19300f711SPeng Fan /*
29300f711SPeng Fan  * Take drivers/gpio/gpio-74x164.c as reference.
39300f711SPeng Fan  *
49300f711SPeng Fan  * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
59300f711SPeng Fan  *
69300f711SPeng Fan  * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
79300f711SPeng Fan  *
89300f711SPeng Fan  * SPDX-License-Identifier:	GPL-2.0+
99300f711SPeng Fan  *
109300f711SPeng Fan  */
119300f711SPeng Fan 
129300f711SPeng Fan #include <common.h>
139300f711SPeng Fan #include <errno.h>
149300f711SPeng Fan #include <dm.h>
159300f711SPeng Fan #include <fdtdec.h>
169300f711SPeng Fan #include <malloc.h>
179300f711SPeng Fan #include <asm/gpio.h>
189300f711SPeng Fan #include <asm/io.h>
199300f711SPeng Fan #include <dt-bindings/gpio/gpio.h>
209300f711SPeng Fan #include <spi.h>
219300f711SPeng Fan 
229300f711SPeng Fan DECLARE_GLOBAL_DATA_PTR;
239300f711SPeng Fan 
249300f711SPeng Fan /*
259300f711SPeng Fan  * struct gen_74x164_chip - Data for 74Hx164
269300f711SPeng Fan  *
279300f711SPeng Fan  * @oe: OE pin
289300f711SPeng Fan  * @nregs: number of registers
299300f711SPeng Fan  * @buffer: buffer for chained chips
309300f711SPeng Fan  */
319300f711SPeng Fan #define GEN_74X164_NUMBER_GPIOS 8
329300f711SPeng Fan 
339300f711SPeng Fan struct gen_74x164_priv {
349300f711SPeng Fan 	struct gpio_desc oe;
359300f711SPeng Fan 	u32 nregs;
369300f711SPeng Fan 	/*
379300f711SPeng Fan 	 * Since the nregs are chained, every byte sent will make
389300f711SPeng Fan 	 * the previous byte shift to the next register in the
399300f711SPeng Fan 	 * chain. Thus, the first byte sent will end up in the last
409300f711SPeng Fan 	 * register at the end of the transfer. So, to have a logical
419300f711SPeng Fan 	 * numbering, store the bytes in reverse order.
429300f711SPeng Fan 	 */
439300f711SPeng Fan 	u8 *buffer;
449300f711SPeng Fan };
459300f711SPeng Fan 
469300f711SPeng Fan static int gen_74x164_write_conf(struct udevice *dev)
479300f711SPeng Fan {
489300f711SPeng Fan 	struct gen_74x164_priv *priv = dev_get_priv(dev);
499300f711SPeng Fan 	int ret;
509300f711SPeng Fan 
519300f711SPeng Fan 	ret = dm_spi_claim_bus(dev);
529300f711SPeng Fan 	if (ret)
539300f711SPeng Fan 		return ret;
549300f711SPeng Fan 
559300f711SPeng Fan 	ret = dm_spi_xfer(dev, priv->nregs * 8, priv->buffer, NULL,
569300f711SPeng Fan 			  SPI_XFER_BEGIN | SPI_XFER_END);
579300f711SPeng Fan 
589300f711SPeng Fan 	dm_spi_release_bus(dev);
599300f711SPeng Fan 
609300f711SPeng Fan 	return ret;
619300f711SPeng Fan }
629300f711SPeng Fan 
639300f711SPeng Fan static int gen_74x164_get_value(struct udevice *dev, unsigned offset)
649300f711SPeng Fan {
659300f711SPeng Fan 	struct gen_74x164_priv *priv = dev_get_priv(dev);
669300f711SPeng Fan 	uint bank = priv->nregs - 1 - offset / 8;
679300f711SPeng Fan 	uint pin = offset % 8;
689300f711SPeng Fan 
699300f711SPeng Fan 	return (priv->buffer[bank] >> pin) & 0x1;
709300f711SPeng Fan }
719300f711SPeng Fan 
729300f711SPeng Fan static int gen_74x164_set_value(struct udevice *dev, unsigned offset,
739300f711SPeng Fan 				int value)
749300f711SPeng Fan {
759300f711SPeng Fan 	struct gen_74x164_priv *priv = dev_get_priv(dev);
769300f711SPeng Fan 	uint bank = priv->nregs - 1 - offset / 8;
779300f711SPeng Fan 	uint pin = offset % 8;
789300f711SPeng Fan 	int ret;
799300f711SPeng Fan 
809300f711SPeng Fan 	if (value)
819300f711SPeng Fan 		priv->buffer[bank] |= 1 << pin;
829300f711SPeng Fan 	else
839300f711SPeng Fan 		priv->buffer[bank] &= ~(1 << pin);
849300f711SPeng Fan 
859300f711SPeng Fan 	ret = gen_74x164_write_conf(dev);
869300f711SPeng Fan 	if (ret)
879300f711SPeng Fan 		return ret;
889300f711SPeng Fan 
899300f711SPeng Fan 	return 0;
909300f711SPeng Fan }
919300f711SPeng Fan 
929300f711SPeng Fan static int gen_74x164_direction_input(struct udevice *dev, unsigned offset)
939300f711SPeng Fan {
949300f711SPeng Fan 	return -ENOSYS;
959300f711SPeng Fan }
969300f711SPeng Fan 
979300f711SPeng Fan static int gen_74x164_direction_output(struct udevice *dev, unsigned offset,
989300f711SPeng Fan 				      int value)
999300f711SPeng Fan {
1009300f711SPeng Fan 	return gen_74x164_set_value(dev, offset, value);
1019300f711SPeng Fan }
1029300f711SPeng Fan 
1039300f711SPeng Fan static int gen_74x164_get_function(struct udevice *dev, unsigned offset)
1049300f711SPeng Fan {
1059300f711SPeng Fan 	return GPIOF_OUTPUT;
1069300f711SPeng Fan }
1079300f711SPeng Fan 
1089300f711SPeng Fan static int gen_74x164_xlate(struct udevice *dev, struct gpio_desc *desc,
1099300f711SPeng Fan 			    struct fdtdec_phandle_args *args)
1109300f711SPeng Fan {
1119300f711SPeng Fan 	desc->offset = args->args[0];
1129300f711SPeng Fan 	desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
1139300f711SPeng Fan 
1149300f711SPeng Fan 	return 0;
1159300f711SPeng Fan }
1169300f711SPeng Fan 
1179300f711SPeng Fan static const struct dm_gpio_ops gen_74x164_ops = {
1189300f711SPeng Fan 	.direction_input	= gen_74x164_direction_input,
1199300f711SPeng Fan 	.direction_output	= gen_74x164_direction_output,
1209300f711SPeng Fan 	.get_value		= gen_74x164_get_value,
1219300f711SPeng Fan 	.set_value		= gen_74x164_set_value,
1229300f711SPeng Fan 	.get_function		= gen_74x164_get_function,
1239300f711SPeng Fan 	.xlate			= gen_74x164_xlate,
1249300f711SPeng Fan };
1259300f711SPeng Fan 
1269300f711SPeng Fan static int gen_74x164_probe(struct udevice *dev)
1279300f711SPeng Fan {
1289300f711SPeng Fan 	struct gen_74x164_priv *priv = dev_get_priv(dev);
1299300f711SPeng Fan 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1309300f711SPeng Fan 	char *str, name[32];
1319300f711SPeng Fan 	int ret;
1329300f711SPeng Fan 	const void *fdt = gd->fdt_blob;
133*e160f7d4SSimon Glass 	int node = dev_of_offset(dev);
1349300f711SPeng Fan 
1359300f711SPeng Fan 	snprintf(name, sizeof(name), "%s_", dev->name);
1369300f711SPeng Fan 	str = strdup(name);
1379300f711SPeng Fan 	if (!str)
1389300f711SPeng Fan 		return -ENOMEM;
1399300f711SPeng Fan 
1409300f711SPeng Fan 	/*
1419300f711SPeng Fan 	 * See Linux kernel:
1429300f711SPeng Fan 	 * Documentation/devicetree/bindings/gpio/gpio-74x164.txt
1439300f711SPeng Fan 	 */
1449300f711SPeng Fan 	priv->nregs = fdtdec_get_int(fdt, node, "registers-number", 1);
1459300f711SPeng Fan 	priv->buffer = calloc(priv->nregs, sizeof(u8));
1469300f711SPeng Fan 	if (!priv->buffer) {
1479300f711SPeng Fan 		ret = -ENOMEM;
1489300f711SPeng Fan 		goto free_str;
1499300f711SPeng Fan 	}
1509300f711SPeng Fan 
1519300f711SPeng Fan 	ret = fdtdec_get_byte_array(fdt, node, "registers-default",
1529300f711SPeng Fan 				    priv->buffer, priv->nregs);
1539300f711SPeng Fan 	if (ret)
1549300f711SPeng Fan 		dev_dbg(dev, "No registers-default property\n");
1559300f711SPeng Fan 
1569300f711SPeng Fan 	ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe,
1579300f711SPeng Fan 				   GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
1589300f711SPeng Fan 	if (ret) {
1599300f711SPeng Fan 		dev_err(dev, "No oe-pins property\n");
1609300f711SPeng Fan 		goto free_buf;
1619300f711SPeng Fan 	}
1629300f711SPeng Fan 
1639300f711SPeng Fan 	uc_priv->bank_name = str;
1649300f711SPeng Fan 	uc_priv->gpio_count = priv->nregs * 8;
1659300f711SPeng Fan 
1669300f711SPeng Fan 	ret = gen_74x164_write_conf(dev);
1679300f711SPeng Fan 	if (ret)
1689300f711SPeng Fan 		goto free_buf;
1699300f711SPeng Fan 
1709300f711SPeng Fan 	dev_dbg(dev, "%s is ready\n", dev->name);
1719300f711SPeng Fan 
1729300f711SPeng Fan 	return 0;
1739300f711SPeng Fan 
1749300f711SPeng Fan free_buf:
1759300f711SPeng Fan 	free(priv->buffer);
1769300f711SPeng Fan free_str:
1779300f711SPeng Fan 	free(str);
1789300f711SPeng Fan 	return ret;
1799300f711SPeng Fan }
1809300f711SPeng Fan 
1819300f711SPeng Fan static const struct udevice_id gen_74x164_ids[] = {
1829300f711SPeng Fan 	{ .compatible = "fairchild,74hc595" },
1839300f711SPeng Fan 	{ }
1849300f711SPeng Fan };
1859300f711SPeng Fan 
1869300f711SPeng Fan U_BOOT_DRIVER(74x164) = {
1879300f711SPeng Fan 	.name		= "74x164",
1889300f711SPeng Fan 	.id		= UCLASS_GPIO,
1899300f711SPeng Fan 	.ops		= &gen_74x164_ops,
1909300f711SPeng Fan 	.probe		= gen_74x164_probe,
1919300f711SPeng Fan 	.priv_auto_alloc_size = sizeof(struct gen_74x164_priv),
1929300f711SPeng Fan 	.of_match	= gen_74x164_ids,
1939300f711SPeng Fan };
194