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
45struct 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
55struct 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
72struct pcamux_crs {
73	uint16_t i2c_addr;
74	struct aml_node *devnode;
75};
76#endif
77
78int	pcamux_match(struct device *, void *, void *);
79void	pcamux_attach(struct device *, struct device *, void *);
80
81const struct cfattach pcamux_ca = {
82	sizeof(struct pcamux_softc), pcamux_match, pcamux_attach
83};
84
85struct cfdriver pcamux_cd = {
86	NULL, "pcamux", DV_DULL
87};
88
89void	pcamux_attach_fdt(struct pcamux_softc *, struct i2c_attach_args *);
90void	pcamux_attach_acpi(struct pcamux_softc *, struct i2c_attach_args *);
91
92#if NACPI > 0
93int	pcamux_attach_acpi_mux(struct aml_node *, void *);
94void	pcamux_acpi_bus_scan(struct device *,
95	    struct i2cbus_attach_args *, void *);
96int	pcamux_acpi_found_hid(struct aml_node *, void *);
97int	pcamux_acpi_parse_crs(int, union acpi_resource *, void *);
98#endif
99
100int	pcamux_acquire_bus(void *, int);
101void	pcamux_release_bus(void *, int);
102int	pcamux_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
103	    void *, size_t, int);
104void	pcamux_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
105
106int
107pcamux_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
119void
120pcamux_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
151void
152pcamux_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
190void
191pcamux_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
202int
203pcamux_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
244void
245pcamux_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
254int
255pcamux_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
308int
309pcamux_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
328int
329pcamux_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
353int
354pcamux_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
385void
386pcamux_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
396int
397pcamux_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
409void
410pcamux_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