1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * PCF8575 I2C GPIO EXPANDER DRIVER
4  *
5  * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
6  *
7  * Vignesh R <vigneshr@ti.com>
8  *
9  *
10  * Driver for TI PCF-8575 16-bit I2C gpio expander. Based on
11  * gpio-pcf857x Linux Kernel(v4.7) driver.
12  *
13  * Copyright (C) 2007 David Brownell
14  *
15  */
16 
17 /*
18  * NOTE: The driver and devicetree bindings are borrowed from Linux
19  * Kernel, but driver does not support all PCF857x devices. It currently
20  * supports PCF8575 16-bit expander by TI and NXP.
21  *
22  * TODO(vigneshr@ti.com):
23  * Support 8 bit PCF857x compatible expanders.
24  */
25 
26 #include <common.h>
27 #include <dm.h>
28 #include <i2c.h>
29 #include <log.h>
30 #include <asm-generic/gpio.h>
31 #include <asm/global_data.h>
32 #include <linux/bitops.h>
33 
34 DECLARE_GLOBAL_DATA_PTR;
35 
36 struct pcf8575_chip {
37 	int gpio_count;		/* No. GPIOs supported by the chip */
38 
39 	/* NOTE:  these chips have strange "quasi-bidirectional" I/O pins.
40 	 * We can't actually know whether a pin is configured (a) as output
41 	 * and driving the signal low, or (b) as input and reporting a low
42 	 * value ... without knowing the last value written since the chip
43 	 * came out of reset (if any).  We can't read the latched output.
44 	 * In short, the only reliable solution for setting up pin direction
45 	 * is to do it explicitly.
46 	 *
47 	 * Using "out" avoids that trouble.  When left initialized to zero,
48 	 * our software copy of the "latch" then matches the chip's all-ones
49 	 * reset state.  Otherwise it flags pins to be driven low.
50 	 */
51 	unsigned int out;	/* software latch */
52 	const char *bank_name;	/* Name of the expander bank */
53 };
54 
55 /* Read/Write to 16-bit I/O expander */
56 
pcf8575_i2c_write_le16(struct udevice * dev,unsigned int word)57 static int pcf8575_i2c_write_le16(struct udevice *dev, unsigned int word)
58 {
59 	struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
60 	u8 buf[2] = { word & 0xff, word >> 8, };
61 	int ret;
62 
63 	ret = dm_i2c_write(dev, 0, buf, 2);
64 	if (ret)
65 		printf("%s i2c write failed to addr %x\n", __func__,
66 		       chip->chip_addr);
67 
68 	return ret;
69 }
70 
pcf8575_i2c_read_le16(struct udevice * dev)71 static int pcf8575_i2c_read_le16(struct udevice *dev)
72 {
73 	struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
74 	u8 buf[2];
75 	int ret;
76 
77 	ret = dm_i2c_read(dev, 0, buf, 2);
78 	if (ret) {
79 		printf("%s i2c read failed from addr %x\n", __func__,
80 		       chip->chip_addr);
81 		return ret;
82 	}
83 
84 	return (buf[1] << 8) | buf[0];
85 }
86 
pcf8575_direction_input(struct udevice * dev,unsigned offset)87 static int pcf8575_direction_input(struct udevice *dev, unsigned offset)
88 {
89 	struct pcf8575_chip *plat = dev_get_plat(dev);
90 	int status;
91 
92 	plat->out |= BIT(offset);
93 	status = pcf8575_i2c_write_le16(dev, plat->out);
94 
95 	return status;
96 }
97 
pcf8575_direction_output(struct udevice * dev,unsigned int offset,int value)98 static int pcf8575_direction_output(struct udevice *dev,
99 				    unsigned int offset, int value)
100 {
101 	struct pcf8575_chip *plat = dev_get_plat(dev);
102 	int ret;
103 
104 	if (value)
105 		plat->out |= BIT(offset);
106 	else
107 		plat->out &= ~BIT(offset);
108 
109 	ret = pcf8575_i2c_write_le16(dev, plat->out);
110 
111 	return ret;
112 }
113 
pcf8575_get_value(struct udevice * dev,unsigned int offset)114 static int pcf8575_get_value(struct udevice *dev, unsigned int offset)
115 {
116 	int             value;
117 
118 	value = pcf8575_i2c_read_le16(dev);
119 
120 	return (value < 0) ? value : ((value & BIT(offset)) >> offset);
121 }
122 
pcf8575_set_value(struct udevice * dev,unsigned int offset,int value)123 static int pcf8575_set_value(struct udevice *dev, unsigned int offset,
124 			     int value)
125 {
126 	return pcf8575_direction_output(dev, offset, value);
127 }
128 
pcf8575_ofdata_plat(struct udevice * dev)129 static int pcf8575_ofdata_plat(struct udevice *dev)
130 {
131 	struct pcf8575_chip *plat = dev_get_plat(dev);
132 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
133 
134 	int n_latch;
135 
136 	uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
137 					     "gpio-count", 16);
138 	uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
139 					 "gpio-bank-name", NULL);
140 	if (!uc_priv->bank_name)
141 		uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
142 						  dev_of_offset(dev), NULL);
143 
144 	n_latch = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
145 				  "lines-initial-states", 0);
146 	plat->out = ~n_latch;
147 
148 	return 0;
149 }
150 
pcf8575_gpio_probe(struct udevice * dev)151 static int pcf8575_gpio_probe(struct udevice  *dev)
152 {
153 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
154 
155 	debug("%s GPIO controller with %d gpios probed\n",
156 	      uc_priv->bank_name, uc_priv->gpio_count);
157 
158 	return 0;
159 }
160 
161 static const struct dm_gpio_ops pcf8575_gpio_ops = {
162 	.direction_input	= pcf8575_direction_input,
163 	.direction_output	= pcf8575_direction_output,
164 	.get_value		= pcf8575_get_value,
165 	.set_value		= pcf8575_set_value,
166 };
167 
168 static const struct udevice_id pcf8575_gpio_ids[] = {
169 	{ .compatible = "nxp,pcf8575" },
170 	{ .compatible = "ti,pcf8575" },
171 	{ }
172 };
173 
174 U_BOOT_DRIVER(gpio_pcf8575) = {
175 	.name	= "gpio_pcf8575",
176 	.id	= UCLASS_GPIO,
177 	.ops	= &pcf8575_gpio_ops,
178 	.of_match = pcf8575_gpio_ids,
179 	.of_to_plat = pcf8575_ofdata_plat,
180 	.probe	= pcf8575_gpio_probe,
181 	.plat_auto	= sizeof(struct pcf8575_chip),
182 };
183