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 79static int 80iicmux_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 99static void 100iicmux_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 109static int 110iicmux_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 123static int 124iicmux_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. */ 151static int 152iicmux_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 164static void 165iicmux_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 213static void 214iicmux_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 241static void 242iicmux_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 278void 279iicmux_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