xref: /netbsd-src/sys/dev/i2c/i2cmux.c (revision 50f18f7158f93922c385ca6f75ca3e3ce4fa558c)
1*50f18f71Smsaitoh /*	$NetBSD: i2cmux.c,v 1.7 2021/11/10 15:39:03 msaitoh Exp $	*/
29f14734dSthorpej 
39f14734dSthorpej /*-
49f14734dSthorpej  * Copyright (c) 2020 The NetBSD Foundation, Inc.
59f14734dSthorpej  * All rights reserved.
69f14734dSthorpej  *
79f14734dSthorpej  * This code is derived from software contributed to The NetBSD Foundation
89f14734dSthorpej  * by Jason R. Thorpe.
99f14734dSthorpej  *
109f14734dSthorpej  * Redistribution and use in source and binary forms, with or without
119f14734dSthorpej  * modification, are permitted provided that the following conditions
129f14734dSthorpej  * are met:
139f14734dSthorpej  * 1. Redistributions of source code must retain the above copyright
149f14734dSthorpej  *    notice, this list of conditions and the following disclaimer.
159f14734dSthorpej  * 2. Redistributions in binary form must reproduce the above copyright
169f14734dSthorpej  *    notice, this list of conditions and the following disclaimer in the
179f14734dSthorpej  *    documentation and/or other materials provided with the distribution.
189f14734dSthorpej  *
199f14734dSthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209f14734dSthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219f14734dSthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229f14734dSthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239f14734dSthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249f14734dSthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259f14734dSthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269f14734dSthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279f14734dSthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289f14734dSthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299f14734dSthorpej  * POSSIBILITY OF SUCH DAMAGE.
309f14734dSthorpej  */
319f14734dSthorpej 
32608e11c7Sjmcneill #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
33608e11c7Sjmcneill #include "acpica.h"
34608e11c7Sjmcneill #endif
35608e11c7Sjmcneill 
369f14734dSthorpej #include <sys/cdefs.h>
37*50f18f71Smsaitoh __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.7 2021/11/10 15:39:03 msaitoh Exp $");
389f14734dSthorpej 
399f14734dSthorpej #include <sys/types.h>
409f14734dSthorpej #include <sys/device.h>
419f14734dSthorpej #include <sys/kernel.h>
429f14734dSthorpej #include <sys/kmem.h>
439f14734dSthorpej 
449f14734dSthorpej #include <dev/fdt/fdtvar.h>
459f14734dSthorpej #include <dev/i2c/i2cvar.h>
469f14734dSthorpej #include <dev/i2c/i2cmuxvar.h>
479f14734dSthorpej 
48608e11c7Sjmcneill #if NACPICA > 0
49608e11c7Sjmcneill #include <dev/acpi/acpivar.h>
50608e11c7Sjmcneill #include <dev/acpi/acpi_i2c.h>
51608e11c7Sjmcneill #endif
52608e11c7Sjmcneill 
539f14734dSthorpej /*
549f14734dSthorpej  * i2c mux
559f14734dSthorpej  *
569f14734dSthorpej  * This works by interposing a set of virtual controllers behind the real
579f14734dSthorpej  * i2c controller.  We provide our own acquire and release functions that
589f14734dSthorpej  * perform the following tasks:
599f14734dSthorpej  *
609f14734dSthorpej  *	acquire -> acquire parent controller, program mux
619f14734dSthorpej  *
629f14734dSthorpej  *	release -> idle mux, release parent controller
639f14734dSthorpej  *
649f14734dSthorpej  * All of the actual I/O operations are transparently passed through.
659f14734dSthorpej  *
669f14734dSthorpej  * N.B. the locking order; the generic I2C layer has already acquired
679f14734dSthorpej  * our virtual controller's mutex before calling our acquire function,
689f14734dSthorpej  * and we will then acquire the real controller's mutex when we acquire
699f14734dSthorpej  * the bus, so the order is:
709f14734dSthorpej  *
719f14734dSthorpej  *	mux virtual controller -> parent controller
729f14734dSthorpej  *
739f14734dSthorpej  * These are common routines used by various i2c mux controller
749f14734dSthorpej  * implementations (gpio, pin mux, i2c device, etc.).
759f14734dSthorpej  */
769f14734dSthorpej 
779f14734dSthorpej /*****************************************************************************/
789f14734dSthorpej 
799f14734dSthorpej static int
iicmux_acquire_bus(void * const v,int const flags)809f14734dSthorpej iicmux_acquire_bus(void * const v, int const flags)
819f14734dSthorpej {
829f14734dSthorpej 	struct iicmux_bus * const bus = v;
839f14734dSthorpej 	struct iicmux_softc * const sc = bus->mux;
849f14734dSthorpej 	int error;
859f14734dSthorpej 
869f14734dSthorpej 	error = iic_acquire_bus(sc->sc_i2c_parent, flags);
879f14734dSthorpej 	if (error) {
889f14734dSthorpej 		return error;
899f14734dSthorpej 	}
909f14734dSthorpej 
919f14734dSthorpej 	error = sc->sc_config->acquire_bus(bus, flags);
929f14734dSthorpej 	if (error) {
939f14734dSthorpej 		iic_release_bus(sc->sc_i2c_parent, flags);
949f14734dSthorpej 	}
959f14734dSthorpej 
969f14734dSthorpej 	return error;
979f14734dSthorpej }
989f14734dSthorpej 
999f14734dSthorpej static void
iicmux_release_bus(void * const v,int const flags)1009f14734dSthorpej iicmux_release_bus(void * const v, int const flags)
1019f14734dSthorpej {
1029f14734dSthorpej 	struct iicmux_bus * const bus = v;
1039f14734dSthorpej 	struct iicmux_softc * const sc = bus->mux;
1049f14734dSthorpej 
1059f14734dSthorpej 	sc->sc_config->release_bus(bus, flags);
1069f14734dSthorpej 	iic_release_bus(sc->sc_i2c_parent, flags);
1079f14734dSthorpej }
1089f14734dSthorpej 
1099f14734dSthorpej 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)1109f14734dSthorpej iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
1119f14734dSthorpej     const void * const cmdbuf, size_t const cmdlen, void * const databuf,
1129f14734dSthorpej     size_t const datalen, int const flags)
1139f14734dSthorpej {
1149f14734dSthorpej 	struct iicmux_bus * const bus = v;
1159f14734dSthorpej 	struct iicmux_softc * const sc = bus->mux;
1169f14734dSthorpej 
1179f14734dSthorpej 	return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
1189f14734dSthorpej 			databuf, datalen, flags);
1199f14734dSthorpej }
1209f14734dSthorpej 
1219f14734dSthorpej /*****************************************************************************/
1229f14734dSthorpej 
1239f14734dSthorpej static int
iicmux_count_children(struct iicmux_softc * const sc)1249f14734dSthorpej iicmux_count_children(struct iicmux_softc * const sc)
1259f14734dSthorpej {
1269f14734dSthorpej 	char name[32];
1279f14734dSthorpej 	int child, count;
1289f14734dSthorpej 
1299f14734dSthorpej  restart:
1309f14734dSthorpej 	for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child;
1319f14734dSthorpej 	     child = OF_peer(child)) {
1329f14734dSthorpej 		if (OF_getprop(child, "name", name, sizeof(name)) <= 0) {
1339f14734dSthorpej 			continue;
1349f14734dSthorpej 		}
1359f14734dSthorpej 		if (strcmp(name, "i2c-mux") == 0) {
1369f14734dSthorpej 			/*
137*50f18f71Smsaitoh 			 * The node we encountered is the actual parent
1389f14734dSthorpej 			 * of the i2c bus children.  Stash its phandle
1399f14734dSthorpej 			 * and restart the enumeration.
1409f14734dSthorpej 			 */
1419f14734dSthorpej 			sc->sc_i2c_mux_phandle = child;
1429f14734dSthorpej 			goto restart;
1439f14734dSthorpej 		}
1449f14734dSthorpej 		count++;
1459f14734dSthorpej 	}
1469f14734dSthorpej 
1479f14734dSthorpej 	return count;
1489f14734dSthorpej }
1499f14734dSthorpej 
1509f14734dSthorpej /* XXX iicbus_print() should be able to do this. */
1519f14734dSthorpej static int
iicmux_print(void * const aux,const char * const pnp)1529f14734dSthorpej iicmux_print(void * const aux, const char * const pnp)
1539f14734dSthorpej {
1549f14734dSthorpej 	i2c_tag_t const tag = aux;
1559f14734dSthorpej 	struct iicmux_bus * const bus = tag->ic_cookie;
1569f14734dSthorpej 	int rv;
1579f14734dSthorpej 
1589f14734dSthorpej 	rv = iicbus_print(aux, pnp);
1599f14734dSthorpej 	aprint_normal(" bus %d", bus->busidx);
1609f14734dSthorpej 
1619f14734dSthorpej 	return rv;
1629f14734dSthorpej }
1639f14734dSthorpej 
1649f14734dSthorpej static void
iicmux_attach_bus(struct iicmux_softc * const sc,uintptr_t const handle,enum i2c_cookie_type handletype,int const busidx)1659f14734dSthorpej iicmux_attach_bus(struct iicmux_softc * const sc,
166608e11c7Sjmcneill     uintptr_t const handle, enum i2c_cookie_type handletype, int const busidx)
1679f14734dSthorpej {
1689f14734dSthorpej 	struct iicmux_bus * const bus = &sc->sc_busses[busidx];
1699f14734dSthorpej 
1709f14734dSthorpej 	bus->mux = sc;
1719f14734dSthorpej 	bus->busidx = busidx;
172608e11c7Sjmcneill 	bus->handle = handle;
173608e11c7Sjmcneill 	bus->handletype = handletype;
1749f14734dSthorpej 
1759f14734dSthorpej 	bus->bus_data = sc->sc_config->get_bus_info(bus);
1769f14734dSthorpej 	if (bus->bus_data == NULL) {
1779f14734dSthorpej 		aprint_error_dev(sc->sc_dev,
1789f14734dSthorpej 		    "unable to get info for bus %d\n", busidx);
1799f14734dSthorpej 		return;
1809f14734dSthorpej 	}
1819f14734dSthorpej 
1829f14734dSthorpej 	iic_tag_init(&bus->controller);
1839f14734dSthorpej 	bus->controller.ic_cookie = bus;
1849f14734dSthorpej 	bus->controller.ic_acquire_bus = iicmux_acquire_bus;
1859f14734dSthorpej 	bus->controller.ic_release_bus = iicmux_release_bus;
1869f14734dSthorpej 	bus->controller.ic_exec = iicmux_exec;
1879f14734dSthorpej 
188608e11c7Sjmcneill 	switch (handletype) {
189608e11c7Sjmcneill 	case I2C_COOKIE_OF:
190608e11c7Sjmcneill 		fdtbus_register_i2c_controller(&bus->controller,
191608e11c7Sjmcneill 		    (int)bus->handle);
1929f14734dSthorpej 
193608e11c7Sjmcneill 		fdtbus_attach_i2cbus(sc->sc_dev, (int)bus->handle,
194608e11c7Sjmcneill 		    &bus->controller, iicmux_print);
195608e11c7Sjmcneill 		break;
196608e11c7Sjmcneill #if NACPICA > 0
197608e11c7Sjmcneill 	case I2C_COOKIE_ACPI: {
198608e11c7Sjmcneill 		struct acpi_devnode *ad = acpi_match_node((ACPI_HANDLE)handle);
199608e11c7Sjmcneill 		KASSERT(ad != NULL);
200608e11c7Sjmcneill 		struct i2cbus_attach_args iba = {
201608e11c7Sjmcneill 			.iba_tag = &bus->controller,
202172e088eSjmcneill 			.iba_child_devices = acpi_enter_i2c_devs(NULL, ad)
203608e11c7Sjmcneill 		};
204c7fb772bSthorpej 		config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE);
205608e11c7Sjmcneill 	}	break;
206608e11c7Sjmcneill #endif
207608e11c7Sjmcneill 	default:
208608e11c7Sjmcneill 		aprint_error_dev(sc->sc_dev, "unknown handle type\n");
209608e11c7Sjmcneill 		break;
210608e11c7Sjmcneill 	}
2119f14734dSthorpej }
2129f14734dSthorpej 
213608e11c7Sjmcneill static void
iicmux_attach_fdt(struct iicmux_softc * const sc)214608e11c7Sjmcneill iicmux_attach_fdt(struct iicmux_softc * const sc)
2159f14734dSthorpej {
2169f14734dSthorpej 	/*
2179f14734dSthorpej 	 * We start out assuming that the i2c bus nodes are children of
2189f14734dSthorpej 	 * our own node.  We'll adjust later if we encounter an "i2c-mux"
2199f14734dSthorpej 	 * node when counting our children.  If we encounter such a node,
2209f14734dSthorpej 	 * then it's that node that is the parent of the i2c bus children.
2219f14734dSthorpej 	 */
222608e11c7Sjmcneill 	sc->sc_i2c_mux_phandle = (int)sc->sc_handle;
2239f14734dSthorpej 
2249f14734dSthorpej 	sc->sc_nbusses = iicmux_count_children(sc);
2259f14734dSthorpej 	if (sc->sc_nbusses == 0) {
2269f14734dSthorpej 		return;
2279f14734dSthorpej 	}
2289f14734dSthorpej 
2299f14734dSthorpej 	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
2309f14734dSthorpej 	    KM_SLEEP);
2319f14734dSthorpej 
2329f14734dSthorpej 	int child, idx;
2339f14734dSthorpej 	for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child;
2349f14734dSthorpej 	     child = OF_peer(child), idx++) {
2359f14734dSthorpej 		KASSERT(idx < sc->sc_nbusses);
236608e11c7Sjmcneill 		iicmux_attach_bus(sc, child, I2C_COOKIE_OF, idx);
237608e11c7Sjmcneill 	}
238608e11c7Sjmcneill }
239608e11c7Sjmcneill 
240608e11c7Sjmcneill #if NACPICA > 0
241608e11c7Sjmcneill static void
iicmux_attach_acpi(struct iicmux_softc * const sc)242608e11c7Sjmcneill iicmux_attach_acpi(struct iicmux_softc * const sc)
243608e11c7Sjmcneill {
244608e11c7Sjmcneill 	ACPI_HANDLE hdl = (ACPI_HANDLE)sc->sc_handle;
245608e11c7Sjmcneill 	struct acpi_devnode *devnode, *ad;
246608e11c7Sjmcneill 	int idx;
247608e11c7Sjmcneill 
248608e11c7Sjmcneill 	devnode = acpi_match_node(hdl);
249608e11c7Sjmcneill 	KASSERT(devnode != NULL);
250608e11c7Sjmcneill 
251608e11c7Sjmcneill 	/* Count child busses */
252608e11c7Sjmcneill 	sc->sc_nbusses = 0;
253608e11c7Sjmcneill 	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
254608e11c7Sjmcneill 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
255608e11c7Sjmcneill 		    !acpi_device_present(ad->ad_handle)) {
256608e11c7Sjmcneill 			continue;
257608e11c7Sjmcneill 		}
258608e11c7Sjmcneill 		sc->sc_nbusses++;
259608e11c7Sjmcneill 	}
260608e11c7Sjmcneill 
261608e11c7Sjmcneill 	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
262608e11c7Sjmcneill 	    KM_SLEEP);
263608e11c7Sjmcneill 
264608e11c7Sjmcneill 	/* Attach child busses */
265608e11c7Sjmcneill 	idx = 0;
266608e11c7Sjmcneill 	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
267608e11c7Sjmcneill 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
268608e11c7Sjmcneill 		    !acpi_device_present(ad->ad_handle)) {
269608e11c7Sjmcneill 			continue;
270608e11c7Sjmcneill 		}
271608e11c7Sjmcneill 		iicmux_attach_bus(sc, (uintptr_t)ad->ad_handle,
272608e11c7Sjmcneill 		    I2C_COOKIE_ACPI, idx);
273608e11c7Sjmcneill 		idx++;
274608e11c7Sjmcneill 	}
275608e11c7Sjmcneill }
276608e11c7Sjmcneill #endif
277608e11c7Sjmcneill 
278608e11c7Sjmcneill void
iicmux_attach(struct iicmux_softc * const sc)279608e11c7Sjmcneill iicmux_attach(struct iicmux_softc * const sc)
280608e11c7Sjmcneill {
281608e11c7Sjmcneill 	/*
282608e11c7Sjmcneill 	 * We expect sc->sc_handle, sc->sc_config, and sc->sc_i2c_parent
283608e11c7Sjmcneill 	 * to be initialized by the front-end.
284608e11c7Sjmcneill 	 */
285608e11c7Sjmcneill 	KASSERT(sc->sc_handle > 0);
286608e11c7Sjmcneill 	KASSERT(sc->sc_config != NULL);
287608e11c7Sjmcneill 	KASSERT(sc->sc_i2c_parent != NULL);
288608e11c7Sjmcneill 
289608e11c7Sjmcneill 	/*
290608e11c7Sjmcneill 	 * Gather up all of the various bits of information needed
291608e11c7Sjmcneill 	 * for this particular type of i2c mux.
292608e11c7Sjmcneill 	 */
293608e11c7Sjmcneill 	sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
294608e11c7Sjmcneill 	if (sc->sc_mux_data == NULL) {
295608e11c7Sjmcneill 		aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
296608e11c7Sjmcneill 		return;
297608e11c7Sjmcneill 	}
298608e11c7Sjmcneill 
299608e11c7Sjmcneill 	/*
300608e11c7Sjmcneill 	 * Do configuration method (OF, ACPI) specific setup.
301608e11c7Sjmcneill 	 */
302608e11c7Sjmcneill 	switch (sc->sc_handletype) {
303608e11c7Sjmcneill 	case I2C_COOKIE_OF:
304608e11c7Sjmcneill 		iicmux_attach_fdt(sc);
305608e11c7Sjmcneill 		break;
306608e11c7Sjmcneill #if NACPICA > 0
307608e11c7Sjmcneill 	case I2C_COOKIE_ACPI:
308608e11c7Sjmcneill 		iicmux_attach_acpi(sc);
309608e11c7Sjmcneill 		break;
310608e11c7Sjmcneill #endif
311608e11c7Sjmcneill 	default:
312608e11c7Sjmcneill 		aprint_error_dev(sc->sc_dev, "could not configure mux: "
313608e11c7Sjmcneill 		    "handle type %u not supported\n", sc->sc_handletype);
314608e11c7Sjmcneill 		break;
3159f14734dSthorpej 	}
3169f14734dSthorpej }
317