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