Module Name: src Committed By: brad Date: Thu Nov 17 19:20:06 UTC 2022
Modified Files: src/distrib/sets/lists/debug: module.mi src/distrib/sets/lists/man: mi src/distrib/sets/lists/modules: mi src/share/man/man4: Makefile src/sys/dev/i2c: files.i2c src/sys/modules: Makefile Added Files: src/share/man/man4: aht20temp.4 src/sys/dev/i2c: aht20.c aht20reg.h aht20var.h src/sys/modules/aht20temp: Makefile aht20temp.ioconf Log Message: A driver for the Aosong AHT20 temperature and humidity sensor. While slow for an I2C sensor it is inexpensive and should work well enough in most indoor conditions. All features of the chip are supported. To generate a diff of this commit: cvs rdiff -u -r1.20 -r1.21 src/distrib/sets/lists/debug/module.mi cvs rdiff -u -r1.1754 -r1.1755 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.154 -r1.155 src/distrib/sets/lists/modules/mi cvs rdiff -u -r1.728 -r1.729 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/aht20temp.4 cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/aht20.c src/sys/dev/i2c/aht20reg.h \ src/sys/dev/i2c/aht20var.h cvs rdiff -u -r1.123 -r1.124 src/sys/dev/i2c/files.i2c cvs rdiff -u -r1.271 -r1.272 src/sys/modules/Makefile cvs rdiff -u -r0 -r1.1 src/sys/modules/aht20temp/Makefile \ src/sys/modules/aht20temp/aht20temp.ioconf Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/debug/module.mi diff -u src/distrib/sets/lists/debug/module.mi:1.20 src/distrib/sets/lists/debug/module.mi:1.21 --- src/distrib/sets/lists/debug/module.mi:1.20 Sat Jun 4 03:32:04 2022 +++ src/distrib/sets/lists/debug/module.mi Thu Nov 17 19:20:06 2022 @@ -1,4 +1,4 @@ -# $NetBSD: module.mi,v 1.20 2022/06/04 03:32:04 pgoyette Exp $ +# $NetBSD: module.mi,v 1.21 2022/11/17 19:20:06 brad Exp $ ./usr/libdata/debug/@MODULEDIR@ modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/accf_dataready modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/accf_dataready/accf_dataready.kmod.debug modules-base-kernel kmod,debug @@ -8,6 +8,8 @@ ./usr/libdata/debug/@MODULEDIR@/adiantum/adiantum.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/adosfs modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/adosfs/adosfs.kmod.debug modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/aht20temp modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/aht20temp/aht20temp.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/aio modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/aio/aio.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/am2315temp modules-base-kernel kmod,debug Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1754 src/distrib/sets/lists/man/mi:1.1755 --- src/distrib/sets/lists/man/mi:1.1754 Sat Aug 27 21:53:38 2022 +++ src/distrib/sets/lists/man/mi Thu Nov 17 19:20:06 2022 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1754 2022/08/27 21:53:38 dholland Exp $ +# $NetBSD: mi,v 1.1755 2022/11/17 19:20:06 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -781,6 +781,7 @@ ./usr/share/man/cat4/ahci.0 man-sys-catman .cat ./usr/share/man/cat4/ahcisata.0 man-sys-catman .cat ./usr/share/man/cat4/ahd.0 man-sys-catman .cat +./usr/share/man/cat4/aht20temp.0 man-sys-catman .cat ./usr/share/man/cat4/ai.0 man-sys-catman .cat ./usr/share/man/cat4/aiboost.0 man-sys-catman .cat ./usr/share/man/cat4/aibs.0 man-sys-catman .cat @@ -4088,6 +4089,7 @@ ./usr/share/man/html4/ahci.html man-sys-htmlman html ./usr/share/man/html4/ahcisata.html man-sys-htmlman html ./usr/share/man/html4/ahd.html man-sys-htmlman html +./usr/share/man/html4/aht20temp.html man-sys-htmlman html ./usr/share/man/html4/ai.html man-sys-htmlman html ./usr/share/man/html4/aiboost.html man-sys-htmlman html ./usr/share/man/html4/aibs.html man-sys-htmlman html @@ -7089,6 +7091,7 @@ ./usr/share/man/man4/ahci.4 man-sys-man .man ./usr/share/man/man4/ahcisata.4 man-sys-man .man ./usr/share/man/man4/ahd.4 man-sys-man .man +./usr/share/man/man4/aht20temp.4 man-sys-man .man ./usr/share/man/man4/ai.4 man-sys-man .man ./usr/share/man/man4/aiboost.4 man-sys-man .man ./usr/share/man/man4/aibs.4 man-sys-man .man Index: src/distrib/sets/lists/modules/mi diff -u src/distrib/sets/lists/modules/mi:1.154 src/distrib/sets/lists/modules/mi:1.155 --- src/distrib/sets/lists/modules/mi:1.154 Sat Jun 4 03:32:04 2022 +++ src/distrib/sets/lists/modules/mi Thu Nov 17 19:20:06 2022 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.154 2022/06/04 03:32:04 pgoyette Exp $ +# $NetBSD: mi,v 1.155 2022/11/17 19:20:06 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -13,6 +13,8 @@ ./@MODULEDIR@/adosfs/adosfs.kmod modules-base-kernel kmod ./@MODULEDIR@/adiantum modules-base-kernel kmod ./@MODULEDIR@/adiantum/adiantum.kmod modules-base-kernel kmod +./@MODULEDIR@/aht20temp modules-base-kernel kmod +./@MODULEDIR@/aht20temp/aht20temp.kmod modules-base-kernel kmod ./@MODULEDIR@/aio modules-base-kernel kmod ./@MODULEDIR@/aio/aio.kmod modules-base-kernel kmod ./@MODULEDIR@/am2315temp modules-base-kernel kmod Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.728 src/share/man/man4/Makefile:1.729 --- src/share/man/man4/Makefile:1.728 Fri Aug 12 11:15:41 2022 +++ src/share/man/man4/Makefile Thu Nov 17 19:20:05 2022 @@ -1,10 +1,10 @@ -# $NetBSD: Makefile,v 1.728 2022/08/12 11:15:41 riastradh Exp $ +# $NetBSD: Makefile,v 1.729 2022/11/17 19:20:05 brad Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ adbbt.4 adbkbd.4 adbms.4 \ adc.4 adm1026hm.4 admtemp.4 adv.4 adw.4 age.4 agp.4 agr.4 ahb.4 ahc.4 \ - ahcisata.4 ahd.4 aibs.4 alc.4 ale.4 alipm.4 altmem.4 altq.4 \ + ahcisata.4 ahd.4 aht20temp.4 aibs.4 alc.4 ale.4 alipm.4 altmem.4 altq.4 \ am2315temp.4 amdpm.4 amdtemp.4 amhphy.4 amr.4 aps.4 asus.4 \ an.4 aq.4 arcmsr.4 arcofi.4 aria.4 artsata.4 ata.4 atalk.4 ataraid.4 \ ath.4 athn.4 atphy.4 atppc.4 attimer.4 atw.4 \ Index: src/sys/dev/i2c/files.i2c diff -u src/sys/dev/i2c/files.i2c:1.123 src/sys/dev/i2c/files.i2c:1.124 --- src/sys/dev/i2c/files.i2c:1.123 Wed Jul 20 10:01:11 2022 +++ src/sys/dev/i2c/files.i2c Thu Nov 17 19:20:06 2022 @@ -1,4 +1,4 @@ -# $NetBSD: files.i2c,v 1.123 2022/07/20 10:01:11 riastradh Exp $ +# $NetBSD: files.i2c,v 1.124 2022/11/17 19:20:06 brad Exp $ obsolete defflag opt_i2cbus.h I2C_SCAN define i2cbus { } @@ -430,3 +430,8 @@ file dev/i2c/pcf8574.c pcf8574io # Sparkfun Serial motor controller attach scmd at iic with scmdi2c file dev/i2c/scmdi2c.c scmdi2c + +# Aosong AHT20 Temperature and Humidity sensor +device aht20temp +attach aht20temp at iic +file dev/i2c/aht20.c aht20temp Index: src/sys/modules/Makefile diff -u src/sys/modules/Makefile:1.271 src/sys/modules/Makefile:1.272 --- src/sys/modules/Makefile:1.271 Wed Sep 7 11:26:23 2022 +++ src/sys/modules/Makefile Thu Nov 17 19:20:05 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.271 2022/09/07 11:26:23 martin Exp $ +# $NetBSD: Makefile,v 1.272 2022/11/17 19:20:05 brad Exp $ .include <bsd.own.mk> @@ -25,6 +25,7 @@ SUBDIR+= accf_dataready SUBDIR+= accf_httpready SUBDIR+= adosfs SUBDIR+= adiantum +SUBDIR+= aht20temp SUBDIR+= aio SUBDIR+= audio SUBDIR+= autofs Added files: Index: src/share/man/man4/aht20temp.4 diff -u /dev/null src/share/man/man4/aht20temp.4:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/share/man/man4/aht20temp.4 Thu Nov 17 19:20:05 2022 @@ -0,0 +1,76 @@ +.\" $NetBSD: aht20temp.4,v 1.1 2022/11/17 19:20:05 brad Exp $ +.\" +.\" Copyright (c) 2022 Brad Spencer <b...@anduin.eldar.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. +.\" +.Dd November 15, 2022 +.Dt AHT20TEMP 4 +.Os +.Sh NAME +.Nm aht20temp +.Nd Driver for Guangzhou Aosong AHT20 sensor chip via I2C bus +.Sh SYNOPSIS +.Cd "aht20temp* at iic? addr 0x38" +.Sh DESCRIPTION +The +.Nm +driver provides measurements from the AHT20 humidity/temperature +sensors via the +.Xr envsys 4 +framework. +The +.Nm +.Ar addr +argument selects the address at the +.Xr iic 4 +bus. +The crc validity can be changed through +.Xr sysctl 8 +nodes. +.Sh SYSCTL VARIABLES +The following +.Xr sysctl 3 +variables are provided: +.Bl -tag -width indent +.It Li hw.aht20temp0.ignorecrc +If set, the crc calculation for %RH and temperature will be ignored. +.It Li hw.aht20temp0.debug +If the driver is compiled with +.Dv AHT20_DEBUG , +this node will appear and can be used to set the debugging level. +.It Li hw.aht20temp0.readattempts +To read %RH or temperature the chip requires that the command be sent, +then a delay must be observed before a read can be done to get the values +back. +The delays are documented in the datasheet for the chip. +The driver will attempt to read back the values readattempts number of +times. +The default is 10 which should be more than enough for most purposes. +.El +.Sh SEE ALSO +.Xr envsys 4 , +.Xr iic 4 , +.Xr envstat 8 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Nx 10.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Brad Spencer Aq Mt b...@anduin.eldar.org . Index: src/sys/dev/i2c/aht20.c diff -u /dev/null src/sys/dev/i2c/aht20.c:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/sys/dev/i2c/aht20.c Thu Nov 17 19:20:06 2022 @@ -0,0 +1,561 @@ +/* $NetBSD: aht20.c,v 1.1 2022/11/17 19:20:06 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer <b...@anduin.eldar.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/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: aht20.c,v 1.1 2022/11/17 19:20:06 brad Exp $"); + +/* + Driver for the Guangzhou Aosong AHT20 temperature and humidity sensor +*/ + +#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/aht20reg.h> +#include <dev/i2c/aht20var.h> + + +static uint8_t aht20_crc(uint8_t *, size_t); +static int aht20_poke(i2c_tag_t, i2c_addr_t, bool); +static int aht20_match(device_t, cfdata_t, void *); +static void aht20_attach(device_t, device_t, void *); +static int aht20_detach(device_t, int); +static void aht20_refresh(struct sysmon_envsys *, envsys_data_t *); +static int aht20_verify_sysctl(SYSCTLFN_ARGS); + +#define AHT20_DEBUG +#ifdef AHT20_DEBUG +#define DPRINTF(s, l, x) \ + do { \ + if (l <= s->sc_aht20debug) \ + printf x; \ + } while (/*CONSTCOND*/0) +#else +#define DPRINTF(s, l, x) +#endif + +CFATTACH_DECL_NEW(aht20temp, sizeof(struct aht20_sc), + aht20_match, aht20_attach, aht20_detach, NULL); + +static struct aht20_sensor aht20_sensors[] = { + { + .desc = "humidity", + .type = ENVSYS_SRELHUMIDITY, + }, + { + .desc = "temperature", + .type = ENVSYS_STEMP, + } +}; + +/* + * The delays are mentioned in the datasheet for the chip, except for + * the get status command. + */ + +static struct aht20_timing aht20_timings[] = { + { + .cmd = AHT20_INITIALIZE, + .typicaldelay = 10000, + }, + { + .cmd = AHT20_TRIGGER_MEASUREMENT, + .typicaldelay = 80000, + }, + { + .cmd = AHT20_GET_STATUS, + .typicaldelay = 5000, + }, + { + .cmd = AHT20_SOFT_RESET, + .typicaldelay = 20000, + } +}; + +int +aht20_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 +aht20_cmddelay(uint8_t cmd) +{ + int r = -1; + + for(int i = 0;i < __arraycount(aht20_timings);i++) { + if (cmd == aht20_timings[i].cmd) { + r = aht20_timings[i].typicaldelay; + break; + } + } + + if (r == -1) { + panic("Bad command look up in cmd delay: cmd: %d\n",cmd); + } + + return r; +} + +static int +aht20_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd, + uint8_t clen, uint8_t *buf, size_t blen, int readattempts) +{ + int error; + int cmddelay; + + error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,cmd,clen,NULL,0,0); + + /* Every command returns something except for the soft reset and + initialize which returns nothing. + */ + + if (error == 0) { + cmddelay = aht20_cmddelay(cmd[0]); + delay(cmddelay); + + if (cmd[0] != AHT20_SOFT_RESET && + cmd[0] != AHT20_INITIALIZE) { + for (int aint = 0; aint < readattempts; aint++) { + error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0); + if (error == 0) + break; + delay(1000); + } + } + } + + return error; +} + +static int +aht20_cmdr(struct aht20_sc *sc, uint8_t *cmd, uint8_t clen, uint8_t *buf, size_t blen) +{ + KASSERT(clen > 0); + + return aht20_cmd(sc->sc_tag, sc->sc_addr, cmd, clen, buf, blen, sc->sc_readattempts); +} + +static uint8_t +aht20_crc(uint8_t * data, size_t size) +{ + uint8_t crc = 0xFF; + + 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) ^ 0x31; + else + crc <<= 1; + } + } + return crc; +} + +static int +aht20_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug) +{ + uint8_t reg = AHT20_GET_STATUS; + uint8_t buf[6]; + int error; + + error = aht20_cmd(tag, addr, ®, 1, buf, 1, 10); + if (matchdebug) { + printf("poke X 1: %d\n", error); + } + return error; +} + +static int +aht20_sysctl_init(struct aht20_sc *sc) +{ + int error; + const struct sysctlnode *cnode; + int sysctlroot_num; + + if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode, + 0, CTLTYPE_NODE, device_xname(sc->sc_dev), + SYSCTL_DESCR("aht20 controls"), NULL, 0, NULL, 0, CTL_HW, + CTL_CREATE, CTL_EOL)) != 0) + return error; + + sysctlroot_num = cnode->sysctl_num; + +#ifdef AHT20_DEBUG + if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "debug", + SYSCTL_DESCR("Debug level"), aht20_verify_sysctl, 0, + &sc->sc_aht20debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE, + CTL_EOL)) != 0) + return error; + +#endif + + if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts", + SYSCTL_DESCR("The number of times to attempt to read the values"), + aht20_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW, + sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + + if ((error = sysctl_createv(&sc->sc_aht20log, 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; + + return 0; +} + +static int +aht20_match(device_t parent, cfdata_t match, void *aux) +{ + struct i2c_attach_args *ia = aux; + int error, match_result; + const bool matchdebug = false; + + if (iic_use_direct_match(ia, match, NULL, &match_result)) + return match_result; + + /* indirect config - check for configured address */ + if (ia->ia_addr != AHT20_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 = aht20_poke(ia->ia_tag, ia->ia_addr, matchdebug); + iic_release_bus(ia->ia_tag, 0); + + return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0; +} + +static void +aht20_attach(device_t parent, device_t self, void *aux) +{ + struct aht20_sc *sc; + struct i2c_attach_args *ia; + uint8_t cmd[1]; + int error, i; + + ia = aux; + sc = device_private(self); + + sc->sc_dev = self; + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + sc->sc_aht20debug = 0; + sc->sc_readattempts = 10; + sc->sc_ignorecrc = false; + sc->sc_sme = NULL; + + aprint_normal("\n"); + + mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); + sc->sc_numsensors = __arraycount(aht20_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 = aht20_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; + } + + cmd[0] = AHT20_SOFT_RESET; + error = aht20_cmdr(sc, cmd, 1, NULL, 0); + if (error != 0) + aprint_error_dev(self, "Reset failed: %d\n", error); + + iic_release_bus(sc->sc_tag, 0); + + if (error != 0) { + 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, aht20_sensors[i].desc, + sizeof(sc->sc_sensors[i].desc)); + + sc->sc_sensors[i].units = aht20_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 = aht20_refresh; + + DPRINTF(sc, 2, ("aht20_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; + } + + aprint_normal_dev(self, "Guangzhou Aosong AHT20\n"); + + return; +out: + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; +} + +static void +aht20_refresh(struct sysmon_envsys * sme, envsys_data_t * edata) +{ + struct aht20_sc *sc; + sc = sme->sme_cookie; + int error; + uint8_t cmd[3]; + uint8_t rawdata[7]; + 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; + } + + /* + The documented conversion calculations for the raw values are as follows: + + %RH = (Srh / 2^20) * 100% + + T in Celsius = ((St / 2^20) * 200) - 50 + + It follows then: + + T in Kelvin = ((St / 2^20) * 200) + 223.15 + + given the relationship between Celsius and Kelvin. + + What follows reorders the calculation a bit and scales it up to avoid + the use of any floating point. All that would really have to happen + is a scale up to 10^6 for the sysenv framework, which wants + temperature in micro-kelvin and percent relative humidity scaled up + 10^6, but since this conversion uses 64 bits due to intermediate + values that are bigger than 32 bits the conversion first scales up to + 10^9 and the scales back down by 10^3 at the end. This preserves some + precision in the conversion that would otherwise be lost. + */ + + cmd[0] = AHT20_TRIGGER_MEASUREMENT; + cmd[1] = AHT20_TRIGGER_PARAM1; + cmd[2] = AHT20_TRIGGER_PARAM2; + error = aht20_cmdr(sc, cmd, 3, rawdata, 7); + + if (error == 0) { + if (rawdata[0] & AHT20_STATUS_BUSY_MASK) { + aprint_error_dev(sc->sc_dev, + "Chip is busy. Status register: %02x\n", + rawdata[0]); + error = EINVAL; + } + + if (error == 0 && + rawdata[0] & AHT20_STATUS_CAL_MASK) { + + uint8_t testcrc; + + testcrc = aht20_crc(&rawdata[0],6); + + DPRINTF(sc, 2, ("%s: Raw data: STATUS: %02x - RH: %02x %02x - %02x - TEMP: %02x %02x - CRC: %02x -- %02x\n", + device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2], + rawdata[3], rawdata[4], rawdata[5], rawdata[6], testcrc)); + + /* This chip splits the %rh and temp raw files ove the 3 byte returned. Since + there is no choice but to get both, split them both apart every time */ + + uint64_t rawhum; + uint64_t rawtemp; + + rawhum = (rawdata[1] << 12) | (rawdata[2] << 4) | ((rawdata[3] & 0xf0) >> 4); + rawtemp = ((rawdata[3] & 0x0f) << 16) | (rawdata[4] << 8) | rawdata[5]; + + DPRINTF(sc, 2, ("%s: Raw broken data: RH: %04jx (%jd) - TEMP: %04jx (%jd)\n", + device_xname(sc->sc_dev), rawhum, rawhum, rawtemp, rawtemp)); + + /* Fake out the CRC check if being asked to ignore CRC */ + if (sc->sc_ignorecrc) { + testcrc = rawdata[6]; + } + + if (rawdata[6] == testcrc) { + uint64_t q = 0; + + switch (edata->sensor) { + case AHT20_TEMP_SENSOR: + q = (((rawtemp * 1000000000) / 10485760) * 2) + 223150000; + + break; + case AHT20_HUMIDITY_SENSOR: + q = (rawhum * 1000000000) / 10485760; + + break; + default: + error = EINVAL; + break; + } + + DPRINTF(sc, 2, ("%s: Computed sensor: %#jx (%jd)\n", + device_xname(sc->sc_dev), (uintmax_t)q, (uintmax_t)q)); + + /* The results will fit in 32 bits, so nothing will be lost */ + edata->value_cur = (uint32_t) q; + edata->state = ENVSYS_SVALID; + } else { + error = EINVAL; + } + } else { + if (error == 0) { + aprint_error_dev(sc->sc_dev,"Calibration needs to be run on the chip.\n"); + + cmd[0] = AHT20_INITIALIZE; + cmd[1] = AHT20_INITIALIZE_PARAM1; + cmd[2] = AHT20_INITIALIZE_PARAM2; + error = aht20_cmdr(sc, cmd, 3, NULL, 0); + + if (error) { + DPRINTF(sc, 2, ("%s: Calibration failed to run: %d\n", + device_xname(sc->sc_dev), error)); + } + } + } + } + + if (error) { + DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n", + device_xname(sc->sc_dev), error)); + } + + iic_release_bus(sc->sc_tag, 0); +out: + mutex_exit(&sc->sc_mutex); +} + +static int +aht20_detach(device_t self, int flags) +{ + struct aht20_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_aht20log); + + /* Remove the mutex */ + mutex_destroy(&sc->sc_mutex); + + return 0; +} + +MODULE(MODULE_CLASS_DRIVER, aht20temp, "iic,sysmon_envsys"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +aht20temp_modcmd(modcmd_t cmd, void *opaque) +{ + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + return config_init_component(cfdriver_ioconf_aht20temp, + cfattach_ioconf_aht20temp, cfdata_ioconf_aht20temp); +#else + return 0; +#endif + case MODULE_CMD_FINI: +#ifdef _MODULE + return config_fini_component(cfdriver_ioconf_aht20temp, + cfattach_ioconf_aht20temp, cfdata_ioconf_aht20temp); +#else + return 0; +#endif + default: + return ENOTTY; + } +} Index: src/sys/dev/i2c/aht20reg.h diff -u /dev/null src/sys/dev/i2c/aht20reg.h:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/sys/dev/i2c/aht20reg.h Thu Nov 17 19:20:06 2022 @@ -0,0 +1,37 @@ +/* $NetBSD: aht20reg.h,v 1.1 2022/11/17 19:20:06 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer <b...@anduin.eldar.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. + */ + +#ifndef _DEV_I2C_AHT20REG_H_ +#define _DEV_I2C_AHT20REG_H_ + +#define AHT20_TYPICAL_ADDR 0x38 + +#define AHT20_INITIALIZE 0xBE +#define AHT20_INITIALIZE_PARAM1 0x08 +#define AHT20_INITIALIZE_PARAM2 0x00 + +#define AHT20_TRIGGER_MEASUREMENT 0xAC +#define AHT20_TRIGGER_PARAM1 0x33 +#define AHT20_TRIGGER_PARAM2 0x00 + +#define AHT20_GET_STATUS 0x71 +#define AHT20_STATUS_BUSY_MASK 0x80 +#define AHT20_STATUS_CAL_MASK 0x08 +#define AHT20_SOFT_RESET 0xBA + +#endif Index: src/sys/dev/i2c/aht20var.h diff -u /dev/null src/sys/dev/i2c/aht20var.h:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/sys/dev/i2c/aht20var.h Thu Nov 17 19:20:06 2022 @@ -0,0 +1,50 @@ +/* $NetBSD: aht20var.h,v 1.1 2022/11/17 19:20:06 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer <b...@anduin.eldar.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. + */ + +#ifndef _DEV_I2C_AHT20VAR_H_ +#define _DEV_I2C_AHT20VAR_H_ + +#define AHT20_NUM_SENSORS 2 +#define AHT20_HUMIDITY_SENSOR 0 +#define AHT20_TEMP_SENSOR 1 + +struct aht20_sc { + int sc_aht20debug; + 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; + struct sysctllog *sc_aht20log; + envsys_data_t sc_sensors[AHT20_NUM_SENSORS]; + bool sc_ignorecrc; + int sc_readattempts; +}; + +struct aht20_sensor { + const char *desc; + enum envsys_units type; +}; + +struct aht20_timing { + uint8_t cmd; + int typicaldelay; +}; + +#endif Index: src/sys/modules/aht20temp/Makefile diff -u /dev/null src/sys/modules/aht20temp/Makefile:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/sys/modules/aht20temp/Makefile Thu Nov 17 19:20:05 2022 @@ -0,0 +1,11 @@ +.include "../Makefile.inc" + +.PATH: ${S}/dev/i2c + +KMOD= aht20temp +IOCONF= aht20temp.ioconf +SRCS= aht20.c + +WARNS= 3 + +.include <bsd.kmodule.mk> Index: src/sys/modules/aht20temp/aht20temp.ioconf diff -u /dev/null src/sys/modules/aht20temp/aht20temp.ioconf:1.1 --- /dev/null Thu Nov 17 19:20:06 2022 +++ src/sys/modules/aht20temp/aht20temp.ioconf Thu Nov 17 19:20:05 2022 @@ -0,0 +1,7 @@ +ioconf aht20temp + +include "conf/files" + +pseudo-root iic* + +aht20temp* at iic? addr 0x38