git: d6f71e09a789 - stable/13 - pcf8591: driver for adc/dac with i2c interface
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 13 Nov 2021 09:11:05 UTC
The branch stable/13 has been updated by avg: URL: https://cgit.FreeBSD.org/src/commit/?id=d6f71e09a789f57f28d397c032ead62dc0b09c48 commit d6f71e09a789f57f28d397c032ead62dc0b09c48 Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2020-08-17 10:05:11 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-11-13 09:10:44 +0000 pcf8591: driver for adc/dac with i2c interface (cherry picked from commit a60b30469741392e66ec6fd21a8d5c4810a2d407) --- share/man/man4/Makefile | 1 + share/man/man4/pcf8591.4 | 122 ++++++++++++++++++ sys/conf/files | 1 + sys/dev/iicbus/pcf8591.c | 271 +++++++++++++++++++++++++++++++++++++++ sys/modules/i2c/Makefile | 1 + sys/modules/i2c/pcf8591/Makefile | 17 +++ 6 files changed, 413 insertions(+) diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 2f3283e14573..e80b451f20b2 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -424,6 +424,7 @@ MAN= aac.4 \ pccard.4 \ pccbb.4 \ pcf.4 \ + pcf8591.4 \ ${_pchtherm.4} \ pci.4 \ pcib.4 \ diff --git a/share/man/man4/pcf8591.4 b/share/man/man4/pcf8591.4 new file mode 100644 index 000000000000..2876ad057f90 --- /dev/null +++ b/share/man/man4/pcf8591.4 @@ -0,0 +1,122 @@ +.\" +.\" 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: head/share/man/man4/cp2112.4 364144 2020-08-12 09:42:05Z avg $ +.\" +.Dd November 6, 2021 +.Dt PCF8591 4 +.Os +.Sh NAME +.Nm pcf8591 +.Nd driver for the PCF8591 8-bit A/D and D/A converter +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcf8591" +.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 +pcf8591_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports reading four inputs and setting one output over I2C. +The hardware supports configuring the input lines as: +.Bl -bullet +.It +four single-ended inputs +.It +three differential inputs (one input line is shared between all three inputs) +.It +two single-ended inputs and one differential input +.It +two differential inputs. +.El +.Pp +The +.Nm +driver reports data via +.Xr sysctl 8 +entries in the device's node in the +.Xr sysctl 8 +tree: +.Bl -tag -width inputs.%d +.It Va inputs.%d +The input level of the corresponding input in steps between 0 and 255. +Absolute voltage depends on an actual reference voltage. +.El +.Pp +On an +.Xr FDT 4 +based system the following properties must be set: +.Bl -tag -width "compatible" +.It Va compatible +Must be set to "nxp,pcf8591". +.It Va reg +The I2C address of +.Nm . +It should be in the range from 0x40 to 0x4f (7-bit). +.El +.Pp +The DTS part for a +.Nm +device usually looks like: +.Bd -literal +/ { + + ... + pcf8591adc { + compatible = "nxp,pcf8591"; + reg = <0x48>; + }; +}; +.Ed +.Sh SEE ALSO +.Xr fdt 4 , +.Xr iicbus 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver and this manual page was written by +.An Andriy Gapon Aq Mt avg@FreeBSD.org . +.Sh BUGS +The +.Nm +driver does not support changing the input configuration. +All input lines are configured as single-ended inputs. +.Pp +The +.Nm +driver does not support setting the output. +It is always disabled (tri-state). diff --git a/sys/conf/files b/sys/conf/files index 282a86d74347..92e68a0b103e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1885,6 +1885,7 @@ 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/pcf8591.c optional pcf8591 dev/iicbus/rtc8583.c optional rtc8583 dev/iicbus/rtc/rx8803.c optional rx8803 iicbus fdt dev/iicbus/s35390a.c optional s35390a diff --git a/sys/dev/iicbus/pcf8591.c b/sys/dev/iicbus/pcf8591.c new file mode 100644 index 000000000000..ff769b368d91 --- /dev/null +++ b/sys/dev/iicbus/pcf8591.c @@ -0,0 +1,271 @@ +/*- + * 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/sysctl.h> +#include <sys/systm.h> + +#include <machine/bus.h> + +#include <dev/iicbus/iicbus.h> +#include <dev/iicbus/iiconf.h> + +#ifdef FDT +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif + +/* + * Driver for PCF8591 I2C 8-bit ADC and DAC. + */ +#define CTRL_CH_SELECT_MASK 0x03 +#define CTRL_AUTOINC_EN 0x04 +#define CTRL_CH_CONFIG_MASK 0x30 +#define CTRL_CH_CONFIG_4_SINGLE 0x00 +#define CTRL_CH_CONFIG_3_DIFF 0x10 +#define CTRL_CH_CONFIG_2_SINGLE_1_DIFF 0x20 +#define CTRL_CH_CONFIG_2_DIFF 0x30 +#define CTRL_OUTPUT_EN 0x40 + +struct pcf8591_softc { + device_t sc_dev; + int sc_ch_count; + uint8_t sc_addr; + uint8_t sc_cfg; + uint8_t sc_output; +}; + +#ifdef FDT +static struct ofw_compat_data compat_data[] = { + { "nxp,pcf8591", true }, + { NULL, false } +}; +#endif + +static int +pcf8591_set_config(device_t dev) +{ + + struct iic_msg msg; + uint8_t data[2]; + struct pcf8591_softc *sc; + int error; + + sc = device_get_softc(dev); + data[0] = sc->sc_cfg; + data[1] = sc->sc_output; + msg.slave = sc->sc_addr; + msg.flags = IIC_M_WR; + msg.len = nitems(data); + msg.buf = data; + + error = iicbus_transfer_excl(dev, &msg, 1, IIC_INTRWAIT); + return (error); +} + +static int +pcf8591_get_reading(device_t dev, uint8_t *reading) +{ + struct iic_msg msg; + struct pcf8591_softc *sc; + int error; + + sc = device_get_softc(dev); + + msg.slave = sc->sc_addr; + msg.flags = IIC_M_RD; + msg.len = 1; + msg.buf = reading; + + error = iicbus_transfer_excl(dev, &msg, 1, IIC_INTRWAIT); + return (error); +} + +static int +pcf8591_select_channel(device_t dev, int channel) +{ + struct pcf8591_softc *sc; + int error; + uint8_t unused; + + sc = device_get_softc(dev); + if (channel >= sc->sc_ch_count) + return (EINVAL); + sc->sc_cfg &= ~CTRL_CH_SELECT_MASK; + sc->sc_cfg += channel; + error = pcf8591_set_config(dev); + if (error != 0) + return (error); + + /* + * The next read is still for the old channel, + * so do it and discard. + */ + error = pcf8591_get_reading(dev, &unused); + return (error); +} + +static int +pcf8591_channel_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct pcf8591_softc *sc; + device_t dev; + int error, channel, val; + uint8_t reading; + + dev = arg1; + channel = arg2; + sc = device_get_softc(dev); + + if (req->oldptr != NULL) { + error = pcf8591_select_channel(dev, channel); + if (error != 0) + return (EIO); + error = pcf8591_get_reading(dev, &reading); + if (error != 0) + return (EIO); + val = reading; + } + error = sysctl_handle_int(oidp, &val, 0, req); + return (error); +} + +static void +pcf8591_start(void *arg) +{ + device_t dev; + struct pcf8591_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree_node; + struct sysctl_oid_list *tree; + struct sysctl_oid *inputs_node; + struct sysctl_oid_list *inputs; + int error; + + sc = arg; + dev = sc->sc_dev; + + /* + * Set initial -- and, for the time being, fixed -- configuration. + * Channel auto-incrementi is disabled, although it could be more + * performant and precise for bulk channel queries. + * The inputs are configured as four single channels. + * The output is disabled. + */ + sc->sc_cfg = 0; + sc->sc_output = 0; + sc->sc_ch_count = 4; + error = pcf8591_set_config(dev); + + ctx = device_get_sysctl_ctx(dev); + tree_node = device_get_sysctl_tree(dev); + tree = SYSCTL_CHILDREN(tree_node); + + inputs_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "inputs", + CTLTYPE_NODE, NULL, "Input channels"); + inputs = SYSCTL_CHILDREN(inputs_node); + for (int i = 0; i < sc->sc_ch_count; i++) { + char buf[4]; + + snprintf(buf, sizeof(buf), "%d", i); + SYSCTL_ADD_PROC(ctx, inputs, OID_AUTO, buf, + CTLTYPE_INT | CTLFLAG_RD, dev, i, + pcf8591_channel_sysctl, "I", "Input level from 0 to 255 " + "(relative to Vref)"); + } +} + +static int +pcf8591_probe(device_t dev) +{ +#ifdef FDT + if (!ofw_bus_status_okay(dev)) + return (ENXIO); +#endif + device_set_desc(dev, "PCF8591 8-bit ADC / DAC"); +#ifdef FDT + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (BUS_PROBE_GENERIC); +#endif + return (BUS_PROBE_NOWILDCARD); +} + +static int +pcf8591_attach(device_t dev) +{ + struct pcf8591_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_addr = iicbus_get_addr(dev); + + /* + * We have to wait until interrupts are enabled. Usually I2C read + * and write only works when the interrupts are available. + */ + config_intrhook_oneshot(pcf8591_start, sc); + return (0); +} + +static int +pcf8591_detach(device_t dev) +{ + return (0); +} + +static device_method_t pcf8591_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcf8591_probe), + DEVMETHOD(device_attach, pcf8591_attach), + DEVMETHOD(device_detach, pcf8591_detach), + + DEVMETHOD_END +}; + +static driver_t pcf8591_driver = { + "pcf8591", + pcf8591_methods, + sizeof(struct pcf8591_softc) +}; + +static devclass_t pcf8591_devclass; + +DRIVER_MODULE(pcf8591, iicbus, pcf8591_driver, pcf8591_devclass, 0, 0); +MODULE_DEPEND(pcf8591, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_VERSION(pcf8591, 1); +#ifdef FDT +IICBUS_FDT_PNP_INFO(compat_data); +#endif diff --git a/sys/modules/i2c/Makefile b/sys/modules/i2c/Makefile index edd92943c6c1..b79e2a98fc7d 100644 --- a/sys/modules/i2c/Makefile +++ b/sys/modules/i2c/Makefile @@ -20,6 +20,7 @@ SUBDIR = \ jedec_dimm \ mux \ nxprtc \ + pcf8591 \ rtc8583 \ s35390a \ smb \ diff --git a/sys/modules/i2c/pcf8591/Makefile b/sys/modules/i2c/pcf8591/Makefile new file mode 100644 index 000000000000..82148867a00f --- /dev/null +++ b/sys/modules/i2c/pcf8591/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus +KMOD= pcf8591 +SRCS= pcf8591.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>