xref: /netbsd-src/sys/dev/i2c/i2cmux.c (revision 50f18f7158f93922c385ca6f75ca3e3ce4fa558c)
1 /*	$NetBSD: i2cmux.c,v 1.7 2021/11/10 15:39:03 msaitoh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
33 #include "acpica.h"
34 #endif
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.7 2021/11/10 15:39:03 msaitoh Exp $");
38 
39 #include <sys/types.h>
40 #include <sys/device.h>
41 #include <sys/kernel.h>
42 #include <sys/kmem.h>
43 
44 #include <dev/fdt/fdtvar.h>
45 #include <dev/i2c/i2cvar.h>
46 #include <dev/i2c/i2cmuxvar.h>
47 
48 #if NACPICA > 0
49 #include <dev/acpi/acpivar.h>
50 #include <dev/acpi/acpi_i2c.h>
51 #endif
52 
53 /*
54  * i2c mux
55  *
56  * This works by interposing a set of virtual controllers behind the real
57  * i2c controller.  We provide our own acquire and release functions that
58  * perform the following tasks:
59  *
60  *	acquire -> acquire parent controller, program mux
61  *
62  *	release -> idle mux, release parent controller
63  *
64  * All of the actual I/O operations are transparently passed through.
65  *
66  * N.B. the locking order; the generic I2C layer has already acquired
67  * our virtual controller's mutex before calling our acquire function,
68  * and we will then acquire the real controller's mutex when we acquire
69  * the bus, so the order is:
70  *
71  *	mux virtual controller -> parent controller
72  *
73  * These are common routines used by various i2c mux controller
74  * implementations (gpio, pin mux, i2c device, etc.).
75  */
76 
77 /*****************************************************************************/
78 
79 static int
iicmux_acquire_bus(void * const v,int const flags)80 iicmux_acquire_bus(void * const v, int const flags)
81 {
82 	struct iicmux_bus * const bus = v;
83 	struct iicmux_softc * const sc = bus->mux;
84 	int error;
85 
86 	error = iic_acquire_bus(sc->sc_i2c_parent, flags);
87 	if (error) {
88 		return error;
89 	}
90 
91 	error = sc->sc_config->acquire_bus(bus, flags);
92 	if (error) {
93 		iic_release_bus(sc->sc_i2c_parent, flags);
94 	}
95 
96 	return error;
97 }
98 
99 static void
iicmux_release_bus(void * const v,int const flags)100 iicmux_release_bus(void * const v, int const flags)
101 {
102 	struct iicmux_bus * const bus = v;
103 	struct iicmux_softc * const sc = bus->mux;
104 
105 	sc->sc_config->release_bus(bus, flags);
106 	iic_release_bus(sc->sc_i2c_parent, flags);
107 }
108 
109 static int
iicmux_exec(void * const v,i2c_op_t const op,i2c_addr_t const addr,const void * const cmdbuf,size_t const cmdlen,void * const databuf,size_t const datalen,int const flags)110 iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
111     const void * const cmdbuf, size_t const cmdlen, void * const databuf,
112     size_t const datalen, int const flags)
113 {
114 	struct iicmux_bus * const bus = v;
115 	struct iicmux_softc * const sc = bus->mux;
116 
117 	return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
118 			databuf, datalen, flags);
119 }
120 
121 /*****************************************************************************/
122 
123 static int
iicmux_count_children(struct iicmux_softc * const sc)124 iicmux_count_children(struct iicmux_softc * const sc)
125 {
126 	char name[32];
127 	int child, count;
128 
129  restart:
130 	for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child;
131 	     child = OF_peer(child)) {
132 		if (OF_getprop(child, "name", name, sizeof(name)) <= 0) {
133 			continue;
134 		}
135 		if (strcmp(name, "i2c-mux") == 0) {
136 			/*
137 			 * The node we encountered is the actual parent
138 			 * of the i2c bus children.  Stash its phandle
139 			 * and restart the enumeration.
140 			 */
141 			sc->sc_i2c_mux_phandle = child;
142 			goto restart;
143 		}
144 		count++;
145 	}
146 
147 	return count;
148 }
149 
150 /* XXX iicbus_print() should be able to do this. */
151 static int
iicmux_print(void * const aux,const char * const pnp)152 iicmux_print(void * const aux, const char * const pnp)
153 {
154 	i2c_tag_t const tag = aux;
155 	struct iicmux_bus * const bus = tag->ic_cookie;
156 	int rv;
157 
158 	rv = iicbus_print(aux, pnp);
159 	aprint_normal(" bus %d", bus->busidx);
160 
161 	return rv;
162 }
163 
164 static void
iicmux_attach_bus(struct iicmux_softc * const sc,uintptr_t const handle,enum i2c_cookie_type handletype,int const busidx)165 iicmux_attach_bus(struct iicmux_softc * const sc,
166     uintptr_t const handle, enum i2c_cookie_type handletype, int const busidx)
167 {
168 	struct iicmux_bus * const bus = &sc->sc_busses[busidx];
169 
170 	bus->mux = sc;
171 	bus->busidx = busidx;
172 	bus->handle = handle;
173 	bus->handletype = handletype;
174 
175 	bus->bus_data = sc->sc_config->get_bus_info(bus);
176 	if (bus->bus_data == NULL) {
177 		aprint_error_dev(sc->sc_dev,
178 		    "unable to get info for bus %d\n", busidx);
179 		return;
180 	}
181 
182 	iic_tag_init(&bus->controller);
183 	bus->controller.ic_cookie = bus;
184 	bus->controller.ic_acquire_bus = iicmux_acquire_bus;
185 	bus->controller.ic_release_bus = iicmux_release_bus;
186 	bus->controller.ic_exec = iicmux_exec;
187 
188 	switch (handletype) {
189 	case I2C_COOKIE_OF:
190 		fdtbus_register_i2c_controller(&bus->controller,
191 		    (int)bus->handle);
192 
193 		fdtbus_attach_i2cbus(sc->sc_dev, (int)bus->handle,
194 		    &bus->controller, iicmux_print);
195 		break;
196 #if NACPICA > 0
197 	case I2C_COOKIE_ACPI: {
198 		struct acpi_devnode *ad = acpi_match_node((ACPI_HANDLE)handle);
199 		KASSERT(ad != NULL);
200 		struct i2cbus_attach_args iba = {
201 			.iba_tag = &bus->controller,
202 			.iba_child_devices = acpi_enter_i2c_devs(NULL, ad)
203 		};
204 		config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE);
205 	}	break;
206 #endif
207 	default:
208 		aprint_error_dev(sc->sc_dev, "unknown handle type\n");
209 		break;
210 	}
211 }
212 
213 static void
iicmux_attach_fdt(struct iicmux_softc * const sc)214 iicmux_attach_fdt(struct iicmux_softc * const sc)
215 {
216 	/*
217 	 * We start out assuming that the i2c bus nodes are children of
218 	 * our own node.  We'll adjust later if we encounter an "i2c-mux"
219 	 * node when counting our children.  If we encounter such a node,
220 	 * then it's that node that is the parent of the i2c bus children.
221 	 */
222 	sc->sc_i2c_mux_phandle = (int)sc->sc_handle;
223 
224 	sc->sc_nbusses = iicmux_count_children(sc);
225 	if (sc->sc_nbusses == 0) {
226 		return;
227 	}
228 
229 	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
230 	    KM_SLEEP);
231 
232 	int child, idx;
233 	for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child;
234 	     child = OF_peer(child), idx++) {
235 		KASSERT(idx < sc->sc_nbusses);
236 		iicmux_attach_bus(sc, child, I2C_COOKIE_OF, idx);
237 	}
238 }
239 
240 #if NACPICA > 0
241 static void
iicmux_attach_acpi(struct iicmux_softc * const sc)242 iicmux_attach_acpi(struct iicmux_softc * const sc)
243 {
244 	ACPI_HANDLE hdl = (ACPI_HANDLE)sc->sc_handle;
245 	struct acpi_devnode *devnode, *ad;
246 	int idx;
247 
248 	devnode = acpi_match_node(hdl);
249 	KASSERT(devnode != NULL);
250 
251 	/* Count child busses */
252 	sc->sc_nbusses = 0;
253 	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
254 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
255 		    !acpi_device_present(ad->ad_handle)) {
256 			continue;
257 		}
258 		sc->sc_nbusses++;
259 	}
260 
261 	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
262 	    KM_SLEEP);
263 
264 	/* Attach child busses */
265 	idx = 0;
266 	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
267 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
268 		    !acpi_device_present(ad->ad_handle)) {
269 			continue;
270 		}
271 		iicmux_attach_bus(sc, (uintptr_t)ad->ad_handle,
272 		    I2C_COOKIE_ACPI, idx);
273 		idx++;
274 	}
275 }
276 #endif
277 
278 void
iicmux_attach(struct iicmux_softc * const sc)279 iicmux_attach(struct iicmux_softc * const sc)
280 {
281 	/*
282 	 * We expect sc->sc_handle, sc->sc_config, and sc->sc_i2c_parent
283 	 * to be initialized by the front-end.
284 	 */
285 	KASSERT(sc->sc_handle > 0);
286 	KASSERT(sc->sc_config != NULL);
287 	KASSERT(sc->sc_i2c_parent != NULL);
288 
289 	/*
290 	 * Gather up all of the various bits of information needed
291 	 * for this particular type of i2c mux.
292 	 */
293 	sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
294 	if (sc->sc_mux_data == NULL) {
295 		aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
296 		return;
297 	}
298 
299 	/*
300 	 * Do configuration method (OF, ACPI) specific setup.
301 	 */
302 	switch (sc->sc_handletype) {
303 	case I2C_COOKIE_OF:
304 		iicmux_attach_fdt(sc);
305 		break;
306 #if NACPICA > 0
307 	case I2C_COOKIE_ACPI:
308 		iicmux_attach_acpi(sc);
309 		break;
310 #endif
311 	default:
312 		aprint_error_dev(sc->sc_dev, "could not configure mux: "
313 		    "handle type %u not supported\n", sc->sc_handletype);
314 		break;
315 	}
316 }
317