Page MenuHomeFreeBSD

D22891.id65856.diff
No OneTemporary

D22891.id65856.diff

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 <ian@freebsd.org>
+.\"
+.\" 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 December 19, 2019
+.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,133 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+.\"
+.\" 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 December 19, 2019
+.Dt IICMUX 4
+.Os
+.Sh NAME
+.Nm iicmux
+.Nd I2C bus mulitiplexer framework
+.Sh SYNOPSIS
+.In dev/iicbus/mux/iicmux.h
+.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 that needs to be specifically configured
+into the kernel or loaded as a module,
+it is a collection of support functions and driver methods which are
+used by individual mux hardware drivers.
+It will be included 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 automatically
+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.<driver>.<unit>.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 <ian@freebsd.org>
+.\"
+.\" 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 December 19, 2019
+.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.<unit>.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
@@ -2322,13 +2322,15 @@
# iicbb generic I2C bit-banging code (needed by lpbb, bktr)
#
device iicbus # Bus support, required for ic/iic/iicsmb below.
-device iicbb
+device iicbb # bitbang driver; implements i2c on a pair of gpio pins
device ic
-device iic
+device iic # userland access to i2c slave devices via ioctl(8)
device iicsmb # smb over i2c bridge
device iicoc # OpenCores I2C controller support
+device iic_gpiomux # i2c bus multiplexer (mux) controlled via gpio pins
+
# I2C peripheral devices
#
device ad7418 # Analog Devices temp and voltage sensor
@@ -2340,6 +2342,7 @@
device icee # AT24Cxxx and compatible EEPROMs
device isl12xx # Intersil ISL12xx RTC
device lm75 # LM75 compatible temperature sensor
+device ltc430x # LTC4305 and LTC4306 bus multiplexer (mux)
device nxprtc # NXP RTCs: PCA/PFC212x PCA/PCF85xx
device rtc8583 # Epson RTC-8583
device s35390a # Seiko Instruments S-35390A RTC
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1799,6 +1799,9 @@
dev/iicbus/iicoc.c optional iicoc
dev/iicbus/isl12xx.c optional isl12xx
dev/iicbus/lm75.c optional lm75
+dev/iicbus/mux/iicmux.c optional ltc430x | iic_gpiomux
+dev/iicbus/mux/iic_gpiomux optional fdt iic_gpiomux
+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,8 +47,27 @@
#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
*/
#define IIC_MASTER 0x1
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,245 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/mux/iicmux.h>
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+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 "iicmux.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. */
+ iicmux_softc_init(dev, busdev, numchannels, gpiomux_bus_select);
+ if ((err = iicmux_add_children(dev)) == 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_delete_children(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),
+
+ 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,77 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+ *
+ * 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. */
+
+/*
+ * The iicmux internals call a function of this type to select/activate a
+ * downstream bus by index. IICMUX_SELECT_NONE means deactivate all buses. If
+ * this function returns non-zero, the hardware should be left in the state it
+ * had on entry (if possible). Note that the bus has already been reserved
+ * before this is called, so if the chip driver does i2c IO it may need to add
+ * the IIC_RECURSIVE flag to allow recursive bus reservation.
+ */
+#define IICMUX_SELECT_IDLE (-1)
+struct iic_reqbus_data;
+typedef int (*iicmux_bus_select_t)(device_t dev, int busidx,
+ struct iic_reqbus_data *rd);
+
+/*
+ * The iicmux softc; chip drivers should embed one of these as the first member
+ * variable of their own softc struct, and must call iicmux_softc_init 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. */
+ iicmux_bus_select_t
+ bus_select; /* Chip func to select a downstream bus. */
+ 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.
+ */
+int iicmux_add_child(device_t dev, device_t child, int busidx);
+int iicmux_add_children(device_t dev);
+int iicmux_delete_children(device_t dev);
+void iicmux_softc_init(device_t dev, device_t upstream_busdev, int numbuses,
+ iicmux_bus_select_t func);
+
+#endif
Index: sys/dev/iicbus/mux/iicmux.c
===================================================================
--- sys/dev/iicbus/mux/iicmux.c
+++ sys/dev/iicbus/mux/iicmux.c
@@ -0,0 +1,388 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+#endif
+
+#include <dev/iicbus/iiconf.h>
+#include "iicbus_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 *childrd, myrd;
+ 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.
+ */
+ childrd = (struct iic_reqbus_data *)data;
+ if (!(childrd->flags & IIC_REQBUS_DEV))
+ return (iic2errno(EINVAL));
+
+ for (i = 0; i <= sc->maxbus && sc->childdevs[i] != childrd->bus; ++i)
+ continue;
+ if (i > sc->maxbus)
+ return (iic2errno(ENOENT));
+
+ /*
+ * Craft a new iic_reqbus_data struct to pass to our parent bus when we
+ * request or release ownership. It must use the flags that were passed
+ * to us so that IIC_NOWAIT is honored if the originating slave device
+ * is calling from a context where sleeping is disallowed. We are
+ * making this request of our parent bus as a slave device (the mux
+ * device) so it gets our device_t, not the original caller's (this is
+ * required to support cascaded muxes).
+ */
+ myrd.flags = childrd->flags;
+ myrd.dev = sc->dev;
+ myrd.bus = sc->busdev;
+
+ /*
+ * 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) {
+ sc->bus_select(dev, IICMUX_SELECT_IDLE, &myrd);
+ iicbus_release_bus(sc->busdev, dev);
+ return (IIC_NOERR);
+ }
+
+ /*
+ * 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, childrd->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 = sc->bus_select(dev, i, &myrd)) != 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_add_children(device_t dev)
+{
+ struct iicmux_softc *sc = device_get_softc(dev);
+ int i, numadded;
+
+ 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_delete_children(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);
+}
+
+void
+iicmux_softc_init(device_t dev, device_t busdev, int numbuses,
+ iicmux_bus_select_t func)
+{
+ struct iicmux_softc *sc = device_get_softc(dev);
+
+ KASSERT(numbuses <= IICMUX_MAX_BUSES,
+ ("iicmux_softc_init: numbuses %d exceeds max %d\n",
+ numbuses, IICMUX_MAX_BUSES));
+ KASSERT(func != NULL, ("iicmux_softc_init: NULL bus_select func"));
+
+ memset(sc, 0, sizeof(*sc));
+ sc->dev = dev;
+ sc->busdev = busdev;
+ sc->numbuses = numbuses;
+ sc->bus_select = func;
+}
+
+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/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 <ian@freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#include "iicbus_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 <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+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 "iicmux.h"
+
+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, numchannels;
+
+ 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);
+ }
+ numchannels = chip_infos[chip].numchannels;
+
+
+ iicmux_softc_init(dev, device_get_parent(dev), numchannels,
+ ltc430x_bus_select);
+
+ if ((err = iicmux_add_children(dev)) == 0)
+ bus_generic_attach(dev);
+
+ return (err);
+}
+
+static int
+ltc430x_detach(device_t dev)
+{
+ int err;
+
+ if ((err = iicmux_delete_children(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),
+
+ 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 <bsd.subdir.mk>
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,19 @@
+# $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 \
+ opt_platform.h \
+
+.if !empty(OPT_FDT)
+SRCS+= ofw_bus_if.h
+.endif
+
+.include <bsd.kmod.mk>
Index: sys/modules/i2c/mux/iicmux/Makefile
===================================================================
--- sys/modules/i2c/mux/iicmux/Makefile
+++ sys/modules/i2c/mux/iicmux/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus/mux
+
+KMOD= iicmux
+SRCS= iicmux.c
+
+SRCS+= \
+ bus_if.h \
+ device_if.h \
+ iicbus_if.h \
+ opt_platform.h \
+
+.if !empty(OPT_FDT)
+SRCS+= ofw_bus_if.h
+.endif
+
+.include <bsd.kmod.mk>
Index: sys/modules/i2c/mux/ltc430x/Makefile
===================================================================
--- sys/modules/i2c/mux/ltc430x/Makefile
+++ sys/modules/i2c/mux/ltc430x/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus/mux
+
+KMOD= ltc430x
+SRCS= ltc430x.c
+
+SRCS+= \
+ bus_if.h \
+ device_if.h \
+ iicbus_if.h \
+ opt_platform.h \
+
+.if !empty(OPT_FDT)
+SRCS+= ofw_bus_if.h
+.endif
+
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Sat, May 16, 2:18 PM (1 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33108605
Default Alt Text
D22891.id65856.diff (46 KB)

Event Timeline