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