Home | History | Annotate | Line # | Download | only in i2c
      1 /*	$NetBSD: sgp40.c,v 1.5 2022/05/24 06:28:01 andvar Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2021 Brad Spencer <brad (at) anduin.eldar.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGL`IGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __KERNEL_RCSID(0, "$NetBSD: sgp40.c,v 1.5 2022/05/24 06:28:01 andvar Exp $");
     21 
     22 /*
     23   Driver for the Sensirion SGP40 MOx gas sensor for air quality
     24 */
     25 
     26 #include <sys/param.h>
     27 #include <sys/systm.h>
     28 #include <sys/kernel.h>
     29 #include <sys/device.h>
     30 #include <sys/module.h>
     31 #include <sys/sysctl.h>
     32 #include <sys/mutex.h>
     33 #include <sys/condvar.h>
     34 #include <sys/kthread.h>
     35 
     36 #include <dev/sysmon/sysmonvar.h>
     37 #include <dev/i2c/i2cvar.h>
     38 #include <dev/i2c/sgp40reg.h>
     39 #include <dev/i2c/sgp40var.h>
     40 
     41 #include <dev/i2c/sensirion_arch_config.h>
     42 #include <dev/i2c/sensirion_voc_algorithm.h>
     43 
     44 static uint8_t 	sgp40_crc(uint8_t *, size_t);
     45 static int      sgp40_cmdr(struct sgp40_sc *, uint16_t, uint8_t *, uint8_t,
     46     uint8_t *, size_t);
     47 static int 	sgp40_poke(i2c_tag_t, i2c_addr_t, bool);
     48 static int 	sgp40_match(device_t, cfdata_t, void *);
     49 static void 	sgp40_attach(device_t, device_t, void *);
     50 static int 	sgp40_detach(device_t, int);
     51 static void 	sgp40_refresh(struct sysmon_envsys *, envsys_data_t *);
     52 static int 	sgp40_verify_sysctl(SYSCTLFN_ARGS);
     53 static int 	sgp40_verify_temp_sysctl(SYSCTLFN_ARGS);
     54 static int 	sgp40_verify_rh_sysctl(SYSCTLFN_ARGS);
     55 static void     sgp40_thread(void *);
     56 static void     sgp40_stop_thread(void *);
     57 static void     sgp40_take_measurement(void *, VocAlgorithmParams *);
     58 
     59 #define SGP40_DEBUG
     60 #ifdef SGP40_DEBUG
     61 #define DPRINTF(s, l, x) \
     62     do { \
     63 	if (l <= s->sc_sgp40debug) \
     64 	    printf x; \
     65     } while (/*CONSTCOND*/0)
     66 #else
     67 #define DPRINTF(s, l, x)
     68 #endif
     69 
     70 CFATTACH_DECL_NEW(sgp40mox, sizeof(struct sgp40_sc),
     71     sgp40_match, sgp40_attach, sgp40_detach, NULL);
     72 
     73 static struct sgp40_sensor sgp40_sensors[] = {
     74 	{
     75 		.desc = "VOC index",
     76 		.type = ENVSYS_INTEGER,
     77 	}
     78 };
     79 
     80 static struct sgp40_timing sgp40_timings[] = {
     81 	{
     82 		.cmd = SGP40_MEASURE_RAW,
     83 		.typicaldelay = 25000,
     84 	},
     85 	{
     86 		.cmd = SGP40_MEASURE_TEST,
     87 		.typicaldelay = 240000,
     88 	},
     89 	{
     90 		.cmd = SGP40_HEATER_OFF,
     91 		.typicaldelay = 100,
     92 	},
     93 	{
     94 		.cmd = SGP40_GET_SERIAL_NUMBER,
     95 		.typicaldelay = 100,
     96 	},
     97 	{
     98 		.cmd = SGP40_GET_FEATURESET,
     99 		.typicaldelay = 1000,
    100 	}
    101 };
    102 
    103 void
    104 sgp40_thread(void *aux)
    105 {
    106 	struct sgp40_sc *sc = aux;
    107 	int rv;
    108 	VocAlgorithmParams voc_algorithm_params;
    109 
    110 	mutex_enter(&sc->sc_threadmutex);
    111 
    112 	VocAlgorithm_init(&voc_algorithm_params);
    113 
    114 	while (!sc->sc_stopping) {
    115 		rv = cv_timedwait(&sc->sc_condvar, &sc->sc_threadmutex,
    116 		    mstohz(1000));
    117 		if (rv == EWOULDBLOCK && !sc->sc_stopping) {
    118 			sgp40_take_measurement(sc,&voc_algorithm_params);
    119 		}
    120 	}
    121 	mutex_exit(&sc->sc_threadmutex);
    122 	kthread_exit(0);
    123 }
    124 
    125 static void
    126 sgp40_stop_thread(void *aux)
    127 {
    128 	struct sgp40_sc *sc;
    129 	sc = aux;
    130 	int error;
    131 
    132 	mutex_enter(&sc->sc_threadmutex);
    133 	sc->sc_stopping = true;
    134 	cv_signal(&sc->sc_condvar);
    135 	mutex_exit(&sc->sc_threadmutex);
    136 
    137 	/* wait for the thread to exit */
    138 	kthread_join(sc->sc_thread);
    139 
    140 	mutex_enter(&sc->sc_mutex);
    141 	error = iic_acquire_bus(sc->sc_tag, 0);
    142 	if (error) {
    143 		DPRINTF(sc, 2, ("%s: Could not acquire iic bus for heater off "
    144 		    "in stop thread: %d\n", device_xname(sc->sc_dev), error));
    145 		goto out;
    146 	}
    147 	error = sgp40_cmdr(sc, SGP40_HEATER_OFF, NULL, 0, NULL, 0);
    148 	if (error) {
    149 		DPRINTF(sc, 2, ("%s: Error turning heater off: %d\n",
    150 		    device_xname(sc->sc_dev), error));
    151 	}
    152 out:
    153 	iic_release_bus(sc->sc_tag, 0);
    154 	mutex_exit(&sc->sc_mutex);
    155 }
    156 
    157 static int
    158 sgp40_compute_temp_comp(int unconverted)
    159 {
    160 	/*
    161 	 * The published algorithm for this conversion is:
    162 	 * (temp_in_celsius + 45) * 65535 / 175
    163 	 *
    164 	 * However, this did not exactly yield the results that
    165 	 * the example in the data sheet, so something a little
    166 	 * different was done.
    167 	 *
    168 	 * (temp_in_celsius + 45) * 65536 / 175
    169 	 *
    170 	 * This was also scaled up by 10^2 and then scaled back to
    171 	 * preserve some precision.  37449 is simply (65536 * 100) / 175
    172 	 * and rounded.
    173 	 */
    174 
    175 	return (((unconverted + 45) * 100) * 37449) / 10000;
    176 }
    177 
    178 static int
    179 sgp40_compute_rh_comp(int unconverted)
    180 {
    181 	int q;
    182 
    183 	/*
    184 	 * The published algorithm for this conversion is:
    185 	 * %rh * 65535 / 100
    186 	 *
    187 	 * However, this did not exactly yield the results that
    188 	 * the example in the data sheet, so something a little
    189 	 * different was done.
    190 	 *
    191 	 * %rh * 65536 / 100
    192 	 *
    193 	 * This was also scaled up by 10^2 and then scaled back to
    194 	 * preserve some precision.  The value is also latched to 65535
    195 	 * as an upper limit.
    196 	 */
    197 
    198 	q = ((unconverted * 100) * 65536) / 10000;
    199 	if (q > 65535)
    200 		q = 65535;
    201 	return q;
    202 }
    203 
    204 static void
    205 sgp40_take_measurement(void *aux, VocAlgorithmParams* params)
    206 {
    207 	struct sgp40_sc *sc;
    208 	sc = aux;
    209 	uint8_t args[6];
    210 	uint8_t buf[3];
    211 	uint16_t rawmeasurement;
    212 	int error;
    213 	uint8_t crc;
    214 	uint16_t convertedrh, convertedtemp;
    215 	int32_t voc_index;
    216 
    217 	mutex_enter(&sc->sc_mutex);
    218 	convertedrh = (uint16_t)sgp40_compute_rh_comp(sc->sc_rhcomp);
    219 	convertedtemp = (uint16_t)sgp40_compute_temp_comp(sc->sc_tempcomp);
    220 
    221 	DPRINTF(sc, 2, ("%s: Converted RH and Temp: %04x %04x\n",
    222 	    device_xname(sc->sc_dev), convertedrh, convertedtemp));
    223 
    224 	args[0] = convertedrh >> 8;
    225 	args[1] = convertedrh & 0x00ff;
    226 	args[2] = sgp40_crc(&args[0], 2);
    227 	args[3] = convertedtemp >> 8;
    228 	args[4] = convertedtemp & 0x00ff;
    229 	args[5] = sgp40_crc(&args[3], 2);
    230 
    231 	/*
    232 	 * The VOC algorithm has a black out time when it first starts to run
    233 	 * and does not return any indicator that is going on, so voc_index
    234 	 * in that case would be 0..  however, that is also a valid response
    235 	 * otherwise, although an unlikely one.
    236 	 */
    237 	error = iic_acquire_bus(sc->sc_tag, 0);
    238 	if (error) {
    239 		DPRINTF(sc, 2, ("%s: Could not acquire iic bus for take "
    240 		    "measurement: %d\n", device_xname(sc->sc_dev), error));
    241 		sc->sc_voc = 0;
    242 		sc->sc_vocvalid = false;
    243 		goto out;
    244 	}
    245 
    246 	error = sgp40_cmdr(sc, SGP40_MEASURE_RAW, args, 6, buf, 3);
    247 	iic_release_bus(sc->sc_tag, 0);
    248 	if (error) {
    249 		DPRINTF(sc, 2, ("%s: Failed to get measurement %d\n",
    250 		    device_xname(sc->sc_dev), error));
    251 		goto out;
    252 	}
    253 
    254 	crc = sgp40_crc(&buf[0], 2);
    255 	DPRINTF(sc, 2, ("%s: Raw ticks and crc: %02x%02x %02x "
    256 	    "%02x\n", device_xname(sc->sc_dev), buf[0], buf[1],
    257 	    buf[2], crc));
    258 	if (buf[2] != crc)
    259 		goto out;
    260 
    261 	rawmeasurement = buf[0] << 8;
    262 	rawmeasurement |= buf[1];
    263 	VocAlgorithm_process(params, rawmeasurement,
    264 	    &voc_index);
    265 	DPRINTF(sc, 2, ("%s: VOC index: %d\n",
    266 	    device_xname(sc->sc_dev), voc_index));
    267 	sc->sc_voc = voc_index;
    268 	sc->sc_vocvalid = true;
    269 
    270 	mutex_exit(&sc->sc_mutex);
    271 	return;
    272 out:
    273 	sc->sc_voc = 0;
    274 	sc->sc_vocvalid = false;
    275 	mutex_exit(&sc->sc_mutex);
    276 }
    277 
    278 int
    279 sgp40_verify_sysctl(SYSCTLFN_ARGS)
    280 {
    281 	int error, t;
    282 	struct sysctlnode node;
    283 
    284 	node = *rnode;
    285 	t = *(int *)rnode->sysctl_data;
    286 	node.sysctl_data = &t;
    287 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    288 	if (error || newp == NULL)
    289 		return error;
    290 
    291 	if (t < 0)
    292 		return EINVAL;
    293 
    294 	*(int *)rnode->sysctl_data = t;
    295 
    296 	return 0;
    297 }
    298 
    299 int
    300 sgp40_verify_temp_sysctl(SYSCTLFN_ARGS)
    301 {
    302 	int error, t;
    303 	struct sysctlnode node;
    304 
    305 	node = *rnode;
    306 	t = *(int *)rnode->sysctl_data;
    307 	node.sysctl_data = &t;
    308 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    309 	if (error || newp == NULL)
    310 		return error;
    311 
    312 	if (t < -45 || t > 130)
    313 		return EINVAL;
    314 
    315 	*(int *)rnode->sysctl_data = t;
    316 
    317 	return 0;
    318 }
    319 
    320 int
    321 sgp40_verify_rh_sysctl(SYSCTLFN_ARGS)
    322 {
    323 	int error, t;
    324 	struct sysctlnode node;
    325 
    326 	node = *rnode;
    327 	t = *(int *)rnode->sysctl_data;
    328 	node.sysctl_data = &t;
    329 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    330 	if (error || newp == NULL)
    331 		return error;
    332 
    333 	if (t < 0 || t > 100)
    334 		return EINVAL;
    335 
    336 	*(int *)rnode->sysctl_data = t;
    337 
    338 	return 0;
    339 }
    340 
    341 static int
    342 sgp40_cmddelay(uint16_t cmd)
    343 {
    344 	int r = -1;
    345 
    346 	for(int i = 0;i < __arraycount(sgp40_timings);i++) {
    347 		if (cmd == sgp40_timings[i].cmd) {
    348 			r = sgp40_timings[i].typicaldelay;
    349 			break;
    350 		}
    351 	}
    352 
    353 	if (r == -1) {
    354 		panic("sgp40: Bad command look up in cmd delay: cmd: %d\n",
    355 		    cmd);
    356 	}
    357 
    358 	return r;
    359 }
    360 
    361 static int
    362 sgp40_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
    363     uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
    364 {
    365 	int error;
    366 	int cmddelay;
    367 	uint16_t cmd16;
    368 
    369 	cmd16 = cmd[0] << 8;
    370 	cmd16 = cmd16 | cmd[1];
    371 
    372 	error = iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, cmd, clen, NULL, 0,
    373 	    0);
    374 	if (error)
    375 		return error;
    376 
    377 	/*
    378 	 * Every command returns something except for turning the heater off
    379 	 * and the general soft reset which returns nothing.
    380 	 */
    381 	if (cmd16 == SGP40_HEATER_OFF)
    382 		return 0;
    383 	/*
    384 	 * Every command has a particular delay for how long
    385 	 * it typically takes and the max time it will take.
    386 	 */
    387 	cmddelay = sgp40_cmddelay(cmd16);
    388 	delay(cmddelay);
    389 
    390 	for (int aint = 0; aint < readattempts; aint++) {
    391 		error = iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0,
    392 		    buf, blen, 0);
    393 		if (error == 0)
    394 			break;
    395 		delay(1000);
    396 	}
    397 
    398 	return error;
    399 }
    400 
    401 static int
    402 sgp40_cmdr(struct sgp40_sc *sc, uint16_t cmd, uint8_t *extraargs,
    403     uint8_t argslen, uint8_t *buf, size_t blen)
    404 {
    405 	uint8_t fullcmd[8];
    406 	uint8_t cmdlen;
    407 	int n;
    408 
    409 	/*
    410 	 * The biggest documented command + arguments is 8 uint8_t bytes long.
    411 	 * Catch anything that ties to have an arglen more than 6
    412 	 */
    413 	KASSERT(argslen <= 6);
    414 
    415 	memset(fullcmd, 0, 8);
    416 
    417 	fullcmd[0] = cmd >> 8;
    418 	fullcmd[1] = cmd & 0x00ff;
    419 	cmdlen = 2;
    420 
    421 	n = 0;
    422 	while (extraargs != NULL && n < argslen && cmdlen <= 7) {
    423 		fullcmd[cmdlen] = extraargs[n];
    424 		cmdlen++;
    425 		n++;
    426 	}
    427 	DPRINTF(sc, 2, ("%s: Full command and arguments: %02x %02x %02x %02x "
    428 	    "%02x %02x %02x %02x\n",
    429 	    device_xname(sc->sc_dev), fullcmd[0], fullcmd[1],
    430 	    fullcmd[2], fullcmd[3], fullcmd[4], fullcmd[5],
    431 	    fullcmd[6], fullcmd[7]));
    432 	return sgp40_cmd(sc->sc_tag, sc->sc_addr, fullcmd, cmdlen, buf, blen,
    433 	    sc->sc_readattempts);
    434 }
    435 
    436 static	uint8_t
    437 sgp40_crc(uint8_t * data, size_t size)
    438 {
    439 	uint8_t crc = 0xFF;
    440 
    441 	for (size_t i = 0; i < size; i++) {
    442 		crc ^= data[i];
    443 		for (size_t j = 8; j > 0; j--) {
    444 			if (crc & 0x80)
    445 				crc = (crc << 1) ^ 0x31;
    446 			else
    447 				crc <<= 1;
    448 		}
    449 	}
    450 	return crc;
    451 }
    452 
    453 static int
    454 sgp40_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
    455 {
    456 	uint8_t reg[2];
    457 	uint8_t buf[9];
    458 	int error;
    459 
    460 	/*
    461 	 * Possible bug...  this command may not work if the chip is not idle,
    462 	 * however, it appears to be used by a lot of other code as a probe.
    463 	 */
    464 	reg[0] = SGP40_GET_SERIAL_NUMBER >> 8;
    465 	reg[1] = SGP40_GET_SERIAL_NUMBER & 0x00ff;
    466 
    467 	error = sgp40_cmd(tag, addr, reg, 2, buf, 9, 10);
    468 	if (matchdebug) {
    469 		printf("poke X 1: %d\n", error);
    470 	}
    471 	return error;
    472 }
    473 
    474 static int
    475 sgp40_sysctl_init(struct sgp40_sc *sc)
    476 {
    477 	int error;
    478 	const struct sysctlnode *cnode;
    479 	int sysctlroot_num;
    480 
    481 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    482 	    0, CTLTYPE_NODE, device_xname(sc->sc_dev),
    483 	    SYSCTL_DESCR("SGP40 controls"), NULL, 0, NULL, 0, CTL_HW,
    484 	    CTL_CREATE, CTL_EOL)) != 0)
    485 		return error;
    486 
    487 	sysctlroot_num = cnode->sysctl_num;
    488 
    489 #ifdef SGP40_DEBUG
    490 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    491 	    CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
    492 	    SYSCTL_DESCR("Debug level"), sgp40_verify_sysctl, 0,
    493 	    &sc->sc_sgp40debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
    494 	    CTL_EOL)) != 0)
    495 		return error;
    496 
    497 #endif
    498 
    499 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    500 	    CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
    501 	    SYSCTL_DESCR("The number of times to attempt to read the values"),
    502 	    sgp40_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
    503 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    504 		return error;
    505 
    506 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    507 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
    508 	    SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
    509 	    0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    510 		return error;
    511 
    512 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    513 	    0, CTLTYPE_NODE, "compensation",
    514 	    SYSCTL_DESCR("SGP40 measurement compensations"), NULL, 0, NULL, 0,
    515 	    CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    516 		return error;
    517 	int compensation_num = cnode->sysctl_num;
    518 
    519 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    520 	    CTLFLAG_READWRITE, CTLTYPE_INT, "temperature",
    521 	    SYSCTL_DESCR("Temperature compensation in celsius"),
    522 	    sgp40_verify_temp_sysctl, 0, &sc->sc_tempcomp, 0, CTL_HW,
    523 	    sysctlroot_num, compensation_num, CTL_CREATE, CTL_EOL)) != 0)
    524 		return error;
    525 
    526 	if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode,
    527 	    CTLFLAG_READWRITE, CTLTYPE_INT, "humidity",
    528 	    SYSCTL_DESCR("Humidity compensation in %RH"),
    529 	    sgp40_verify_rh_sysctl, 0, &sc->sc_rhcomp, 0, CTL_HW,
    530 	    sysctlroot_num, compensation_num, CTL_CREATE, CTL_EOL)) != 0)
    531 		return error;
    532 
    533 	return 0;
    534 }
    535 
    536 static int
    537 sgp40_match(device_t parent, cfdata_t match, void *aux)
    538 {
    539 	struct i2c_attach_args *ia = aux;
    540 	int error, match_result;
    541 	const bool matchdebug = false;
    542 
    543 	if (matchdebug)
    544 		printf("in match\n");
    545 
    546 	if (iic_use_direct_match(ia, match, NULL, &match_result))
    547 		return match_result;
    548 
    549 	/* indirect config - check for configured address */
    550 	if (ia->ia_addr != SGP40_TYPICAL_ADDR)
    551 		return 0;
    552 
    553 	/*
    554 	 * Check to see if something is really at this i2c address. This will
    555 	 * keep phantom devices from appearing
    556 	 */
    557 	if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
    558 		if (matchdebug)
    559 			printf("in match acquire bus failed\n");
    560 		return 0;
    561 	}
    562 
    563 	error = sgp40_poke(ia->ia_tag, ia->ia_addr, matchdebug);
    564 	iic_release_bus(ia->ia_tag, 0);
    565 
    566 	return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
    567 }
    568 
    569 static void
    570 sgp40_attach(device_t parent, device_t self, void *aux)
    571 {
    572 	struct sgp40_sc *sc;
    573 	struct i2c_attach_args *ia;
    574 	int error, i;
    575 	int ecount = 0;
    576 	uint8_t buf[9];
    577 	uint8_t tstcrc;
    578 	uint16_t chiptestvalue;
    579 	uint64_t serial_number = 0;
    580 	uint8_t sn_crc1, sn_crc2, sn_crc3, sn_crcv1, sn_crcv2, sn_crcv3;
    581 	uint8_t fs_crc, fs_crcv;
    582 	uint16_t featureset;
    583 
    584 	ia = aux;
    585 	sc = device_private(self);
    586 
    587 	sc->sc_dev = self;
    588 	sc->sc_tag = ia->ia_tag;
    589 	sc->sc_addr = ia->ia_addr;
    590 	sc->sc_sgp40debug = 0;
    591 	sc->sc_readattempts = 10;
    592 	sc->sc_ignorecrc = false;
    593 	sc->sc_stopping = false;
    594 	sc->sc_voc = 0;
    595 	sc->sc_vocvalid = false;
    596 	sc->sc_tempcomp = SGP40_DEFAULT_TEMP_COMP;
    597 	sc->sc_rhcomp = SGP40_DEFAULT_RH_COMP;
    598 	sc->sc_sme = NULL;
    599 
    600 	aprint_normal("\n");
    601 
    602 	mutex_init(&sc->sc_threadmutex, MUTEX_DEFAULT, IPL_NONE);
    603 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
    604 	cv_init(&sc->sc_condvar, "sgp40cv");
    605 	sc->sc_numsensors = __arraycount(sgp40_sensors);
    606 
    607 	if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
    608 		aprint_error_dev(self,
    609 		    "Unable to create sysmon structure\n");
    610 		sc->sc_sme = NULL;
    611 		return;
    612 	}
    613 	if ((error = sgp40_sysctl_init(sc)) != 0) {
    614 		aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
    615 		goto out;
    616 	}
    617 
    618 	error = iic_acquire_bus(sc->sc_tag, 0);
    619 	if (error) {
    620 		aprint_error_dev(self, "Could not acquire iic bus: %d\n",
    621 		    error);
    622 		goto out;
    623 	}
    624 
    625 	/*
    626 	 * Usually one would reset the chip here, but that is not possible
    627 	 * without resetting the entire bus, so we won't do that.
    628 	 *
    629 	 * What we will do is make sure that the chip is idle by running the
    630 	 * turn-the-heater command.
    631 	 */
    632 
    633 	error = sgp40_cmdr(sc, SGP40_HEATER_OFF, NULL, 0, NULL, 0);
    634 	if (error) {
    635 		aprint_error_dev(self, "Failed to turn off the heater: %d\n",
    636 		    error);
    637 		ecount++;
    638 	}
    639 
    640 	error = sgp40_cmdr(sc, SGP40_GET_SERIAL_NUMBER, NULL, 0, buf, 9);
    641 	if (error) {
    642 		aprint_error_dev(self, "Failed to get serial number: %d\n",
    643 		    error);
    644 		ecount++;
    645 	}
    646 
    647 	sn_crc1 = sgp40_crc(&buf[0], 2);
    648 	sn_crc2 = sgp40_crc(&buf[3], 2);
    649 	sn_crc3 = sgp40_crc(&buf[6], 2);
    650 	sn_crcv1 = buf[2];
    651 	sn_crcv2 = buf[5];
    652 	sn_crcv3 = buf[8];
    653 	serial_number = buf[0];
    654 	serial_number = (serial_number << 8) | buf[1];
    655 	serial_number = (serial_number << 8) | buf[3];
    656 	serial_number = (serial_number << 8) | buf[4];
    657 	serial_number = (serial_number << 8) | buf[6];
    658 	serial_number = (serial_number << 8) | buf[7];
    659 
    660 	DPRINTF(sc, 2, ("%s: raw serial number: %02x %02x %02x %02x %02x %02x "
    661 	    "%02x %02x %02x\n",
    662 	    device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4],
    663 	    buf[5], buf[6], buf[7], buf[8]));
    664 
    665 	error = sgp40_cmdr(sc, SGP40_GET_FEATURESET, NULL, 0, buf, 3);
    666 	if (error) {
    667 		aprint_error_dev(self, "Failed to get featureset: %d\n",
    668 		    error);
    669 		ecount++;
    670 	}
    671 
    672 	fs_crc = sgp40_crc(&buf[0], 2);
    673 	fs_crcv = buf[2];
    674 	featureset = buf[0];
    675 	featureset = (featureset << 8) | buf[1];
    676 
    677 	DPRINTF(sc, 2, ("%s: raw feature set: %02x %02x %02x\n",
    678 	    device_xname(sc->sc_dev), buf[0], buf[1], buf[2]));
    679 
    680 	error = sgp40_cmdr(sc, SGP40_MEASURE_TEST, NULL, 0, buf, 3);
    681 	if (error) {
    682 		aprint_error_dev(self, "Failed to perform a chip test: %d\n",
    683 		    error);
    684 		ecount++;
    685 	}
    686 
    687 	tstcrc = sgp40_crc(&buf[0], 2);
    688 
    689 	DPRINTF(sc, 2, ("%s: chip test values: %02x%02x - %02x ; %02x\n",
    690 	    device_xname(sc->sc_dev), buf[0], buf[1], buf[2], tstcrc));
    691 
    692 	iic_release_bus(sc->sc_tag, 0);
    693 	if (error != 0) {
    694 		aprint_error_dev(self, "Unable to setup device\n");
    695 		goto out;
    696 	}
    697 
    698 	chiptestvalue = buf[0] << 8;
    699 	chiptestvalue |= buf[1];
    700 
    701 	for (i = 0; i < sc->sc_numsensors; i++) {
    702 		strlcpy(sc->sc_sensors[i].desc, sgp40_sensors[i].desc,
    703 		    sizeof(sc->sc_sensors[i].desc));
    704 
    705 		sc->sc_sensors[i].units = sgp40_sensors[i].type;
    706 		sc->sc_sensors[i].state = ENVSYS_SINVALID;
    707 
    708 		DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
    709 		    sc->sc_sensors[i].desc));
    710 
    711 		error = sysmon_envsys_sensor_attach(sc->sc_sme,
    712 		    &sc->sc_sensors[i]);
    713 		if (error) {
    714 			aprint_error_dev(self,
    715 			    "Unable to attach sensor %d: %d\n", i, error);
    716 			goto out;
    717 		}
    718 	}
    719 
    720 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
    721 	sc->sc_sme->sme_cookie = sc;
    722 	sc->sc_sme->sme_refresh = sgp40_refresh;
    723 
    724 	DPRINTF(sc, 2, ("sgp40_attach: registering with envsys\n"));
    725 
    726 	if (sysmon_envsys_register(sc->sc_sme)) {
    727 		aprint_error_dev(self,
    728 			"unable to register with sysmon\n");
    729 		sysmon_envsys_destroy(sc->sc_sme);
    730 		sc->sc_sme = NULL;
    731 		return;
    732 	}
    733 
    734 	error = kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL,
    735 	    sgp40_thread, sc, &sc->sc_thread, "%s", device_xname(sc->sc_dev));
    736 	if (error) {
    737 		aprint_error_dev(self,"Unable to create measurement thread\n");
    738 		goto out;
    739 	}
    740 
    741 	aprint_normal_dev(self, "Sensirion SGP40, Serial number: %jx%s"
    742 	    "Feature set word: 0x%jx%s%s%s", serial_number,
    743 	    (sn_crc1 == sn_crcv1 && sn_crc2 == sn_crcv2 && sn_crc3 == sn_crcv3)
    744 	    ? ", " : " (bad crc), ",
    745 	    (uintmax_t)featureset,
    746 	    (fs_crc == fs_crcv) ? ", " : " (bad crc), ",
    747 	    (chiptestvalue == SGP40_TEST_RESULTS_ALL_PASSED) ?
    748 		"All chip tests passed" :
    749 	    (chiptestvalue == SGP40_TEST_RESULTS_SOME_FAILED) ?
    750 		"Some chip tests failed" :
    751 	    "Unknown test results",
    752 	    (tstcrc == buf[2]) ? "\n" : " (bad crc)\n");
    753 	return;
    754 out:
    755 	sysmon_envsys_destroy(sc->sc_sme);
    756 	sc->sc_sme = NULL;
    757 }
    758 
    759 static void
    760 sgp40_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
    761 {
    762 	struct sgp40_sc *sc;
    763 	sc = sme->sme_cookie;
    764 
    765 	mutex_enter(&sc->sc_mutex);
    766 	if (sc->sc_vocvalid == true) {
    767 		edata->value_cur = (uint32_t)sc->sc_voc;
    768 		edata->state = ENVSYS_SVALID;
    769 	} else {
    770 		edata->state = ENVSYS_SINVALID;
    771 	}
    772 	mutex_exit(&sc->sc_mutex);
    773 }
    774 
    775 static int
    776 sgp40_detach(device_t self, int flags)
    777 {
    778 	struct sgp40_sc *sc;
    779 
    780 	sc = device_private(self);
    781 
    782 	/* stop the measurement thread */
    783 	sgp40_stop_thread(sc);
    784 
    785 	/* Remove the sensors */
    786 	mutex_enter(&sc->sc_mutex);
    787 	if (sc->sc_sme != NULL) {
    788 		sysmon_envsys_unregister(sc->sc_sme);
    789 		sc->sc_sme = NULL;
    790 	}
    791 	mutex_exit(&sc->sc_mutex);
    792 
    793 	/* Remove the sysctl tree */
    794 	sysctl_teardown(&sc->sc_sgp40log);
    795 
    796 	/* Remove the mutex */
    797 	mutex_destroy(&sc->sc_mutex);
    798 	mutex_destroy(&sc->sc_threadmutex);
    799 
    800 	return 0;
    801 }
    802 
    803 MODULE(MODULE_CLASS_DRIVER, sgp40mox, "iic,sysmon_envsys");
    804 
    805 #ifdef _MODULE
    806 #include "ioconf.c"
    807 #endif
    808 
    809 static int
    810 sgp40mox_modcmd(modcmd_t cmd, void *opaque)
    811 {
    812 
    813 	switch (cmd) {
    814 	case MODULE_CMD_INIT:
    815 #ifdef _MODULE
    816 		return config_init_component(cfdriver_ioconf_sgp40mox,
    817 		    cfattach_ioconf_sgp40mox, cfdata_ioconf_sgp40mox);
    818 #else
    819 		return 0;
    820 #endif
    821 	case MODULE_CMD_FINI:
    822 #ifdef _MODULE
    823 		return config_fini_component(cfdriver_ioconf_sgp40mox,
    824 		      cfattach_ioconf_sgp40mox, cfdata_ioconf_sgp40mox);
    825 #else
    826 		return 0;
    827 #endif
    828 	default:
    829 		return ENOTTY;
    830 	}
    831 }
    832