1 /* $OpenBSD: pca9554.c,v 1.19 2022/04/06 18:59:28 naddy 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 const 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
pcagpio_match(struct device * parent,void * match,void * aux)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
pcagpio_attach(struct device * parent,struct device * self,void * aux)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
pcagpio_init(struct pcagpio_softc * sc,int port)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
pcagpio_config_pin(void * arg,uint32_t * cells,int config)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
pcagpio_get_pin(void * arg,uint32_t * cells)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
pcagpio_set_pin(void * arg,uint32_t * cells,int value)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