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