Index: share/man/man4/Makefile =================================================================== --- share/man/man4/Makefile +++ share/man/man4/Makefile @@ -204,8 +204,10 @@ ig4.4 \ igmp.4 \ iic.4 \ + iic_gpiomux.4 \ iicbb.4 \ iicbus.4 \ + iicmux.4 \ iicsmb.4 \ iir.4 \ ${_imcsmb.4} \ @@ -258,6 +260,7 @@ lp.4 \ lpbb.4 \ lpt.4 \ + ltc430x.4 \ mac.4 \ mac_biba.4 \ mac_bsdextended.4 \ Index: share/man/man4/iic_gpiomux.4 =================================================================== --- share/man/man4/iic_gpiomux.4 +++ share/man/man4/iic_gpiomux.4 @@ -0,0 +1,88 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Ian Lepore +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 1, 2020 +.Dt IIC_GPIOMUX 4 +.Os +.Sh NAME +.Nm iic_gpiomux +.Nd driver for I2C mux hardware controlled via GPIO +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device iic_gpiomux" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +iic_gpiomux_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports any type of I2C bus multiplexer (mux) hardware that +is controlled by manipulating the state of one or more GPIO pins. +It automatically connects an upstream I2C bus to one of the downstream +buses as needed when slave devices on the downstream buses initiate I/O. +More information on the automatic switching behavior is available in +.Xr iicmux 4 . +.Pp +.Sh FDT CONFIGURATION +On an +.Xr fdt 4 +based system, an +.Nm +device node may be defined as a child node of any arbitrary bus +in the FDT data. +The +.Va i2c-parent +property indicates the connection to the upstream I2C bus. +The children of the +.Nm +node are additional i2c buses, which will have their own i2c slave +devices described in their child nodes. +.Pp +The +.Nm +driver conforms to the standard +.Bk -words +.Li i2c/i2c-mux-gpio.txt +.Ek +bindings document. +.Sh SEE ALSO +.Xr iicbus 4 , +.Xr iicmux 4 , +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 13.0 . Index: share/man/man4/iicmux.4 =================================================================== --- share/man/man4/iicmux.4 +++ share/man/man4/iicmux.4 @@ -0,0 +1,148 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Ian Lepore +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 1, 2020 +.Dt IICMUX 4 +.Os +.Sh NAME +.Nm iicmux +.Nd I2C bus mulitiplexer framework +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device iicmux" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +iicmux_load="YES" +.Ed +.Pp +Note that it is usually not necessary to explicitly load the +driver module, as it will be loaded automatically along with +the driver for the specific mux hardware in use. +.Sh DESCRIPTION +The +.Nm +framework provides support code to help implement drivers for various +I2C bus multiplexer (mux) hardware. +.Nm +is not a standalone driver, +it is a collection of support functions and driver methods which are +used by individual mux hardware drivers. +It will be loaded automatically when needed by a mux hardware driver. +This manual page provides an overview of the I2C mux framework and its +behavior. +.Pp +Generally speaking, an I2C mux is connected to an upstream I2C bus, and to +one or more downstream I2C buses, and it can be commanded to connect +any one of the downstream buses to the upstream bus. +Some hardware may be able to connect multiple downstream buses at the +same time, but that concept is not supported by +.Nm . +.Pp +The +.Nm +framework operates automatically when I2C slave devices initiate I/O. +It does not require (or even allow for) any external control to select +the active downstream bus. +.Pp +When there is no I/O in progress, the mux is said to be in the +.Dq idle +state. +Some mux hardware has the ability to disconnect all downstream buses +when in an idle state. +Other hardware must always have one of the downstream buses connected. +Individual mux hardware drivers typically provide a way to select which +downstream bus (if any) should be connected while in the idle state. +In the absence of such configuration, whichever downstream bus was +last used remains connected to the upstream bus. +.Pp +When an I2C slave device on a bus downstream of a mux initiates I/O, +it first requests exclusive use of the bus by calling +.Fn iicbus_request_bus . +This request is communicated to the bus's parent, which is the +.Nm +framework +mux driver. +Once exclusive bus ownership is obtained, the mux driver +connects the upstream I2C bus to the downstream bus which hosts the +slave device that requested bus ownership. +The mux hardware maintains that upstream-to-downstream connection until +the slave device calls +.Fn iicbus_release_bus . +Before releasing ownership, the mux driver returns the mux hardware to +the idle state. +.Sh FDT CONFIGURATION +On an +.Xr fdt 4 +based system, an I2C mux device node is defined as a child node of its +upstream I2C bus when the mux device is an I2C slave itself. +It may be defined as a child node of any other bus or device in the +system when it is not an I2C slave, in which case the +.Va i2c-parent +property indicates which upstream bus the mux is attached to. +In either case, the children of the mux node are additional I2C buses, which +will have one or more I2C slave devices described in their child nodes. +.Pp +Drivers using the +.Nm +framework conform to the standard +.Bk -words +.Li i2c/i2c-mux.txt +.Ek +bindings document. +.Sh HINTS CONFIGURATION +On a +.Xr device.hints 5 +based system, these values are configurable for +.Nm +framework drivers : +.Bl -tag -width indent +.It Va hint...at +The upstream +.Xr iicbus 4 +the +.Nm +instance is attached to. +.El +.Pp +When configured via hints, the driver automatically adds an iicbus +instance for every downstream bus supported by the chip. +There is currently no way to indicate used versus unused downstream buses. +.Sh SEE ALSO +.Xr iicbus 4 , +.Sh HISTORY +The +.Nm +framework first appeared in +.Fx 13.0 . Index: share/man/man4/ltc430x.4 =================================================================== --- share/man/man4/ltc430x.4 +++ share/man/man4/ltc430x.4 @@ -0,0 +1,112 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Ian Lepore +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 1, 2020 +.Dt LTC430X 4 +.Os +.Sh NAME +.Nm ltc430x +.Nd driver for LTC4305 and LTC4306 I2C mux chips +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device ltc430x" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +ltc430x_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports the LTC4305 and LTC4306 I2C bus multiplexer (mux) chips. +It automatically connects an upstream I2C bus to one of several downstream +buses as needed when slave devices on the downstream buses initiate I/O. +More information on the automatic switching behavior is available in +.Xr iicmux 4 . +.Sh FDT CONFIGURATION +On an +.Xr fdt 4 +based system, an +.Nm +device node is defined as a child node of its upstream i2c bus. +The children of the +.Nm +node are additional i2c buses, which will have their own i2c slave +devices described in their child nodes. +.Pp +The +.Nm +driver conforms to the standard +.Bk -words +.Li i2c/i2c-mux-ltc4306.txt +.Ek +bindings document, except that the following optional properties +are not currently supported and will be ignored if present: +.Bl -bullet -compact -inset -offset indent +.It +enable-gpios +.It +gpio-controller +.It +#gpio-cells +.It +ltc,downstream-accelerators-enable +.It +ltc,upstream-accelerators-enable +.El +.Sh HINTS CONFIGURATION +On a +.Xr device.hints 5 +based system, these values are configurable for +.Nm : +.Bl -tag -width indent +.It Va hint.ltc430x..at +The upstream +.Xr iicbus 4 +the +.Nm +instance is attached to. +.El +.Pp +When configured via hints, the driver automatically adds an iicbus +instance for every downstream bus supported by the chip. +There is currently no way to indicate used versus unused channels. +.Sh SEE ALSO +.Xr iicbus 4 , +.Xr iicmux 4 , +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 13.0 . Index: sys/conf/NOTES =================================================================== --- sys/conf/NOTES +++ sys/conf/NOTES @@ -2329,6 +2329,11 @@ device iicsmb # smb over i2c bridge device iicoc # OpenCores I2C controller support +# I2C bus multiplexer (mux) devices +device iicmux # i2c mux core driver +device iic_gpiomux # i2c mux hardware controlled via gpio pins +device ltc430x # LTC4305 and LTC4306 i2c mux chips + # I2C peripheral devices # device ad7418 # Analog Devices temp and voltage sensor Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1801,6 +1801,10 @@ dev/iicbus/iicoc_pci.c optional iicoc pci dev/iicbus/isl12xx.c optional isl12xx dev/iicbus/lm75.c optional lm75 +dev/iicbus/mux/iicmux.c optional iicmux +dev/iicbus/mux/iicmux.m optional iicmux +dev/iicbus/mux/iic_gpiomux.c optional iic_gpiomux fdt +dev/iicbus/mux/ltc430x.c optional ltc430x dev/iicbus/nxprtc.c optional nxprtc | pcf8563 dev/iicbus/ofw_iicbus.c optional fdt iicbus dev/iicbus/rtc8583.c optional rtc8583 Index: sys/dev/iicbus/iiconf.h =================================================================== --- sys/dev/iicbus/iiconf.h +++ sys/dev/iicbus/iiconf.h @@ -47,6 +47,25 @@ #define IIC_INTR 0x2 #define IIC_INTRWAIT (IIC_INTR | IIC_WAIT) #define IIC_RECURSIVE 0x4 +#define IIC_REQBUS_DEV 0x8 /* See struct iic_reqbus_data, below. */ + +/* + * The original iicbus->bridge callback api took a pointer to an int containing + * flags. The new api allows a pointer to this struct, with IIC_REQBUS_DEV set + * in the flags to let the implementation know the pointer is actually to this + * struct which has the flags word first, followed by the device_t of the + * requesting bus and device. + * + * Note that the requesting device may not be a i2c slave device which is a + * child of the requested bus -- it may be a mux device which is electrically + * part of the bus hierarchy, but whose driver belongs to some other bus + * hierarchy such as gpio. + */ +struct iic_reqbus_data { + int flags; /* Flags from the set defined above. */ + device_t bus; /* The iicbus being requested. */ + device_t dev; /* The device requesting the bus. */ +}; /* * i2c modes Index: sys/dev/iicbus/iiconf.c =================================================================== --- sys/dev/iicbus/iiconf.c +++ sys/dev/iicbus/iiconf.c @@ -137,6 +137,7 @@ int iicbus_request_bus(device_t bus, device_t dev, int how) { + struct iic_reqbus_data reqdata; struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; @@ -175,8 +176,11 @@ */ IICBUS_UNLOCK(sc); /* Ask the underlying layers if the request is ok */ + reqdata.dev = dev; + reqdata.bus = bus; + reqdata.flags = how | IIC_REQBUS_DEV; error = IICBUS_CALLBACK(device_get_parent(bus), - IIC_REQUEST_BUS, (caddr_t)&how); + IIC_REQUEST_BUS, (caddr_t)&reqdata); IICBUS_LOCK(sc); if (error != 0) { @@ -201,6 +205,7 @@ int iicbus_release_bus(device_t bus, device_t dev) { + struct iic_reqbus_data reqdata; struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); IICBUS_LOCK(sc); @@ -213,7 +218,11 @@ if (--sc->owncount == 0) { /* Drop the lock while informing the low-level driver. */ IICBUS_UNLOCK(sc); - IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); + reqdata.dev = dev; + reqdata.bus = bus; + reqdata.flags = IIC_REQBUS_DEV; + IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, + (caddr_t)&reqdata); IICBUS_LOCK(sc); sc->owner = NULL; wakeup_one(sc); Index: sys/dev/iicbus/mux/iic_gpiomux.c =================================================================== --- sys/dev/iicbus/mux/iic_gpiomux.c +++ sys/dev/iicbus/mux/iic_gpiomux.c @@ -0,0 +1,249 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Driver for i2c bus muxes controlled by one or more gpio pins. + * + * This driver has #ifdef FDT sections in it, as if it supports both fdt and + * hinted attachment, but there is currently no support for hinted attachment. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef FDT +#include +#include +#include + +static struct ofw_compat_data compat_data[] = { + {"i2c-mux-gpio", true}, + {NULL, false} +}; +OFWBUS_PNP_INFO(compat_data); +SIMPLEBUS_PNP_INFO(compat_data); +#endif /* FDT */ + +#include +#include "iicmux.h" +#include "iicmux_if.h" + +struct gpiomux_softc { + struct iicmux_softc mux; + int idleidx; + int numpins; + gpio_pin_t pins[IICMUX_MAX_BUSES]; +}; + +#define IDLE_NOOP (-1) /* When asked to idle the bus, do nothing. */ + +static int +gpiomux_bus_select(device_t dev, int busidx, struct iic_reqbus_data *rd) +{ + struct gpiomux_softc *sc = device_get_softc(dev); + int i; + + /* + * The iicmux caller ensures busidx is between 0 and the number of buses + * we passed to iicmux_init_softc(), no need for validation here. The + * bits in the index number are transcribed to the state of the pins, + * except when we're asked to idle the bus. In that case, we transcribe + * sc->idleidx to the pins, unless that is IDLE_NOOP (leave the current + * bus selected), in which case we just bail. + */ + if (busidx == IICMUX_SELECT_IDLE) { + if (sc->idleidx == IDLE_NOOP) + return (0); + busidx = sc->idleidx; + } + + for (i = 0; i < sc->numpins; ++i) + gpio_pin_set_active(sc->pins[i], busidx & (1u << i)); + + return (0); +} + +static int +gpiomux_probe(device_t dev) +{ + int rv; + + rv = ENXIO; + +#ifdef FDT + if (ofw_bus_status_okay(dev) && + ofw_bus_search_compatible(dev, compat_data)->ocd_data) + rv = BUS_PROBE_DEFAULT; +#endif + + device_set_desc(dev, "I2C GPIO Mux"); + + return (rv); +} + +static int +gpiomux_attach(device_t dev) +{ + struct gpiomux_softc *sc = device_get_softc(dev); + ssize_t len; + device_t busdev; + int err, i, idlebits, numchannels; + pcell_t propval; + phandle_t node; + + node = ofw_bus_get_node(dev); + + /* + * Locate the gpio pin(s) that control the mux hardware. There can be + * multiple pins, but there must be at least one. + */ + for (i = 0; ; ++i) { + err = gpio_pin_get_by_ofw_propidx(dev, node, "mux-gpios", i, + &sc->pins[i]); + if (err != 0) { + break; + } + } + sc->numpins = i; + if (sc->numpins == 0) { + device_printf(dev, "cannot acquire pins listed in mux-gpios\n"); + return ((err == 0) ? ENXIO : err); + } + numchannels = 1u << sc->numpins; + if (numchannels > IICMUX_MAX_BUSES) { + device_printf(dev, "too many mux-gpios pins for max %d buses\n", + IICMUX_MAX_BUSES); + return (EINVAL); + } + + /* + * We don't have a parent/child relationship to the upstream bus, we + * have to locate it via the i2c-parent property. Explicitly tell the + * user which upstream we're associated with, since the normal attach + * message is going to mention only our actual parent. + */ + len = OF_getencprop(node, "i2c-parent", &propval, sizeof(propval)); + if (len != sizeof(propval)) { + device_printf(dev, "cannot obtain i2c-parent property\n"); + return (ENXIO); + } + busdev = OF_device_from_xref((phandle_t)propval); + if (busdev == NULL) { + device_printf(dev, + "cannot find device referenced by i2c-parent property\n"); + return (ENXIO); + } + device_printf(dev, "upstream bus is %s\n", device_get_nameunit(busdev)); + + /* + * If there is an idle-state property, that is the value we set the pins + * to when the bus is idle, otherwise idling the bus is a no-op + * (whichever bus was last accessed remains active). + */ + len = OF_getencprop(node, "idle-state", &propval, sizeof(propval)); + if (len == sizeof(propval)) { + if ((int)propval >= numchannels) { + device_printf(dev, + "idle-state property %d exceeds channel count\n", + propval); + } + sc->idleidx = (int)propval; + idlebits = sc->idleidx; + } else { + sc->idleidx = IDLE_NOOP; + idlebits = 0; + } + + /* Preset the mux to the idle state to get things started. */ + for (i = 0; i < sc->numpins; ++i) { + gpio_pin_setflags(sc->pins[i], GPIO_PIN_OUTPUT); + gpio_pin_set_active(sc->pins[i], idlebits & (1u << i)); + } + + /* Init the core driver, have it add our child downstream buses. */ + if ((err = iicmux_attach(dev, busdev, numchannels)) == 0) + bus_generic_attach(dev); + + return (err); +} + +static int +gpiomux_detach(device_t dev) +{ + struct gpiomux_softc *sc = device_get_softc(dev); + int err, i; + + if ((err = iicmux_detach(dev)) != 0) + return (err); + + for (i = 0; i < sc->numpins; ++i) + gpio_pin_release(sc->pins[i]); + + return (0); +} + +static device_method_t gpiomux_methods[] = { + /* device methods */ + DEVMETHOD(device_probe, gpiomux_probe), + DEVMETHOD(device_attach, gpiomux_attach), + DEVMETHOD(device_detach, gpiomux_detach), + + /* iicmux methods */ + DEVMETHOD(iicmux_bus_select, gpiomux_bus_select), + + DEVMETHOD_END +}; + +static devclass_t gpiomux_devclass; + +DEFINE_CLASS_1(iic_gpiomux, iic_gpiomux_driver, gpiomux_methods, + sizeof(struct gpiomux_softc), iicmux_driver); +DRIVER_MODULE(iic_gpiomux, simplebus, iic_gpiomux_driver, gpiomux_devclass, 0, 0); +DRIVER_MODULE(iic_gpiomux, ofw_simplebus, iic_gpiomux_driver, gpiomux_devclass, 0, 0); + +#ifdef FDT +DRIVER_MODULE(ofw_iicbus, iic_gpiomux, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0); +#else +DRIVER_MODULE(iicbus, iic_gpiomux, iicbus_driver, iicbus_devclass, 0, 0); +#endif + +MODULE_DEPEND(iic_gpiomux, iicmux, 1, 1, 1); +MODULE_DEPEND(iic_gpiomux, iicbus, 1, 1, 1); Index: sys/dev/iicbus/mux/iicmux.h =================================================================== --- sys/dev/iicbus/mux/iicmux.h +++ sys/dev/iicbus/mux/iicmux.h @@ -0,0 +1,78 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __IICMUX_H +#define __IICMUX_H + +#define IICMUX_MAX_BUSES 16 /* More than any available mux chip. */ + +/* + * IICMUX_SELECT_IDLE instructs the mux hardware driver to do whatever it is + * configured to do when the downstream buses are idle. Hardware has varying + * capabilities; it may disconnect all downstream buses, or connect a specific + * bus, or just leave whatever bus was last used connected. Hardware which is + * capable of various choices will have some mechanism to configure the choice + * which is handled outside of the iicmux framework. + */ +#define IICMUX_SELECT_IDLE (-1) + +/* + * The iicmux softc; chip drivers should embed one of these as the first member + * variable of their own softc struct, and must call iicmux_attach() to + * initialize it before calling any other iicmux functions. + */ +struct iicmux_softc { + device_t busdev; /* Upstream i2c bus (may not be our parent). */ + device_t dev; /* Ourself. */ + int maxbus; /* Index of highest populated busdevs slot. */ + int numbuses; /* Number of buses supported by the chip. */ + int debugmux; /* Write debug messages when > 0. */ + device_t childdevs[IICMUX_MAX_BUSES]; /* Child bus instances. */ +#ifdef FDT + phandle_t childnodes[IICMUX_MAX_BUSES]; /* Child bus fdt nodes. */ +#endif +}; + +DECLARE_CLASS(iicmux_driver); + +/* + * Helpers to call from attach/detach functions of chip-specific drivers. + * + * The iicmux_attach() function initializes the core driver's portion of the + * softc, and creates child iicbus instances for any children it can identify + * using hints and FDT data. If a chip driver does its own device_add_child() + * calls to add other downstream buses that participate in the mux switching, it + * must call iicmux_add_child() to inform the core driver of the downstream + * busidx<->device_t relationship. + */ +int iicmux_add_child(device_t dev, device_t child, int busidx); +int iicmux_attach(device_t dev, device_t busdev, int numbuses); +int iicmux_detach(device_t dev); + +#endif Index: sys/dev/iicbus/mux/iicmux.c =================================================================== --- sys/dev/iicbus/mux/iicmux.c +++ sys/dev/iicbus/mux/iicmux.c @@ -0,0 +1,387 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#include +#endif + +#include +#include "iicbus_if.h" +#include "iicmux_if.h" +#include "iicmux.h" + +/*------------------------------------------------------------------------------ + * iicbus methods, called by the iicbus functions in iiconf.c. + * + * All these functions return an IIC adapter-layer error code (because we are + * pretending to be a host bridge/i2c controller). Standard errno values + * returned from these must be encoded using iic2errno(). + *----------------------------------------------------------------------------*/ + +static int +iicmux_callback(device_t dev, int index, caddr_t data) +{ + struct iicmux_softc *sc = device_get_softc(dev); + struct iic_reqbus_data *rd; + int err, i; + + /* If it's not one of the operations we know about, bail early. */ + if (index != IIC_REQUEST_BUS && index != IIC_RELEASE_BUS) + return (iic2errno(EOPNOTSUPP)); + + /* + * Ensure that the data passed to us includes the device_t of the child + * bus and device. If missing, someone bypassed iicbus_request_bus() + * and called this method directly using the old calling standard. If + * present, find the index of the child bus that called us. + */ + rd = (struct iic_reqbus_data *)data; + if (!(rd->flags & IIC_REQBUS_DEV)) + return (iic2errno(EINVAL)); + + for (i = 0; i <= sc->maxbus && sc->childdevs[i] != rd->bus; ++i) + continue; + if (i > sc->maxbus) + return (iic2errno(ENOENT)); + + /* + * If the operation is a release it "cannot fail". Idle the downstream + * bus, then release exclusive use of the upstream bus, and we're done. + */ + if (index == IIC_RELEASE_BUS) { + if (sc->debugmux > 0) { + device_printf(dev, "idle the bus for %s on bus %s\n", + device_get_nameunit(rd->dev), + device_get_nameunit(rd->bus)); + } + IICMUX_BUS_SELECT(dev, IICMUX_SELECT_IDLE, rd); + iicbus_release_bus(sc->busdev, dev); + return (IIC_NOERR); + } + + if (sc->debugmux > 0) { + device_printf(dev, "select bus idx %d for %s on bus %s\n", i, + device_get_nameunit(rd->dev), device_get_nameunit(rd->bus)); + } + + /* + * The operation is a request for exclusive use. First we have to + * request exclusive use of our upstream bus. If multiple slave devices + * from our different child buses attempt to do IO at the same time, + * this is what ensures that they don't switch the bus out from under + * each other. The first one in proceeds and others wait here (or get an + * EWOULDBLOCK return if they're using IIC_DONTWAIT). + */ + if ((err = iicbus_request_bus(sc->busdev, dev, rd->flags)) != 0) + return (err); /* Already an IIC error code. */ + + /* + * Now that we own exclusive use of the upstream bus, connect it to the + * downstream bus where the request came from. + */ + if ((err = IICMUX_BUS_SELECT(dev, i, rd)) != 0) + iicbus_release_bus(sc->busdev, dev); + + return (err); +} + +static u_int +iicmux_get_frequency(device_t dev, u_char speed) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (IICBUS_GET_FREQUENCY(sc->busdev, speed)); +} + +#ifdef FDT +static phandle_t +iicmux_get_node(device_t dev, device_t child) +{ + struct iicmux_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i <= sc->maxbus; ++i) { + if (sc->childdevs[i] == child) + return (sc->childnodes[i]); + } + return (0); /* null handle */ +} +#endif + +static int +iicmux_intr(device_t dev, int event, char *buf) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + /* XXX iicbus_intr() in iiconf.c should return status. */ + + iicbus_intr(sc->busdev, event, buf); + return (0); +} + +static int +iicmux_read(device_t dev, char *buf, int len, int *bytes, int last, int delay) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_read(sc->busdev, buf, len, bytes, last, delay)); +} + +static int +iicmux_repeated_start(device_t dev, u_char slave, int timeout) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_repeated_start(sc->busdev, slave, timeout)); +} + +static int +iicmux_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_reset(sc->busdev, speed, addr, oldaddr)); +} + +static int +iicmux_start(device_t dev, u_char slave, int timeout) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_start(sc->busdev, slave, timeout)); +} + +static int +iicmux_stop(device_t dev) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_stop(sc->busdev)); +} + +static int +iicmux_transfer( device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_transfer(sc->busdev, msgs, nmsgs)); +} + +static int +iicmux_write(device_t dev, const char *buf, int len, int *bytes, int timeout) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + return (iicbus_write(sc->busdev, buf, len, bytes, timeout)); +} + +/*------------------------------------------------------------------------------ + * iicmux helper functions, called by hardware-specific drivers. + * All these functions return a standard errno value. + *----------------------------------------------------------------------------*/ + +int +iicmux_add_child(device_t dev, device_t child, int busidx) +{ + struct iicmux_softc *sc = device_get_softc(dev); + + KASSERT(busidx < sc->numbuses, + ("iicmux_add_child: bus idx %d too big", busidx)); + KASSERT(sc->childdevs[busidx] == NULL, + ("iicmux_add_child: bus idx %d already added", busidx)); + + sc->childdevs[busidx] = child; + if (sc->maxbus < busidx) + sc->maxbus = busidx; + + return (0); +} + +int +iicmux_attach(device_t dev, device_t busdev, int numbuses) +{ + struct iicmux_softc *sc = device_get_softc(dev); + int i, numadded; + + /* + * Init the softc... + */ + KASSERT(numbuses <= IICMUX_MAX_BUSES, + ("iicmux_attach: numbuses %d exceeds max %d\n", + numbuses, IICMUX_MAX_BUSES)); + + sc->dev = dev; + sc->busdev = busdev; + sc->numbuses = numbuses; + + SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "debugmux", CTLFLAG_RWTUN, &sc->debugmux, 0, "debug mux operations"); + + /* + * Add children... + */ + numadded = 0; + +#ifdef FDT + phandle_t child, node, parent; + pcell_t idx; + + /* + * Find our FDT node. Child nodes within our node will become our + * iicbus children. + */ + if((node = ofw_bus_get_node(sc->dev)) == 0) { + device_printf(sc->dev, "cannot find FDT node\n"); + return (ENOENT); + } + + /* + * First we have to see if there is a child node named "i2c-mux". If it + * exists, all children of that node are buses, else all children of the + * device node are buses. + */ + if ((parent = ofw_bus_find_child(node, "i2c-mux")) == 0) + parent = node; + + /* + * Attach the children represented in the device tree. + */ + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + if (OF_getencprop(child, "reg", &idx, sizeof(idx)) == -1) { + device_printf(dev, + "child bus missing required 'reg' property\n"); + continue; + } + if (idx >= sc->numbuses) { + device_printf(dev, + "child bus 'reg' property %d exceeds the number " + "of buses supported by the device (%d)\n", + idx, sc->numbuses); + continue; + } + sc->childdevs[idx] = device_add_child(sc->dev, "iicbus", -1); + sc->childnodes[idx] = child; + if (sc->maxbus < idx) + sc->maxbus = idx; + ++numadded; + } +#endif /* FDT */ + + /* + * If we configured anything using FDT data, we're done. Otherwise add + * an iicbus child for every downstream bus supported by the mux chip. + */ + if (numadded > 0) + return (0); + + for (i = 0; i < sc->numbuses; ++i) { + sc->childdevs[i] = device_add_child(sc->dev, "iicbus", -1); + if (sc->maxbus < i) + sc->maxbus = i; + } + + return (0); +} + +int +iicmux_detach(device_t dev) +{ + struct iicmux_softc *sc = device_get_softc(dev); + int err, i; + + /* Delete only the children we added in iicmux_add* functions. */ + for (i = 0; i <= sc->maxbus; ++i) { + if (sc->childdevs[i] == NULL) + continue; + if ((err = device_delete_child(dev, sc->childdevs[i])) != 0) + return (err); + sc->childdevs[i] = NULL; + } + + return (0); +} + +static device_method_t iicmux_methods [] = { + /* iicbus_if methods */ + DEVMETHOD(iicbus_intr, iicmux_intr), + DEVMETHOD(iicbus_callback, iicmux_callback), + DEVMETHOD(iicbus_repeated_start, iicmux_repeated_start), + DEVMETHOD(iicbus_start, iicmux_start), + DEVMETHOD(iicbus_stop, iicmux_stop), + DEVMETHOD(iicbus_read, iicmux_read), + DEVMETHOD(iicbus_write, iicmux_write), + DEVMETHOD(iicbus_reset, iicmux_reset), + DEVMETHOD(iicbus_transfer, iicmux_transfer), + DEVMETHOD(iicbus_get_frequency, iicmux_get_frequency), + +#ifdef FDT + /* ofwbus_if methods */ + DEVMETHOD(ofw_bus_get_node, iicmux_get_node), +#endif + + DEVMETHOD_END +}; + +static int +iicmux_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + return 0; + case MOD_UNLOAD: + return 0; + } + return EINVAL; +} + +static moduledata_t iicmux_mod = { + "iicmux", + iicmux_modevent, + 0 +}; + +DEFINE_CLASS_0(iicmux, iicmux_driver, iicmux_methods, + sizeof(struct iicmux_softc)); + +DECLARE_MODULE(iicmux, iicmux_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(iicmux, 1); + +MODULE_DEPEND(iicmux, iicbus, 1, 1, 1); Index: sys/dev/iicbus/mux/iicmux_if.m =================================================================== --- sys/dev/iicbus/mux/iicmux_if.m +++ sys/dev/iicbus/mux/iicmux_if.m @@ -0,0 +1,49 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Ian Lepore +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +INTERFACE iicmux; + +HEADER { + struct iic_reqbus_data; +} + +# +# iicmux_bus_select(device_t dev, int busidx, struct iic_reqbus_data *rd) +# Do whatever the mux hardware requires to connect the downstream +# bus selected by busidx to the upstream bus. This must return +# an IIC adapter-layer error code; standard errno values returned +# from this must be encoded using iic2errno(). +# +METHOD int bus_select { + device_t dev; + int busidx; + struct iic_reqbus_data *rd; +}; Index: sys/dev/iicbus/mux/ltc430x.c =================================================================== --- sys/dev/iicbus/mux/ltc430x.c +++ sys/dev/iicbus/mux/ltc430x.c @@ -0,0 +1,221 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include + +#include +#include +#include "iicbus_if.h" +#include "iicmux_if.h" + +static struct chip_info { + const char *partname; + const char *description; + int numchannels; +} chip_infos[] = { + {"ltc4305", "LTC4305 I2C Mux", 2}, + {"ltc4306", "LTC4306 I2C Mux", 4}, +}; +#define CHIP_NONE (-1) +#define CHIP_LTC4305 0 +#define CHIP_LTC4306 1 + +#ifdef FDT +#include +#include +#include + +static struct ofw_compat_data compat_data[] = { + {"lltc,ltc4305", CHIP_LTC4305}, + {"lltc,ltc4306", CHIP_LTC4306}, + {NULL, CHIP_NONE} +}; +IICBUS_FDT_PNP_INFO(compat_data); +#endif + +#include + +struct ltc430x_softc { + struct iicmux_softc mux; + bool idle_disconnect; +}; + +/* + * The datasheet doesn't name the registers, it calls them control register 0-3. + */ +#define LTC430X_CTLREG_0 0 +#define LTC430X_CTLREG_1 1 +#define LTC430X_CTLREG_2 2 +#define LTC430X_CTLREG_3 3 + +static int +ltc430x_bus_select(device_t dev, int busidx, struct iic_reqbus_data *rd) +{ + struct ltc430x_softc *sc = device_get_softc(dev); + uint8_t busbits; + + /* + * The iicmux caller ensures busidx is between 0 and the number of buses + * we passed to iicmux_init_softc(), no need for validation here. If + * the fdt data has the idle_disconnect property we idle the bus by + * selecting no downstream buses, otherwise we just leave the current + * bus active. The upper bits of control register 3 activate the + * downstream buses; bit 7 is the first bus, bit 6 the second, etc. + */ + if (busidx == IICMUX_SELECT_IDLE) { + if (sc->idle_disconnect) + busbits = 0; + else + return (0); + } else { + busbits = 1u << (7 - busidx); + } + + /* + * We have to add the IIC_RECURSIVE flag because the iicmux core has + * already reserved the bus for us, and iicdev_writeto() is going to try + * to reserve it again, which is allowed with the recursive flag. + */ + return (iicdev_writeto(dev, LTC430X_CTLREG_3, &busbits, sizeof(busbits), + rd->flags | IIC_RECURSIVE)); +} + +static int +ltc430x_find_chiptype(device_t dev) +{ +#ifdef FDT + return (ofw_bus_search_compatible(dev, compat_data)->ocd_data); +#else + const char *type; + int i; + + if (resource_string_value(device_get_name(dev), device_get_unit(dev), + "chip_type", &type) == 0) { + for (i = 0; i < nitems(chip_infos); ++i) { + if (strcasecmp(type, chip_infos[i].partname) == 0) { + return (i); + } + } + } + return (CHIP_NONE); +#endif +} + +static int +ltc430x_probe(device_t dev) +{ + int type; + +#ifdef FDT + if (!ofw_bus_status_okay(dev)) + return (ENXIO); +#endif + + type = ltc430x_find_chiptype(dev); + if (type == CHIP_NONE) + return (ENXIO); + + device_set_desc(dev, chip_infos[type].description); + + return (BUS_PROBE_DEFAULT); +} + +static int +ltc430x_attach(device_t dev) +{ + struct ltc430x_softc *sc __unused; + int chip, err, numchan; + + sc = device_get_softc(dev); + +#ifdef FDT + phandle_t node; + + node = ofw_bus_get_node(dev); + sc->idle_disconnect = OF_hasprop(node, "i2c-mux-idle-disconnect"); +#endif + + /* We found the chip type when probing, so now it "can't fail". */ + if ((chip = ltc430x_find_chiptype(dev)) == CHIP_NONE) { + device_printf(dev, "impossible: can't identify chip type\n"); + return (ENXIO); + } + numchan = chip_infos[chip].numchannels; + + if ((err = iicmux_attach(dev, device_get_parent(dev), numchan)) == 0) + bus_generic_attach(dev); + + return (err); +} + +static int +ltc430x_detach(device_t dev) +{ + int err; + + if ((err = iicmux_detach(dev)) != 0) + return (err); + + return (0); +} + +static device_method_t ltc430x_methods[] = { + /* device methods */ + DEVMETHOD(device_probe, ltc430x_probe), + DEVMETHOD(device_attach, ltc430x_attach), + DEVMETHOD(device_detach, ltc430x_detach), + + /* iicmux methods */ + DEVMETHOD(iicmux_bus_select, ltc430x_bus_select), + + DEVMETHOD_END +}; + +static devclass_t ltc430x_devclass; + +DEFINE_CLASS_1(ltc430x, ltc430x_driver, ltc430x_methods, + sizeof(struct ltc430x_softc), iicmux_driver); +DRIVER_MODULE(ltc430x, iicbus, ltc430x_driver, ltc430x_devclass, 0, 0); + +#ifdef FDT +DRIVER_MODULE(ofw_iicbus, ltc430x, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0); +#else +DRIVER_MODULE(iicbus, ltc430x, iicbus_driver, iicbus_devclass, 0, 0); +#endif + +MODULE_DEPEND(ltc430x, iicmux, 1, 1, 1); +MODULE_DEPEND(ltc430x, iicbus, 1, 1, 1); + Index: sys/modules/i2c/Makefile =================================================================== --- sys/modules/i2c/Makefile +++ sys/modules/i2c/Makefile @@ -17,6 +17,7 @@ isl \ isl12xx \ jedec_dimm \ + mux \ nxprtc \ rtc8583 \ s35390a \ Index: sys/modules/i2c/mux/Makefile =================================================================== --- sys/modules/i2c/mux/Makefile +++ sys/modules/i2c/mux/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +SUBDIR = \ + iicmux \ + ltc430x \ + +.if !empty(OPT_FDT) +SUBDIR+= iic_gpiomux +.endif + +.include Index: sys/modules/i2c/mux/iic_gpiomux/Makefile =================================================================== --- sys/modules/i2c/mux/iic_gpiomux/Makefile +++ sys/modules/i2c/mux/iic_gpiomux/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/mux + +KMOD= iic_gpiomux +SRCS= iic_gpiomux.c + +SRCS+= \ + bus_if.h \ + device_if.h \ + gpio_if.h \ + iicbus_if.h \ + iicmux_if.h \ + opt_platform.h \ + +.if !empty(OPT_FDT) +SRCS+= ofw_bus_if.h +.endif + +.include Index: sys/modules/i2c/mux/iicmux/Makefile =================================================================== --- sys/modules/i2c/mux/iicmux/Makefile +++ sys/modules/i2c/mux/iicmux/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/mux + +KMOD= iicmux +SRCS= iicmux.c iicmux_if.c + +SRCS+= \ + bus_if.h \ + device_if.h \ + iicbus_if.h \ + iicmux_if.h \ + opt_platform.h \ + +.if !empty(OPT_FDT) +SRCS+= ofw_bus_if.h +.endif + +.include Index: sys/modules/i2c/mux/ltc430x/Makefile =================================================================== --- sys/modules/i2c/mux/ltc430x/Makefile +++ sys/modules/i2c/mux/ltc430x/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/mux + +KMOD= ltc430x +SRCS= ltc430x.c + +SRCS+= \ + bus_if.h \ + device_if.h \ + iicbus_if.h \ + iicmux_if.h \ + opt_platform.h \ + +.if !empty(OPT_FDT) +SRCS+= ofw_bus_if.h +.endif + + +.include