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