git: c0525ab1d1ce - main - pca954x: driver for PCA954x / TCA954x I2C switches
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 13 Nov 2021 09:28:19 UTC
The branch main has been updated by avg: URL: https://cgit.FreeBSD.org/src/commit/?id=c0525ab1d1ce69ab3d589e95733caedb04e0dcbd commit c0525ab1d1ce69ab3d589e95733caedb04e0dcbd Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2020-08-18 09:16:28 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-11-13 09:27:41 +0000 pca954x: driver for PCA954x / TCA954x I2C switches At the moment only PCA9548A is supported and has been tested. MFC after: 2 weeks --- share/man/man4/Makefile | 1 + share/man/man4/pca954x.4 | 119 +++++++++++++++++++ sys/conf/files | 1 + sys/dev/iicbus/mux/pca954x.c | 214 +++++++++++++++++++++++++++++++++++ sys/modules/i2c/mux/Makefile | 1 + sys/modules/i2c/mux/pca954x/Makefile | 20 ++++ 6 files changed, 356 insertions(+) diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index d3cd58b495f4..dfef254bff56 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -418,6 +418,7 @@ MAN= aac.4 \ owc.4 \ ${_padlock.4} \ pass.4 \ + pca954x.4 \ pccard.4 \ pccbb.4 \ pcf.4 \ diff --git a/share/man/man4/pca954x.4 b/share/man/man4/pca954x.4 new file mode 100644 index 000000000000..d845740f9b6c --- /dev/null +++ b/share/man/man4/pca954x.4 @@ -0,0 +1,119 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2020 Andriy Gapon <avg@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 November 13, 2021 +.Dt PCA954X 4 +.Os +.Sh NAME +.Nm pca954x +.Nd driver for PCA9548A I2C switch +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pca954x" +.Cd "device iicmux" +.Cd "device iicbus" +.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 +pca954x_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports the PCA9548A I2C bus switch and compatible chips such as +TCA9548A. +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 attaches to nodes where the +.Va compatible +property is set to one of +.Bl -bullet +.It +.Qq nxp,pca9548 +.El +.Pp +The +.Nm +driver supports the following optional properties in addition to the standard +I2C mux properties: +.Bl -tag -width i2c-mux-idle-disconnect +.It Va i2c-mux-idle-disconnect +if defined, forces the switch to disconnect all children in idle state. +.El +.Sh HINTS CONFIGURATION +On a +.Xr device.hints 5 +based system, these values are configurable for +.Nm : +.Bl -tag -width hint.pca954x.<unit>.chip_type +.It Va hint.pca954x.<unit>.at +The upstream +.Xr iicbus 4 +the +.Nm +instance is attached to. +.It Va hint.pca954x.<unit>.chip_type +The type of the chip. +At present, only +.Qq pca9548 +is supported. +.El +.Pp +When configured via hints, the driver automatically adds an +.Xr iicbus 4 +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 and this manual page was written by +.An Andriy Gapon Aq Mt avg@FreeBSD.org . diff --git a/sys/conf/files b/sys/conf/files index 30f98817e290..4cc6e3dcde8c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1855,6 +1855,7 @@ dev/iicbus/mux/iicmux.c optional iicmux dev/iicbus/mux/iicmux_if.m optional iicmux dev/iicbus/mux/iic_gpiomux.c optional iic_gpiomux fdt dev/iicbus/mux/ltc430x.c optional ltc430x +dev/iicbus/mux/pca954x.c optional pca954x dev/iicbus/nxprtc.c optional nxprtc | pcf8563 dev/iicbus/ofw_iicbus.c optional fdt iicbus dev/iicbus/pcf8574.c optional pcf8574 diff --git a/sys/dev/iicbus/mux/pca954x.c b/sys/dev/iicbus/mux/pca954x.c new file mode 100644 index 000000000000..44c81539691e --- /dev/null +++ b/sys/dev/iicbus/mux/pca954x.c @@ -0,0 +1,214 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) Andriy Gapon + * + * 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> + +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> +#endif + +#include <dev/iicbus/iicbus.h> +#include <dev/iicbus/iiconf.h> +#include "iicbus_if.h" +#include "iicmux_if.h" +#include <dev/iicbus/mux/iicmux.h> + +struct pca954x_descr { + const char *partname; + const char *description; + int numchannels; +}; + +static struct pca954x_descr pca9548_descr = { + .partname = "pca9548", + .description = "PCA9548A I2C Mux", + .numchannels = 8, +}; + +#ifdef FDT +static struct ofw_compat_data compat_data[] = { + { "nxp,pca9548", (uintptr_t)&pca9548_descr }, + { NULL, 0 }, +}; +#else +static struct pca954x_descr *part_descrs[] = { + &pca9548_descr, +}; +#endif + +struct pca954x_softc { + struct iicmux_softc mux; + uint8_t addr; + bool idle_disconnect; +}; + +static int +pca954x_bus_select(device_t dev, int busidx, struct iic_reqbus_data *rd) +{ + struct iic_msg msg; + struct pca954x_softc *sc = device_get_softc(dev); + uint8_t busbits; + int error; + + /* + * 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. + */ + if (busidx == IICMUX_SELECT_IDLE) { + if (sc->idle_disconnect) + busbits = 0; + else + return (0); + } else { + busbits = 1u << busidx; + } + + msg.slave = sc->addr; + msg.flags = IIC_M_WR; + msg.len = 1; + msg.buf = &busbits; + error = iicbus_transfer(dev, &msg, 1); + return (error); +} + +static const struct pca954x_descr * +pca954x_find_chip(device_t dev) +{ +#ifdef FDT + const struct ofw_compat_data *compat; + + compat = ofw_bus_search_compatible(dev, compat_data); + if (compat == NULL) + return (NULL); + return ((const struct pca954x_descr *)compat->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(part_descrs) - 1; ++i) { + if (strcasecmp(type, part_descrs[i]->partname) == 0) + return (part_descrs[i]); + } + } + return (NULL); +#endif +} + +static int +pca954x_probe(device_t dev) +{ + const struct pca954x_descr *descr; + + descr = pca954x_find_chip(dev); + if (descr == NULL) + return (ENXIO); + + device_set_desc(dev, descr->description); + return (BUS_PROBE_DEFAULT); +} + +static int +pca954x_attach(device_t dev) +{ +#ifdef FDT + phandle_t node; +#endif + struct pca954x_softc *sc; + const struct pca954x_descr *descr; + int err; + + sc = device_get_softc(dev); + sc->addr = iicbus_get_addr(dev); +#ifdef FDT + node = ofw_bus_get_node(dev); + sc->idle_disconnect = OF_hasprop(node, "i2c-mux-idle-disconnect"); +#endif + + descr = pca954x_find_chip(dev); + err = iicmux_attach(dev, device_get_parent(dev), descr->numchannels); + if (err == 0) + bus_generic_attach(dev); + return (err); +} + +static int +pca954x_detach(device_t dev) +{ + int err; + + err = iicmux_detach(dev); + return (err); +} + +static device_method_t pca954x_methods[] = { + /* device methods */ + DEVMETHOD(device_probe, pca954x_probe), + DEVMETHOD(device_attach, pca954x_attach), + DEVMETHOD(device_detach, pca954x_detach), + + /* iicmux methods */ + DEVMETHOD(iicmux_bus_select, pca954x_bus_select), + + DEVMETHOD_END +}; + +static devclass_t pca954x_devclass; + +DEFINE_CLASS_1(pca9548, pca954x_driver, pca954x_methods, + sizeof(struct pca954x_softc), iicmux_driver); +DRIVER_MODULE(pca9548, iicbus, pca954x_driver, pca954x_devclass, 0, 0); + +#ifdef FDT +DRIVER_MODULE(ofw_iicbus, pca9548, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0); +#else +DRIVER_MODULE(iicbus, pca9548, iicbus_driver, iicbus_devclass, 0, 0); +#endif + +MODULE_DEPEND(pca9548, iicmux, 1, 1, 1); +MODULE_DEPEND(pca9548, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_VERSION(pca9548, 1); + +#ifdef FDT +IICBUS_FDT_PNP_INFO(compat_data); +#endif diff --git a/sys/modules/i2c/mux/Makefile b/sys/modules/i2c/mux/Makefile index 8e228a7a7fef..a972a00a3b3a 100644 --- a/sys/modules/i2c/mux/Makefile +++ b/sys/modules/i2c/mux/Makefile @@ -3,6 +3,7 @@ SUBDIR = \ iicmux \ ltc430x \ + pca954x \ .if !empty(OPT_FDT) SUBDIR+= iic_gpiomux diff --git a/sys/modules/i2c/mux/pca954x/Makefile b/sys/modules/i2c/mux/pca954x/Makefile new file mode 100644 index 000000000000..622b327204c5 --- /dev/null +++ b/sys/modules/i2c/mux/pca954x/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/mux + +KMOD= pca954x +SRCS= pca954x.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 <bsd.kmod.mk>