Module Name: src Committed By: brad Date: Thu Jan 23 19:13:19 UTC 2025
Modified Files: src/share/man/man4: si70xxtemp.4 src/sys/dev/i2c: si70xx.c si70xxvar.h Log Message: The SI70XX temperature and humidity sensor has a set of commands that can do the measurement by using I2C clock stretching. Add a sysctl to allow the driver to use those commands. This is more efficient than doing the usual polling method of getting the measurements back from the chip, but only works if the I2C master supports clock stretching. Also... change the use of the Read without Stop with a command and zero length read to just a Write of the command in the non-clock stretching case. I was confused by the datasheet when I write the driver initially. This allows the driver to work with more I2C masters. Also... there were a couple of cases where more bytes were read on the bus than would be sent by the chip. Fix those. The second part of the serial number only has 6 bytes not 8, the firmware version has 1 byte not 8. To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.8 src/share/man/man4/si70xxtemp.4 cvs rdiff -u -r1.11 -r1.12 src/sys/dev/i2c/si70xx.c cvs rdiff -u -r1.3 -r1.4 src/sys/dev/i2c/si70xxvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/si70xxtemp.4 diff -u src/share/man/man4/si70xxtemp.4:1.7 src/share/man/man4/si70xxtemp.4:1.8 --- src/share/man/man4/si70xxtemp.4:1.7 Fri Nov 12 15:12:11 2021 +++ src/share/man/man4/si70xxtemp.4 Thu Jan 23 19:13:19 2025 @@ -1,4 +1,4 @@ -.\" $NetBSD: si70xxtemp.4,v 1.7 2021/11/12 15:12:11 brad Exp $ +.\" $NetBSD: si70xxtemp.4,v 1.8 2025/01/23 19:13:19 brad Exp $ .\" .\" Copyright (c) 2017 Brad Spencer <b...@anduin.eldar.org> .\" @@ -75,6 +75,13 @@ times. The default is 40 which should be enough for most purposes. There is an initial wait of 10,500 microseconds followed by a additional 1,000 microseconds per read attempt. +.It hw.si70xxtemp0.clockstretch +The chip supports a set of commands that lets it use I2C clock +stretching to perform the temperature or humidity measurement. If +this is set to 1 then use the clock stretching commands with the +device. Note that the I2C controller must support clock stretching +in order for this to work reliability. When this option is enabled, +the readattempts sysctl noted above will not be used. .El .Sh SEE ALSO .Xr envsys 4 , Index: src/sys/dev/i2c/si70xx.c diff -u src/sys/dev/i2c/si70xx.c:1.11 src/sys/dev/i2c/si70xx.c:1.12 --- src/sys/dev/i2c/si70xx.c:1.11 Wed Mar 30 00:06:50 2022 +++ src/sys/dev/i2c/si70xx.c Thu Jan 23 19:13:19 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: si70xx.c,v 1.11 2022/03/30 00:06:50 pgoyette Exp $ */ +/* $NetBSD: si70xx.c,v 1.12 2025/01/23 19:13:19 brad Exp $ */ /* * Copyright (c) 2017 Brad Spencer <b...@anduin.eldar.org> @@ -17,7 +17,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: si70xx.c,v 1.11 2022/03/30 00:06:50 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: si70xx.c,v 1.12 2025/01/23 19:13:19 brad Exp $"); /* Driver for the Silicon Labs SI7013/SI7020/SI7021, HTU21D and SHT21 @@ -214,6 +214,8 @@ si70xx_dir(uint8_t cmd, size_t len) case SI70XX_READ_ID_PT2B: case SI70XX_READ_FW_VERA: case SI70XX_READ_FW_VERB: + case SI70XX_MEASURE_RH_HOLD: + case SI70XX_MEASURE_TEMP_HOLD: return I2C_OP_READ_WITH_STOP; case SI70XX_WRITE_USER_REG_1: case SI70XX_WRITE_HEATER_REG: @@ -221,7 +223,7 @@ si70xx_dir(uint8_t cmd, size_t len) return I2C_OP_WRITE_WITH_STOP; case SI70XX_MEASURE_RH_NOHOLD: case SI70XX_MEASURE_TEMP_NOHOLD: - return len == 0 ? I2C_OP_READ : I2C_OP_READ_WITH_STOP; + return len == 0 ? I2C_OP_WRITE : I2C_OP_READ_WITH_STOP; default: panic("%s: bad command %#x\n", __func__, cmd); return 0; @@ -505,15 +507,12 @@ si70xx_sysctl_init(struct si70xx_sc *sc) #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, + CTLFLAG_READWRITE, CTLTYPE_BOOL, "clockstretch", + SYSCTL_DESCR("Use clock stretch commands for measurements"), NULL, 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", @@ -620,9 +619,7 @@ si70xx_attach(device_t parent, device_t 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_clockstretch = false; sc->sc_readattempts = 40; sc->sc_ignorecrc = false; sc->sc_sme = NULL; @@ -686,7 +683,7 @@ si70xx_attach(device_t parent, device_t crc1, validcrcpt1)); error = si70xx_cmd2(sc, SI70XX_READ_ID_PT2A, SI70XX_READ_ID_PT2B, - buf, 8); + buf, 6); if (error != 0) { aprint_error_dev(self, "Failed to read second part of ID: %d\n", error); @@ -718,7 +715,7 @@ si70xx_attach(device_t parent, device_t buf[3], buf[4], buf[5], crc2, validcrcpt2)); error = si70xx_cmd2(sc, SI70XX_READ_FW_VERA, SI70XX_READ_FW_VERB, - buf, 8); + buf, 1); if (error) { aprint_error_dev(self, "Failed to read firmware version: Error %d\n", @@ -841,6 +838,7 @@ si70xx_exec(struct si70xx_sc *sc, uint8_ switch (cmd) { case SI70XX_MEASURE_RH_NOHOLD: + case SI70XX_MEASURE_RH_HOLD: /* * The published conversion for RH is: %RH = * ((125 * RHCODE) / 65536) - 6 @@ -868,6 +866,7 @@ si70xx_exec(struct si70xx_sc *sc, uint8_ name = "RH"; break; case SI70XX_MEASURE_TEMP_NOHOLD: + case SI70XX_MEASURE_TEMP_HOLD: /* * The published conversion for temp is: * degree C = ((175.72 * TEMPCODE) / 65536) - @@ -897,41 +896,39 @@ si70xx_exec(struct si70xx_sc *sc, uint8_ 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; - } + if (sc->sc_clockstretch) { + error = si70xx_cmd1(sc, cmd, buf, sizeof(buf)); + if (error) { + DPRINTF(sc, 2, ("%s: Failed to read HOLD %s %d %d\n", + device_xname(sc->sc_dev), name, 1, error)); + return error; + } + } else { + error = si70xx_cmd1(sc, cmd, NULL, 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); + /* + * 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)); - delay(1000); + 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)); + 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], @@ -983,11 +980,17 @@ si70xx_refresh(struct sysmon_envsys * sm } switch (edata->sensor) { case SI70XX_HUMIDITY_SENSOR: - error = si70xx_exec(sc, SI70XX_MEASURE_RH_NOHOLD, edata); + if (sc->sc_clockstretch) + error = si70xx_exec(sc, SI70XX_MEASURE_RH_HOLD, edata); + else + error = si70xx_exec(sc, SI70XX_MEASURE_RH_NOHOLD, edata); break; case SI70XX_TEMP_SENSOR: - error = si70xx_exec(sc, SI70XX_MEASURE_TEMP_NOHOLD, edata); + if (sc->sc_clockstretch) + error = si70xx_exec(sc, SI70XX_MEASURE_TEMP_HOLD, edata); + else + error = si70xx_exec(sc, SI70XX_MEASURE_TEMP_NOHOLD, edata); break; default: error = EINVAL; Index: src/sys/dev/i2c/si70xxvar.h diff -u src/sys/dev/i2c/si70xxvar.h:1.3 src/sys/dev/i2c/si70xxvar.h:1.4 --- src/sys/dev/i2c/si70xxvar.h:1.3 Thu Nov 11 14:16:04 2021 +++ src/sys/dev/i2c/si70xxvar.h Thu Jan 23 19:13:19 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: si70xxvar.h,v 1.3 2021/11/11 14:16:04 brad Exp $ */ +/* $NetBSD: si70xxvar.h,v 1.4 2025/01/23 19:13:19 brad Exp $ */ /* * Copyright (c) 2017 Brad Spencer <b...@anduin.eldar.org> @@ -41,9 +41,7 @@ struct si70xx_sc { bool sc_vddok; bool sc_heateron; int sc_heaterval; -#ifdef HAVE_I2C_EXECV - uint32_t sc_clockstretch; -#endif + bool sc_clockstretch; int sc_readattempts; bool sc_noheater; bool sc_nofw;