xref: /openbsd-src/sys/dev/i2c/pca9548.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
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", &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(&reg[0]);
433 		ia.ia_name = name;
434 		ia.ia_cookie = &node;
435 		config_found(self, &ia, iic_print);
436 	}
437 }
438