xref: /rk3399_rockchip-uboot/drivers/gpio/pcf8575_gpio.c (revision 21342d4aed6c77a4aa7a5b2579b3c23e21aea31a)
15746b0dfSVignesh R /*
25746b0dfSVignesh R  * PCF8575 I2C GPIO EXPANDER DRIVER
35746b0dfSVignesh R  *
45746b0dfSVignesh R  * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
55746b0dfSVignesh R  *
65746b0dfSVignesh R  * Vignesh R <vigneshr@ti.com>
75746b0dfSVignesh R  *
85746b0dfSVignesh R  * SPDX-License-Identifier:	GPL-2.0
95746b0dfSVignesh R  *
105746b0dfSVignesh R  *
115746b0dfSVignesh R  * Driver for TI PCF-8575 16-bit I2C gpio expander. Based on
125746b0dfSVignesh R  * gpio-pcf857x Linux Kernel(v4.7) driver.
135746b0dfSVignesh R  *
145746b0dfSVignesh R  * Copyright (C) 2007 David Brownell
155746b0dfSVignesh R  *
165746b0dfSVignesh R  */
175746b0dfSVignesh R 
185746b0dfSVignesh R /*
195746b0dfSVignesh R  * NOTE: The driver and devicetree bindings are borrowed from Linux
205746b0dfSVignesh R  * Kernel, but driver does not support all PCF857x devices. It currently
215746b0dfSVignesh R  * supports PCF8575 16-bit expander by TI and NXP.
225746b0dfSVignesh R  *
235746b0dfSVignesh R  * TODO(vigneshr@ti.com):
245746b0dfSVignesh R  * Support 8 bit PCF857x compatible expanders.
255746b0dfSVignesh R  */
265746b0dfSVignesh R 
275746b0dfSVignesh R #include <common.h>
285746b0dfSVignesh R #include <dm.h>
295746b0dfSVignesh R #include <i2c.h>
305746b0dfSVignesh R #include <asm-generic/gpio.h>
315746b0dfSVignesh R 
325746b0dfSVignesh R DECLARE_GLOBAL_DATA_PTR;
335746b0dfSVignesh R 
345746b0dfSVignesh R struct pcf8575_chip {
355746b0dfSVignesh R 	int gpio_count;		/* No. GPIOs supported by the chip */
365746b0dfSVignesh R 
375746b0dfSVignesh R 	/* NOTE:  these chips have strange "quasi-bidirectional" I/O pins.
385746b0dfSVignesh R 	 * We can't actually know whether a pin is configured (a) as output
395746b0dfSVignesh R 	 * and driving the signal low, or (b) as input and reporting a low
405746b0dfSVignesh R 	 * value ... without knowing the last value written since the chip
415746b0dfSVignesh R 	 * came out of reset (if any).  We can't read the latched output.
425746b0dfSVignesh R 	 * In short, the only reliable solution for setting up pin direction
435746b0dfSVignesh R 	 * is to do it explicitly.
445746b0dfSVignesh R 	 *
455746b0dfSVignesh R 	 * Using "out" avoids that trouble.  When left initialized to zero,
465746b0dfSVignesh R 	 * our software copy of the "latch" then matches the chip's all-ones
475746b0dfSVignesh R 	 * reset state.  Otherwise it flags pins to be driven low.
485746b0dfSVignesh R 	 */
495746b0dfSVignesh R 	unsigned int out;	/* software latch */
505746b0dfSVignesh R 	const char *bank_name;	/* Name of the expander bank */
515746b0dfSVignesh R };
525746b0dfSVignesh R 
535746b0dfSVignesh R /* Read/Write to 16-bit I/O expander */
545746b0dfSVignesh R 
pcf8575_i2c_write_le16(struct udevice * dev,unsigned int word)555746b0dfSVignesh R static int pcf8575_i2c_write_le16(struct udevice *dev, unsigned int word)
565746b0dfSVignesh R {
575746b0dfSVignesh R 	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
585746b0dfSVignesh R 	u8 buf[2] = { word & 0xff, word >> 8, };
595746b0dfSVignesh R 	int ret;
605746b0dfSVignesh R 
615746b0dfSVignesh R 	ret = dm_i2c_write(dev, 0, buf, 2);
625746b0dfSVignesh R 	if (ret)
635746b0dfSVignesh R 		printf("%s i2c write failed to addr %x\n", __func__,
645746b0dfSVignesh R 		       chip->chip_addr);
655746b0dfSVignesh R 
665746b0dfSVignesh R 	return ret;
675746b0dfSVignesh R }
685746b0dfSVignesh R 
pcf8575_i2c_read_le16(struct udevice * dev)695746b0dfSVignesh R static int pcf8575_i2c_read_le16(struct udevice *dev)
705746b0dfSVignesh R {
715746b0dfSVignesh R 	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
725746b0dfSVignesh R 	u8 buf[2];
735746b0dfSVignesh R 	int ret;
745746b0dfSVignesh R 
755746b0dfSVignesh R 	ret = dm_i2c_read(dev, 0, buf, 2);
765746b0dfSVignesh R 	if (ret) {
775746b0dfSVignesh R 		printf("%s i2c read failed from addr %x\n", __func__,
785746b0dfSVignesh R 		       chip->chip_addr);
795746b0dfSVignesh R 		return ret;
805746b0dfSVignesh R 	}
815746b0dfSVignesh R 
825746b0dfSVignesh R 	return (buf[1] << 8) | buf[0];
835746b0dfSVignesh R }
845746b0dfSVignesh R 
pcf8575_direction_input(struct udevice * dev,unsigned offset)855746b0dfSVignesh R static int pcf8575_direction_input(struct udevice *dev, unsigned offset)
865746b0dfSVignesh R {
875746b0dfSVignesh R 	struct pcf8575_chip *plat = dev_get_platdata(dev);
885746b0dfSVignesh R 	int status;
895746b0dfSVignesh R 
905746b0dfSVignesh R 	plat->out |= BIT(offset);
915746b0dfSVignesh R 	status = pcf8575_i2c_write_le16(dev, plat->out);
925746b0dfSVignesh R 
935746b0dfSVignesh R 	return status;
945746b0dfSVignesh R }
955746b0dfSVignesh R 
pcf8575_direction_output(struct udevice * dev,unsigned int offset,int value)965746b0dfSVignesh R static int pcf8575_direction_output(struct udevice *dev,
975746b0dfSVignesh R 				    unsigned int offset, int value)
985746b0dfSVignesh R {
995746b0dfSVignesh R 	struct pcf8575_chip *plat = dev_get_platdata(dev);
1005746b0dfSVignesh R 	int ret;
1015746b0dfSVignesh R 
1025746b0dfSVignesh R 	if (value)
1035746b0dfSVignesh R 		plat->out |= BIT(offset);
1045746b0dfSVignesh R 	else
1055746b0dfSVignesh R 		plat->out &= ~BIT(offset);
1065746b0dfSVignesh R 
1075746b0dfSVignesh R 	ret = pcf8575_i2c_write_le16(dev, plat->out);
1085746b0dfSVignesh R 
1095746b0dfSVignesh R 	return ret;
1105746b0dfSVignesh R }
1115746b0dfSVignesh R 
pcf8575_get_value(struct udevice * dev,unsigned int offset)1125746b0dfSVignesh R static int pcf8575_get_value(struct udevice *dev, unsigned int offset)
1135746b0dfSVignesh R {
1145746b0dfSVignesh R 	int             value;
1155746b0dfSVignesh R 
1165746b0dfSVignesh R 	value = pcf8575_i2c_read_le16(dev);
1175746b0dfSVignesh R 
1185746b0dfSVignesh R 	return (value < 0) ? value : ((value & BIT(offset)) >> offset);
1195746b0dfSVignesh R }
1205746b0dfSVignesh R 
pcf8575_set_value(struct udevice * dev,unsigned int offset,int value)1215746b0dfSVignesh R static int pcf8575_set_value(struct udevice *dev, unsigned int offset,
1225746b0dfSVignesh R 			     int value)
1235746b0dfSVignesh R {
1245746b0dfSVignesh R 	return pcf8575_direction_output(dev, offset, value);
1255746b0dfSVignesh R }
1265746b0dfSVignesh R 
pcf8575_ofdata_platdata(struct udevice * dev)1275746b0dfSVignesh R static int pcf8575_ofdata_platdata(struct udevice *dev)
1285746b0dfSVignesh R {
1295746b0dfSVignesh R 	struct pcf8575_chip *plat = dev_get_platdata(dev);
1305746b0dfSVignesh R 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1315746b0dfSVignesh R 
1325746b0dfSVignesh R 	int n_latch;
1335746b0dfSVignesh R 
134*e160f7d4SSimon Glass 	uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
1355746b0dfSVignesh R 					     "gpio-count", 16);
136*e160f7d4SSimon Glass 	uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
1375746b0dfSVignesh R 					 "gpio-bank-name", NULL);
1385746b0dfSVignesh R 	if (!uc_priv->bank_name)
1395746b0dfSVignesh R 		uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
140*e160f7d4SSimon Glass 						  dev_of_offset(dev), NULL);
1415746b0dfSVignesh R 
142*e160f7d4SSimon Glass 	n_latch = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
1435746b0dfSVignesh R 				  "lines-initial-states", 0);
1445746b0dfSVignesh R 	plat->out = ~n_latch;
1455746b0dfSVignesh R 
1465746b0dfSVignesh R 	return 0;
1475746b0dfSVignesh R }
1485746b0dfSVignesh R 
pcf8575_gpio_probe(struct udevice * dev)1495746b0dfSVignesh R static int pcf8575_gpio_probe(struct udevice  *dev)
1505746b0dfSVignesh R {
1515746b0dfSVignesh R 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1525746b0dfSVignesh R 
1535746b0dfSVignesh R 	debug("%s GPIO controller with %d gpios probed\n",
1545746b0dfSVignesh R 	      uc_priv->bank_name, uc_priv->gpio_count);
1555746b0dfSVignesh R 
1565746b0dfSVignesh R 	return 0;
1575746b0dfSVignesh R }
1585746b0dfSVignesh R 
1595746b0dfSVignesh R static const struct dm_gpio_ops pcf8575_gpio_ops = {
1605746b0dfSVignesh R 	.direction_input	= pcf8575_direction_input,
1615746b0dfSVignesh R 	.direction_output	= pcf8575_direction_output,
1625746b0dfSVignesh R 	.get_value		= pcf8575_get_value,
1635746b0dfSVignesh R 	.set_value		= pcf8575_set_value,
1645746b0dfSVignesh R };
1655746b0dfSVignesh R 
1665746b0dfSVignesh R static const struct udevice_id pcf8575_gpio_ids[] = {
1675746b0dfSVignesh R 	{ .compatible = "nxp,pcf8575" },
1685746b0dfSVignesh R 	{ .compatible = "ti,pcf8575" },
1695746b0dfSVignesh R 	{ }
1705746b0dfSVignesh R };
1715746b0dfSVignesh R 
1725746b0dfSVignesh R U_BOOT_DRIVER(gpio_pcf8575) = {
1735746b0dfSVignesh R 	.name	= "gpio_pcf8575",
1745746b0dfSVignesh R 	.id	= UCLASS_GPIO,
1755746b0dfSVignesh R 	.ops	= &pcf8575_gpio_ops,
1765746b0dfSVignesh R 	.of_match = pcf8575_gpio_ids,
1775746b0dfSVignesh R 	.ofdata_to_platdata = pcf8575_ofdata_platdata,
1785746b0dfSVignesh R 	.probe	= pcf8575_gpio_probe,
1795746b0dfSVignesh R 	.platdata_auto_alloc_size = sizeof(struct pcf8575_chip),
1805746b0dfSVignesh R };
181