pcf8591: driver for adc/dac with i2c interface
This commit is contained in:
parent
200bc58953
commit
a60b304697
@ -420,6 +420,7 @@ MAN= aac.4 \
|
||||
pccard.4 \
|
||||
pccbb.4 \
|
||||
pcf.4 \
|
||||
pcf8591.4 \
|
||||
${_pchtherm.4} \
|
||||
pci.4 \
|
||||
pcib.4 \
|
||||
|
122
share/man/man4/pcf8591.4
Normal file
122
share/man/man4/pcf8591.4
Normal file
@ -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).
|
@ -1855,6 +1855,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
|
||||
|
271
sys/dev/iicbus/pcf8591.c
Normal file
271
sys/dev/iicbus/pcf8591.c
Normal file
@ -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
|
@ -20,6 +20,7 @@ SUBDIR = \
|
||||
jedec_dimm \
|
||||
mux \
|
||||
nxprtc \
|
||||
pcf8591 \
|
||||
rtc8583 \
|
||||
s35390a \
|
||||
smb \
|
||||
|
17
sys/modules/i2c/pcf8591/Makefile
Normal file
17
sys/modules/i2c/pcf8591/Makefile
Normal file
@ -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>
|
Loading…
Reference in New Issue
Block a user