Module Name: src
Committed By: christos
Date: Thu Dec 28 23:23:47 UTC 2017
Modified Files:
src/sys/dev/i2c: files.i2c
Added Files:
src/sys/dev/i2c: am2315.c am2315reg.h am2315var.h si70xx.c si70xxreg.h
si70xxvar.h
Log Message:
PR/52848: Brad Spencer: Two environment sensor drivers: AM2315 and SI70xx
XXX: Please check that my refactoring did not break them!
To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/am2315.c src/sys/dev/i2c/am2315reg.h \
src/sys/dev/i2c/am2315var.h src/sys/dev/i2c/si70xx.c \
src/sys/dev/i2c/si70xxreg.h src/sys/dev/i2c/si70xxvar.h
cvs rdiff -u -r1.79 -r1.80 src/sys/dev/i2c/files.i2c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.79 src/sys/dev/i2c/files.i2c:1.80
--- src/sys/dev/i2c/files.i2c:1.79 Sun Dec 10 12:05:54 2017
+++ src/sys/dev/i2c/files.i2c Thu Dec 28 18:23:47 2017
@@ -1,4 +1,4 @@
-# $NetBSD: files.i2c,v 1.79 2017/12/10 17:05:54 bouyer Exp $
+# $NetBSD: files.i2c,v 1.80 2017/12/28 23:23:47 christos Exp $
obsolete defflag opt_i2cbus.h I2C_SCAN
define i2cbus { }
@@ -280,6 +280,16 @@ device tcagpio
attach tcagpio at iic
file dev/i2c/tcagpio.c tcagpio
+# Silicon Lab SI7013/SI7020/SI7021 Temperature and Humidity sensor
+device si70xxtemp
+attach si70xxtemp at iic
+file dev/i2c/si70xx.c si70xxtemp
+
+# Aosong AM2315 Temperature and Humidity sensor
+device am2315temp
+attach am2315temp at iic
+file dev/i2c/am2315.c am2315temp
+
# Silergy SY8106A regulator
device sy8106a
attach sy8106a at iic
Added files:
Index: src/sys/dev/i2c/am2315.c
diff -u /dev/null src/sys/dev/i2c/am2315.c:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/am2315.c Thu Dec 28 18:23:47 2017
@@ -0,0 +1,531 @@
+/* $NetBSD: am2315.c,v 1.1 2017/12/28 23:23:47 christos Exp $ */
+
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: am2315.c,v 1.1 2017/12/28 23:23:47 christos Exp $");
+
+/*
+ * Driver for the Aosong AM2315
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/am2315reg.h>
+#include <dev/i2c/am2315var.h>
+
+static uint16_t am2315_crc(uint8_t *, size_t);
+static int am2315_poke(struct am2315_sc *);
+static int am2315_poke_m(i2c_tag_t, i2c_addr_t, const char *, bool);
+static int am2315_match(device_t, cfdata_t, void *);
+static void am2315_attach(device_t, device_t, void *);
+static int am2315_detach(device_t, int);
+static void am2315_refresh(struct sysmon_envsys *, envsys_data_t *);
+static int am2315_verify_sysctl(SYSCTLFN_ARGS);
+
+#define AM2315_DEBUG
+#ifdef AM2315_DEBUG
+#define DPRINTF(s, l, x) \
+ do { \
+ if (l <= s->sc_am2315debug) \
+ printf x; \
+ } while (/*CONSTCOND*/0)
+#else
+#define DPRINTF(s, l, x)
+#endif
+
+CFATTACH_DECL_NEW(am2315temp, sizeof(struct am2315_sc),
+ am2315_match, am2315_attach, am2315_detach, NULL);
+
+static struct am2315_sensor am2315_sensors[] = {
+ {
+ .desc = "humidity",
+ .type = ENVSYS_SRELHUMIDITY,
+ },
+ {
+ .desc = "temperature",
+ .type = ENVSYS_STEMP,
+ }
+};
+
+static uint16_t
+am2315_crc(uint8_t *data, size_t len)
+{
+ uint16_t crc = 0xffff;
+
+ for (size_t j = 0; j < len; j++) {
+ crc ^= data[j];
+ for (size_t i = 0; i < 8; i++) {
+ if (crc & 0x01) {
+ crc >>= 1;
+ crc ^= 0xA001;
+ } else {
+ crc >>= 1;
+ }
+ }
+ }
+
+ return crc;
+}
+
+int
+am2315_verify_sysctl(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < 0)
+ return EINVAL;
+
+ *(int *) rnode->sysctl_data = t;
+
+ return 0;
+}
+
+static int
+am2315_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t dir, uint8_t cmd,
+ uint8_t clen, uint8_t *buf, size_t blen)
+{
+ uint8_t command[] = { dir, cmd, clen };
+ if (buf)
+ memset(buf, 0xff, blen);
+ uint8_t reg = dir == AM2315_READ_REGISTERS ?
+ I2C_OP_READ_WITH_STOP : I2C_OP_WRITE_WITH_STOP;
+
+ return iic_exec(tag, reg, addr, command,
+ __arraycount(command), buf, blen, 0);
+}
+
+static int
+am2315_read_regs(struct am2315_sc *sc, uint8_t cmd, uint8_t clen, uint8_t *buf,
+ size_t blen)
+{
+ return am2315_cmd(sc->sc_tag, sc->sc_addr, AM2315_READ_REGISTERS,
+ cmd, clen, buf, blen);
+}
+
+static int
+am2315_poke(struct am2315_sc *sc)
+{
+ return am2315_poke_m(sc->sc_tag, sc->sc_addr, device_xname(sc->sc_dev),
+ sc->sc_am2315debug >= 2);
+}
+
+static int
+am2315_poke_m(i2c_tag_t tag, i2c_addr_t addr, const char *name, bool debug)
+{
+ uint8_t buf[5];
+ int error;
+
+ error = am2315_cmd(tag, addr, AM2315_WRITE_REGISTERS,
+ AM2315_REGISTER_HIGH_USER1, 1, NULL, 0);
+ if (debug)
+ printf("%s: poke 1: %d\n", name, error);
+
+ if (error != 0)
+ delay(2800);
+
+ error = am2315_cmd(tag, addr, AM2315_READ_REGISTERS,
+ AM2315_REGISTER_STATUS, 1, buf, __arraycount(buf));
+ if (debug)
+ printf("%s: poke 2: %d %02x %02x %02x %02x%02x\n", name, error,
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ if (error != 0)
+ delay(2800);
+ return error;
+}
+
+static int
+am2315_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct i2c_attach_args *ia;
+ int rv;
+ const bool matchdebug = false;
+
+ ia = aux;
+
+ if (ia->ia_name) {
+ /* direct config - check name */
+ if (strcmp(ia->ia_name, "am2315temp") != 0)
+ return 0;
+ } else {
+ /* indirect config - check for configured address */
+ if (ia->ia_addr != AM2315_TYPICAL_ADDR)
+ return 0;
+ }
+
+ /*
+ * Check to see if something is really at this i2c address. This will
+ * keep phantom devices from appearing
+ */
+ if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
+ if (matchdebug)
+ printf("in match acquire bus failed\n");
+ return 0;
+ }
+
+ if ((rv = am2315_poke_m(ia->ia_tag, ia->ia_addr, __func__, matchdebug))
+ != 0) {
+ if (matchdebug)
+ printf("match rv poke %d\n", rv);
+ iic_release_bus(ia->ia_tag, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+am2315_attach(device_t parent, device_t self, void *aux)
+{
+ struct am2315_sc *sc = device_private(self);
+ struct i2c_attach_args *ia = aux;
+ uint8_t buf[11];
+ int error;
+ uint16_t crc, readcrc, model;
+ uint8_t chipver;
+ uint32_t id;
+ bool modelgood, chipvergood, idgood;
+
+ sc->sc_dev = self;
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+ sc->sc_am2315debug = 0;
+ sc->sc_readcount = 2;
+ sc->sc_readticks = 100;
+ sc->sc_sme = NULL;
+
+ aprint_normal("\n");
+
+ mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_waitmutex, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&sc->sc_condwait, "am2315wait");
+
+ sc->sc_numsensors = __arraycount(am2315_sensors);
+
+ if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
+ aprint_error_dev(self, "unable to create sysmon structure\n");
+ return;
+ }
+
+ /* XXX: sysctl's not destroyed on failure */
+ const struct sysctlnode *cnode;
+ int sysctlroot_num;
+ if ((error = sysctl_createv(&sc->sc_am2315log, 0, NULL, &cnode, 0,
+ CTLTYPE_NODE, device_xname(self),
+ SYSCTL_DESCR("am2315 controls"), NULL, 0, NULL, 0, CTL_HW,
+ CTL_CREATE, CTL_EOL)) != 0)
+ goto badsysctl;
+ sysctlroot_num = cnode->sysctl_num;
+
+#ifdef AM2315_DEBUG
+ if ((error = sysctl_createv(&sc->sc_am2315log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
+ SYSCTL_DESCR("Debug level"), am2315_verify_sysctl, 0,
+ &sc->sc_am2315debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ goto badsysctl;
+
+#endif
+
+ if ((error = sysctl_createv(&sc->sc_am2315log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "readcount",
+ SYSCTL_DESCR("Number of times to read the sensor"),
+ am2315_verify_sysctl, 0, &sc->sc_readcount, 0, CTL_HW,
+ sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ goto badsysctl;
+
+ if ((error = sysctl_createv(&sc->sc_am2315log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "readticks",
+ SYSCTL_DESCR("Number of ticks between reads"),
+ am2315_verify_sysctl, 0, &sc->sc_readticks, 0, CTL_HW,
+ sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ goto badsysctl;
+
+ if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) {
+ aprint_error_dev(self,
+ "Could not acquire iic bus: %d\n", error);
+ return;
+ }
+ am2315_poke(sc);
+
+#define DUMP(a) \
+ DPRINTF(sc, 2, ("%s: read cmd+len+%s+crcl+crch values: %02x %02x " \
+ "%02x%02x %02x%02x -- %02x%02x%02x%02x%02x -- %04x %04x\n", a, \
+ device_xname(self), buf[0], buf[1], buf[2], buf[3], \
+ buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], \
+ buf[10], crc, readcrc))
+
+ error = am2315_read_regs(sc, AM2315_REGISTER_HIGH_MODEL, 2, buf, 6);
+ if (error)
+ aprint_error_dev(sc->sc_dev, "read model: %d\n", error);
+ readcrc = buf[5] << 8 | buf[4];
+ crc = am2315_crc(buf, 4);
+ DUMP("modh+modl");
+ model = buf[2] << 8 | buf[3];
+ modelgood = buf[0] == AM2315_READ_REGISTERS && buf[1] == 2
+ && crc == readcrc;
+
+ error = am2315_read_regs(sc, AM2315_REGISTER_VERSION, 1, buf, 5);
+ if (error != 0)
+ aprint_error_dev(self, "read chipver: %d\n", error);
+ readcrc = buf[4] << 8 | buf[3];
+ crc = am2315_crc(buf, 3);
+ DUMP("ver");
+ chipver = buf[2];
+ chipvergood = buf[0] == AM2315_READ_REGISTERS && buf[1] == 1
+ && crc == readcrc;
+
+ error = am2315_read_regs(sc, AM2315_REGISTER_ID_PT_24_31, 2, buf, 6);
+ if (error != 0)
+ aprint_error_dev(self, "read id 1: %d\n", error);
+ readcrc = buf[5] << 8 | buf[4];
+ crc = am2315_crc(buf, 4);
+ DUMP("id1+id2");
+ id = buf[2] << 8 | buf[3];
+ idgood = buf[0] == AM2315_READ_REGISTERS && buf[1] == 2
+ && crc == readcrc;
+
+ error = am2315_read_regs(sc, AM2315_REGISTER_ID_PT_8_15, 2, buf, 6);
+ if (error != 0)
+ aprint_error_dev(self, "read id 2: %d\n", error);
+ readcrc = buf[5] << 8 | buf[4];
+ crc = am2315_crc(buf, 4);
+ DUMP("id3+id4");
+ id = id << 8 | buf[2];
+ id = id << 8 | buf[3];
+ idgood = buf[0] == AM2315_READ_REGISTERS && buf[1] == 2
+ && crc == readcrc && idgood;
+
+ iic_release_bus(sc->sc_tag, 0);
+
+ for (int i = 0; i < sc->sc_numsensors; i++) {
+ strlcpy(sc->sc_sensors[i].desc, am2315_sensors[i].desc,
+ sizeof(sc->sc_sensors[i].desc));
+
+ sc->sc_sensors[i].units = am2315_sensors[i].type;
+ sc->sc_sensors[i].state = ENVSYS_SINVALID;
+
+ DPRINTF(sc, 2, ("am2315_attach: registering sensor %d (%s)\n",
+ i, sc->sc_sensors[i].desc));
+
+ error = sysmon_envsys_sensor_attach(sc->sc_sme,
+ &sc->sc_sensors[i]);
+ if (error) {
+ aprint_error_dev(self, "unable to attach sensor %d\n",
+ error);
+ goto badregister;
+ }
+ }
+
+ sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+ sc->sc_sme->sme_cookie = sc;
+ sc->sc_sme->sme_refresh = am2315_refresh;
+
+ DPRINTF(sc, 2, ("am2315_attach: registering with envsys\n"));
+
+ error = sysmon_envsys_register(sc->sc_sme);
+ if (error) {
+ aprint_error_dev(self, "unable to register with sysmon %d\n",
+ error);
+ goto badregister;
+ }
+ aprint_normal_dev(self, "Aosong AM2315, Model: %04x%s Version: %02x%s"
+ " ID: %08x%s",
+ model, (modelgood ? "," : "(inaccurate),"),
+ chipver, (chipvergood ? "," : "(inaccurate),"),
+ id, (idgood ? "\n" : "(inaccurate)\n"));
+ return;
+
+badsysctl:
+ aprint_error_dev(self, ": can't setup sysctl tree (%d)\n", error);
+ return;
+badregister:
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+}
+
+static void
+am2315_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
+{
+ struct am2315_sc *sc;
+ uint8_t buf[11], thecommand;
+ uint16_t crc, readcrc;
+ int error, rv;
+ uint32_t val32;
+ bool istempneg = false;
+
+ sc = sme->sme_cookie;
+ edata->state = ENVSYS_SINVALID;
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error == 0) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ switch (edata->sensor) {
+ case AM2315_HUMIDITY_SENSOR:
+ thecommand = AM2315_REGISTER_HIGH_RH;
+ break;
+ case AM2315_TEMP_SENSOR:
+ thecommand = AM2315_REGISTER_HIGH_TEMP;
+ break;
+ default:
+ DPRINTF(sc, 2, ("%s: bad sensor %d\n",
+ device_xname(sc->sc_dev), edata->sensor));
+ goto out;
+ }
+
+ for (int count = 0; ;) {
+ am2315_poke(sc);
+
+ if ((error = am2315_read_regs(sc, thecommand, 2, buf, 6)) != 0)
+ aprint_error_dev(sc->sc_dev,
+ "Read sensor %d error: %d\n", edata->sensor, error);
+
+ readcrc = buf[5] << 8 | buf[4];
+ crc = am2315_crc(buf, 4);
+
+ DPRINTF(sc, 2, ("%s: read cmd+len+dh+dl+crch+crcl values: %02x"
+ " %02x %02x%02x %02x%02x -- %04x %04x -- %d\n",
+ device_xname(sc->sc_dev), buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], crc, readcrc, count));
+ if (++count == sc->sc_readcount)
+ break;
+ mutex_enter(&sc->sc_waitmutex);
+ rv = cv_timedwait(&sc->sc_condwait, &sc->sc_waitmutex,
+ sc->sc_readticks);
+ DPRINTF(sc, 2, ("%s: wait rv: %d\n", device_xname(sc->sc_dev),
+ rv));
+ mutex_exit(&sc->sc_waitmutex);
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+
+ if (buf[0] != AM2315_READ_REGISTERS || buf[1] != 2 ||
+ crc != readcrc) {
+ DPRINTF(sc, 2, ("%s: Invalid sensor data for %d\n",
+ device_xname(sc->sc_dev), edata->sensor));
+ goto out;
+ }
+
+ switch (edata->sensor) {
+ case AM2315_HUMIDITY_SENSOR:
+ val32 = buf[2] << 8 | buf[3];
+ val32 = val32 * 100000;
+ DPRINTF(sc, 2, ("%s: read translated values RH: %x\n",
+ device_xname(sc->sc_dev), val32));
+ edata->value_cur = val32;
+ edata->state = ENVSYS_SVALID;
+ break;
+ case AM2315_TEMP_SENSOR:
+ istempneg = (buf[2] & AM2315_TEMP_NEGATIVE);
+ buf[2] = buf[2] & (~AM2315_TEMP_NEGATIVE);
+ val32 = buf[2] << 8 | buf[3];
+ if (istempneg) {
+ val32 = 273150000 - (val32 * 100000);
+ } else {
+ val32 = (val32 * 100000) + 273150000;
+ }
+ DPRINTF(sc, 2, ("%s: read translated values TEMP: %x\n",
+ device_xname(sc->sc_dev), val32));
+ edata->value_cur = val32;
+ edata->state = ENVSYS_SVALID;
+ break;
+ default:
+ panic("bad sensor %d\n", edata->sensor);
+ }
+out:
+ mutex_exit(&sc->sc_mutex);
+}
+
+static int
+am2315_detach(device_t self, int flags)
+{
+ struct am2315_sc *sc = device_private(self);
+
+ mutex_enter(&sc->sc_mutex);
+
+ /* Remove the sensors */
+ if (sc->sc_sme != NULL) {
+ sysmon_envsys_unregister(sc->sc_sme);
+ sc->sc_sme = NULL;
+ }
+ mutex_exit(&sc->sc_mutex);
+
+ /* Destroy the wait cond */
+ cv_destroy(&sc->sc_condwait);
+
+ /* Remove the sysctl tree */
+ sysctl_teardown(&sc->sc_am2315log);
+
+ /* Remove the mutex */
+ mutex_destroy(&sc->sc_waitmutex);
+ mutex_destroy(&sc->sc_mutex);
+
+ return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, am2315temp, "i2cexec,sysmon_envsys");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+am2315temp_modcmd(modcmd_t cmd, void *opaque)
+{
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ return config_init_component(cfdriver_ioconf_am2315temp,
+ cfattach_ioconf_am2315temp, cfdata_ioconf_am2315temp);
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ return config_fini_component(cfdriver_ioconf_am2315temp,
+ cfattach_ioconf_am2315temp, cfdata_ioconf_am2315temp);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
Index: src/sys/dev/i2c/am2315reg.h
diff -u /dev/null src/sys/dev/i2c/am2315reg.h:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/am2315reg.h Thu Dec 28 18:23:47 2017
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DEV_I2C_AM2315REG_H_
+#define _DEV_I2C_AM2315REG_H_
+
+#define AM2315_TYPICAL_ADDR 0x5c
+
+#define AM2315_READ_REGISTERS 0x03
+#define AM2315_WRITE_REGISTERS 0x10
+
+#define AM2315_REGISTER_HIGH_RH 0x00
+#define AM2315_REGISTER_LOW_RH 0x01
+#define AM2315_REGISTER_HIGH_TEMP 0x02
+#define AM2315_REGISTER_LOW_TEMP 0x03
+#define AM2315_REGISTER_RETENTION_04 0x04
+#define AM2315_REGISTER_RETENTION_05 0x05
+#define AM2315_REGISTER_RETENTION_06 0x06
+#define AM2315_REGISTER_RETENTION_07 0x07
+#define AM2315_REGISTER_HIGH_MODEL 0x08
+#define AM2315_REGISTER_LOW_MODEL 0x09
+#define AM2315_REGISTER_VERSION 0x0a
+#define AM2315_REGISTER_ID_PT_24_31 0x0b
+#define AM2315_REGISTER_ID_PT_16_23 0x0c
+#define AM2315_REGISTER_ID_PT_8_15 0x0d
+#define AM2315_REGISTER_ID_PT_0_7 0x0e
+#define AM2315_REGISTER_STATUS 0x0f
+#define AM2315_REGISTER_HIGH_USER1 0x10
+#define AM2315_REGISTER_LOW_USER1 0x11
+#define AM2315_REGISTER_HIGH_USER2 0x12
+#define AM2315_REGISTER_LOW_USER2 0x13
+#define AM2315_REGISTER_RETENTION_14 0x14
+#define AM2315_REGISTER_RETENTION_15 0x15
+#define AM2315_REGISTER_RETENTION_16 0x16
+#define AM2315_REGISTER_RETENTION_17 0x17
+#define AM2315_REGISTER_RETENTION_18 0x18
+#define AM2315_REGISTER_RETENTION_19 0x19
+#define AM2315_REGISTER_RETENTION_1a 0x1a
+#define AM2315_REGISTER_RETENTION_1b 0x1b
+#define AM2315_REGISTER_RETENTION_1c 0x1c
+#define AM2315_REGISTER_RETENTION_1d 0x1d
+#define AM2315_REGISTER_RETENTION_1e 0x1e
+#define AM2315_REGISTER_RETENTION_1f 0x1f
+
+#define AM2315_TEMP_NEGATIVE 0x80
+
+#endif
Index: src/sys/dev/i2c/am2315var.h
diff -u /dev/null src/sys/dev/i2c/am2315var.h:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/am2315var.h Thu Dec 28 18:23:47 2017
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DEV_I2C_AM2315VAR_H_
+#define _DEV_I2C_AM2315VAR_H_
+
+#include <sys/time.h>
+
+#define AM2315_NUM_SENSORS 2
+#define AM2315_HUMIDITY_SENSOR 0
+#define AM2315_TEMP_SENSOR 1
+
+struct am2315_sc {
+ int sc_am2315debug;
+ device_t sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+ kmutex_t sc_mutex;
+ kmutex_t sc_waitmutex;
+ kcondvar_t sc_condwait;
+ int sc_numsensors;
+ struct sysmon_envsys *sc_sme;
+ envsys_data_t sc_sensors[AM2315_NUM_SENSORS];
+ struct sysctllog *sc_am2315log;
+ int sc_readcount;
+ int sc_readticks;
+};
+
+struct am2315_sensor {
+ const char *desc;
+ enum envsys_units type;
+};
+
+#endif
Index: src/sys/dev/i2c/si70xx.c
diff -u /dev/null src/sys/dev/i2c/si70xx.c:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/si70xx.c Thu Dec 28 18:23:47 2017
@@ -0,0 +1,1010 @@
+/* $NetBSD: si70xx.c,v 1.1 2017/12/28 23:23:47 christos Exp $ */
+
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: si70xx.c,v 1.1 2017/12/28 23:23:47 christos Exp $");
+
+/*
+ Driver for the Silicon Labs SI7013/SI7020/SI7021
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/mutex.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/si70xxreg.h>
+#include <dev/i2c/si70xxvar.h>
+
+
+static uint8_t si70xx_crc(uint8_t *, size_t);
+static int si70xx_poke(i2c_tag_t, i2c_addr_t, bool);
+static int si70xx_match(device_t, cfdata_t, void *);
+static void si70xx_attach(device_t, device_t, void *);
+static int si70xx_detach(device_t, int);
+static void si70xx_refresh(struct sysmon_envsys *, envsys_data_t *);
+static int si70xx_update_status(struct si70xx_sc *);
+static int si70xx_set_heateron(struct si70xx_sc *);
+static int si70xx_set_resolution(struct si70xx_sc *, size_t);
+static int si70xx_set_heatervalue(struct si70xx_sc *, size_t);
+static int si70xx_verify_sysctl(SYSCTLFN_ARGS);
+static int si70xx_verify_sysctl_resolution(SYSCTLFN_ARGS);
+static int si70xx_verify_sysctl_heateron(SYSCTLFN_ARGS);
+static int si70xx_verify_sysctl_heatervalue(SYSCTLFN_ARGS);
+
+#define SI70XX_DEBUG
+#ifdef SI70XX_DEBUG
+#define DPRINTF(s, l, x) \
+ do { \
+ if (l <= s->sc_si70xxdebug) \
+ printf x; \
+ } while (/*CONSTCOND*/0)
+#else
+#define DPRINTF(s, l, x)
+#endif
+
+CFATTACH_DECL_NEW(si70xxtemp, sizeof(struct si70xx_sc),
+ si70xx_match, si70xx_attach, si70xx_detach, NULL);
+
+static struct si70xx_sensor si70xx_sensors[] = {
+ {
+ .desc = "humidity",
+ .type = ENVSYS_SRELHUMIDITY,
+ },
+ {
+ .desc = "temperature",
+ .type = ENVSYS_STEMP,
+ }
+};
+
+static struct si70xx_resolution si70xx_resolutions[] = {
+ {
+ .text = "12bit/14bit",
+ .num = 0x00,
+ },
+ {
+ .text = "8bit/12bit",
+ .num = 0x01,
+ },
+ {
+ .text = "10bit/13bit",
+ .num = 0x80,
+ },
+ {
+ .text = "11bit/11bit",
+ .num = 0x81,
+ }
+};
+
+static const char si70xx_resolution_names[] =
+ "12bit/14bit, 8bit/12bit, 10bit/13bit, 11bit/11bit";
+
+static const int si70xx_heatervalues[] = {
+ 0xdeadbeef, 0x00, 0x01, 0x02, 0x04, 0x08, 0x0f
+};
+
+int
+si70xx_verify_sysctl(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < 0)
+ return EINVAL;
+
+ *(int *)rnode->sysctl_data = t;
+
+ return 0;
+}
+
+int
+si70xx_verify_sysctl_resolution(SYSCTLFN_ARGS)
+{
+ char buf[SI70XX_RES_NAME];
+ struct si70xx_sc *sc;
+ struct sysctlnode node;
+ int error = 0;
+ size_t i;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ (void) memcpy(buf, sc->sc_resolution, SI70XX_RES_NAME);
+ node.sysctl_data = buf;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ for (i = 0; i < __arraycount(si70xx_resolutions); i++) {
+ if (memcmp(node.sysctl_data, si70xx_resolutions[i].text,
+ SI70XX_RES_NAME) == 0)
+ break;
+ }
+
+ if (i == __arraycount(si70xx_resolutions))
+ return EINVAL;
+ (void) memcpy(sc->sc_resolution, node.sysctl_data, SI70XX_RES_NAME);
+
+ error = si70xx_set_resolution(sc, i);
+
+ return error;
+}
+
+int
+si70xx_verify_sysctl_heateron(SYSCTLFN_ARGS)
+{
+ int error;
+ bool t;
+ struct si70xx_sc *sc;
+ struct sysctlnode node;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ t = sc->sc_heateron;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ sc->sc_heateron = t;
+ error = si70xx_set_heateron(sc);
+
+ return error;
+}
+
+int
+si70xx_verify_sysctl_heatervalue(SYSCTLFN_ARGS)
+{
+ int error = 0, t;
+ struct si70xx_sc *sc;
+ struct sysctlnode node;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ t = sc->sc_heaterval;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return (error);
+
+ if (t < 1 || t >= __arraycount(si70xx_heatervalues))
+ return (EINVAL);
+
+ sc->sc_heaterval = t;
+ error = si70xx_set_heatervalue(sc, t);
+
+ return error;
+}
+
+static int
+si70xx_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
+ uint8_t clen, uint8_t *buf, size_t blen)
+{
+ uint8_t dir;
+ switch (cmd[0]) {
+ case SI70XX_READ_USER_REG_1:
+ case SI70XX_READ_HEATER_REG:
+ case SI70XX_READ_ID_PT1A:
+ case SI70XX_READ_ID_PT1B:
+ case SI70XX_READ_ID_PT2A:
+ case SI70XX_READ_ID_PT2B:
+ case SI70XX_READ_FW_VERA:
+ case SI70XX_READ_FW_VERB:
+ dir = I2C_OP_READ_WITH_STOP;
+ break;
+ case SI70XX_WRITE_USER_REG_1:
+ case SI70XX_WRITE_HEATER_REG:
+ case SI70XX_RESET:
+ dir = I2C_OP_WRITE_WITH_STOP;
+ break;
+ case SI70XX_MEASURE_RH_NOHOLD:
+ case SI70XX_MEASURE_TEMP_NOHOLD:
+ dir = blen == 0 ? I2C_OP_READ : I2C_OP_READ_WITH_STOP;
+ break;
+ default:
+ panic("%s: bad command %#x\n", __func__, cmd[0]);
+ }
+
+ memset(buf, 0, blen);
+ return iic_exec(tag, dir, addr, cmd, clen, buf, blen, 0);
+}
+
+static int
+si70xx_cmd0(struct si70xx_sc *sc, uint8_t *buf, size_t blen)
+{
+ return si70xx_cmd(sc->sc_tag, sc->sc_addr, NULL, 0, buf, blen);
+}
+
+static int
+si70xx_cmd1(struct si70xx_sc *sc, uint8_t cmd, uint8_t *buf, size_t blen)
+{
+ return si70xx_cmd(sc->sc_tag, sc->sc_addr, &cmd, 1, buf, blen);
+}
+
+static int
+si70xx_cmd2(struct si70xx_sc *sc, uint8_t cmd1, uint8_t cmd2, uint8_t *buf,
+ size_t blen)
+{
+ uint8_t cmd[] = { cmd1, cmd2 };
+ return si70xx_cmd(sc->sc_tag, sc->sc_addr, cmd, __arraycount(cmd),
+ buf, blen);
+}
+
+static int
+si70xx_set_heateron(struct si70xx_sc * sc)
+{
+ int error;
+ uint8_t userregister;
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s:%s: Failed to acquire bus: %d\n",
+ device_xname(sc->sc_dev), __func__, error));
+ return error;
+ }
+
+ error = si70xx_cmd1(sc, SI70XX_READ_USER_REG_1, &userregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read user register 1: %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ DPRINTF(sc, 2, ("%s:%s: reg 1 values before: %#x\n",
+ device_xname(sc->sc_dev), __func__, userregister));
+ if (sc->sc_heateron) {
+ userregister |= SI70XX_HTRE_MASK;
+ } else {
+ userregister &= ~SI70XX_HTRE_MASK;
+ }
+ DPRINTF(sc, 2, ("%s:%s: user reg 1 values after: %#x\n",
+ device_xname(sc->sc_dev), __func__, userregister));
+
+ error = si70xx_cmd1(sc, SI70XX_WRITE_USER_REG_1, &userregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to write user register 1: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+out:
+ iic_release_bus(sc->sc_tag, 0);
+ return error;
+}
+
+static int
+si70xx_set_resolution(struct si70xx_sc * sc, size_t index)
+{
+ int error;
+ uint8_t userregister;
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to acquire bus: %d\n",
+ device_xname(sc->sc_dev), error));
+ return error;
+ }
+
+ error = si70xx_cmd1(sc, SI70XX_READ_USER_REG_1, &userregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read user register 1: %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ DPRINTF(sc, 2, ("%s:%s: reg 1 values before: %#x\n",
+ device_xname(sc->sc_dev), __func__, userregister));
+ userregister &= (~SI70XX_RESOLUTION_MASK);
+ userregister |= si70xx_resolutions[index].num;
+ DPRINTF(sc, 2, ("%s:%s: reg 1 values after: %#x\n",
+ device_xname(sc->sc_dev), __func__, userregister));
+
+ error = si70xx_cmd1(sc, SI70XX_WRITE_USER_REG_1, &userregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to write user register 1: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+out:
+ iic_release_bus(sc->sc_tag, 0);
+ return error;
+}
+
+static int
+si70xx_set_heatervalue(struct si70xx_sc * sc, size_t index)
+{
+ int error;
+ uint8_t heaterregister;
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to acquire bus: %d\n",
+ device_xname(sc->sc_dev), error));
+ return error;
+ }
+ error = si70xx_cmd1(sc, SI70XX_READ_HEATER_REG, &heaterregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read heater register: %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ DPRINTF(sc, 2, ("%s:%s: heater values before: %#x\n",
+ device_xname(sc->sc_dev), __func__, heaterregister));
+ heaterregister &= ~SI70XX_HEATER_MASK;
+ heaterregister |= si70xx_heatervalues[index];
+ DPRINTF(sc, 2, ("%s:%s: heater values after: %#x\n",
+ device_xname(sc->sc_dev), __func__, heaterregister));
+
+ error = si70xx_cmd1(sc, SI70XX_WRITE_USER_REG_1, &heaterregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to write heater register: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+out:
+ iic_release_bus(sc->sc_tag, 0);
+ return error;
+}
+
+static int
+si70xx_update_heater(struct si70xx_sc *sc)
+{
+ size_t i;
+ int error;
+ uint8_t heaterregister;
+
+ error = si70xx_cmd1(sc, SI70XX_READ_HEATER_REG, &heaterregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read heater register: %d\n",
+ device_xname(sc->sc_dev), error));
+ return error;
+ }
+
+ DPRINTF(sc, 2, ("%s: read heater reg values: %02x\n",
+ device_xname(sc->sc_dev), heaterregister));
+
+ uint8_t heat = heaterregister & SI70XX_HEATER_MASK;
+ for (i = 0; i < __arraycount(si70xx_heatervalues); i++) {
+ if (si70xx_heatervalues[i] == heat)
+ break;
+ }
+ sc->sc_heaterval = i != __arraycount(si70xx_heatervalues) ? i : 0;
+ return 0;
+}
+
+static int
+si70xx_update_user(struct si70xx_sc *sc)
+{
+ size_t i;
+ int error;
+ uint8_t userregister;
+
+ error = si70xx_cmd1(sc, SI70XX_READ_USER_REG_1, &userregister, 1);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read user register 1: %d\n",
+ device_xname(sc->sc_dev), error));
+ return error;
+ }
+ DPRINTF(sc, 2, ("%s: read user reg 1 values: %#x\n",
+ device_xname(sc->sc_dev), userregister));
+
+ uint8_t res = userregister & SI70XX_RESOLUTION_MASK;
+ for (i = 0; i < __arraycount(si70xx_resolutions); i++) {
+ if (si70xx_resolutions[i].num == res)
+ break;
+ }
+
+ if (i != __arraycount(si70xx_resolutions)) {
+ memcpy(sc->sc_resolution, si70xx_resolutions[i].text,
+ SI70XX_RES_NAME);
+ } else {
+ snprintf(sc->sc_resolution, SI70XX_RES_NAME, "%02x", res);
+ }
+
+ sc->sc_vddok = (userregister & SI70XX_VDDS_MASK) == 0;
+ sc->sc_heaterval = userregister & SI70XX_HTRE_MASK;
+ return 0;
+}
+
+static int
+si70xx_update_status(struct si70xx_sc *sc)
+{
+ int error1 = si70xx_update_user(sc);
+ int error2 = si70xx_update_heater(sc);
+ return error1 ? error1 : error2;
+}
+
+static uint8_t
+si70xx_crc(uint8_t * data, size_t size)
+{
+ uint8_t crc = 0;
+
+ for (size_t i = 0; i < size; i++) {
+ crc ^= data[i];
+ for (size_t j = 8; j > 0; j--) {
+ if (crc & 0x80)
+ crc = (crc << 1) ^ 0x131;
+ else
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+
+static int
+si70xx_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
+{
+ uint8_t reg = SI70XX_READ_USER_REG_1;
+ uint8_t buf;
+ int error;
+
+ error = si70xx_cmd(tag, addr, ®, 1, &buf, 1);
+ if (matchdebug) {
+ printf("poke X 1: %d\n", error);
+ }
+ return error;
+}
+
+static int
+si70xx_sysctl_init(struct si70xx_sc *sc)
+{
+ int error;
+ const struct sysctlnode *cnode;
+ int sysctlroot_num;
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ 0, CTLTYPE_NODE, device_xname(sc->sc_dev),
+ SYSCTL_DESCR("si70xx controls"), NULL, 0, NULL, 0, CTL_HW,
+ CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ sysctlroot_num = cnode->sysctl_num;
+
+#ifdef SI70XX_DEBUG
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
+ SYSCTL_DESCR("Debug level"), si70xx_verify_sysctl, 0,
+ &sc->sc_si70xxdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+
+#endif
+
+#ifdef HAVE_I2C_EXECV
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "clockstretch",
+ SYSCTL_DESCR("Clockstretch value"), si70xx_verify_sysctl, 0,
+ &sc->sc_clockstretch, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+#endif
+
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
+ SYSCTL_DESCR("The number of times to attempt to read the values"),
+ si70xx_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
+ sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READONLY, CTLTYPE_STRING, "resolutions",
+ SYSCTL_DESCR("Valid resolutions"), 0, 0,
+ __UNCONST(si70xx_resolution_names),
+ sizeof(si70xx_resolution_names) + 1,
+ CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_STRING, "resolution",
+ SYSCTL_DESCR("Resolution of RH and Temp"),
+ si70xx_verify_sysctl_resolution, 0, (void *) sc,
+ SI70XX_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
+ SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READONLY, CTLTYPE_BOOL, "vddok",
+ SYSCTL_DESCR("Vdd at least 1.9v"), NULL, 0, &sc->sc_vddok, 0,
+ CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_BOOL, "heateron",
+ SYSCTL_DESCR("Heater on"), si70xx_verify_sysctl_heateron, 0,
+ (void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ return sysctl_createv(&sc->sc_si70xxlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "heaterstrength",
+ SYSCTL_DESCR("Heater strength 1 to 6"),
+ si70xx_verify_sysctl_heatervalue, 0, (void *)sc, 0, CTL_HW,
+ sysctlroot_num, CTL_CREATE, CTL_EOL);
+}
+
+static int
+si70xx_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct i2c_attach_args *ia;
+ int error;
+ const bool matchdebug = false;
+
+ ia = aux;
+
+ if (ia->ia_name) {
+ /* direct config - check name */
+ if (strcmp(ia->ia_name, "si70xxtemp") != 0)
+ return 0;
+ } else {
+ /* indirect config - check for configured address */
+ if (ia->ia_addr != SI70XX_TYPICAL_ADDR)
+ return 0;
+ }
+
+ /*
+ * Check to see if something is really at this i2c address. This will
+ * keep phantom devices from appearing
+ */
+ if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
+ if (matchdebug)
+ printf("in match acquire bus failed\n");
+ return 0;
+ }
+
+ error = si70xx_poke(ia->ia_tag, ia->ia_addr, matchdebug);
+ iic_release_bus(ia->ia_tag, 0);
+
+ return error == 0;
+}
+
+static void
+si70xx_attach(device_t parent, device_t self, void *aux)
+{
+ struct si70xx_sc *sc;
+ struct i2c_attach_args *ia;
+ int error, i;
+ int ecount = 0;
+ uint8_t buf[8];
+ uint8_t testcrcpt1[4];
+ uint8_t testcrcpt2[4];
+ uint8_t crc1 = 0, crc2 = 0;
+ uint8_t readcrc1 = 0, readcrc2 = 0;
+ uint8_t fwversion, model;
+
+ ia = aux;
+ sc = device_private(self);
+
+ sc->sc_dev = self;
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+ sc->sc_si70xxdebug = 0;
+#ifdef HAVE_I2C_EXECV
+ sc->sc_clockstretch = 2048;
+#endif
+ sc->sc_readattempts = 15;
+ sc->sc_ignorecrc = false;
+ sc->sc_sme = NULL;
+
+ aprint_normal("\n");
+
+ mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+ sc->sc_numsensors = __arraycount(si70xx_sensors);
+
+ if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
+ aprint_error_dev(self,
+ "Unable to create sysmon structure\n");
+ sc->sc_sme = NULL;
+ return;
+ }
+ if ((error = si70xx_sysctl_init(sc)) != 0) {
+ aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
+ goto out;
+ }
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ aprint_error_dev(self, "Could not acquire iic bus: %d\n",
+ error);
+ goto out;
+ }
+ error = si70xx_cmd1(sc, SI70XX_RESET, NULL, 0);
+ if (error != 0)
+ aprint_error_dev(self, "Reset failed: %d\n", error);
+
+ delay(15000); /* 15 ms max */
+
+ error = si70xx_cmd2(sc, SI70XX_READ_ID_PT1A, SI70XX_READ_ID_PT1B,
+ buf, 8);
+ if (error) {
+ aprint_error_dev(self, "Failed to read first part of ID: %d\n",
+ error);
+ ecount++;
+ }
+ testcrcpt1[0] = buf[0];
+ testcrcpt1[1] = buf[2];
+ testcrcpt1[2] = buf[4];
+ testcrcpt1[3] = buf[6];
+ readcrc1 = buf[7];
+ crc1 = si70xx_crc(testcrcpt1, 4);
+
+ DPRINTF(sc, 2, ("%s: read 1 values: %02x%02x%02x%02x%02x%02x%02x%02x "
+ "- %02x\n", device_xname(sc->sc_dev), buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+ crc1));
+
+ error = si70xx_cmd2(sc, SI70XX_READ_ID_PT2A, SI70XX_READ_ID_PT2B,
+ buf, 8);
+ if (error != 0) {
+ aprint_error_dev(self, "Failed to read second part of ID: %d\n",
+ error);
+ ecount++;
+ }
+ model = testcrcpt2[0] = buf[0];
+ testcrcpt2[1] = buf[1];
+ testcrcpt2[2] = buf[3];
+ testcrcpt2[3] = buf[4];
+ readcrc2 = buf[5];
+ crc2 = si70xx_crc(testcrcpt2, 4);
+
+ DPRINTF(sc, 2, ("%s: read 2 values: %02x%02x%02x%02x%02x%02x - %02x\n",
+ device_xname(sc->sc_dev), buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], crc2));
+
+ error = si70xx_cmd2(sc, SI70XX_READ_FW_VERA, SI70XX_READ_FW_VERB,
+ buf, 8);
+
+ if (error) {
+ aprint_error_dev(self, "Failed to read firware version: %d\n",
+ error);
+ ecount++;
+ }
+ fwversion = buf[0];
+ DPRINTF(sc, 2, ("%s: read fw values: %#x\n", device_xname(sc->sc_dev),
+ fwversion));
+
+ error = si70xx_update_status(sc);
+ iic_release_bus(sc->sc_tag, 0);
+ if (error != 0) {
+ aprint_error_dev(self, "Failed to update status: %x\n", error);
+ aprint_error_dev(self, "Unable to setup device\n");
+ goto out;
+ }
+
+ for (i = 0; i < sc->sc_numsensors; i++) {
+ strlcpy(sc->sc_sensors[i].desc, si70xx_sensors[i].desc,
+ sizeof(sc->sc_sensors[i].desc));
+
+ sc->sc_sensors[i].units = si70xx_sensors[i].type;
+ sc->sc_sensors[i].state = ENVSYS_SINVALID;
+
+ DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
+ sc->sc_sensors[i].desc));
+
+ error = sysmon_envsys_sensor_attach(sc->sc_sme,
+ &sc->sc_sensors[i]);
+ if (error) {
+ aprint_error_dev(self,
+ "Unable to attach sensor %d: %d\n", i, error);
+ goto out;
+ }
+ }
+
+ sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+ sc->sc_sme->sme_cookie = sc;
+ sc->sc_sme->sme_refresh = si70xx_refresh;
+
+ DPRINTF(sc, 2, ("si70xx_attach: registering with envsys\n"));
+
+ if (sysmon_envsys_register(sc->sc_sme)) {
+ aprint_error_dev(self,
+ "unable to register with sysmon\n");
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+ return;
+ }
+ if (ecount != 0) {
+ aprint_normal_dev(self, "Could not read model, "
+ "probably an HTU21D\n");
+ return;
+ }
+
+ char modelstr[64];
+ switch (model) {
+ case 0:
+ case 0xff:
+ snprintf(modelstr, sizeof(modelstr), "Engineering Sample");
+ case 13:
+ case 20:
+ case 21:
+ snprintf(modelstr, sizeof(modelstr), "SI70%d", model);
+ break;
+ default:
+ snprintf(modelstr, sizeof(modelstr), "Unknown SI70%d", model);
+ break;
+ }
+
+ const char *fwversionstr;
+ switch (fwversion) {
+ case 0xff:
+ fwversionstr = "1.0";
+ break;
+ case 0x20:
+ fwversionstr = "2.0";
+ break;
+ default:
+ fwversionstr = "unknown";
+ break;
+ }
+
+ aprint_normal_dev(self, "Silicon Labs Model: %s, "
+ "Firmware version: %s, "
+ "Serial number: %02x%02x%02x%02x%02x%02x%02x%02x%s",
+ modelstr, fwversionstr, testcrcpt1[0], testcrcpt1[1],
+ testcrcpt1[2], testcrcpt1[3], testcrcpt2[0], testcrcpt2[1],
+ testcrcpt2[2], testcrcpt2[3],
+ (crc1 == readcrc1 && crc2 == readcrc2) ? "\n" : " (bad crc)\n");
+ return;
+out:
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+}
+
+static int
+si70xx_exec(struct si70xx_sc *sc, uint8_t cmd, envsys_data_t *edata)
+{
+ int error;
+ int xdelay;
+ const char *name;
+ int64_t mul, offs;
+ uint8_t buf[3];
+
+ switch (cmd) {
+ case SI70XX_MEASURE_RH_NOHOLD:
+ /*
+ * The published conversion for RH is: %RH =
+ * ((125 * RHCODE) / 65536) - 6
+ *
+ * The sysmon infrastructure for RH wants %RH *
+ * 10^6 The result will fit in 32 bits, but
+ * the intermediate values will not.
+ */
+ mul = 125000000;
+ offs = -6000000;
+ /*
+ * Conversion times for %RH in ms
+ *
+ * Typical Max
+ * 12-bit 10.0 12.0
+ * 11-bit 5.8 7.0
+ * 10-bit 3.7 4.5
+ * 8-bit 2.6 3.1
+ *
+ * A call to read %RH will also read temperature. The
+ * conversion time will be the amount of time above
+ * plus the amount of time for temperature below
+ */
+ xdelay = 10500;
+ name = "RH";
+ break;
+ case SI70XX_MEASURE_TEMP_NOHOLD:
+ /*
+ * The published conversion for temp is:
+ * degree C = ((175.72 * TEMPCODE) / 65536) -
+ * 46.85
+ *
+ * The sysmon infrastructure for temp wants
+ * microkelvin. This is simple, as degree C
+ * converts directly with K with simple
+ * addition. The result will fit in 32 bits,
+ * but the intermediate values will not.
+ */
+ mul = 175720000;
+ offs = 226300000;
+ /*
+ * Conversion times for temperature in ms
+ *
+ * Typical Max
+ * 14-bit 7.0 10.8
+ * 13-bit 4.0 6.2
+ * 12-bit 2.4 3.8
+ * 11-bit 1.5 2.4
+ */
+ xdelay = 4750;
+ name = "TEMP";
+ break;
+ default:
+ return EINVAL;
+ }
+
+#if HAVE_I2C_EXECV
+ memset(buf, 0, sizeof(buf));
+ error = iic_execv(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+ &cmd, 1, buf, sizeof(buf), 0, I2C_ATTR_CLOCKSTRETCH,
+ sc->sc_clockstretch, I2C_ATTR_EOL);
+#else
+ /*
+ * The lower level driver must support the ability to
+ * do a zero length read, otherwise this breaks
+ */
+ error = si70xx_cmd1(sc, cmd, buf, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to read NO HOLD %s %d %d\n",
+ device_xname(sc->sc_dev), name, 1, error));
+ return error;
+ }
+
+ /*
+ * It will probably be at least this long... we would
+ * not have to do this sort of thing if clock
+ * stretching worked. Even this is a problem for the
+ * RPI without a patch to remove a [apparently] not
+ * needed KASSERT()
+ */
+ delay(xdelay);
+
+ for (int aint = 0; aint < sc->sc_readattempts; aint++) {
+ error = si70xx_cmd0(sc, buf, sizeof(buf));
+ if (error == 0)
+ break;
+ DPRINTF(sc, 2, ("%s: Failed to read NO HOLD RH"
+ " %d %d\n", device_xname(sc->sc_dev), 2, error));
+ if (aint < 10) {
+ delay(1000);
+ }
+ }
+#endif
+
+ DPRINTF(sc, 2, ("%s: %s values: %02x%02x%02x - %02x\n",
+ device_xname(sc->sc_dev), name, buf[0], buf[1], buf[2],
+ si70xx_crc(buf, 2)));
+
+ uint8_t crc;
+ if (sc->sc_ignorecrc) {
+ crc = buf[2];
+ } else {
+ crc = si70xx_crc(buf, 2);
+ }
+
+ if (crc != buf[2]) {
+ DPRINTF(sc, 2, ("%s: Bad CRC for %s: %#x and %#x\n",
+ device_xname(sc->sc_dev), name, crc, buf[2]));
+ return EINVAL;
+ }
+
+ uint16_t val16 = (buf[0] << 8) | buf[1];
+ uint64_t val64 = ((mul * val16) >> 16) + offs;
+ DPRINTF(sc, 2, ("%s: %s calculated values: %x %#jx\n",
+ device_xname(sc->sc_dev), name, val16, (uintmax_t)val64));
+ edata->value_cur = (uint32_t) val64;
+ edata->state = ENVSYS_SVALID;
+ return 0;
+}
+
+static void
+si70xx_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
+{
+ struct si70xx_sc *sc;
+ int error;
+
+ sc = sme->sme_cookie;
+ edata->state = ENVSYS_SINVALID;
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ error = si70xx_update_status(sc);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to update status in refresh %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out1;
+ }
+ switch (edata->sensor) {
+ case SI70XX_HUMIDITY_SENSOR:
+ error = si70xx_exec(sc, SI70XX_MEASURE_RH_HOLD, edata);
+ break;
+
+ case SI70XX_TEMP_SENSOR:
+ error = si70xx_exec(sc, SI70XX_MEASURE_TEMP_HOLD, edata);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+out1:
+ iic_release_bus(sc->sc_tag, 0);
+out:
+ mutex_exit(&sc->sc_mutex);
+}
+
+static int
+si70xx_detach(device_t self, int flags)
+{
+ struct si70xx_sc *sc;
+
+ sc = device_private(self);
+
+ mutex_enter(&sc->sc_mutex);
+
+ /* Remove the sensors */
+ if (sc->sc_sme != NULL) {
+ sysmon_envsys_unregister(sc->sc_sme);
+ sc->sc_sme = NULL;
+ }
+ mutex_exit(&sc->sc_mutex);
+
+ /* Remove the sysctl tree */
+ sysctl_teardown(&sc->sc_si70xxlog);
+
+ /* Remove the mutex */
+ mutex_destroy(&sc->sc_mutex);
+
+ return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, si70xxtemp, "i2cexec,sysmon_envsys");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+si70xxtemp_modcmd(modcmd_t cmd, void *opaque)
+{
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ return config_init_component(cfdriver_ioconf_si70xxtemp,
+ cfattach_ioconf_si70xxtemp, cfdata_ioconf_si70xxtemp);
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ error = config_fini_component(cfdriver_ioconf_si70xxtemp,
+ cfattach_ioconf_si70xxtemp, cfdata_ioconf_si70xxtemp);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
Index: src/sys/dev/i2c/si70xxreg.h
diff -u /dev/null src/sys/dev/i2c/si70xxreg.h:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/si70xxreg.h Thu Dec 28 18:23:47 2017
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DEV_I2C_SI70XXREG_H_
+#define _DEV_I2C_SI70XXREG_H_
+
+#define SI70XX_TYPICAL_ADDR 0x40
+
+#define SI70XX_MEASURE_RH_HOLD 0xE5
+#define SI70XX_MEASURE_RH_NOHOLD 0xF5
+#define SI70XX_MEASURE_TEMP_HOLD 0xE3
+#define SI70XX_MEASURE_TEMP_NOHOLD 0xF3
+#define SI70XX_READ_PREVIOUS_TEMP 0xE0
+#define SI70XX_RESET 0xFE
+#define SI70XX_WRITE_USER_REG_1 0xE6
+#define SI70XX_READ_USER_REG_1 0xE7
+#define SI70XX_WRITE_HEATER_REG 0x51
+#define SI70XX_READ_HEATER_REG 0x11
+#define SI70XX_READ_ID_PT1A 0xFA
+#define SI70XX_READ_ID_PT1B 0x0F
+#define SI70XX_READ_ID_PT2A 0xFC
+#define SI70XX_READ_ID_PT2B 0xC9
+#define SI70XX_READ_FW_VERA 0x84
+#define SI70XX_READ_FW_VERB 0xB8
+
+#define SI70XX_VDDS_MASK 0x40
+#define SI70XX_HTRE_MASK 0x04
+#define SI70XX_RESOLUTION_MASK 0x81
+#define SI70XX_HEATER_MASK 0x0F
+
+#endif
Index: src/sys/dev/i2c/si70xxvar.h
diff -u /dev/null src/sys/dev/i2c/si70xxvar.h:1.1
--- /dev/null Thu Dec 28 18:23:47 2017
+++ src/sys/dev/i2c/si70xxvar.h Thu Dec 28 18:23:47 2017
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Brad Spencer <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DEV_I2C_SI70XXVAR_H_
+#define _DEV_I2C_SI70XXVAR_H_
+
+#define SI70XX_NUM_SENSORS 2
+#define SI70XX_HUMIDITY_SENSOR 0
+#define SI70XX_TEMP_SENSOR 1
+
+#define SI70XX_RES_NAME 12
+
+struct si70xx_sc {
+ int sc_si70xxdebug;
+ device_t sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+ kmutex_t sc_mutex;
+ int sc_numsensors;
+ struct sysmon_envsys *sc_sme;
+ envsys_data_t sc_sensors[SI70XX_NUM_SENSORS];
+ struct sysctllog *sc_si70xxlog;
+ char sc_resolution[SI70XX_RES_NAME];
+ int sc_resnumber;
+ bool sc_ignorecrc;
+ bool sc_vddok;
+ bool sc_heateron;
+ int sc_heaterval;
+#ifdef HAVE_I2C_EXECV
+ uint32_t sc_clockstretch;
+#endif
+ int sc_readattempts;
+};
+
+struct si70xx_sensor {
+ const char *desc;
+ enum envsys_units type;
+};
+
+struct si70xx_resolution {
+ const char *text;
+ int num;
+};
+
+#endif