Home | History | Annotate | Line # | Download | only in i2c
      1  1.1  brad /*	$NetBSD: aht20.c,v 1.1 2022/11/17 19:20:06 brad Exp $	*/
      2  1.1  brad 
      3  1.1  brad /*
      4  1.1  brad  * Copyright (c) 2022 Brad Spencer <brad (at) anduin.eldar.org>
      5  1.1  brad  *
      6  1.1  brad  * Permission to use, copy, modify, and distribute this software for any
      7  1.1  brad  * purpose with or without fee is hereby granted, provided that the above
      8  1.1  brad  * copyright notice and this permission notice appear in all copies.
      9  1.1  brad  *
     10  1.1  brad  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1  brad  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1  brad  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1  brad  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1  brad  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  1.1  brad  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  1.1  brad  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1  brad  */
     18  1.1  brad 
     19  1.1  brad #include <sys/cdefs.h>
     20  1.1  brad __KERNEL_RCSID(0, "$NetBSD: aht20.c,v 1.1 2022/11/17 19:20:06 brad Exp $");
     21  1.1  brad 
     22  1.1  brad /*
     23  1.1  brad   Driver for the Guangzhou Aosong AHT20 temperature and humidity sensor
     24  1.1  brad */
     25  1.1  brad 
     26  1.1  brad #include <sys/param.h>
     27  1.1  brad #include <sys/systm.h>
     28  1.1  brad #include <sys/kernel.h>
     29  1.1  brad #include <sys/device.h>
     30  1.1  brad #include <sys/module.h>
     31  1.1  brad #include <sys/sysctl.h>
     32  1.1  brad #include <sys/mutex.h>
     33  1.1  brad 
     34  1.1  brad #include <dev/sysmon/sysmonvar.h>
     35  1.1  brad #include <dev/i2c/i2cvar.h>
     36  1.1  brad #include <dev/i2c/aht20reg.h>
     37  1.1  brad #include <dev/i2c/aht20var.h>
     38  1.1  brad 
     39  1.1  brad 
     40  1.1  brad static uint8_t 	aht20_crc(uint8_t *, size_t);
     41  1.1  brad static int 	aht20_poke(i2c_tag_t, i2c_addr_t, bool);
     42  1.1  brad static int 	aht20_match(device_t, cfdata_t, void *);
     43  1.1  brad static void 	aht20_attach(device_t, device_t, void *);
     44  1.1  brad static int 	aht20_detach(device_t, int);
     45  1.1  brad static void 	aht20_refresh(struct sysmon_envsys *, envsys_data_t *);
     46  1.1  brad static int 	aht20_verify_sysctl(SYSCTLFN_ARGS);
     47  1.1  brad 
     48  1.1  brad #define AHT20_DEBUG
     49  1.1  brad #ifdef AHT20_DEBUG
     50  1.1  brad #define DPRINTF(s, l, x) \
     51  1.1  brad     do { \
     52  1.1  brad 	if (l <= s->sc_aht20debug) \
     53  1.1  brad 	    printf x; \
     54  1.1  brad     } while (/*CONSTCOND*/0)
     55  1.1  brad #else
     56  1.1  brad #define DPRINTF(s, l, x)
     57  1.1  brad #endif
     58  1.1  brad 
     59  1.1  brad CFATTACH_DECL_NEW(aht20temp, sizeof(struct aht20_sc),
     60  1.1  brad     aht20_match, aht20_attach, aht20_detach, NULL);
     61  1.1  brad 
     62  1.1  brad static struct aht20_sensor aht20_sensors[] = {
     63  1.1  brad 	{
     64  1.1  brad 		.desc = "humidity",
     65  1.1  brad 		.type = ENVSYS_SRELHUMIDITY,
     66  1.1  brad 	},
     67  1.1  brad 	{
     68  1.1  brad 		.desc = "temperature",
     69  1.1  brad 		.type = ENVSYS_STEMP,
     70  1.1  brad 	}
     71  1.1  brad };
     72  1.1  brad 
     73  1.1  brad /*
     74  1.1  brad  * The delays are mentioned in the datasheet for the chip, except for
     75  1.1  brad  * the get status command.
     76  1.1  brad  */
     77  1.1  brad 
     78  1.1  brad static struct aht20_timing aht20_timings[] = {
     79  1.1  brad 	{
     80  1.1  brad 		.cmd = AHT20_INITIALIZE,
     81  1.1  brad 		.typicaldelay = 10000,
     82  1.1  brad 	},
     83  1.1  brad 	{
     84  1.1  brad 		.cmd = AHT20_TRIGGER_MEASUREMENT,
     85  1.1  brad 		.typicaldelay = 80000,
     86  1.1  brad 	},
     87  1.1  brad 	{
     88  1.1  brad 		.cmd = AHT20_GET_STATUS,
     89  1.1  brad 		.typicaldelay = 5000,
     90  1.1  brad 	},
     91  1.1  brad 	{
     92  1.1  brad 		.cmd = AHT20_SOFT_RESET,
     93  1.1  brad 		.typicaldelay = 20000,
     94  1.1  brad 	}
     95  1.1  brad };
     96  1.1  brad 
     97  1.1  brad int
     98  1.1  brad aht20_verify_sysctl(SYSCTLFN_ARGS)
     99  1.1  brad {
    100  1.1  brad 	int error, t;
    101  1.1  brad 	struct sysctlnode node;
    102  1.1  brad 
    103  1.1  brad 	node = *rnode;
    104  1.1  brad 	t = *(int *)rnode->sysctl_data;
    105  1.1  brad 	node.sysctl_data = &t;
    106  1.1  brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    107  1.1  brad 	if (error || newp == NULL)
    108  1.1  brad 		return error;
    109  1.1  brad 
    110  1.1  brad 	if (t < 0)
    111  1.1  brad 		return EINVAL;
    112  1.1  brad 
    113  1.1  brad 	*(int *)rnode->sysctl_data = t;
    114  1.1  brad 
    115  1.1  brad 	return 0;
    116  1.1  brad }
    117  1.1  brad 
    118  1.1  brad static int
    119  1.1  brad aht20_cmddelay(uint8_t cmd)
    120  1.1  brad {
    121  1.1  brad 	int r = -1;
    122  1.1  brad 
    123  1.1  brad 	for(int i = 0;i < __arraycount(aht20_timings);i++) {
    124  1.1  brad 		if (cmd == aht20_timings[i].cmd) {
    125  1.1  brad 			r = aht20_timings[i].typicaldelay;
    126  1.1  brad 			break;
    127  1.1  brad 		}
    128  1.1  brad 	}
    129  1.1  brad 
    130  1.1  brad 	if (r == -1) {
    131  1.1  brad 		panic("Bad command look up in cmd delay: cmd: %d\n",cmd);
    132  1.1  brad 	}
    133  1.1  brad 
    134  1.1  brad 	return r;
    135  1.1  brad }
    136  1.1  brad 
    137  1.1  brad static int
    138  1.1  brad aht20_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
    139  1.1  brad     uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
    140  1.1  brad {
    141  1.1  brad 	int error;
    142  1.1  brad 	int cmddelay;
    143  1.1  brad 
    144  1.1  brad 	error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,cmd,clen,NULL,0,0);
    145  1.1  brad 
    146  1.1  brad 	/* Every command returns something except for the soft reset and
    147  1.1  brad 	   initialize which returns nothing.
    148  1.1  brad 	*/
    149  1.1  brad 
    150  1.1  brad 	if (error == 0) {
    151  1.1  brad 		cmddelay = aht20_cmddelay(cmd[0]);
    152  1.1  brad 		delay(cmddelay);
    153  1.1  brad 
    154  1.1  brad 		if (cmd[0] != AHT20_SOFT_RESET &&
    155  1.1  brad 		    cmd[0] != AHT20_INITIALIZE) {
    156  1.1  brad 			for (int aint = 0; aint < readattempts; aint++) {
    157  1.1  brad 				error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0);
    158  1.1  brad 				if (error == 0)
    159  1.1  brad 					break;
    160  1.1  brad 				delay(1000);
    161  1.1  brad 			}
    162  1.1  brad 		}
    163  1.1  brad 	}
    164  1.1  brad 
    165  1.1  brad 	return error;
    166  1.1  brad }
    167  1.1  brad 
    168  1.1  brad static int
    169  1.1  brad aht20_cmdr(struct aht20_sc *sc, uint8_t *cmd, uint8_t clen, uint8_t *buf, size_t blen)
    170  1.1  brad {
    171  1.1  brad 	KASSERT(clen > 0);
    172  1.1  brad 
    173  1.1  brad 	return aht20_cmd(sc->sc_tag, sc->sc_addr, cmd, clen, buf, blen, sc->sc_readattempts);
    174  1.1  brad }
    175  1.1  brad 
    176  1.1  brad static	uint8_t
    177  1.1  brad aht20_crc(uint8_t * data, size_t size)
    178  1.1  brad {
    179  1.1  brad 	uint8_t crc = 0xFF;
    180  1.1  brad 
    181  1.1  brad 	for (size_t i = 0; i < size; i++) {
    182  1.1  brad 		crc ^= data[i];
    183  1.1  brad 		for (size_t j = 8; j > 0; j--) {
    184  1.1  brad 			if (crc & 0x80)
    185  1.1  brad 				crc = (crc << 1) ^ 0x31;
    186  1.1  brad 			else
    187  1.1  brad 				crc <<= 1;
    188  1.1  brad 		}
    189  1.1  brad 	}
    190  1.1  brad 	return crc;
    191  1.1  brad }
    192  1.1  brad 
    193  1.1  brad static int
    194  1.1  brad aht20_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
    195  1.1  brad {
    196  1.1  brad 	uint8_t reg = AHT20_GET_STATUS;
    197  1.1  brad 	uint8_t buf[6];
    198  1.1  brad 	int error;
    199  1.1  brad 
    200  1.1  brad 	error = aht20_cmd(tag, addr, &reg, 1, buf, 1, 10);
    201  1.1  brad 	if (matchdebug) {
    202  1.1  brad 		printf("poke X 1: %d\n", error);
    203  1.1  brad 	}
    204  1.1  brad 	return error;
    205  1.1  brad }
    206  1.1  brad 
    207  1.1  brad static int
    208  1.1  brad aht20_sysctl_init(struct aht20_sc *sc)
    209  1.1  brad {
    210  1.1  brad 	int error;
    211  1.1  brad 	const struct sysctlnode *cnode;
    212  1.1  brad 	int sysctlroot_num;
    213  1.1  brad 
    214  1.1  brad 	if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode,
    215  1.1  brad 	    0, CTLTYPE_NODE, device_xname(sc->sc_dev),
    216  1.1  brad 	    SYSCTL_DESCR("aht20 controls"), NULL, 0, NULL, 0, CTL_HW,
    217  1.1  brad 	    CTL_CREATE, CTL_EOL)) != 0)
    218  1.1  brad 		return error;
    219  1.1  brad 
    220  1.1  brad 	sysctlroot_num = cnode->sysctl_num;
    221  1.1  brad 
    222  1.1  brad #ifdef AHT20_DEBUG
    223  1.1  brad 	if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode,
    224  1.1  brad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
    225  1.1  brad 	    SYSCTL_DESCR("Debug level"), aht20_verify_sysctl, 0,
    226  1.1  brad 	    &sc->sc_aht20debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
    227  1.1  brad 	    CTL_EOL)) != 0)
    228  1.1  brad 		return error;
    229  1.1  brad 
    230  1.1  brad #endif
    231  1.1  brad 
    232  1.1  brad 	if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode,
    233  1.1  brad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
    234  1.1  brad 	    SYSCTL_DESCR("The number of times to attempt to read the values"),
    235  1.1  brad 	    aht20_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
    236  1.1  brad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    237  1.1  brad 		return error;
    238  1.1  brad 
    239  1.1  brad 	if ((error = sysctl_createv(&sc->sc_aht20log, 0, NULL, &cnode,
    240  1.1  brad 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
    241  1.1  brad 	    SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
    242  1.1  brad 	    0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    243  1.1  brad 		return error;
    244  1.1  brad 
    245  1.1  brad 	return 0;
    246  1.1  brad }
    247  1.1  brad 
    248  1.1  brad static int
    249  1.1  brad aht20_match(device_t parent, cfdata_t match, void *aux)
    250  1.1  brad {
    251  1.1  brad 	struct i2c_attach_args *ia = aux;
    252  1.1  brad 	int error, match_result;
    253  1.1  brad 	const bool matchdebug = false;
    254  1.1  brad 
    255  1.1  brad 	if (iic_use_direct_match(ia, match, NULL, &match_result))
    256  1.1  brad 		return match_result;
    257  1.1  brad 
    258  1.1  brad 	/* indirect config - check for configured address */
    259  1.1  brad 	if (ia->ia_addr != AHT20_TYPICAL_ADDR)
    260  1.1  brad 		return 0;
    261  1.1  brad 
    262  1.1  brad 	/*
    263  1.1  brad 	 * Check to see if something is really at this i2c address. This will
    264  1.1  brad 	 * keep phantom devices from appearing
    265  1.1  brad 	 */
    266  1.1  brad 	if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
    267  1.1  brad 		if (matchdebug)
    268  1.1  brad 			printf("in match acquire bus failed\n");
    269  1.1  brad 		return 0;
    270  1.1  brad 	}
    271  1.1  brad 
    272  1.1  brad 	error = aht20_poke(ia->ia_tag, ia->ia_addr, matchdebug);
    273  1.1  brad 	iic_release_bus(ia->ia_tag, 0);
    274  1.1  brad 
    275  1.1  brad 	return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
    276  1.1  brad }
    277  1.1  brad 
    278  1.1  brad static void
    279  1.1  brad aht20_attach(device_t parent, device_t self, void *aux)
    280  1.1  brad {
    281  1.1  brad 	struct aht20_sc *sc;
    282  1.1  brad 	struct i2c_attach_args *ia;
    283  1.1  brad 	uint8_t cmd[1];
    284  1.1  brad 	int error, i;
    285  1.1  brad 
    286  1.1  brad 	ia = aux;
    287  1.1  brad 	sc = device_private(self);
    288  1.1  brad 
    289  1.1  brad 	sc->sc_dev = self;
    290  1.1  brad 	sc->sc_tag = ia->ia_tag;
    291  1.1  brad 	sc->sc_addr = ia->ia_addr;
    292  1.1  brad 	sc->sc_aht20debug = 0;
    293  1.1  brad 	sc->sc_readattempts = 10;
    294  1.1  brad 	sc->sc_ignorecrc = false;
    295  1.1  brad 	sc->sc_sme = NULL;
    296  1.1  brad 
    297  1.1  brad 	aprint_normal("\n");
    298  1.1  brad 
    299  1.1  brad 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
    300  1.1  brad 	sc->sc_numsensors = __arraycount(aht20_sensors);
    301  1.1  brad 
    302  1.1  brad 	if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
    303  1.1  brad 		aprint_error_dev(self,
    304  1.1  brad 		    "Unable to create sysmon structure\n");
    305  1.1  brad 		sc->sc_sme = NULL;
    306  1.1  brad 		return;
    307  1.1  brad 	}
    308  1.1  brad 	if ((error = aht20_sysctl_init(sc)) != 0) {
    309  1.1  brad 		aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
    310  1.1  brad 		goto out;
    311  1.1  brad 	}
    312  1.1  brad 
    313  1.1  brad 	error = iic_acquire_bus(sc->sc_tag, 0);
    314  1.1  brad 	if (error) {
    315  1.1  brad 		aprint_error_dev(self, "Could not acquire iic bus: %d\n",
    316  1.1  brad 		    error);
    317  1.1  brad 		goto out;
    318  1.1  brad 	}
    319  1.1  brad 
    320  1.1  brad 	cmd[0] = AHT20_SOFT_RESET;
    321  1.1  brad 	error = aht20_cmdr(sc, cmd, 1, NULL, 0);
    322  1.1  brad 	if (error != 0)
    323  1.1  brad 		aprint_error_dev(self, "Reset failed: %d\n", error);
    324  1.1  brad 
    325  1.1  brad 	iic_release_bus(sc->sc_tag, 0);
    326  1.1  brad 
    327  1.1  brad 	if (error != 0) {
    328  1.1  brad 		aprint_error_dev(self, "Unable to setup device\n");
    329  1.1  brad 		goto out;
    330  1.1  brad 	}
    331  1.1  brad 
    332  1.1  brad 	for (i = 0; i < sc->sc_numsensors; i++) {
    333  1.1  brad 		strlcpy(sc->sc_sensors[i].desc, aht20_sensors[i].desc,
    334  1.1  brad 		    sizeof(sc->sc_sensors[i].desc));
    335  1.1  brad 
    336  1.1  brad 		sc->sc_sensors[i].units = aht20_sensors[i].type;
    337  1.1  brad 		sc->sc_sensors[i].state = ENVSYS_SINVALID;
    338  1.1  brad 
    339  1.1  brad 		DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
    340  1.1  brad 		    sc->sc_sensors[i].desc));
    341  1.1  brad 
    342  1.1  brad 		error = sysmon_envsys_sensor_attach(sc->sc_sme,
    343  1.1  brad 		    &sc->sc_sensors[i]);
    344  1.1  brad 		if (error) {
    345  1.1  brad 			aprint_error_dev(self,
    346  1.1  brad 			    "Unable to attach sensor %d: %d\n", i, error);
    347  1.1  brad 			goto out;
    348  1.1  brad 		}
    349  1.1  brad 	}
    350  1.1  brad 
    351  1.1  brad 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
    352  1.1  brad 	sc->sc_sme->sme_cookie = sc;
    353  1.1  brad 	sc->sc_sme->sme_refresh = aht20_refresh;
    354  1.1  brad 
    355  1.1  brad 	DPRINTF(sc, 2, ("aht20_attach: registering with envsys\n"));
    356  1.1  brad 
    357  1.1  brad 	if (sysmon_envsys_register(sc->sc_sme)) {
    358  1.1  brad 		aprint_error_dev(self,
    359  1.1  brad 			"unable to register with sysmon\n");
    360  1.1  brad 		sysmon_envsys_destroy(sc->sc_sme);
    361  1.1  brad 		sc->sc_sme = NULL;
    362  1.1  brad 		return;
    363  1.1  brad 	}
    364  1.1  brad 
    365  1.1  brad 	aprint_normal_dev(self, "Guangzhou Aosong AHT20\n");
    366  1.1  brad 
    367  1.1  brad 	return;
    368  1.1  brad out:
    369  1.1  brad 	sysmon_envsys_destroy(sc->sc_sme);
    370  1.1  brad 	sc->sc_sme = NULL;
    371  1.1  brad }
    372  1.1  brad 
    373  1.1  brad static void
    374  1.1  brad aht20_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
    375  1.1  brad {
    376  1.1  brad 	struct aht20_sc *sc;
    377  1.1  brad 	sc = sme->sme_cookie;
    378  1.1  brad 	int error;
    379  1.1  brad 	uint8_t cmd[3];
    380  1.1  brad 	uint8_t rawdata[7];
    381  1.1  brad 	edata->state = ENVSYS_SINVALID;
    382  1.1  brad 
    383  1.1  brad 	mutex_enter(&sc->sc_mutex);
    384  1.1  brad 	error = iic_acquire_bus(sc->sc_tag, 0);
    385  1.1  brad 	if (error) {
    386  1.1  brad 		DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
    387  1.1  brad 		    device_xname(sc->sc_dev), error));
    388  1.1  brad 		goto out;
    389  1.1  brad 	}
    390  1.1  brad 
    391  1.1  brad 	/*
    392  1.1  brad 	  The documented conversion calculations for the raw values are as follows:
    393  1.1  brad 
    394  1.1  brad 	  %RH = (Srh / 2^20) * 100%
    395  1.1  brad 
    396  1.1  brad 	  T in Celsius = ((St / 2^20) * 200) - 50
    397  1.1  brad 
    398  1.1  brad 	  It follows then:
    399  1.1  brad 
    400  1.1  brad 	  T in Kelvin = ((St / 2^20) * 200) + 223.15
    401  1.1  brad 
    402  1.1  brad 	  given the relationship between Celsius and Kelvin.
    403  1.1  brad 
    404  1.1  brad 	  What follows reorders the calculation a bit and scales it up to avoid
    405  1.1  brad 	  the use of any floating point.  All that would really have to happen
    406  1.1  brad 	  is a scale up to 10^6 for the sysenv framework, which wants
    407  1.1  brad 	  temperature in micro-kelvin and percent relative humidity scaled up
    408  1.1  brad 	  10^6, but since this conversion uses 64 bits due to intermediate
    409  1.1  brad 	  values that are bigger than 32 bits the conversion first scales up to
    410  1.1  brad 	  10^9 and the scales back down by 10^3 at the end.  This preserves some
    411  1.1  brad 	  precision in the conversion that would otherwise be lost.
    412  1.1  brad 	 */
    413  1.1  brad 
    414  1.1  brad 	cmd[0] = AHT20_TRIGGER_MEASUREMENT;
    415  1.1  brad 	cmd[1] = AHT20_TRIGGER_PARAM1;
    416  1.1  brad 	cmd[2] = AHT20_TRIGGER_PARAM2;
    417  1.1  brad 	error = aht20_cmdr(sc, cmd, 3, rawdata, 7);
    418  1.1  brad 
    419  1.1  brad 	if (error == 0) {
    420  1.1  brad 		if (rawdata[0] & AHT20_STATUS_BUSY_MASK) {
    421  1.1  brad 			aprint_error_dev(sc->sc_dev,
    422  1.1  brad 			    "Chip is busy.  Status register: %02x\n",
    423  1.1  brad 			    rawdata[0]);
    424  1.1  brad 			error = EINVAL;
    425  1.1  brad 		}
    426  1.1  brad 
    427  1.1  brad 		if (error == 0 &&
    428  1.1  brad 		    rawdata[0] & AHT20_STATUS_CAL_MASK) {
    429  1.1  brad 
    430  1.1  brad 			uint8_t testcrc;
    431  1.1  brad 
    432  1.1  brad 			testcrc = aht20_crc(&rawdata[0],6);
    433  1.1  brad 
    434  1.1  brad 			DPRINTF(sc, 2, ("%s: Raw data: STATUS: %02x - RH: %02x %02x - %02x - TEMP: %02x %02x - CRC: %02x -- %02x\n",
    435  1.1  brad 			    device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
    436  1.1  brad 			    rawdata[3], rawdata[4], rawdata[5], rawdata[6], testcrc));
    437  1.1  brad 
    438  1.1  brad 			/* This chip splits the %rh and temp raw files ove the 3 byte returned.  Since
    439  1.1  brad 			   there is no choice but to get both, split them both apart every time */
    440  1.1  brad 
    441  1.1  brad 			uint64_t rawhum;
    442  1.1  brad 			uint64_t rawtemp;
    443  1.1  brad 
    444  1.1  brad 			rawhum = (rawdata[1] << 12) | (rawdata[2] << 4) | ((rawdata[3] & 0xf0) >> 4);
    445  1.1  brad 			rawtemp = ((rawdata[3] & 0x0f) << 16) | (rawdata[4] << 8) | rawdata[5];
    446  1.1  brad 
    447  1.1  brad 			DPRINTF(sc, 2, ("%s: Raw broken data: RH: %04jx (%jd) - TEMP: %04jx (%jd)\n",
    448  1.1  brad 			    device_xname(sc->sc_dev), rawhum, rawhum, rawtemp, rawtemp));
    449  1.1  brad 
    450  1.1  brad 			/* Fake out the CRC check if being asked to ignore CRC */
    451  1.1  brad 			if (sc->sc_ignorecrc) {
    452  1.1  brad 				testcrc = rawdata[6];
    453  1.1  brad 			}
    454  1.1  brad 
    455  1.1  brad 			if (rawdata[6] == testcrc) {
    456  1.1  brad 				uint64_t q = 0;
    457  1.1  brad 
    458  1.1  brad 				switch (edata->sensor) {
    459  1.1  brad 				case AHT20_TEMP_SENSOR:
    460  1.1  brad 					q = (((rawtemp * 1000000000) / 10485760) * 2) + 223150000;
    461  1.1  brad 
    462  1.1  brad 					break;
    463  1.1  brad 				case AHT20_HUMIDITY_SENSOR:
    464  1.1  brad 					q = (rawhum * 1000000000) / 10485760;
    465  1.1  brad 
    466  1.1  brad 					break;
    467  1.1  brad 				default:
    468  1.1  brad 					error = EINVAL;
    469  1.1  brad 					break;
    470  1.1  brad 				}
    471  1.1  brad 
    472  1.1  brad 				DPRINTF(sc, 2, ("%s: Computed sensor: %#jx (%jd)\n",
    473  1.1  brad 				    device_xname(sc->sc_dev), (uintmax_t)q, (uintmax_t)q));
    474  1.1  brad 
    475  1.1  brad 				/* The results will fit in 32 bits, so nothing will be lost */
    476  1.1  brad 				edata->value_cur = (uint32_t) q;
    477  1.1  brad 				edata->state = ENVSYS_SVALID;
    478  1.1  brad 			} else {
    479  1.1  brad 				error = EINVAL;
    480  1.1  brad 			}
    481  1.1  brad 		} else {
    482  1.1  brad 			if (error == 0) {
    483  1.1  brad 				aprint_error_dev(sc->sc_dev,"Calibration needs to be run on the chip.\n");
    484  1.1  brad 
    485  1.1  brad 				cmd[0] = AHT20_INITIALIZE;
    486  1.1  brad 				cmd[1] = AHT20_INITIALIZE_PARAM1;
    487  1.1  brad 				cmd[2] = AHT20_INITIALIZE_PARAM2;
    488  1.1  brad 				error = aht20_cmdr(sc, cmd, 3, NULL, 0);
    489  1.1  brad 
    490  1.1  brad 				if (error) {
    491  1.1  brad 					DPRINTF(sc, 2, ("%s: Calibration failed to run: %d\n",
    492  1.1  brad 					    device_xname(sc->sc_dev), error));
    493  1.1  brad 				}
    494  1.1  brad 			}
    495  1.1  brad 		}
    496  1.1  brad 	}
    497  1.1  brad 
    498  1.1  brad 	if (error) {
    499  1.1  brad 		DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
    500  1.1  brad 		    device_xname(sc->sc_dev), error));
    501  1.1  brad 	}
    502  1.1  brad 
    503  1.1  brad 	iic_release_bus(sc->sc_tag, 0);
    504  1.1  brad out:
    505  1.1  brad 	mutex_exit(&sc->sc_mutex);
    506  1.1  brad }
    507  1.1  brad 
    508  1.1  brad static int
    509  1.1  brad aht20_detach(device_t self, int flags)
    510  1.1  brad {
    511  1.1  brad 	struct aht20_sc *sc;
    512  1.1  brad 
    513  1.1  brad 	sc = device_private(self);
    514  1.1  brad 
    515  1.1  brad 	mutex_enter(&sc->sc_mutex);
    516  1.1  brad 
    517  1.1  brad 	/* Remove the sensors */
    518  1.1  brad 	if (sc->sc_sme != NULL) {
    519  1.1  brad 		sysmon_envsys_unregister(sc->sc_sme);
    520  1.1  brad 		sc->sc_sme = NULL;
    521  1.1  brad 	}
    522  1.1  brad 	mutex_exit(&sc->sc_mutex);
    523  1.1  brad 
    524  1.1  brad 	/* Remove the sysctl tree */
    525  1.1  brad 	sysctl_teardown(&sc->sc_aht20log);
    526  1.1  brad 
    527  1.1  brad 	/* Remove the mutex */
    528  1.1  brad 	mutex_destroy(&sc->sc_mutex);
    529  1.1  brad 
    530  1.1  brad 	return 0;
    531  1.1  brad }
    532  1.1  brad 
    533  1.1  brad MODULE(MODULE_CLASS_DRIVER, aht20temp, "iic,sysmon_envsys");
    534  1.1  brad 
    535  1.1  brad #ifdef _MODULE
    536  1.1  brad #include "ioconf.c"
    537  1.1  brad #endif
    538  1.1  brad 
    539  1.1  brad static int
    540  1.1  brad aht20temp_modcmd(modcmd_t cmd, void *opaque)
    541  1.1  brad {
    542  1.1  brad 
    543  1.1  brad 	switch (cmd) {
    544  1.1  brad 	case MODULE_CMD_INIT:
    545  1.1  brad #ifdef _MODULE
    546  1.1  brad 		return config_init_component(cfdriver_ioconf_aht20temp,
    547  1.1  brad 		    cfattach_ioconf_aht20temp, cfdata_ioconf_aht20temp);
    548  1.1  brad #else
    549  1.1  brad 		return 0;
    550  1.1  brad #endif
    551  1.1  brad 	case MODULE_CMD_FINI:
    552  1.1  brad #ifdef _MODULE
    553  1.1  brad 		return config_fini_component(cfdriver_ioconf_aht20temp,
    554  1.1  brad 		      cfattach_ioconf_aht20temp, cfdata_ioconf_aht20temp);
    555  1.1  brad #else
    556  1.1  brad 		return 0;
    557  1.1  brad #endif
    558  1.1  brad 	default:
    559  1.1  brad 		return ENOTTY;
    560  1.1  brad 	}
    561  1.1  brad }
    562