1 /* $NetBSD: i2cmux.c,v 1.12 2025/09/16 13:09:13 thorpej 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.12 2025/09/16 13:09:13 thorpej 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 #endif 51 52 /* 53 * i2c mux 54 * 55 * This works by interposing a set of virtual controllers behind the real 56 * i2c controller. We provide our own acquire and release functions that 57 * perform the following tasks: 58 * 59 * acquire -> acquire parent controller, program mux 60 * 61 * release -> idle mux, release parent controller 62 * 63 * All of the actual I/O operations are transparently passed through. 64 * 65 * N.B. the locking order; the generic I2C layer has already acquired 66 * our virtual controller's mutex before calling our acquire function, 67 * and we will then acquire the real controller's mutex when we acquire 68 * the bus, so the order is: 69 * 70 * mux virtual controller -> parent controller 71 * 72 * These are common routines used by various i2c mux controller 73 * implementations (gpio, pin mux, i2c device, etc.). 74 */ 75 76 /*****************************************************************************/ 77 78 static int 79 iicmux_acquire_bus(void * const v, int const flags) 80 { 81 struct iicmux_bus * const bus = v; 82 struct iicmux_softc * const sc = bus->mux; 83 int error; 84 85 error = iic_acquire_bus(sc->sc_i2c_parent, flags); 86 if (error) { 87 return error; 88 } 89 90 error = sc->sc_config->acquire_bus(bus, flags); 91 if (error) { 92 iic_release_bus(sc->sc_i2c_parent, flags); 93 } 94 95 return error; 96 } 97 98 static void 99 iicmux_release_bus(void * const v, int const flags) 100 { 101 struct iicmux_bus * const bus = v; 102 struct iicmux_softc * const sc = bus->mux; 103 104 sc->sc_config->release_bus(bus, flags); 105 iic_release_bus(sc->sc_i2c_parent, flags); 106 } 107 108 static int 109 iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr, 110 const void * const cmdbuf, size_t const cmdlen, void * const databuf, 111 size_t const datalen, int const flags) 112 { 113 struct iicmux_bus * const bus = v; 114 struct iicmux_softc * const sc = bus->mux; 115 116 return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen, 117 databuf, datalen, flags); 118 } 119 120 /*****************************************************************************/ 121 122 static int 123 iicmux_count_children(struct iicmux_softc * const sc) 124 { 125 char name[32]; 126 int child, count; 127 int mux_phandle = devhandle_to_of(sc->sc_i2c_mux_devhandle); 128 129 restart: 130 for (child = OF_child(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 mux_phandle = child; 142 goto restart; 143 } 144 count++; 145 } 146 sc->sc_i2c_mux_devhandle = 147 devhandle_from_of(sc->sc_i2c_mux_devhandle, mux_phandle); 148 149 return count; 150 } 151 152 #if 0 153 /* XXX iicbus_print() should be able to do this. */ 154 static int 155 iicmux_print(void * const aux, const char * const pnp) 156 { 157 i2c_tag_t const tag = aux; 158 struct iicmux_bus * const bus = tag->ic_cookie; 159 int rv; 160 161 rv = iicbus_print(aux, pnp); 162 aprint_normal(" bus %d", bus->busidx); 163 164 return rv; 165 } 166 #endif 167 168 static void 169 iicmux_attach_bus(struct iicmux_softc * const sc, devhandle_t bus_devhandle, 170 int const busidx) 171 { 172 struct iicmux_bus * const bus = &sc->sc_busses[busidx]; 173 174 bus->mux = sc; 175 bus->busidx = busidx; 176 bus->devhandle = bus_devhandle; 177 178 bus->bus_data = sc->sc_config->get_bus_info(bus); 179 if (bus->bus_data == NULL) { 180 aprint_error_dev(sc->sc_dev, 181 "unable to get info for bus %d\n", busidx); 182 return; 183 } 184 185 iic_tag_init(&bus->controller); 186 bus->controller.ic_cookie = bus; 187 bus->controller.ic_acquire_bus = iicmux_acquire_bus; 188 bus->controller.ic_release_bus = iicmux_release_bus; 189 bus->controller.ic_exec = iicmux_exec; 190 191 iicbus_attach_with_devhandle(sc->sc_dev, &bus->controller, 192 bus->devhandle); 193 } 194 195 static void 196 iicmux_attach_fdt(struct iicmux_softc * const sc) 197 { 198 199 sc->sc_nbusses = iicmux_count_children(sc); 200 if (sc->sc_nbusses == 0) { 201 return; 202 } 203 204 sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses, 205 KM_SLEEP); 206 207 #define BUS_DEVHANDLE(sc, c) \ 208 devhandle_from_of((sc)->sc_i2c_mux_devhandle, (c)) 209 210 const int mux_phandle = devhandle_to_of(sc->sc_i2c_mux_devhandle); 211 int child, idx; 212 for (child = OF_child(mux_phandle), idx = 0; child; 213 child = OF_peer(child), idx++) { 214 KASSERT(idx < sc->sc_nbusses); 215 iicmux_attach_bus(sc, BUS_DEVHANDLE(sc, child), idx); 216 } 217 #undef BUS_DEVHANDLE 218 } 219 220 #if NACPICA > 0 221 static void 222 iicmux_attach_acpi(struct iicmux_softc * const sc) 223 { 224 ACPI_HANDLE hdl = devhandle_to_acpi(sc->sc_i2c_mux_devhandle); 225 struct acpi_devnode *devnode, *ad; 226 int idx; 227 228 devnode = acpi_match_node(hdl); 229 KASSERT(devnode != NULL); 230 231 /* Count child busses */ 232 sc->sc_nbusses = 0; 233 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) { 234 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE || 235 !acpi_device_present(ad->ad_handle)) { 236 continue; 237 } 238 sc->sc_nbusses++; 239 } 240 241 sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses, 242 KM_SLEEP); 243 244 #define BUS_DEVHANDLE(sc, c) \ 245 devhandle_from_acpi((sc)->sc_i2c_mux_devhandle, (c)) 246 247 /* Attach child busses */ 248 idx = 0; 249 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) { 250 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE || 251 !acpi_device_present(ad->ad_handle)) { 252 continue; 253 } 254 iicmux_attach_bus(sc, BUS_DEVHANDLE(sc, ad->ad_handle), idx); 255 idx++; 256 } 257 #undef BUS_DEVHANDLE 258 } 259 #endif /* NACPICA > 0 */ 260 261 void 262 iicmux_attach(struct iicmux_softc * const sc) 263 { 264 /* 265 * We expect sc->sc_config and sc->sc_i2c_parent to be initialized 266 * by the front-end. 267 */ 268 KASSERT(sc->sc_config != NULL); 269 KASSERT(sc->sc_i2c_parent != NULL); 270 271 /* 272 * We start out assuming that the i2c bus nodes are children of 273 * our own node. We'll adjust later if we encounter an "i2c-mux" 274 * node when counting our children. If we encounter such a node, 275 * then it's that node that is the parent of the i2c bus children. 276 */ 277 sc->sc_i2c_mux_devhandle = device_handle(sc->sc_dev); 278 279 /* 280 * Gather up all of the various bits of information needed 281 * for this particular type of i2c mux. 282 */ 283 sc->sc_mux_data = sc->sc_config->get_mux_info(sc); 284 if (sc->sc_mux_data == NULL) { 285 aprint_error_dev(sc->sc_dev, "unable to get info for mux\n"); 286 return; 287 } 288 289 /* 290 * Do configuration method (OF, ACPI) specific setup. 291 * 292 * XXX We can eliminate 99% of the platform device tree 293 * XXX specific stuff here, but to do that we need a generic 294 * XXX way to enumerate children of an arbitrary devhandle. 295 */ 296 switch (devhandle_type(sc->sc_i2c_mux_devhandle)) { 297 case DEVHANDLE_TYPE_OF: 298 iicmux_attach_fdt(sc); 299 break; 300 #if NACPICA > 0 301 case DEVHANDLE_TYPE_ACPI: 302 iicmux_attach_acpi(sc); 303 break; 304 #endif 305 default: 306 aprint_error_dev(sc->sc_dev, "could not configure mux: " 307 "handle type 0x%08x not supported\n", 308 devhandle_type(sc->sc_i2c_mux_devhandle)); 309 break; 310 } 311 } 312