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