xref: /openbsd-src/sys/dev/i2c/pca9554.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 /*	$OpenBSD: pca9554.c,v 1.17 2008/09/10 16:13:43 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Theo de Raadt
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/gpio.h>
23 #include <sys/sensors.h>
24 
25 #include <dev/i2c/i2cvar.h>
26 
27 #include <dev/gpio/gpiovar.h>
28 
29 /* Philips 9554/6/7 registers */
30 #define PCA9554_IN		0x00
31 #define PCA9554_OUT		0x01
32 #define PCA9554_POLARITY	0x02
33 #define PCA9554_CONFIG		0x03
34 
35 /* Philips 9555 registers */
36 #define PCA9555_IN0		0x00
37 #define PCA9555_IN1		0x01
38 #define PCA9555_OUT0		0x02
39 #define PCA9555_OUT1		0x03
40 #define PCA9555_POLARITY0	0x04
41 #define PCA9555_POLARITY1	0x05
42 #define PCA9555_CONFIG0		0x06
43 #define PCA9555_CONFIG1		0x07
44 
45 /* Sensors */
46 #define PCAGPIO_NPINS	16
47 
48 #define PCAGPIO_NPORTS	2
49 #define PCAGPIO_PORT(_pin)	((_pin) > 7 ? 1 : 0)
50 #define PCAGPIO_BIT(_pin)	(1 << ((_pin) % 8))
51 
52 /* Register mapping index */
53 enum pcigpio_cmd {
54 	PCAGPIO_IN		= 0,
55 	PCAGPIO_OUT,
56 	PCAGPIO_POLARITY,
57 	PCAGPIO_CONFIG,
58 	PCAGPIO_MAX
59 };
60 
61 struct pcagpio_softc {
62 	struct device	sc_dev;
63 	i2c_tag_t	sc_tag;
64 	i2c_addr_t	sc_addr;
65 
66 	u_int8_t	sc_npins;
67 	u_int8_t	sc_control[PCAGPIO_NPORTS];
68 	u_int8_t	sc_polarity[PCAGPIO_NPORTS];
69 	u_int8_t	sc_regs[PCAGPIO_NPORTS][PCAGPIO_MAX];
70 
71 	struct gpio_chipset_tag sc_gpio_gc;
72         gpio_pin_t sc_gpio_pins[PCAGPIO_NPINS];
73 
74 	struct ksensor sc_sensor[PCAGPIO_NPINS];
75 	struct ksensordev sc_sensordev;
76 };
77 
78 int	pcagpio_match(struct device *, void *, void *);
79 void	pcagpio_attach(struct device *, struct device *, void *);
80 int	pcagpio_init(struct pcagpio_softc *, int, u_int8_t *);
81 void	pcagpio_refresh(void *);
82 
83 int     pcagpio_gpio_pin_read(void *, int);
84 void    pcagpio_gpio_pin_write(void *, int, int);
85 void    pcagpio_gpio_pin_ctl(void *, int, int);
86 
87 struct cfattach pcagpio_ca = {
88 	sizeof(struct pcagpio_softc), pcagpio_match, pcagpio_attach
89 };
90 
91 struct cfdriver pcagpio_cd = {
92 	NULL, "pcagpio", DV_DULL
93 };
94 
95 int
96 pcagpio_match(struct device *parent, void *match, void *aux)
97 {
98 	struct i2c_attach_args *ia = aux;
99 
100 	if (strcmp(ia->ia_name, "PCA9554") == 0 ||
101 	    strcmp(ia->ia_name, "PCA9554M") == 0 ||
102 	    strcmp(ia->ia_name, "pca9555") == 0 ||
103 	    strcmp(ia->ia_name, "pca9556") == 0 ||
104 	    strcmp(ia->ia_name, "pca9557") == 0)
105 		return (1);
106 	return (0);
107 }
108 
109 void
110 pcagpio_attach(struct device *parent, struct device *self, void *aux)
111 {
112 	struct pcagpio_softc *sc = (struct pcagpio_softc *)self;
113 	struct i2c_attach_args *ia = aux;
114 	struct gpiobus_attach_args gba;
115 	int outputs = 0, i, port, bit;
116 	u_int8_t data[PCAGPIO_NPORTS];
117 
118 	sc->sc_tag = ia->ia_tag;
119 	sc->sc_addr = ia->ia_addr;
120 
121 	if (strcmp(ia->ia_name, "pca9555") == 0) {
122 		/* The pca9555 has two 8 bit ports */
123 		sc->sc_regs[0][PCAGPIO_IN] = PCA9555_IN0;
124 		sc->sc_regs[0][PCAGPIO_OUT] = PCA9555_OUT0;
125 		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9555_POLARITY0;
126 		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9555_CONFIG0;
127 		sc->sc_regs[1][PCAGPIO_IN] = PCA9555_IN1;
128 		sc->sc_regs[1][PCAGPIO_OUT] = PCA9555_OUT1;
129 		sc->sc_regs[1][PCAGPIO_POLARITY] = PCA9555_POLARITY1;
130 		sc->sc_regs[1][PCAGPIO_CONFIG] = PCA9555_CONFIG1;
131 		sc->sc_npins = 16;
132 	} else {
133 		/* All other supported devices have one 8 bit port */
134 		sc->sc_regs[0][PCAGPIO_IN] = PCA9554_IN;
135 		sc->sc_regs[0][PCAGPIO_OUT] = PCA9554_OUT;
136 		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9554_POLARITY;
137 		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9554_CONFIG;
138 		sc->sc_npins = 8;
139 	}
140 	if (pcagpio_init(sc, 0, &data[0]) != 0)
141 		return;
142 	if (sc->sc_npins > 8 && pcagpio_init(sc, 1, &data[1]) != 0)
143 		return;
144 
145 	/* Initialize sensor data. */
146 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
147 	    sizeof(sc->sc_sensordev.xname));
148 
149 	for (i = 0; i < sc->sc_npins; i++) {
150 		port = PCAGPIO_PORT(i);
151 		bit = PCAGPIO_BIT(i);
152 		sc->sc_sensor[i].type = SENSOR_INDICATOR;
153 		if ((sc->sc_control[port] & bit) == 0) {
154 			strlcpy(sc->sc_sensor[i].desc, "out",
155 			    sizeof(sc->sc_sensor[i].desc));
156 			outputs++;
157 		} else
158 			strlcpy(sc->sc_sensor[i].desc, "in",
159 			    sizeof(sc->sc_sensor[i].desc));
160 	}
161 
162 	if (sensor_task_register(sc, pcagpio_refresh, 5) == NULL) {
163 		printf(", unable to register update task\n");
164 		return;
165 	}
166 
167 #if 0
168 	for (i = 0; i < sc->sc_npins; i++)
169 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
170 	sensordev_install(&sc->sc_sensordev);
171 #endif
172 
173 	printf(":");
174 	if (sc->sc_npins - outputs)
175 		printf(" %d inputs", sc->sc_npins - outputs);
176 	if (outputs)
177 		printf(" %d outputs", outputs);
178 	printf("\n");
179 
180 	for (i = 0; i < sc->sc_npins; i++) {
181 		port = PCAGPIO_PORT(i);
182 		bit = PCAGPIO_BIT(i);
183 
184 		sc->sc_gpio_pins[i].pin_num = i;
185 		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
186 
187 		if ((sc->sc_control[port] & bit) == 0) {
188 			sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_OUTPUT;
189 			sc->sc_gpio_pins[i].pin_state = data[port] &
190 			    bit ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
191 		}
192 	}
193 
194 	/* Create controller tag */
195 	sc->sc_gpio_gc.gp_cookie = sc;
196 	sc->sc_gpio_gc.gp_pin_read = pcagpio_gpio_pin_read;
197 	sc->sc_gpio_gc.gp_pin_write = pcagpio_gpio_pin_write;
198 	sc->sc_gpio_gc.gp_pin_ctl = pcagpio_gpio_pin_ctl;
199 
200 	gba.gba_name = "gpio";
201 	gba.gba_gc = &sc->sc_gpio_gc;
202 	gba.gba_pins = sc->sc_gpio_pins;
203 	gba.gba_npins = sc->sc_npins;
204 
205 	config_found(&sc->sc_dev, &gba, gpiobus_print);
206 
207 }
208 
209 int
210 pcagpio_init(struct pcagpio_softc *sc, int port, u_int8_t *datap)
211 {
212 	u_int8_t cmd, data;
213 
214 	cmd = sc->sc_regs[port][PCAGPIO_CONFIG];
215 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
216 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
217 		printf(": failed to initialize\n");
218 		return (-1);
219 	}
220 	sc->sc_control[port] = data;
221 	cmd = sc->sc_regs[port][PCAGPIO_POLARITY];
222 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
223 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
224 		printf(": failed to initialize\n");
225 		return (-1);
226 	}
227 	sc->sc_polarity[port] = data;
228 	cmd = sc->sc_regs[port][PCAGPIO_OUT];
229 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
230 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
231 		printf(": failed to initialize\n");
232 		return (-1);
233 	}
234 
235 	*datap = data;
236 	return (0);
237 }
238 
239 void
240 pcagpio_refresh(void *arg)
241 {
242 	struct pcagpio_softc *sc = arg;
243 	u_int8_t cmd, bit, in[PCAGPIO_NPORTS], out[PCAGPIO_NPORTS];
244 	int i, port;
245 
246 	iic_acquire_bus(sc->sc_tag, 0);
247 
248 	for (i = 0; i < PCAGPIO_NPORTS; i++) {
249 		cmd = sc->sc_regs[i][PCAGPIO_IN];
250 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
251 		    sc->sc_addr, &cmd, sizeof cmd, &in[i], sizeof in[i], 0))
252 			goto invalid;
253 
254 		cmd = sc->sc_regs[i][PCAGPIO_OUT];
255 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
256 		    sc->sc_addr, &cmd, sizeof cmd, &out[i], sizeof out[i], 0))
257 			goto invalid;
258 	}
259 
260 	for (i = 0; i < sc->sc_npins; i++) {
261 		port = PCAGPIO_PORT(i);
262 		bit = PCAGPIO_BIT(i);
263 		if ((sc->sc_control[port] & bit))
264 			sc->sc_sensor[i].value = (in[port] & bit) ? 1 : 0;
265 		else
266 			sc->sc_sensor[i].value = (out[port] & bit) ? 1 : 0;
267 	}
268 
269 invalid:
270 	iic_release_bus(sc->sc_tag, 0);
271 }
272 
273 
274 int
275 pcagpio_gpio_pin_read(void *arg, int pin)
276 {
277 	struct pcagpio_softc *sc = arg;
278 	u_int8_t cmd, in;
279 	int port, bit;
280 
281 	port = PCAGPIO_PORT(pin);
282 	bit = PCAGPIO_BIT(pin);
283 
284 	cmd = sc->sc_regs[port][PCAGPIO_IN];
285 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
286 	    sc->sc_addr, &cmd, sizeof cmd, &in, sizeof in, 0))
287 		return 0;
288 	return ((in ^ sc->sc_polarity[port]) & bit) ? 1 : 0;
289 }
290 
291 void
292 pcagpio_gpio_pin_write(void *arg, int pin, int value)
293 {
294 	struct pcagpio_softc *sc = arg;
295 	u_int8_t cmd, out, mask;
296 	int port, bit;
297 
298 	port = PCAGPIO_PORT(pin);
299 	bit = PCAGPIO_BIT(pin);
300 
301 	mask = 0xff ^ bit;
302 	cmd = sc->sc_regs[port][PCAGPIO_OUT];
303 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
304 	    sc->sc_addr, &cmd, sizeof cmd, &out, sizeof out, 0))
305 		return;
306 	out = (out & mask) | (value ? bit : 0);
307 
308 	cmd = sc->sc_regs[port][PCAGPIO_OUT];
309 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
310 	    sc->sc_addr, &cmd, sizeof cmd, &out, sizeof out, 0))
311 		return;
312 }
313 
314 void
315 pcagpio_gpio_pin_ctl(void *arg, int pin, int flags)
316 {
317 #if 0
318 	struct pcagpio_softc *sc = arg;
319 	u_int32_t conf;
320 
321 	pcagpio_gpio_pin_select(sc, pin);
322 	conf = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
323 	    GSCGPIO_CONF);
324 
325 	conf &= ~(GSCGPIO_CONF_OUTPUTEN | GSCGPIO_CONF_PUSHPULL |
326 	    GSCGPIO_CONF_PULLUP);
327 	if ((flags & GPIO_PIN_TRISTATE) == 0)
328 		conf |= GSCGPIO_CONF_OUTPUTEN;
329 	if (flags & GPIO_PIN_PUSHPULL)
330 		conf |= GSCGPIO_CONF_PUSHPULL;
331 	if (flags & GPIO_PIN_PULLUP)
332 		conf |= GSCGPIO_CONF_PULLUP;
333 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh,
334 	    GSCGPIO_CONF, conf);
335 #endif
336 }
337