xref: /openbsd/sys/dev/i2c/pca9554.c (revision 3cab2bb3)
1 /*	$OpenBSD: pca9554.c,v 1.18 2018/07/09 18:48:52 patrick 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 
23 #include <dev/i2c/i2cvar.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/ofw_gpio.h>
27 
28 /* Philips 9554/6/7 registers */
29 #define PCA9554_IN		0x00
30 #define PCA9554_OUT		0x01
31 #define PCA9554_POLARITY	0x02
32 #define PCA9554_CONFIG		0x03
33 
34 /* Philips 9555 registers */
35 #define PCA9555_IN0		0x00
36 #define PCA9555_IN1		0x01
37 #define PCA9555_OUT0		0x02
38 #define PCA9555_OUT1		0x03
39 #define PCA9555_POLARITY0	0x04
40 #define PCA9555_POLARITY1	0x05
41 #define PCA9555_CONFIG0		0x06
42 #define PCA9555_CONFIG1		0x07
43 
44 /* Sensors */
45 #define PCAGPIO_NPINS	16
46 
47 #define PCAGPIO_NPORTS	2
48 #define PCAGPIO_PORT(_pin)	((_pin) > 7 ? 1 : 0)
49 #define PCAGPIO_BIT(_pin)	(1 << ((_pin) % 8))
50 
51 /* Register mapping index */
52 enum pcigpio_cmd {
53 	PCAGPIO_IN		= 0,
54 	PCAGPIO_OUT,
55 	PCAGPIO_POLARITY,
56 	PCAGPIO_CONFIG,
57 	PCAGPIO_MAX
58 };
59 
60 struct pcagpio_softc {
61 	struct device	sc_dev;
62 	i2c_tag_t	sc_tag;
63 	i2c_addr_t	sc_addr;
64 	int		sc_node;
65 
66 	u_int8_t	sc_npins;
67 	u_int8_t	sc_regs[PCAGPIO_NPORTS][PCAGPIO_MAX];
68 
69 	struct gpio_controller sc_gc;
70 };
71 
72 int	pcagpio_match(struct device *, void *, void *);
73 void	pcagpio_attach(struct device *, struct device *, void *);
74 int	pcagpio_init(struct pcagpio_softc *, int);
75 
76 void	pcagpio_config_pin(void *, uint32_t *, int);
77 int	pcagpio_get_pin(void *, uint32_t *);
78 void	pcagpio_set_pin(void *, uint32_t *, int);
79 
80 struct cfattach pcagpio_ca = {
81 	sizeof(struct pcagpio_softc), pcagpio_match, pcagpio_attach
82 };
83 
84 struct cfdriver pcagpio_cd = {
85 	NULL, "pcagpio", DV_DULL
86 };
87 
88 int
89 pcagpio_match(struct device *parent, void *match, void *aux)
90 {
91 	struct i2c_attach_args *ia = aux;
92 
93 	if (strcmp(ia->ia_name, "nxp,pca9554") == 0 ||
94 	    strcmp(ia->ia_name, "nxp,pca9555") == 0 ||
95 	    strcmp(ia->ia_name, "nxp,pca9557") == 0)
96 		return (1);
97 	return (0);
98 }
99 
100 void
101 pcagpio_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct pcagpio_softc *sc = (struct pcagpio_softc *)self;
104 	struct i2c_attach_args *ia = aux;
105 
106 	sc->sc_tag = ia->ia_tag;
107 	sc->sc_addr = ia->ia_addr;
108 	sc->sc_node = *(int *)ia->ia_cookie;
109 
110 	if (strcmp(ia->ia_name, "nxp,pca9555") == 0) {
111 		/* The pca9555 has two 8 bit ports */
112 		sc->sc_regs[0][PCAGPIO_IN] = PCA9555_IN0;
113 		sc->sc_regs[0][PCAGPIO_OUT] = PCA9555_OUT0;
114 		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9555_POLARITY0;
115 		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9555_CONFIG0;
116 		sc->sc_regs[1][PCAGPIO_IN] = PCA9555_IN1;
117 		sc->sc_regs[1][PCAGPIO_OUT] = PCA9555_OUT1;
118 		sc->sc_regs[1][PCAGPIO_POLARITY] = PCA9555_POLARITY1;
119 		sc->sc_regs[1][PCAGPIO_CONFIG] = PCA9555_CONFIG1;
120 		sc->sc_npins = 16;
121 	} else {
122 		/* All other supported devices have one 8 bit port */
123 		sc->sc_regs[0][PCAGPIO_IN] = PCA9554_IN;
124 		sc->sc_regs[0][PCAGPIO_OUT] = PCA9554_OUT;
125 		sc->sc_regs[0][PCAGPIO_POLARITY] = PCA9554_POLARITY;
126 		sc->sc_regs[0][PCAGPIO_CONFIG] = PCA9554_CONFIG;
127 		sc->sc_npins = 8;
128 	}
129 	if (pcagpio_init(sc, 0) != 0)
130 		return;
131 	if (sc->sc_npins > 8 && pcagpio_init(sc, 1) != 0)
132 		return;
133 
134 	printf("\n");
135 
136 	/* Create controller tag */
137 	sc->sc_gc.gc_node = sc->sc_node;
138 	sc->sc_gc.gc_cookie = sc;
139 	sc->sc_gc.gc_config_pin = pcagpio_config_pin;
140 	sc->sc_gc.gc_get_pin = pcagpio_get_pin;
141 	sc->sc_gc.gc_set_pin = pcagpio_set_pin;
142 	gpio_controller_register(&sc->sc_gc);
143 }
144 
145 int
146 pcagpio_init(struct pcagpio_softc *sc, int port)
147 {
148 	u_int8_t cmd, data;
149 
150 	/* Don't invert input. */
151 	data = 0;
152 	cmd = sc->sc_regs[port][PCAGPIO_POLARITY];
153 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
154 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
155 		printf(": failed to initialize\n");
156 		return (-1);
157 	}
158 
159 	return (0);
160 }
161 
162 void
163 pcagpio_config_pin(void *arg, uint32_t *cells, int config)
164 {
165 	struct pcagpio_softc *sc = arg;
166 	uint32_t pin = cells[0];
167 	u_int8_t cmd, data;
168 	int port, bit;
169 
170 	if (pin >= 16)
171 		return;
172 
173 	port = PCAGPIO_PORT(pin);
174 	bit = PCAGPIO_BIT(pin);
175 
176 	cmd = sc->sc_regs[port][PCAGPIO_CONFIG];
177 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
178 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
179 		return;
180 
181 	if (config & GPIO_CONFIG_OUTPUT)
182 		data &= ~bit;
183 	else
184 		data |= bit;
185 
186 	cmd = sc->sc_regs[port][PCAGPIO_CONFIG];
187 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
188 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
189 		return;
190 }
191 
192 int
193 pcagpio_get_pin(void *arg, uint32_t *cells)
194 {
195 	struct pcagpio_softc *sc = arg;
196 	uint32_t pin = cells[0];
197 	uint32_t flags = cells[1];
198 	u_int8_t cmd, data;
199 	int port, bit, value;
200 
201 	if (pin >= 16)
202 		return 0;
203 
204 	port = PCAGPIO_PORT(pin);
205 	bit = PCAGPIO_BIT(pin);
206 
207 	cmd = sc->sc_regs[port][PCAGPIO_IN];
208 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
209 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
210 		return 0;
211 
212 	value = !!(data & bit);
213 	if (flags & GPIO_ACTIVE_LOW)
214 		value = !value;
215 
216 	return value;
217 }
218 
219 void
220 pcagpio_set_pin(void *arg, uint32_t *cells, int value)
221 {
222 	struct pcagpio_softc *sc = arg;
223 	uint32_t pin = cells[0];
224 	uint32_t flags = cells[1];
225 	u_int8_t cmd, data;
226 	int port, bit;
227 
228 	if (pin >= 16)
229 		return;
230 
231 	port = PCAGPIO_PORT(pin);
232 	bit = PCAGPIO_BIT(pin);
233 
234 	cmd = sc->sc_regs[port][PCAGPIO_OUT];
235 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
236 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
237 		return;
238 
239 	if (flags & GPIO_ACTIVE_LOW)
240 		value = !value;
241 
242 	data &= ~bit;
243 	if (value)
244 		data |= bit;
245 
246 	cmd = sc->sc_regs[port][PCAGPIO_OUT];
247 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
248 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
249 		return;
250 }
251