1 /* $OpenBSD: pca9548.c,v 1.7 2024/05/13 01:15:50 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2020 Mark Kettenis
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 <machine/bus.h>
24
25 #define _I2C_PRIVATE
26 #include <dev/i2c/i2cvar.h>
27
28 #ifdef __HAVE_ACPI
29 #include "acpi.h"
30 #endif
31
32 #if NACPI > 0
33 #include <dev/acpi/acpireg.h>
34 #include <dev/acpi/acpivar.h>
35 #include <dev/acpi/acpidev.h>
36 #include <dev/acpi/amltypes.h>
37 #include <dev/acpi/dsdt.h>
38 #endif
39
40 #include <dev/ofw/openfirm.h>
41 #include <dev/ofw/ofw_misc.h>
42
43 #define PCAMUX_MAX_CHANNELS 8
44
45 struct pcamux_bus {
46 struct pcamux_softc *pb_sc;
47 int pb_node;
48 void *pb_devnode;
49 int pb_channel;
50 struct i2c_controller pb_ic;
51 struct i2c_bus pb_ib;
52 struct device *pb_iic;
53 };
54
55 struct pcamux_softc {
56 struct device sc_dev;
57 i2c_tag_t sc_tag;
58 i2c_addr_t sc_addr;
59
60 int sc_node;
61 void *sc_devnode;
62 int sc_channel;
63 int sc_nchannel;
64 struct pcamux_bus sc_bus[PCAMUX_MAX_CHANNELS];
65 struct rwlock sc_lock;
66
67 int sc_switch;
68 int sc_enable;
69 };
70
71 #if NACPI > 0
72 struct pcamux_crs {
73 uint16_t i2c_addr;
74 struct aml_node *devnode;
75 };
76 #endif
77
78 int pcamux_match(struct device *, void *, void *);
79 void pcamux_attach(struct device *, struct device *, void *);
80
81 const struct cfattach pcamux_ca = {
82 sizeof(struct pcamux_softc), pcamux_match, pcamux_attach
83 };
84
85 struct cfdriver pcamux_cd = {
86 NULL, "pcamux", DV_DULL
87 };
88
89 void pcamux_attach_fdt(struct pcamux_softc *, struct i2c_attach_args *);
90 void pcamux_attach_acpi(struct pcamux_softc *, struct i2c_attach_args *);
91
92 #if NACPI > 0
93 int pcamux_attach_acpi_mux(struct aml_node *, void *);
94 void pcamux_acpi_bus_scan(struct device *,
95 struct i2cbus_attach_args *, void *);
96 int pcamux_acpi_found_hid(struct aml_node *, void *);
97 int pcamux_acpi_parse_crs(int, union acpi_resource *, void *);
98 #endif
99
100 int pcamux_acquire_bus(void *, int);
101 void pcamux_release_bus(void *, int);
102 int pcamux_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
103 void *, size_t, int);
104 void pcamux_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
105
106 int
pcamux_match(struct device * parent,void * match,void * aux)107 pcamux_match(struct device *parent, void *match, void *aux)
108 {
109 struct i2c_attach_args *ia = aux;
110
111 if (strcmp(ia->ia_name, "nxp,pca9546") == 0 ||
112 strcmp(ia->ia_name, "nxp,pca9547") == 0 ||
113 strcmp(ia->ia_name, "nxp,pca9548") == 0 ||
114 strcmp(ia->ia_name, "NXP0002") == 0)
115 return (1);
116 return (0);
117 }
118
119 void
pcamux_attach(struct device * parent,struct device * self,void * aux)120 pcamux_attach(struct device *parent, struct device *self, void *aux)
121 {
122 struct pcamux_softc *sc = (struct pcamux_softc *)self;
123 struct i2c_attach_args *ia = aux;
124
125 sc->sc_tag = ia->ia_tag;
126 sc->sc_addr = ia->ia_addr;
127
128 sc->sc_channel = -1; /* unknown */
129 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
130
131 if (strcmp(ia->ia_name, "nxp,pca9546") == 0) {
132 sc->sc_switch = 1;
133 sc->sc_nchannel = 4;
134 } else if (strcmp(ia->ia_name, "nxp,pca9547") == 0 ||
135 strcmp(ia->ia_name, "NXP0002") == 0) {
136 sc->sc_enable = 1 << 3;
137 sc->sc_nchannel = 8;
138 } else if (strcmp(ia->ia_name, "nxp,pca9548") == 0) {
139 sc->sc_switch = 1;
140 sc->sc_nchannel = 8;
141 }
142
143 printf("\n");
144
145 if (strcmp(ia->ia_name, "NXP0002") == 0)
146 pcamux_attach_acpi(sc, ia);
147 else
148 pcamux_attach_fdt(sc, ia);
149 }
150
151 void
pcamux_attach_fdt(struct pcamux_softc * sc,struct i2c_attach_args * ia)152 pcamux_attach_fdt(struct pcamux_softc *sc, struct i2c_attach_args *ia)
153 {
154 int node = *(int *)ia->ia_cookie;
155
156 sc->sc_node = node;
157 for (node = OF_child(node); node; node = OF_peer(node)) {
158 struct i2cbus_attach_args iba;
159 struct pcamux_bus *pb;
160 uint32_t channel;
161
162 channel = OF_getpropint(node, "reg", -1);
163 if (channel >= sc->sc_nchannel)
164 continue;
165
166 pb = &sc->sc_bus[channel];
167 pb->pb_sc = sc;
168 pb->pb_node = node;
169 pb->pb_channel = channel;
170 pb->pb_ic.ic_cookie = pb;
171 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus;
172 pb->pb_ic.ic_release_bus = pcamux_release_bus;
173 pb->pb_ic.ic_exec = pcamux_exec;
174
175 /* Configure the child busses. */
176 memset(&iba, 0, sizeof(iba));
177 iba.iba_name = "iic";
178 iba.iba_tag = &pb->pb_ic;
179 iba.iba_bus_scan = pcamux_bus_scan;
180 iba.iba_bus_scan_arg = &pb->pb_node;
181
182 config_found(&sc->sc_dev, &iba, iicbus_print);
183
184 pb->pb_ib.ib_node = node;
185 pb->pb_ib.ib_ic = &pb->pb_ic;
186 i2c_register(&pb->pb_ib);
187 }
188 }
189
190 void
pcamux_attach_acpi(struct pcamux_softc * sc,struct i2c_attach_args * ia)191 pcamux_attach_acpi(struct pcamux_softc *sc, struct i2c_attach_args *ia)
192 {
193 #if NACPI > 0
194 struct aml_node *node = ia->ia_cookie;
195
196 sc->sc_devnode = node;
197 aml_walknodes(node, AML_WALK_PRE, pcamux_attach_acpi_mux, sc);
198 #endif
199 }
200
201 #if NACPI > 0
202 int
pcamux_attach_acpi_mux(struct aml_node * node,void * arg)203 pcamux_attach_acpi_mux(struct aml_node *node, void *arg)
204 {
205 struct pcamux_softc *sc = arg;
206 struct i2cbus_attach_args iba;
207 struct pcamux_bus *pb;
208 uint64_t channel;
209
210 /* Only the node's direct children */
211 if (node->parent != sc->sc_devnode)
212 return 0;
213
214 /* Must have channel as address */
215 if (aml_evalinteger(acpi_softc, node, "_ADR", 0, NULL, &channel) ||
216 channel >= sc->sc_nchannel)
217 return 0;
218
219 pb = &sc->sc_bus[channel];
220 pb->pb_sc = sc;
221 pb->pb_devnode = node;
222 pb->pb_channel = channel;
223 pb->pb_ic.ic_cookie = pb;
224 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus;
225 pb->pb_ic.ic_release_bus = pcamux_release_bus;
226 pb->pb_ic.ic_exec = pcamux_exec;
227
228 /* Configure the child busses. */
229 memset(&iba, 0, sizeof(iba));
230 iba.iba_name = "iic";
231 iba.iba_tag = &pb->pb_ic;
232 iba.iba_bus_scan = pcamux_acpi_bus_scan;
233 iba.iba_bus_scan_arg = pb;
234
235 config_found(&sc->sc_dev, &iba, iicbus_print);
236
237 #ifndef SMALL_KERNEL
238 node->i2c = &pb->pb_ic;
239 acpi_register_gsb(acpi_softc, node);
240 #endif
241 return 0;
242 }
243
244 void
pcamux_acpi_bus_scan(struct device * iic,struct i2cbus_attach_args * iba,void * aux)245 pcamux_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba,
246 void *aux)
247 {
248 struct pcamux_bus *pb = aux;
249
250 pb->pb_iic = iic;
251 aml_find_node(pb->pb_devnode, "_HID", pcamux_acpi_found_hid, aux);
252 }
253
254 int
pcamux_acpi_found_hid(struct aml_node * node,void * arg)255 pcamux_acpi_found_hid(struct aml_node *node, void *arg)
256 {
257 struct pcamux_bus *pb = arg;
258 struct pcamux_softc *sc = pb->pb_sc;
259 struct pcamux_crs crs;
260 struct aml_value res;
261 int64_t sta;
262 char cdev[16], dev[16];
263 struct i2c_attach_args ia;
264
265 /* Skip our own _HID. */
266 if (node->parent == pb->pb_devnode)
267 return 0;
268
269 /* Only direct descendants, because of possible muxes. */
270 if (node->parent && node->parent->parent != pb->pb_devnode)
271 return 0;
272
273 if (acpi_parsehid(node, arg, cdev, dev, 16) != 0)
274 return 0;
275
276 sta = acpi_getsta(acpi_softc, node->parent);
277 if ((sta & STA_PRESENT) == 0)
278 return 0;
279
280 if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res))
281 return 0;
282
283 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
284 printf("%s: invalid _CRS object (type %d len %d)\n",
285 sc->sc_dev.dv_xname, res.type, res.length);
286 aml_freevalue(&res);
287 return (0);
288 }
289 memset(&crs, 0, sizeof(crs));
290 crs.devnode = sc->sc_devnode;
291 aml_parse_resource(&res, pcamux_acpi_parse_crs, &crs);
292 aml_freevalue(&res);
293
294 acpi_attach_deps(acpi_softc, node->parent);
295
296 memset(&ia, 0, sizeof(ia));
297 ia.ia_tag = &pb->pb_ic;
298 ia.ia_name = dev;
299 ia.ia_addr = crs.i2c_addr;
300 ia.ia_cookie = node->parent;
301
302 config_found(pb->pb_iic, &ia, iic_print);
303 node->parent->attached = 1;
304
305 return 0;
306 }
307
308 int
pcamux_acpi_parse_crs(int crsidx,union acpi_resource * crs,void * arg)309 pcamux_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg)
310 {
311 struct pcamux_crs *sc_crs = arg;
312
313 switch (AML_CRSTYPE(crs)) {
314 case LR_SERBUS:
315 if (crs->lr_serbus.type == LR_SERBUS_I2C)
316 sc_crs->i2c_addr = crs->lr_i2cbus._adr;
317 break;
318
319 default:
320 printf("%s: unknown resource type %d\n", __func__,
321 AML_CRSTYPE(crs));
322 }
323
324 return 0;
325 }
326 #endif
327
328 int
pcamux_set_channel(struct pcamux_softc * sc,int channel,int flags)329 pcamux_set_channel(struct pcamux_softc *sc, int channel, int flags)
330 {
331 uint8_t data;
332 int error;
333
334 if (channel < -1 || channel >= sc->sc_nchannel)
335 return ENXIO;
336
337 if (sc->sc_channel == channel)
338 return 0;
339
340 data = 0;
341 if (channel != -1) {
342 if (sc->sc_switch)
343 data = 1 << channel;
344 else
345 data = sc->sc_enable | channel;
346 }
347 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
348 sc->sc_addr, NULL, 0, &data, sizeof data, flags);
349
350 return error;
351 }
352
353 int
pcamux_acquire_bus(void * cookie,int flags)354 pcamux_acquire_bus(void *cookie, int flags)
355 {
356 struct pcamux_bus *pb = cookie;
357 struct pcamux_softc *sc = pb->pb_sc;
358 int rwflags = RW_WRITE;
359 int error;
360
361 if (flags & I2C_F_POLL)
362 rwflags |= RW_NOSLEEP;
363
364 error = rw_enter(&sc->sc_lock, rwflags);
365 if (error)
366 return error;
367
368 /* Acquire parent bus. */
369 error = iic_acquire_bus(sc->sc_tag, flags);
370 if (error) {
371 rw_exit_write(&sc->sc_lock);
372 return error;
373 }
374
375 error = pcamux_set_channel(sc, pb->pb_channel, flags);
376 if (error) {
377 iic_release_bus(sc->sc_tag, flags);
378 rw_exit_write(&sc->sc_lock);
379 return error;
380 }
381
382 return 0;
383 }
384
385 void
pcamux_release_bus(void * cookie,int flags)386 pcamux_release_bus(void *cookie, int flags)
387 {
388 struct pcamux_bus *pb = cookie;
389 struct pcamux_softc *sc = pb->pb_sc;
390
391 /* Release parent bus. */
392 iic_release_bus(sc->sc_tag, flags);
393 rw_exit_write(&sc->sc_lock);
394 }
395
396 int
pcamux_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)397 pcamux_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
398 size_t cmdlen, void *buf, size_t buflen, int flags)
399 {
400 struct pcamux_bus *pb = cookie;
401 struct pcamux_softc *sc = pb->pb_sc;
402
403 rw_assert_wrlock(&sc->sc_lock);
404
405 /* Issue the transaction on the parent bus. */
406 return iic_exec(sc->sc_tag, op, addr, cmd, cmdlen, buf, buflen, flags);
407 }
408
409 void
pcamux_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * arg)410 pcamux_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
411 {
412 int iba_node = *(int *)arg;
413 struct i2c_attach_args ia;
414 char name[32];
415 uint32_t reg[1];
416 int node;
417
418 for (node = OF_child(iba_node); node; node = OF_peer(node)) {
419 memset(name, 0, sizeof(name));
420 memset(reg, 0, sizeof(reg));
421
422 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
423 continue;
424 if (name[0] == '\0')
425 continue;
426
427 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
428 continue;
429
430 memset(&ia, 0, sizeof(ia));
431 ia.ia_tag = iba->iba_tag;
432 ia.ia_addr = bemtoh32(®[0]);
433 ia.ia_name = name;
434 ia.ia_cookie = &node;
435 config_found(self, &ia, iic_print);
436 }
437 }
438