1422d05daSIan Lepore /*- 2422d05daSIan Lepore * SPDX-License-Identifier: BSD-2-Clause 3422d05daSIan Lepore * 4422d05daSIan Lepore * Copyright (c) 2019 Ian Lepore <ian@freebsd.org> 5422d05daSIan Lepore * 6422d05daSIan Lepore * Redistribution and use in source and binary forms, with or without 7422d05daSIan Lepore * modification, are permitted provided that the following conditions 8422d05daSIan Lepore * are met: 9422d05daSIan Lepore * 1. Redistributions of source code must retain the above copyright 10422d05daSIan Lepore * notice, this list of conditions and the following disclaimer. 11422d05daSIan Lepore * 2. Redistributions in binary form must reproduce the above copyright 12422d05daSIan Lepore * notice, this list of conditions and the following disclaimer in the 13422d05daSIan Lepore * documentation and/or other materials provided with the distribution. 14422d05daSIan Lepore * 15422d05daSIan Lepore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16422d05daSIan Lepore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17422d05daSIan Lepore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18422d05daSIan Lepore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19422d05daSIan Lepore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20422d05daSIan Lepore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21422d05daSIan Lepore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22422d05daSIan Lepore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23422d05daSIan Lepore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24422d05daSIan Lepore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25422d05daSIan Lepore * SUCH DAMAGE. 26422d05daSIan Lepore */ 27422d05daSIan Lepore 28422d05daSIan Lepore #include <sys/cdefs.h> 29422d05daSIan Lepore #include "opt_platform.h" 30422d05daSIan Lepore 31422d05daSIan Lepore #include <sys/param.h> 32422d05daSIan Lepore #include <sys/bus.h> 33422d05daSIan Lepore #include <sys/kernel.h> 34422d05daSIan Lepore #include <sys/module.h> 35422d05daSIan Lepore #include <sys/sysctl.h> 36422d05daSIan Lepore 37422d05daSIan Lepore #ifdef FDT 38422d05daSIan Lepore #include <dev/ofw/ofw_bus.h> 39422d05daSIan Lepore #include <dev/ofw/ofw_bus_subr.h> 40422d05daSIan Lepore #include <dev/ofw/openfirm.h> 41422d05daSIan Lepore #endif 42422d05daSIan Lepore 43422d05daSIan Lepore #include <dev/iicbus/iiconf.h> 44422d05daSIan Lepore #include "iicbus_if.h" 45422d05daSIan Lepore #include "iicmux_if.h" 46422d05daSIan Lepore #include "iicmux.h" 47422d05daSIan Lepore 48422d05daSIan Lepore /*------------------------------------------------------------------------------ 49422d05daSIan Lepore * iicbus methods, called by the iicbus functions in iiconf.c. 50422d05daSIan Lepore * 51422d05daSIan Lepore * All these functions return an IIC adapter-layer error code (because we are 52422d05daSIan Lepore * pretending to be a host bridge/i2c controller). Standard errno values 53422d05daSIan Lepore * returned from these must be encoded using iic2errno(). 54422d05daSIan Lepore *----------------------------------------------------------------------------*/ 55422d05daSIan Lepore 56422d05daSIan Lepore static int 57422d05daSIan Lepore iicmux_callback(device_t dev, int index, caddr_t data) 58422d05daSIan Lepore { 59422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 60422d05daSIan Lepore struct iic_reqbus_data *rd; 61422d05daSIan Lepore int err, i; 62422d05daSIan Lepore 63422d05daSIan Lepore /* If it's not one of the operations we know about, bail early. */ 64422d05daSIan Lepore if (index != IIC_REQUEST_BUS && index != IIC_RELEASE_BUS) 65422d05daSIan Lepore return (iic2errno(EOPNOTSUPP)); 66422d05daSIan Lepore 67422d05daSIan Lepore /* 68422d05daSIan Lepore * Ensure that the data passed to us includes the device_t of the child 69422d05daSIan Lepore * bus and device. If missing, someone bypassed iicbus_request_bus() 70422d05daSIan Lepore * and called this method directly using the old calling standard. If 71422d05daSIan Lepore * present, find the index of the child bus that called us. 72422d05daSIan Lepore */ 73422d05daSIan Lepore rd = (struct iic_reqbus_data *)data; 74422d05daSIan Lepore if (!(rd->flags & IIC_REQBUS_DEV)) 75422d05daSIan Lepore return (iic2errno(EINVAL)); 76422d05daSIan Lepore 77422d05daSIan Lepore for (i = 0; i <= sc->maxbus && sc->childdevs[i] != rd->bus; ++i) 78422d05daSIan Lepore continue; 79422d05daSIan Lepore if (i > sc->maxbus) 80422d05daSIan Lepore return (iic2errno(ENOENT)); 81422d05daSIan Lepore 82422d05daSIan Lepore /* 83422d05daSIan Lepore * If the operation is a release it "cannot fail". Idle the downstream 84422d05daSIan Lepore * bus, then release exclusive use of the upstream bus, and we're done. 85422d05daSIan Lepore */ 86422d05daSIan Lepore if (index == IIC_RELEASE_BUS) { 87422d05daSIan Lepore if (sc->debugmux > 0) { 88422d05daSIan Lepore device_printf(dev, "idle the bus for %s on bus %s\n", 89422d05daSIan Lepore device_get_nameunit(rd->dev), 90422d05daSIan Lepore device_get_nameunit(rd->bus)); 91422d05daSIan Lepore } 92422d05daSIan Lepore IICMUX_BUS_SELECT(dev, IICMUX_SELECT_IDLE, rd); 93422d05daSIan Lepore iicbus_release_bus(sc->busdev, dev); 94422d05daSIan Lepore return (IIC_NOERR); 95422d05daSIan Lepore } 96422d05daSIan Lepore 97422d05daSIan Lepore if (sc->debugmux > 0) { 98422d05daSIan Lepore device_printf(dev, "select bus idx %d for %s on bus %s\n", i, 99422d05daSIan Lepore device_get_nameunit(rd->dev), device_get_nameunit(rd->bus)); 100422d05daSIan Lepore } 101422d05daSIan Lepore 102422d05daSIan Lepore /* 103422d05daSIan Lepore * The operation is a request for exclusive use. First we have to 104422d05daSIan Lepore * request exclusive use of our upstream bus. If multiple slave devices 105422d05daSIan Lepore * from our different child buses attempt to do IO at the same time, 106422d05daSIan Lepore * this is what ensures that they don't switch the bus out from under 107422d05daSIan Lepore * each other. The first one in proceeds and others wait here (or get an 108422d05daSIan Lepore * EWOULDBLOCK return if they're using IIC_DONTWAIT). 109422d05daSIan Lepore */ 110422d05daSIan Lepore if ((err = iicbus_request_bus(sc->busdev, dev, rd->flags)) != 0) 111422d05daSIan Lepore return (err); /* Already an IIC error code. */ 112422d05daSIan Lepore 113422d05daSIan Lepore /* 114422d05daSIan Lepore * Now that we own exclusive use of the upstream bus, connect it to the 115422d05daSIan Lepore * downstream bus where the request came from. 116422d05daSIan Lepore */ 117422d05daSIan Lepore if ((err = IICMUX_BUS_SELECT(dev, i, rd)) != 0) 118422d05daSIan Lepore iicbus_release_bus(sc->busdev, dev); 119422d05daSIan Lepore 120422d05daSIan Lepore return (err); 121422d05daSIan Lepore } 122422d05daSIan Lepore 123422d05daSIan Lepore static u_int 124422d05daSIan Lepore iicmux_get_frequency(device_t dev, u_char speed) 125422d05daSIan Lepore { 126422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 127422d05daSIan Lepore 128422d05daSIan Lepore return (IICBUS_GET_FREQUENCY(sc->busdev, speed)); 129422d05daSIan Lepore } 130422d05daSIan Lepore 131422d05daSIan Lepore #ifdef FDT 132422d05daSIan Lepore static phandle_t 133422d05daSIan Lepore iicmux_get_node(device_t dev, device_t child) 134422d05daSIan Lepore { 135422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 136422d05daSIan Lepore int i; 137422d05daSIan Lepore 138422d05daSIan Lepore for (i = 0; i <= sc->maxbus; ++i) { 139422d05daSIan Lepore if (sc->childdevs[i] == child) 140422d05daSIan Lepore return (sc->childnodes[i]); 141422d05daSIan Lepore } 142422d05daSIan Lepore return (0); /* null handle */ 143422d05daSIan Lepore } 144422d05daSIan Lepore #endif 145422d05daSIan Lepore 146422d05daSIan Lepore static int 147422d05daSIan Lepore iicmux_intr(device_t dev, int event, char *buf) 148422d05daSIan Lepore { 149422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 150422d05daSIan Lepore 151422d05daSIan Lepore /* XXX iicbus_intr() in iiconf.c should return status. */ 152422d05daSIan Lepore 153422d05daSIan Lepore iicbus_intr(sc->busdev, event, buf); 154422d05daSIan Lepore return (0); 155422d05daSIan Lepore } 156422d05daSIan Lepore 157422d05daSIan Lepore static int 158422d05daSIan Lepore iicmux_read(device_t dev, char *buf, int len, int *bytes, int last, int delay) 159422d05daSIan Lepore { 160422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 161422d05daSIan Lepore 162422d05daSIan Lepore return (iicbus_read(sc->busdev, buf, len, bytes, last, delay)); 163422d05daSIan Lepore } 164422d05daSIan Lepore 165422d05daSIan Lepore static int 166422d05daSIan Lepore iicmux_repeated_start(device_t dev, u_char slave, int timeout) 167422d05daSIan Lepore { 168422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 169422d05daSIan Lepore 170422d05daSIan Lepore return (iicbus_repeated_start(sc->busdev, slave, timeout)); 171422d05daSIan Lepore } 172422d05daSIan Lepore 173422d05daSIan Lepore static int 174422d05daSIan Lepore iicmux_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 175422d05daSIan Lepore { 176422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 177422d05daSIan Lepore 178422d05daSIan Lepore return (iicbus_reset(sc->busdev, speed, addr, oldaddr)); 179422d05daSIan Lepore } 180422d05daSIan Lepore 181422d05daSIan Lepore static int 182422d05daSIan Lepore iicmux_start(device_t dev, u_char slave, int timeout) 183422d05daSIan Lepore { 184422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 185422d05daSIan Lepore 186422d05daSIan Lepore return (iicbus_start(sc->busdev, slave, timeout)); 187422d05daSIan Lepore } 188422d05daSIan Lepore 189422d05daSIan Lepore static int 190422d05daSIan Lepore iicmux_stop(device_t dev) 191422d05daSIan Lepore { 192422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 193422d05daSIan Lepore 194422d05daSIan Lepore return (iicbus_stop(sc->busdev)); 195422d05daSIan Lepore } 196422d05daSIan Lepore 197422d05daSIan Lepore static int 198422d05daSIan Lepore iicmux_transfer( device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 199422d05daSIan Lepore { 200422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 201422d05daSIan Lepore 202422d05daSIan Lepore return (iicbus_transfer(sc->busdev, msgs, nmsgs)); 203422d05daSIan Lepore } 204422d05daSIan Lepore 205422d05daSIan Lepore static int 206422d05daSIan Lepore iicmux_write(device_t dev, const char *buf, int len, int *bytes, int timeout) 207422d05daSIan Lepore { 208422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 209422d05daSIan Lepore 210422d05daSIan Lepore return (iicbus_write(sc->busdev, buf, len, bytes, timeout)); 211422d05daSIan Lepore } 212422d05daSIan Lepore 213422d05daSIan Lepore /*------------------------------------------------------------------------------ 214422d05daSIan Lepore * iicmux helper functions, called by hardware-specific drivers. 215422d05daSIan Lepore * All these functions return a standard errno value. 216422d05daSIan Lepore *----------------------------------------------------------------------------*/ 217422d05daSIan Lepore 218422d05daSIan Lepore int 219422d05daSIan Lepore iicmux_add_child(device_t dev, device_t child, int busidx) 220422d05daSIan Lepore { 221422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 222422d05daSIan Lepore 223a5910414SIan Lepore if (busidx >= sc->numbuses) { 224a5910414SIan Lepore device_printf(dev, 225a5910414SIan Lepore "iicmux_add_child: bus idx %d too big", busidx); 226a5910414SIan Lepore return (EINVAL); 227a5910414SIan Lepore } 228a5910414SIan Lepore if (sc->childdevs[busidx] != NULL) { 229a5910414SIan Lepore device_printf(dev, "iicmux_add_child: bus idx %d already added", 230a5910414SIan Lepore busidx); 231a5910414SIan Lepore return (EINVAL); 232a5910414SIan Lepore } 233422d05daSIan Lepore 234422d05daSIan Lepore sc->childdevs[busidx] = child; 235422d05daSIan Lepore if (sc->maxbus < busidx) 236422d05daSIan Lepore sc->maxbus = busidx; 237422d05daSIan Lepore 238422d05daSIan Lepore return (0); 239422d05daSIan Lepore } 240422d05daSIan Lepore 241ca16c2b8SIan Lepore static int 242ca16c2b8SIan Lepore iicmux_attach_children(struct iicmux_softc *sc) 243422d05daSIan Lepore { 244ca16c2b8SIan Lepore int i; 245422d05daSIan Lepore #ifdef FDT 246422d05daSIan Lepore phandle_t child, node, parent; 247ca16c2b8SIan Lepore pcell_t idx; 248422d05daSIan Lepore 249422d05daSIan Lepore /* 250422d05daSIan Lepore * Find our FDT node. Child nodes within our node will become our 251422d05daSIan Lepore * iicbus children. 252422d05daSIan Lepore */ 253422d05daSIan Lepore if((node = ofw_bus_get_node(sc->dev)) == 0) { 254422d05daSIan Lepore device_printf(sc->dev, "cannot find FDT node\n"); 255422d05daSIan Lepore return (ENOENT); 256422d05daSIan Lepore } 257422d05daSIan Lepore 258422d05daSIan Lepore /* 259422d05daSIan Lepore * First we have to see if there is a child node named "i2c-mux". If it 260422d05daSIan Lepore * exists, all children of that node are buses, else all children of the 261422d05daSIan Lepore * device node are buses. 262422d05daSIan Lepore */ 263422d05daSIan Lepore if ((parent = ofw_bus_find_child(node, "i2c-mux")) == 0) 264422d05daSIan Lepore parent = node; 265422d05daSIan Lepore 266422d05daSIan Lepore /* 267422d05daSIan Lepore * Attach the children represented in the device tree. 268422d05daSIan Lepore */ 269422d05daSIan Lepore for (child = OF_child(parent); child != 0; child = OF_peer(child)) { 270ca16c2b8SIan Lepore if (OF_getencprop(child, "reg", &idx, sizeof(idx)) == -1) { 271ca16c2b8SIan Lepore device_printf(sc->dev, 272422d05daSIan Lepore "child bus missing required 'reg' property\n"); 273422d05daSIan Lepore continue; 274422d05daSIan Lepore } 275422d05daSIan Lepore if (idx >= sc->numbuses) { 276ca16c2b8SIan Lepore device_printf(sc->dev, 277422d05daSIan Lepore "child bus 'reg' property %d exceeds the number " 278422d05daSIan Lepore "of buses supported by the device (%d)\n", 279422d05daSIan Lepore idx, sc->numbuses); 280422d05daSIan Lepore continue; 281422d05daSIan Lepore } 282*5b56413dSWarner Losh sc->childdevs[idx] = device_add_child(sc->dev, "iicbus", DEVICE_UNIT_ANY); 283422d05daSIan Lepore sc->childnodes[idx] = child; 284eda20682SAndriy Gapon if (sc->maxbus < (int)idx) 285422d05daSIan Lepore sc->maxbus = idx; 286422d05daSIan Lepore } 287ca16c2b8SIan Lepore 288ca16c2b8SIan Lepore /* If we configured anything using FDT data, we're done. */ 289ca16c2b8SIan Lepore if (sc->maxbus >= 0) 290ca16c2b8SIan Lepore return (0); 291422d05daSIan Lepore #endif /* FDT */ 292422d05daSIan Lepore 293422d05daSIan Lepore /* 294ca16c2b8SIan Lepore * If we make it to here, we didn't add any children based on FDT data. 295ca16c2b8SIan Lepore * Add an iicbus child for every downstream bus supported by the mux. 296422d05daSIan Lepore */ 297422d05daSIan Lepore for (i = 0; i < sc->numbuses; ++i) { 298*5b56413dSWarner Losh sc->childdevs[i] = device_add_child(sc->dev, "iicbus", DEVICE_UNIT_ANY); 299ca16c2b8SIan Lepore sc->maxbus = i; 300422d05daSIan Lepore } 301ca16c2b8SIan Lepore 302ca16c2b8SIan Lepore return (0); 303ca16c2b8SIan Lepore } 304ca16c2b8SIan Lepore 305ca16c2b8SIan Lepore int 306ca16c2b8SIan Lepore iicmux_attach(device_t dev, device_t busdev, int numbuses) 307ca16c2b8SIan Lepore { 308ca16c2b8SIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 309ca16c2b8SIan Lepore int err; 310ca16c2b8SIan Lepore 311ca16c2b8SIan Lepore if (numbuses >= IICMUX_MAX_BUSES) { 312ca16c2b8SIan Lepore device_printf(dev, "iicmux_attach: numbuses %d > max %d\n", 313ca16c2b8SIan Lepore numbuses, IICMUX_MAX_BUSES); 314ca16c2b8SIan Lepore return (EINVAL); 315ca16c2b8SIan Lepore } 316ca16c2b8SIan Lepore 317ca16c2b8SIan Lepore sc->dev = dev; 318ca16c2b8SIan Lepore sc->busdev = busdev; 319ca16c2b8SIan Lepore sc->maxbus = -1; 320ca16c2b8SIan Lepore sc->numbuses = numbuses; 321ca16c2b8SIan Lepore 322ca16c2b8SIan Lepore if ((err = iicmux_attach_children(sc)) != 0) 323ca16c2b8SIan Lepore return (err); 324ca16c2b8SIan Lepore 325ca16c2b8SIan Lepore SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev), 326ca16c2b8SIan Lepore SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, 327ca16c2b8SIan Lepore "debugmux", CTLFLAG_RWTUN, &sc->debugmux, 0, "debug mux operations"); 328422d05daSIan Lepore 329422d05daSIan Lepore return (0); 330422d05daSIan Lepore } 331422d05daSIan Lepore 332422d05daSIan Lepore int 333422d05daSIan Lepore iicmux_detach(device_t dev) 334422d05daSIan Lepore { 335422d05daSIan Lepore struct iicmux_softc *sc = device_get_softc(dev); 336422d05daSIan Lepore int err, i; 337422d05daSIan Lepore 338422d05daSIan Lepore /* Delete only the children we added in iicmux_add* functions. */ 339422d05daSIan Lepore for (i = 0; i <= sc->maxbus; ++i) { 340422d05daSIan Lepore if (sc->childdevs[i] == NULL) 341422d05daSIan Lepore continue; 342422d05daSIan Lepore if ((err = device_delete_child(dev, sc->childdevs[i])) != 0) 343422d05daSIan Lepore return (err); 344422d05daSIan Lepore sc->childdevs[i] = NULL; 345422d05daSIan Lepore } 346422d05daSIan Lepore 347422d05daSIan Lepore return (0); 348422d05daSIan Lepore } 349422d05daSIan Lepore 350422d05daSIan Lepore static device_method_t iicmux_methods [] = { 351422d05daSIan Lepore /* iicbus_if methods */ 352422d05daSIan Lepore DEVMETHOD(iicbus_intr, iicmux_intr), 353422d05daSIan Lepore DEVMETHOD(iicbus_callback, iicmux_callback), 354422d05daSIan Lepore DEVMETHOD(iicbus_repeated_start, iicmux_repeated_start), 355422d05daSIan Lepore DEVMETHOD(iicbus_start, iicmux_start), 356422d05daSIan Lepore DEVMETHOD(iicbus_stop, iicmux_stop), 357422d05daSIan Lepore DEVMETHOD(iicbus_read, iicmux_read), 358422d05daSIan Lepore DEVMETHOD(iicbus_write, iicmux_write), 359422d05daSIan Lepore DEVMETHOD(iicbus_reset, iicmux_reset), 360422d05daSIan Lepore DEVMETHOD(iicbus_transfer, iicmux_transfer), 361422d05daSIan Lepore DEVMETHOD(iicbus_get_frequency, iicmux_get_frequency), 362422d05daSIan Lepore 363422d05daSIan Lepore #ifdef FDT 364422d05daSIan Lepore /* ofwbus_if methods */ 365422d05daSIan Lepore DEVMETHOD(ofw_bus_get_node, iicmux_get_node), 366422d05daSIan Lepore #endif 367422d05daSIan Lepore 368422d05daSIan Lepore DEVMETHOD_END 369422d05daSIan Lepore }; 370422d05daSIan Lepore 371422d05daSIan Lepore static int 372422d05daSIan Lepore iicmux_modevent(module_t mod, int type, void *unused) 373422d05daSIan Lepore { 374422d05daSIan Lepore switch (type) { 375422d05daSIan Lepore case MOD_LOAD: 376422d05daSIan Lepore return 0; 377422d05daSIan Lepore case MOD_UNLOAD: 378422d05daSIan Lepore return 0; 379422d05daSIan Lepore } 380422d05daSIan Lepore return EINVAL; 381422d05daSIan Lepore } 382422d05daSIan Lepore 383422d05daSIan Lepore static moduledata_t iicmux_mod = { 384422d05daSIan Lepore "iicmux", 385422d05daSIan Lepore iicmux_modevent, 386422d05daSIan Lepore 0 387422d05daSIan Lepore }; 388422d05daSIan Lepore 389422d05daSIan Lepore DEFINE_CLASS_0(iicmux, iicmux_driver, iicmux_methods, 390422d05daSIan Lepore sizeof(struct iicmux_softc)); 391422d05daSIan Lepore 392422d05daSIan Lepore DECLARE_MODULE(iicmux, iicmux_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); 393422d05daSIan Lepore MODULE_VERSION(iicmux, 1); 394422d05daSIan Lepore 395422d05daSIan Lepore MODULE_DEPEND(iicmux, iicbus, 1, 1, 1); 396