summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2006-02-10 05:28:57 +0000
committerDamien Miller <djm@cvs.openbsd.org>2006-02-10 05:28:57 +0000
commit809097c8d3709b9a62c5cd535cc3816444396e50 (patch)
treeb73d72906903e14f30eb7807265e44385f44ce1f /sys
parentfde272a8f6bdd419c31b5dc3ec13e0e93e579535 (diff)
driver for Philips PCF8591 I2C ADC sensor. Because of the mindlessness
of this chip, it is impossible to probe for and we need to be told where it is and how to understand its output. We get this information from openfirmware, but this restricts the use of the driver to sparc64 for now. Only sensors that OFW identifies as temperature sensors are supported at present. prodded by dlg@ "get it in" deraadt@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/sparc64/conf/GENERIC3
-rw-r--r--sys/arch/sparc64/conf/files.sparc6412
-rw-r--r--sys/arch/sparc64/dev/pcf8591_ofw.c213
3 files changed, 226 insertions, 2 deletions
diff --git a/sys/arch/sparc64/conf/GENERIC b/sys/arch/sparc64/conf/GENERIC
index 06943343fbd..976d7ddf339 100644
--- a/sys/arch/sparc64/conf/GENERIC
+++ b/sys/arch/sparc64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.140 2006/02/01 11:03:33 dlg Exp $
+# $OpenBSD: GENERIC,v 1.141 2006/02/10 05:28:56 djm Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -323,6 +323,7 @@ lmenv* at iic? # National Semiconductor LM87
lmtemp* at iic? # NS LM75/LM77 temperature sensor
maxds* at iic? # Maxim DS1631
maxtmp* at iic? # Maxim MAX6642/MAX6690
+pcfadc* at iic? # Philips PCF8591
# Framebuffers
agten* at sbus? # Fujitsu AG-10e framebuffer
diff --git a/sys/arch/sparc64/conf/files.sparc64 b/sys/arch/sparc64/conf/files.sparc64
index 9693d67d5de..08271b82caf 100644
--- a/sys/arch/sparc64/conf/files.sparc64
+++ b/sys/arch/sparc64/conf/files.sparc64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sparc64,v 1.64 2006/02/01 11:03:33 dlg Exp $
+# $OpenBSD: files.sparc64,v 1.65 2006/02/10 05:28:56 djm Exp $
# $NetBSD: files.sparc64,v 1.50 2001/08/10 20:53:50 eeh Exp $
# maxpartitions must be first item in files.${ARCH}
@@ -262,3 +262,13 @@ file arch/sparc64/dev/ofwi2c.c i2cbus
# Machine-independent USB drivers
#
include "dev/usb/files.usb"
+
+#
+# Machine-dependent I2C drivers
+#
+
+# PCF8591 ADC I2C Sensor (needs ofw, so sparc64 only for now)
+device pcfadc
+attach pcfadc at i2c
+file arch/sparc64/dev/pcf8591_ofw.c pcfadc
+
diff --git a/sys/arch/sparc64/dev/pcf8591_ofw.c b/sys/arch/sparc64/dev/pcf8591_ofw.c
new file mode 100644
index 00000000000..bc25659b96e
--- /dev/null
+++ b/sys/arch/sparc64/dev/pcf8591_ofw.c
@@ -0,0 +1,213 @@
+/* $OpenBSD: pcf8591_ofw.c,v 1.1 2006/02/10 05:28:56 djm Exp $ */
+
+/*
+ * Copyright (c) 2006 Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/i2c/i2cvar.h>
+
+#define PCF8591_CHANNELS 4
+
+struct pcfadc_channel {
+ u_int chan_num;
+ struct sensor chan_sensor;
+};
+
+struct pcfadc_softc {
+ struct device sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+ u_char sc_xlate[256];
+ u_int sc_nchan;
+ struct pcfadc_channel sc_channels[PCF8591_CHANNELS];
+};
+
+int pcfadc_match(struct device *, void *, void *);
+void pcfadc_attach(struct device *, struct device *, void *);
+void pcfadc_refresh(void *);
+
+struct cfattach pcfadc_ca = {
+ sizeof(struct pcfadc_softc), pcfadc_match, pcfadc_attach
+};
+
+struct cfdriver pcfadc_cd = {
+ NULL, "pcfadc", DV_DULL
+};
+
+int
+pcfadc_match(struct device *parent, void *match, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+
+ if (strcmp(ia->ia_name, "i2cpcf,8591") != 0)
+ return (0);
+
+ return (1);
+}
+
+void
+pcfadc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct pcfadc_softc *sc = (struct pcfadc_softc *)self;
+ u_char chanuse[PCF8591_CHANNELS * 4], desc[PCF8591_CHANNELS * 32];
+ u_char *cp;
+ u_int8_t junk[PCF8591_CHANNELS + 1];
+ u_int32_t transinfo[PCF8591_CHANNELS * 4];
+ struct i2c_attach_args *ia = aux;
+ int dlen, clen, tlen, node = *(int *)ia->ia_cookie;
+ u_int i;
+
+ if ((dlen = OF_getprop(node, "channels-description", desc,
+ sizeof(desc))) < 0) {
+ printf(": couldn't find \"channels-description\" property\n");
+ return;
+ }
+ if (dlen > sizeof(desc) || desc[dlen - 1] != '\0') {
+ printf(": bad \"channels-description\" property\n");
+ return;
+ }
+ if ((clen = OF_getprop(node, "channels-in-use", chanuse,
+ sizeof(chanuse))) < 0) {
+ printf(": couldn't find \"channels-in-use\" property\n");
+ return;
+ }
+ if ((clen % 4) != 0) {
+ printf(": invalid \"channels-in-use\" length %d\n", clen);
+ return;
+ }
+ sc->sc_nchan = clen / 4;
+ if (sc->sc_nchan > PCF8591_CHANNELS) {
+ printf(": invalid number of channels (%d)\n", sc->sc_nchan);
+ return;
+ }
+
+ if ((tlen = OF_getprop(node, "tables", sc->sc_xlate,
+ sizeof(sc->sc_xlate))) < 0) {
+ printf(": couldn't find \"tables\" property\n");
+ return;
+ }
+ /* We only support complete, single width tables */
+ if (tlen != 256) {
+ printf(": invalid \"tables\" length %d\n", tlen);
+ return;
+ }
+
+ if ((tlen = OF_getprop(node, "translation", transinfo,
+ sizeof(transinfo))) < 0) {
+ printf(": couldn't find \"translation\" property\n");
+ return;
+ }
+ if (tlen != (sc->sc_nchan * 4 * 4)) {
+ printf(": invalid \"translation\" length %d\n", tlen);
+ return;
+ }
+
+ cp = desc;
+ for (i = 0; i < sc->sc_nchan; i++) {
+ struct pcfadc_channel *chp = &sc->sc_channels[i];
+
+ bzero(&chp->chan_sensor, sizeof(chp->chan_sensor));
+ strlcpy(chp->chan_sensor.device, sc->sc_dev.dv_xname,
+ sizeof(chp->chan_sensor.device));
+ chp->chan_sensor.type = SENSOR_TEMP;
+
+ if (cp >= desc + dlen) {
+ printf(": invalid \"channels-description\"\n");
+ return;
+ }
+ strlcpy(chp->chan_sensor.desc, cp,
+ sizeof(chp->chan_sensor.desc));
+ cp += strlen(cp) + 1;
+
+ /*
+ * We only support input temperature channels, with
+ * valid channel numbers, and basic (unscaled) translation
+ *
+ * XXX TODO: support voltage (type 2) channels and type 4
+ * (scaled) translation tables
+ */
+ if (chanuse[(i * 4)] > PCF8591_CHANNELS || /* channel # */
+ chanuse[(i * 4) + 1] != 0 || /* dir == input */
+ chanuse[(i * 4) + 2] != 1 || /* type == temp */
+ transinfo[(i * 4)] != 3 || /* xlate == table */
+ transinfo[(i * 4) + 2] != 0 || /* no xlate offset */
+ transinfo[(i * 4) + 3] != 0x100) { /* xlate tbl length */
+ printf(": unsupported sensor %d\n", i);
+ return;
+ }
+ chp->chan_num = chanuse[(i * 4)];
+ }
+
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+
+ iic_acquire_bus(sc->sc_tag, 0);
+
+ /* Try a read now, so we can fail if it doesn't work */
+ if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+ NULL, 0, junk, sc->sc_nchan + 1, 0)) {
+ printf(": read failed\n");
+ iic_release_bus(sc->sc_tag, 0);
+ return;
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+
+ /* Initialize sensor data. */
+ for (i = 0; i < sc->sc_nchan; i++)
+ if (!(sc->sc_channels[i].chan_sensor.flags & SENSOR_FINVALID))
+ sensor_add(&sc->sc_channels[i].chan_sensor);
+
+ if (sensor_task_register(sc, pcfadc_refresh, 5)) {
+ printf(": unable to register update task\n");
+ return;
+ }
+
+ printf("\n");
+}
+
+void
+pcfadc_refresh(void *arg)
+{
+ struct pcfadc_softc *sc = arg;
+ u_int i;
+ u_int8_t data[PCF8591_CHANNELS + 1];
+
+ iic_acquire_bus(sc->sc_tag, 0);
+ /* NB: first byte out is stale, so read num_channels + 1 */
+ if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+ NULL, 0, data, PCF8591_CHANNELS + 1, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ return;
+ }
+ iic_release_bus(sc->sc_tag, 0);
+
+ /* XXX: so far this only supports temperature channels */
+ for (i = 0; i < sc->sc_nchan; i++) {
+ struct pcfadc_channel *chp = &sc->sc_channels[i];
+
+ if ((chp->chan_sensor.flags & SENSOR_FINVALID) != 0)
+ continue;
+ chp->chan_sensor.value = 273150000 + 1000000 *
+ sc->sc_xlate[data[1 + chp->chan_num]];
+ }
+}
+