Home | History | Annotate | Line # | Download | only in i2c
tsllux.c revision 1.3
      1 /* $NetBSD: tsllux.c,v 1.3 2021/01/27 02:29:48 thorpej Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 Jason R. Thorpe
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: tsllux.c,v 1.3 2021/01/27 02:29:48 thorpej Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/device.h>
     35 #include <sys/conf.h>
     36 #include <sys/bus.h>
     37 #include <sys/kernel.h>
     38 #include <sys/kmem.h>
     39 #include <sys/mutex.h>
     40 #include <sys/proc.h>
     41 #include <sys/sysctl.h>
     42 
     43 #include <dev/i2c/i2cvar.h>
     44 #include <dev/i2c/tsl256xreg.h>
     45 
     46 #include <dev/sysmon/sysmonvar.h>
     47 
     48 struct tsllux_softc {
     49 	device_t	sc_dev;
     50 	i2c_tag_t	sc_i2c;
     51 	i2c_addr_t	sc_addr;
     52 
     53 	uint32_t	sc_poweron;
     54 
     55 	/*
     56 	 * Locking order is:
     57 	 *	tsllux mutex -> i2c bus
     58 	 */
     59 	kmutex_t	sc_lock;
     60 
     61 	uint8_t		sc_itime;
     62 	uint8_t		sc_gain;
     63 	bool		sc_cs_package;
     64 	bool		sc_auto_gain;
     65 
     66 	struct sysmon_envsys *sc_sme;
     67 	envsys_data_t	sc_sensor;
     68 
     69 	struct sysctllog *sc_sysctllog;
     70 };
     71 
     72 #define	TSLLUX_F_CS_PACKAGE	0x01
     73 
     74 static int	tsllux_match(device_t, cfdata_t, void *);
     75 static void	tsllux_attach(device_t, device_t, void *);
     76 
     77 CFATTACH_DECL_NEW(tsllux, sizeof(struct tsllux_softc),
     78     tsllux_match, tsllux_attach, NULL, NULL);
     79 
     80 static const struct device_compatible_entry tsllux_compat_data[] = {
     81 	{ .compat = "amstaos,tsl2560" },
     82 	{ .compat = "amstaos,tsl2561" },
     83 	DEVICE_COMPAT_EOL
     84 };
     85 
     86 static int	tsllux_read1(struct tsllux_softc *, uint8_t, uint8_t *);
     87 static int	tsllux_read2(struct tsllux_softc *, uint8_t, uint16_t *);
     88 static int	tsllux_write1(struct tsllux_softc *, uint8_t, uint8_t);
     89 #if 0
     90 static int	tsllux_write2(struct tsllux_softc *, uint8_t, uint16_t);
     91 #endif
     92 
     93 static void	tsllux_sysctl_attach(struct tsllux_softc *);
     94 
     95 static int	tsllux_poweron(struct tsllux_softc *);
     96 static int	tsllux_poweroff(struct tsllux_softc *);
     97 
     98 static int	tsllux_set_integration_time(struct tsllux_softc *, uint8_t);
     99 static int	tsllux_set_gain(struct tsllux_softc *, uint8_t);
    100 static int	tsllux_set_autogain(struct tsllux_softc *, bool);
    101 
    102 static int	tsllux_get_lux(struct tsllux_softc *, uint32_t *,
    103 			       uint16_t *, uint16_t *);
    104 
    105 static void	tsllux_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
    106 
    107 static int
    108 tsllux_match(device_t parent, cfdata_t match, void *aux)
    109 {
    110 	struct i2c_attach_args *ia = aux;
    111 	uint8_t id_reg;
    112 	int error, match_result;
    113 
    114 	if (iic_use_direct_match(ia, match, tsllux_compat_data, &match_result))
    115 		return (match_result);
    116 
    117 	switch (ia->ia_addr) {
    118 	case TSL256x_SLAVEADDR_GND:
    119 	case TSL256x_SLAVEADDR_FLOAT:
    120 	case TSL256x_SLAVEADDR_VDD:
    121 		break;
    122 
    123 	default:
    124 		return (0);
    125 	}
    126 
    127 	if (iic_acquire_bus(ia->ia_tag, 0) != 0)
    128 		return (0);
    129 	error = iic_smbus_read_byte(ia->ia_tag, ia->ia_addr,
    130 	    TSL256x_REG_ID | COMMAND6x_CMD, &id_reg, 0);
    131 	iic_release_bus(ia->ia_tag, 0);
    132 
    133 	if (error)
    134 		return (0);
    135 
    136 	/* XXX This loses if we have a 2560 rev. 0. */
    137 	if (id_reg == 0)
    138 		return (I2C_MATCH_ADDRESS_ONLY);
    139 
    140 	return (I2C_MATCH_ADDRESS_AND_PROBE);
    141 }
    142 
    143 static void
    144 tsllux_attach(device_t parent, device_t self, void *aux)
    145 {
    146 	struct tsllux_softc *sc = device_private(self);
    147 	struct i2c_attach_args *ia = aux;
    148 	bool have_i2c;
    149 
    150 	/* XXX IPL_NONE changes when we support threshold interrupts. */
    151 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
    152 
    153 	sc->sc_dev = self;
    154 	sc->sc_i2c = ia->ia_tag;
    155 	sc->sc_addr = ia->ia_addr;
    156 
    157 	if (self->dv_cfdata != NULL &&
    158 	    self->dv_cfdata->cf_flags & TSLLUX_F_CS_PACKAGE)
    159 		sc->sc_cs_package = true;
    160 
    161 	if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
    162 		return;
    163 	}
    164 
    165 	have_i2c = true;
    166 
    167 	/* Power on the device and clear any pending interrupts. */
    168 	if (tsllux_write1(sc, TSL256x_REG_CONTROL | COMMAND6x_CLEAR,
    169 			  CONTROL6x_POWER_ON)) {
    170 		aprint_error_dev(self, ": unable to power on device\n");
    171 		goto out;
    172 	}
    173 	sc->sc_poweron = 1;
    174 
    175 	/* Make sure interrupts are disabled. */
    176 	if (tsllux_write1(sc, TSL256x_REG_INTERRUPT | COMMAND6x_CLEAR, 0)) {
    177 		aprint_error_dev(self, ": unable to disable interrupts\n");
    178 		goto out;
    179 	}
    180 
    181 	aprint_naive("\n");
    182 	aprint_normal(": TSL256x Light-to-Digital converter%s\n",
    183 		      sc->sc_cs_package ? " (CS package)" : "");
    184 
    185 	/* Inititalize timing to reasonable defaults. */
    186 	sc->sc_auto_gain = true;
    187 	sc->sc_gain = TIMING6x_GAIN_16X;
    188 	if (tsllux_set_integration_time(sc, TIMING6x_INTEG_101ms)) {
    189 		aprint_error_dev(self, ": unable to set integration time\n");
    190 		goto out;
    191 	}
    192 
    193 	tsllux_poweroff(sc);
    194 
    195 	iic_release_bus(ia->ia_tag, 0);
    196 	have_i2c = false;
    197 
    198 	tsllux_sysctl_attach(sc);
    199 
    200 	sc->sc_sme = sysmon_envsys_create();
    201 	sc->sc_sme->sme_name = device_xname(self);
    202 	sc->sc_sme->sme_cookie = sc;
    203 	sc->sc_sme->sme_refresh = tsllux_sensors_refresh;
    204 
    205 	sc->sc_sensor.units = ENVSYS_LUX;
    206 	sc->sc_sensor.state = ENVSYS_SINVALID;
    207 	snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc),
    208 		 "ambient light");
    209 	sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor);
    210 
    211 	sysmon_envsys_register(sc->sc_sme);
    212 
    213  out:
    214 	if (have_i2c) {
    215 		if (sc->sc_poweron)
    216 			tsllux_poweroff(sc);
    217 		iic_release_bus(ia->ia_tag, 0);
    218 	}
    219 }
    220 
    221 static int
    222 tsllux_sysctl_cs_package(SYSCTLFN_ARGS)
    223 {
    224 	struct tsllux_softc *sc;
    225 	struct sysctlnode node;
    226 	int error;
    227 	u_int val;
    228 
    229 	node = *rnode;
    230 	sc = node.sysctl_data;
    231 
    232 	mutex_enter(&sc->sc_lock);
    233 	val = sc->sc_cs_package ? 1 : 0;
    234 	node.sysctl_data = &val;
    235 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    236 	if (error || newp == NULL) {
    237 		mutex_exit(&sc->sc_lock);
    238 		return (error);
    239 	}
    240 
    241 	/* CS package indicator is used only in software; no need for I2C. */
    242 
    243 	sc->sc_cs_package = val ? true : false;
    244 	mutex_exit(&sc->sc_lock);
    245 
    246 	return (error);
    247 }
    248 
    249 static int
    250 tsllux_sysctl_autogain(SYSCTLFN_ARGS)
    251 {
    252 	struct tsllux_softc *sc;
    253 	struct sysctlnode node;
    254 	int error;
    255 	u_int val;
    256 
    257 	node = *rnode;
    258 	sc = node.sysctl_data;
    259 
    260 	mutex_enter(&sc->sc_lock);
    261 	val = sc->sc_auto_gain ? 1 : 0;
    262 	node.sysctl_data = &val;
    263 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    264 	if (error || newp == NULL) {
    265 		mutex_exit(&sc->sc_lock);
    266 		return (error);
    267 	}
    268 
    269 	/* Auto-gain is a software feature; no need for I2C. */
    270 
    271 	error = tsllux_set_autogain(sc, val ? true : false);
    272 	mutex_exit(&sc->sc_lock);
    273 
    274 	return (error);
    275 }
    276 
    277 static int
    278 tsllux_sysctl_gain(SYSCTLFN_ARGS)
    279 {
    280 	struct tsllux_softc *sc;
    281 	struct sysctlnode node;
    282 	int error;
    283 	u_int val;
    284 	uint8_t new_gain;
    285 
    286 	node = *rnode;
    287 	sc = node.sysctl_data;
    288 
    289 	mutex_enter(&sc->sc_lock);
    290 
    291 	switch (sc->sc_gain) {
    292 	case TIMING6x_GAIN_1X:
    293 		val = 1;
    294 		break;
    295 
    296 	case TIMING6x_GAIN_16X:
    297 		val = 16;
    298 		break;
    299 
    300 	default:
    301 		val = 1;
    302 		break;
    303 	}
    304 	node.sysctl_data = &val;
    305 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    306 	if (error || newp == NULL) {
    307 		mutex_exit(&sc->sc_lock);
    308 		return (error);
    309 	}
    310 
    311 	switch (val) {
    312 	case 1:
    313 		new_gain = TIMING6x_GAIN_1X;
    314 		break;
    315 
    316 	case 16:
    317 		new_gain = TIMING6x_GAIN_16X;
    318 		break;
    319 
    320 	default:
    321 		mutex_exit(&sc->sc_lock);
    322 		return (EINVAL);
    323 	}
    324 
    325 	if ((error = iic_acquire_bus(sc->sc_i2c, 0)) != 0) {
    326 		mutex_exit(&sc->sc_lock);
    327 		return (error);
    328 	}
    329 
    330 	error = tsllux_set_gain(sc, new_gain);
    331 	iic_release_bus(sc->sc_i2c, 0);
    332 	mutex_exit(&sc->sc_lock);
    333 
    334 	return (error);
    335 }
    336 
    337 static int
    338 tsllux_sysctl_itime(SYSCTLFN_ARGS)
    339 {
    340 	struct tsllux_softc *sc;
    341 	struct sysctlnode node;
    342 	int error;
    343 	u_int val;
    344 	uint8_t new_itime;
    345 
    346 	node = *rnode;
    347 	sc = node.sysctl_data;
    348 
    349 	mutex_enter(&sc->sc_lock);
    350 
    351 	switch (sc->sc_itime) {
    352 	case TIMING6x_INTEG_13_7ms:
    353 		val = 13;
    354 		break;
    355 
    356 	case TIMING6x_INTEG_101ms:
    357 		val = 101;
    358 		break;
    359 
    360 	case TIMING6x_INTEG_402ms:
    361 	default:
    362 		val = 402;
    363 		break;
    364 	}
    365 	node.sysctl_data = &val;
    366 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    367 	if (error || newp == NULL) {
    368 		mutex_exit(&sc->sc_lock);
    369 		return (error);
    370 	}
    371 
    372 	switch (val) {
    373 	case 13:
    374 	case 14:
    375 		new_itime = TIMING6x_INTEG_13_7ms;
    376 		break;
    377 
    378 	case 101:
    379 		new_itime = TIMING6x_INTEG_101ms;
    380 		break;
    381 
    382 	case 402:
    383 		new_itime = TIMING6x_INTEG_402ms;
    384 		break;
    385 
    386 	default:
    387 		mutex_exit(&sc->sc_lock);
    388 		return (EINVAL);
    389 	}
    390 
    391 	if ((error = iic_acquire_bus(sc->sc_i2c, 0)) != 0) {
    392 		mutex_exit(&sc->sc_lock);
    393 		return (error);
    394 	}
    395 
    396 	error = tsllux_set_integration_time(sc, new_itime);
    397 	iic_release_bus(sc->sc_i2c, 0);
    398 	mutex_exit(&sc->sc_lock);
    399 
    400 	return (error);
    401 }
    402 
    403 static void
    404 tsllux_sysctl_attach(struct tsllux_softc *sc)
    405 {
    406 	struct sysctllog **log = &sc->sc_sysctllog;
    407 	const struct sysctlnode *rnode, *cnode;
    408 	int error;
    409 
    410 	error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT,
    411 	    CTLTYPE_NODE, device_xname(sc->sc_dev),
    412 	    SYSCTL_DESCR("tsl256x control"),
    413 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
    414 	if (error)
    415 		return;
    416 
    417 	error = sysctl_createv(log, 0, &rnode, &cnode,
    418 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "cs_package",
    419 	    SYSCTL_DESCR("sensor in Chipscale (CS) package"),
    420 	    tsllux_sysctl_cs_package, 0,
    421 	    (void *)sc, 0, CTL_CREATE, CTL_EOL);
    422 	if (error)
    423 		return;
    424 
    425 	error = sysctl_createv(log, 0, &rnode, &cnode,
    426 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "auto_gain",
    427 	    SYSCTL_DESCR("auto-gain algorithm enabled"),
    428 	    tsllux_sysctl_autogain, 0,
    429 	    (void *)sc, 0, CTL_CREATE, CTL_EOL);
    430 	if (error)
    431 		return;
    432 
    433 	error = sysctl_createv(log, 0, &rnode, &cnode,
    434 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "gain",
    435 	    SYSCTL_DESCR("sensor gain"), tsllux_sysctl_gain, 0,
    436 	    (void *)sc, 0, CTL_CREATE, CTL_EOL);
    437 	if (error)
    438 		return;
    439 
    440 	error = sysctl_createv(log, 0, &rnode, &cnode,
    441 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
    442 	    "integration_time",
    443 	    SYSCTL_DESCR("ADC integration time"), tsllux_sysctl_itime, 0,
    444 	    (void *)sc, 0, CTL_CREATE, CTL_EOL);
    445 	if (error)
    446 		return;
    447 }
    448 
    449 static void
    450 tsllux_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    451 {
    452 	struct tsllux_softc *sc = sme->sme_cookie;
    453 	uint32_t lux;
    454 	int error;
    455 
    456 	if (edata != &sc->sc_sensor) {
    457 		edata->state = ENVSYS_SINVALID;
    458 		return;
    459 	}
    460 
    461 	mutex_enter(&sc->sc_lock);
    462 
    463 	if ((error = iic_acquire_bus(sc->sc_i2c, 0)) == 0) {
    464 		error = tsllux_get_lux(sc, &lux, NULL, NULL);
    465 		iic_release_bus(sc->sc_i2c, 0);
    466 	}
    467 
    468 	if (error) {
    469 		edata->state = ENVSYS_SINVALID;
    470 	} else {
    471 		edata->value_cur = lux;
    472 		edata->state = ENVSYS_SVALID;
    473 	}
    474 
    475 	mutex_exit(&sc->sc_lock);
    476 }
    477 
    478 /*
    479  * Allow pending interrupts to be cleared as part of another operation.
    480  */
    481 #define	REGMASK6x		(COMMAND6x_REGMASK | COMMAND6x_CLEAR)
    482 
    483 static int
    484 tsllux_read1(struct tsllux_softc *sc, uint8_t reg, uint8_t *valp)
    485 {
    486 	reg = (reg & REGMASK6x) | COMMAND6x_CMD;
    487 	return (iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, valp, 0));
    488 }
    489 
    490 static int
    491 tsllux_read2(struct tsllux_softc *sc, uint8_t reg, uint16_t *valp)
    492 {
    493 	reg = (reg & REGMASK6x) | COMMAND6x_CMD | COMMAND6x_WORD;
    494 	return (iic_smbus_read_word(sc->sc_i2c, sc->sc_addr, reg, valp, 0));
    495 }
    496 
    497 static int
    498 tsllux_write1(struct tsllux_softc *sc, uint8_t reg, uint8_t val)
    499 {
    500 	reg = (reg & REGMASK6x) | COMMAND6x_CMD;
    501 	return (iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0));
    502 }
    503 
    504 #if 0
    505 static int
    506 tsllux_write2(struct tsllux_softc *sc, uint8_t reg, uint16_t val)
    507 {
    508 	reg = (reg & REGMASK6x) | COMMAND6x_CMD | COMMAND6x_WORD;
    509 	return (iic_smbus_write_word(sc->sc_i2c, sc->sc_addr, reg, val, 0));
    510 }
    511 #endif
    512 
    513 #undef REGMASK
    514 
    515 static int
    516 tsllux_poweron(struct tsllux_softc *sc)
    517 {
    518 	int error;
    519 
    520 	if (sc->sc_poweron++ == 0) {
    521 		uint8_t val;
    522 
    523 		error = tsllux_write1(sc, TSL256x_REG_CONTROL,
    524 				      CONTROL6x_POWER_ON);
    525 		if (error)
    526 			return (error);
    527 
    528 		error = tsllux_read1(sc, TSL256x_REG_CONTROL, &val);
    529 		if (error)
    530 			return (error);
    531 
    532 		if (val != CONTROL6x_POWER_ON) {
    533 			aprint_error_dev(sc->sc_dev,
    534 					 "failed to power on sensor\n");
    535 			return (EIO);
    536 		}
    537 	}
    538 	return (0);
    539 }
    540 
    541 static int
    542 tsllux_poweroff(struct tsllux_softc *sc)
    543 {
    544 	if (sc->sc_poweron && --sc->sc_poweron == 0)
    545 		return (tsllux_write1(sc, TSL256x_REG_CONTROL,
    546 				      CONTROL6x_POWER_OFF));
    547 	return (0);
    548 }
    549 
    550 static int
    551 tsllux_set_integration_time(struct tsllux_softc *sc, uint8_t time)
    552 {
    553 	int error;
    554 
    555 	switch (time) {
    556 	case TIMING6x_INTEG_13_7ms:
    557 	case TIMING6x_INTEG_101ms:
    558 	case TIMING6x_INTEG_402ms:
    559 		break;
    560 
    561 	default:
    562 		return (EINVAL);
    563 	}
    564 
    565 	if ((error = tsllux_poweron(sc)) != 0)
    566 		return (error);
    567 
    568 	if ((error = tsllux_write1(sc, TSL256x_REG_TIMING,
    569 				   time | sc->sc_gain)) != 0)
    570 		goto out;
    571 
    572 	sc->sc_itime = time;
    573 
    574  out:
    575 	(void) tsllux_poweroff(sc);
    576 	return (error);
    577 }
    578 
    579 static int
    580 tsllux_set_gain0(struct tsllux_softc *sc, uint8_t gain)
    581 {
    582 	int error;
    583 
    584 	if ((error = tsllux_write1(sc, TSL256x_REG_TIMING,
    585 				   sc->sc_itime | gain)) != 0)
    586 		return (error);
    587 
    588 	sc->sc_gain = gain;
    589 	return (0);
    590 }
    591 
    592 static int
    593 tsllux_set_gain(struct tsllux_softc *sc, uint8_t gain)
    594 {
    595 	int error;
    596 
    597 	switch (gain) {
    598 	case TIMING6x_GAIN_1X:
    599 	case TIMING6x_GAIN_16X:
    600 		break;
    601 
    602 	default:
    603 		return (EINVAL);
    604 	}
    605 
    606 	if ((error = tsllux_poweron(sc)) != 0)
    607 		return (error);
    608 
    609 	if ((error = tsllux_set_gain0(sc, gain)) != 0)
    610 		goto out;
    611 
    612 	sc->sc_auto_gain = false;
    613 
    614  out:
    615 	(void) tsllux_poweroff(sc);
    616 	return (error);
    617 }
    618 
    619 static int
    620 tsllux_set_autogain(struct tsllux_softc *sc, bool use_autogain)
    621 {
    622 
    623 	sc->sc_auto_gain = use_autogain;
    624 	return (0);
    625 }
    626 
    627 static int
    628 tsllux_wait_for_adcs(struct tsllux_softc *sc)
    629 {
    630 	int ms;
    631 
    632 	switch (sc->sc_itime) {
    633 	case TIMING6x_INTEG_13_7ms:
    634 		/* Wait 15ms for 13.7ms integration */
    635 		ms = 15;
    636 		break;
    637 
    638 	case TIMING6x_INTEG_101ms:
    639 		/* Wait 120ms for 101ms integration */
    640 		ms = 120;
    641 		break;
    642 
    643 	case TIMING6x_INTEG_402ms:
    644 	default:
    645 		/* Wait 450ms for 402ms integration */
    646 		ms = 450;
    647 		break;
    648 	}
    649 
    650 	if (ms < hztoms(1)) {
    651 		/* Just busy-wait if we want to wait for less than 1 tick. */
    652 		delay(ms * 1000);
    653 	} else {
    654 		/* Round up one tick for the case where we sleep. */
    655 		(void) kpause("tslluxwait", false, mstohz(ms) + 1, NULL);
    656 	}
    657 
    658 	return (0);
    659 }
    660 
    661 static int
    662 tsllux_read_adcs(struct tsllux_softc *sc, uint16_t *adc0valp,
    663 		 uint16_t *adc1valp)
    664 {
    665 	int error;
    666 
    667 	if ((error = tsllux_read2(sc, TSL256x_REG_DATA0LOW, adc0valp)) == 0)
    668 		error = tsllux_read2(sc, TSL256x_REG_DATA1LOW, adc1valp);
    669 
    670 	return (error);
    671 }
    672 
    673 /*
    674  * The following code is partially derived from Adafruit's TSL2561
    675  * driver for Arduino (which was in turn derived from the data sheet),
    676  * which carries this notice:
    677  *
    678  * @file Adafruit_TSL2561_U.cpp
    679  *
    680  * @mainpage Adafruit TSL2561 Light/Lux sensor driver
    681  *
    682  * @section intro_sec Introduction
    683  *
    684  * This is the documentation for Adafruit's TSL2561 driver for the
    685  * Arduino platform.  It is designed specifically to work with the
    686  * Adafruit TSL2561 breakout: http://www.adafruit.com/products/439
    687  *
    688  * These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
    689  * to interface with the breakout.
    690  *
    691  * Adafruit invests time and resources providing this open source code,
    692  * please support Adafruit and open-source hardware by purchasing
    693  * products from Adafruit!
    694  *
    695  * @section dependencies Dependencies
    696  *
    697  * This library depends on <a href="https://github.com/adafruit/Adafruit_Sensor">
    698  * Adafruit_Sensor</a> being present on your system. Please make sure you have
    699  * installed the latest version before using this library.
    700  *
    701  * @section author Author
    702  *
    703  * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
    704  *
    705  * @section license License
    706  *
    707  * BSD license, all text here must be included in any redistribution.
    708  *
    709  *   @section  HISTORY
    710  *
    711  *   v2.0 - Rewrote driver for Adafruit_Sensor and Auto-Gain support, and
    712  *          added lux clipping check (returns 0 lux on sensor saturation)
    713  *   v1.0 - First release (previously TSL2561)
    714  */
    715 
    716 static int
    717 tsllux_read_sensors(struct tsllux_softc *sc, uint16_t *adc0p, uint16_t *adc1p)
    718 {
    719 	int error;
    720 
    721 	if ((error = tsllux_poweron(sc)) != 0)
    722 		return (error);
    723 
    724 	if ((error = tsllux_wait_for_adcs(sc)) != 0)
    725 		goto out;
    726 
    727 	error = tsllux_read_adcs(sc, adc0p, adc1p);
    728 
    729  out:
    730 	(void) tsllux_poweroff(sc);
    731 	return (error);
    732 }
    733 
    734 /*
    735  * Auto-gain thresholds:
    736  */
    737 #define	TSL2561_AGC_THI_13MS	(4850)	/* Max value at Ti 13ms = 5047 */
    738 #define	TSL2561_AGC_TLO_13MS	(100)	/* Min value at Ti 13ms = 100 */
    739 #define	TSL2561_AGC_THI_101MS	(36000)	/* Max value at Ti 101ms = 37177 */
    740 #define	TSL2561_AGC_TLO_101MS	(200)	/* Min value at Ti 101ms = 200 */
    741 #define	TSL2561_AGC_THI_402MS	(63000)	/* Max value at Ti 402ms = 65535 */
    742 #define	TSL2561_AGC_TLO_402MS	(500)	/* Min value at Ti 402ms = 500 */
    743 
    744 static int
    745 tsllux_get_sensor_data(struct tsllux_softc *sc, uint16_t *broadband,
    746 		       uint16_t *ir)
    747 {
    748 	int error = 0;
    749 	uint16_t adc0, adc1;
    750 	bool did_adjust_gain, valid;
    751 	uint16_t hi, lo;
    752 
    753 	if (sc->sc_auto_gain == false) {
    754 		error = tsllux_read_sensors(sc, &adc0, &adc1);
    755 		goto out;
    756 	}
    757 
    758 	/* Set the hi / lo threshold based on current integration time. */
    759 	switch (sc->sc_itime) {
    760 	case TIMING6x_INTEG_13_7ms:
    761 		hi = TSL2561_AGC_THI_13MS;
    762 		lo = TSL2561_AGC_TLO_13MS;
    763 		break;
    764 
    765 	case TIMING6x_INTEG_101ms:
    766 		hi = TSL2561_AGC_THI_101MS;
    767 		lo = TSL2561_AGC_TLO_101MS;
    768 		break;
    769 
    770 	case TIMING6x_INTEG_402ms:
    771 	default:
    772 		hi = TSL2561_AGC_THI_402MS;
    773 		lo = TSL2561_AGC_TLO_402MS;
    774 	}
    775 
    776 	/* Read data and adjust the gain until we have a valid range. */
    777 	for (valid = false, did_adjust_gain = false; valid == false; ) {
    778 		if ((error = tsllux_read_sensors(sc, &adc0, &adc1)) != 0)
    779 			goto out;
    780 
    781 		if (did_adjust_gain == false) {
    782 			if (adc0 < lo && sc->sc_gain == TIMING6x_GAIN_1X) {
    783 				/* Increase the gain and try again. */
    784 				if ((error =
    785 				     tsllux_set_gain0(sc,
    786 				     		      TIMING6x_GAIN_16X)) != 0)
    787 					goto out;
    788 				did_adjust_gain = true;
    789 			} else if (adc0 > hi &&
    790 				   sc->sc_gain == TIMING6x_GAIN_16X) {
    791 				/* Decrease the gain and try again. */
    792 				if ((error =
    793 				     tsllux_set_gain0(sc,
    794 				     		      TIMING6x_GAIN_1X)) != 0)
    795 					goto out;
    796 				did_adjust_gain = true;
    797 			} else {
    798 				/*
    799 				 * Reading is either valid or we're already
    800 				 * at the chip's limits.
    801 				 */
    802 				valid = true;
    803 			}
    804 		} else {
    805 			/*
    806 			 * If we've already adjust the gain once, just
    807 			 * return the new results.  This avoids endless
    808 			 * loops where a value is at one extre pre-gain
    809 			 * and at the other extreme post-gain.
    810 			 */
    811 			valid = true;
    812 		}
    813 	}
    814 
    815  out:
    816 	if (error == 0) {
    817 		if (broadband != NULL)
    818 			*broadband = adc0;
    819 		if (ir != NULL)
    820 			*ir = adc1;
    821 	}
    822 	return (error);
    823 }
    824 
    825 /*
    826  * Clipping thresholds:
    827  */
    828 #define	TSL2561_CLIPPING_13MS	(4900)
    829 #define	TSL2561_CLIPPING_101MS	(37000)
    830 #define	TSL2561_CLIPPING_402MS	(65000)
    831 
    832 /*
    833  * Scaling factors:
    834  */
    835 #define	TSL2561_LUX_LUXSCALE      (14)	   /* Scale by 2^14 */
    836 #define	TSL2561_LUX_RATIOSCALE    (9)      /* Scale ratio by 2^9 */
    837 #define	TSL2561_LUX_CHSCALE       (10)     /* Scale channel values by 2^10 */
    838 #define	TSL2561_LUX_CHSCALE_TINT0 (0x7517) /* 322/11 * 2^TSL2561_LUX_CHSCALE */
    839 #define	TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) /* 322/81 * 2^TSL2561_LUX_CHSCALE */
    840 
    841 /*
    842  * Lux factors (the datasheet explains how these magic constants
    843  * are used):
    844  */
    845 /* T, FN and CL package values */
    846 #define TSL2561_LUX_K1T           (0x0040)  /* 0.125 * 2^RATIO_SCALE */
    847 #define TSL2561_LUX_B1T           (0x01f2)  /* 0.0304 * 2^LUX_SCALE */
    848 #define TSL2561_LUX_M1T           (0x01be)  /* 0.0272 * 2^LUX_SCALE */
    849 #define TSL2561_LUX_K2T           (0x0080)  /* 0.250 * 2^RATIO_SCALE */
    850 #define TSL2561_LUX_B2T           (0x0214)  /* 0.0325 * 2^LUX_SCALE */
    851 #define TSL2561_LUX_M2T           (0x02d1)  /* 0.0440 * 2^LUX_SCALE */
    852 #define TSL2561_LUX_K3T           (0x00c0)  /* 0.375 * 2^RATIO_SCALE */
    853 #define TSL2561_LUX_B3T           (0x023f)  /* 0.0351 * 2^LUX_SCALE */
    854 #define TSL2561_LUX_M3T           (0x037b)  /* 0.0544 * 2^LUX_SCALE */
    855 #define TSL2561_LUX_K4T           (0x0100)  /* 0.50 * 2^RATIO_SCALE */
    856 #define TSL2561_LUX_B4T           (0x0270)  /* 0.0381 * 2^LUX_SCALE */
    857 #define TSL2561_LUX_M4T           (0x03fe)  /* 0.0624 * 2^LUX_SCALE */
    858 #define TSL2561_LUX_K5T           (0x0138)  /* 0.61 * 2^RATIO_SCALE */
    859 #define TSL2561_LUX_B5T           (0x016f)  /* 0.0224 * 2^LUX_SCALE */
    860 #define TSL2561_LUX_M5T           (0x01fc)  /* 0.0310 * 2^LUX_SCALE */
    861 #define TSL2561_LUX_K6T           (0x019a)  /* 0.80 * 2^RATIO_SCALE */
    862 #define TSL2561_LUX_B6T           (0x00d2)  /* 0.0128 * 2^LUX_SCALE */
    863 #define TSL2561_LUX_M6T           (0x00fb)  /* 0.0153 * 2^LUX_SCALE */
    864 #define TSL2561_LUX_K7T           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
    865 #define TSL2561_LUX_B7T           (0x0018)  /* 0.00146 * 2^LUX_SCALE */
    866 #define TSL2561_LUX_M7T           (0x0012)  /* 0.00112 * 2^LUX_SCALE */
    867 #define TSL2561_LUX_K8T           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
    868 #define TSL2561_LUX_B8T           (0x0000)  /* 0.000 * 2^LUX_SCALE */
    869 #define TSL2561_LUX_M8T           (0x0000)  /* 0.000 * 2^LUX_SCALE */
    870 
    871 /* CS package values */
    872 #define TSL2561_LUX_K1C           (0x0043)  /* 0.130 * 2^RATIO_SCALE */
    873 #define TSL2561_LUX_B1C           (0x0204)  /* 0.0315 * 2^LUX_SCALE */
    874 #define TSL2561_LUX_M1C           (0x01ad)  /* 0.0262 * 2^LUX_SCALE */
    875 #define TSL2561_LUX_K2C           (0x0085)  /* 0.260 * 2^RATIO_SCALE */
    876 #define TSL2561_LUX_B2C           (0x0228)  /* 0.0337 * 2^LUX_SCALE */
    877 #define TSL2561_LUX_M2C           (0x02c1)  /* 0.0430 * 2^LUX_SCALE */
    878 #define TSL2561_LUX_K3C           (0x00c8)  /* 0.390 * 2^RATIO_SCALE */
    879 #define TSL2561_LUX_B3C           (0x0253)  /* 0.0363 * 2^LUX_SCALE */
    880 #define TSL2561_LUX_M3C           (0x0363)  /* 0.0529 * 2^LUX_SCALE */
    881 #define TSL2561_LUX_K4C           (0x010a)  /* 0.520 * 2^RATIO_SCALE */
    882 #define TSL2561_LUX_B4C           (0x0282)  /* 0.0392 * 2^LUX_SCALE */
    883 #define TSL2561_LUX_M4C           (0x03df)  /* 0.0605 * 2^LUX_SCALE */
    884 #define TSL2561_LUX_K5C           (0x014d)  /* 0.65 * 2^RATIO_SCALE */
    885 #define TSL2561_LUX_B5C           (0x0177)  /* 0.0229 * 2^LUX_SCALE */
    886 #define TSL2561_LUX_M5C           (0x01dd)  /* 0.0291 * 2^LUX_SCALE */
    887 #define TSL2561_LUX_K6C           (0x019a)  /* 0.80 * 2^RATIO_SCALE */
    888 #define TSL2561_LUX_B6C           (0x0101)  /* 0.0157 * 2^LUX_SCALE */
    889 #define TSL2561_LUX_M6C           (0x0127)  /* 0.0180 * 2^LUX_SCALE */
    890 #define TSL2561_LUX_K7C           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
    891 #define TSL2561_LUX_B7C           (0x0037)  /* 0.00338 * 2^LUX_SCALE */
    892 #define TSL2561_LUX_M7C           (0x002b)  /* 0.00260 * 2^LUX_SCALE */
    893 #define TSL2561_LUX_K8C           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
    894 #define TSL2561_LUX_B8C           (0x0000)  /* 0.000 * 2^LUX_SCALE */
    895 #define TSL2561_LUX_M8C           (0x0000)  /* 0.000 * 2^LUX_SCALE */
    896 
    897 struct lux_factor_table_entry {
    898 	uint16_t	k;
    899 	uint16_t	b;
    900 	uint16_t	m;
    901 };
    902 
    903 static const struct lux_factor_table_entry lux_factor_table[] = {
    904 	{ TSL2561_LUX_K1T,	TSL2561_LUX_B1T,	TSL2561_LUX_M1T },
    905 	{ TSL2561_LUX_K2T,	TSL2561_LUX_B2T,	TSL2561_LUX_M2T },
    906 	{ TSL2561_LUX_K3T,	TSL2561_LUX_B3T,	TSL2561_LUX_M3T },
    907 	{ TSL2561_LUX_K4T,	TSL2561_LUX_B4T,	TSL2561_LUX_M4T },
    908 	{ TSL2561_LUX_K5T,	TSL2561_LUX_B5T,	TSL2561_LUX_M5T },
    909 	{ TSL2561_LUX_K6T,	TSL2561_LUX_B6T,	TSL2561_LUX_M6T },
    910 	{ TSL2561_LUX_K7T,	TSL2561_LUX_B7T,	TSL2561_LUX_M7T },
    911 	{ TSL2561_LUX_K8T,	TSL2561_LUX_B8T,	TSL2561_LUX_M8T },
    912 };
    913 static const int lux_factor_table_last_entry =
    914     (sizeof(lux_factor_table) / sizeof(lux_factor_table[0])) - 1;
    915 
    916 static const struct lux_factor_table_entry lux_factor_table_cs_package[] = {
    917 	{ TSL2561_LUX_K1C,	TSL2561_LUX_B1C,	TSL2561_LUX_M1C },
    918 	{ TSL2561_LUX_K2C,	TSL2561_LUX_B2C,	TSL2561_LUX_M2C },
    919 	{ TSL2561_LUX_K3C,	TSL2561_LUX_B3C,	TSL2561_LUX_M3C },
    920 	{ TSL2561_LUX_K4C,	TSL2561_LUX_B4C,	TSL2561_LUX_M4C },
    921 	{ TSL2561_LUX_K5C,	TSL2561_LUX_B5C,	TSL2561_LUX_M5C },
    922 	{ TSL2561_LUX_K6C,	TSL2561_LUX_B6C,	TSL2561_LUX_M6C },
    923 	{ TSL2561_LUX_K7C,	TSL2561_LUX_B7C,	TSL2561_LUX_M7C },
    924 	{ TSL2561_LUX_K8C,	TSL2561_LUX_B8C,	TSL2561_LUX_M8C },
    925 };
    926 static const int lux_factor_table_cs_package_last_entry =
    927     (sizeof(lux_factor_table_cs_package) /
    928      sizeof(lux_factor_table_cs_package[0])) - 1;
    929 
    930 static int
    931 tsllux_get_lux(struct tsllux_softc *sc, uint32_t *luxp,
    932 	       uint16_t *raw_broadband, uint16_t *raw_ir)
    933 {
    934 	uint32_t channel0, channel1, scale, ratio, lux = 0;
    935 	uint16_t broadband, ir;
    936 	uint16_t clip_threshold;
    937 	const struct lux_factor_table_entry *table;
    938 	int idx, last_entry, error;
    939 	int32_t temp;
    940 
    941 	if ((error = tsllux_get_sensor_data(sc, &broadband, &ir)) != 0)
    942 		return (error);
    943 
    944 	if (luxp == NULL) {
    945 		/*
    946 		 * Caller doesn't want the calculated Lux value, so
    947 		 * don't bother calculating it.  Maybe they just want
    948 		 * the raw sensor data?
    949 		 */
    950 		goto out;
    951 	}
    952 
    953 	/*
    954 	 * Check to see if the sensor is saturated.  If so,
    955 	 * just return a "max brightness" value.
    956 	 */
    957 	switch (sc->sc_itime) {
    958 	case TIMING6x_INTEG_13_7ms:
    959 		clip_threshold = TSL2561_CLIPPING_13MS;
    960 		break;
    961 
    962 	case TIMING6x_INTEG_101ms:
    963 		clip_threshold = TSL2561_CLIPPING_101MS;
    964 		break;
    965 
    966 	case TIMING6x_INTEG_402ms:
    967 	default:
    968 		clip_threshold = TSL2561_CLIPPING_402MS;
    969 		break;
    970 	}
    971 
    972 	if (broadband > clip_threshold || ir > clip_threshold) {
    973 		lux = 65536;
    974 		goto out;
    975 	}
    976 
    977 	/* Get correct scale factor based on integration time. */
    978 	switch (sc->sc_itime) {
    979 	case TIMING6x_INTEG_13_7ms:
    980 		scale = TSL2561_LUX_CHSCALE_TINT0;
    981 		break;
    982 
    983 	case TIMING6x_INTEG_101ms:
    984 		scale = TSL2561_LUX_CHSCALE_TINT1;
    985 		break;
    986 
    987 	case TIMING6x_INTEG_402ms:
    988 	default:
    989 		scale = (1 << TSL2561_LUX_CHSCALE);
    990 	}
    991 
    992 	/* Scale for gain. */
    993 	if (sc->sc_gain == TIMING6x_GAIN_1X)
    994 		scale <<= 4;
    995 
    996 	/* Scale the channel values. */
    997 	channel0 = ((uint32_t)broadband * scale) >> TSL2561_LUX_CHSCALE;
    998 	channel1 = ((uint32_t)ir * scale) >> TSL2561_LUX_CHSCALE;
    999 
   1000 	/* Find the ratio of the channel values (ir / broadband) */
   1001 	if (channel0 != 0)
   1002 		ratio = (channel1 << (TSL2561_LUX_RATIOSCALE + 1)) / channel0;
   1003 	else
   1004 		ratio = 0;
   1005 
   1006 	/* Round the ratio value. */
   1007 	ratio = (ratio + 1) >> 1;
   1008 
   1009 	if (sc->sc_cs_package) {
   1010 		table = lux_factor_table_cs_package;
   1011 		last_entry = lux_factor_table_cs_package_last_entry;
   1012 	} else {
   1013 		table = lux_factor_table;
   1014 		last_entry = lux_factor_table_last_entry;
   1015 	}
   1016 
   1017 	/*
   1018 	 * The table is arranged such that we compare <= against
   1019 	 * the key, and if all else fails, we use the last entry.
   1020 	 * The pseudo-code in the data sheet shows what's going on.
   1021 	 */
   1022 	for (idx = 0; idx < last_entry; idx++) {
   1023 		if (ratio <= table[idx].k)
   1024 			break;
   1025 	}
   1026 
   1027 	temp = ((channel0 * table[idx].b) - (channel1 * table[idx].m));
   1028 
   1029 	/* Do not allow negative Lux value. */
   1030 	if (temp < 0)
   1031 		temp = 0;
   1032 
   1033 	/* Round lsb (2^(LUX_SCALE-1)) */
   1034 	temp += (1 << (TSL2561_LUX_LUXSCALE-1));
   1035 
   1036 	/* Strip off fractional portion */
   1037 	lux = temp >> TSL2561_LUX_LUXSCALE;
   1038 
   1039  out:
   1040 	if (error == 0) {
   1041 		if (luxp != NULL)
   1042 			*luxp = lux;
   1043 		if (raw_broadband != NULL)
   1044 			*raw_broadband = broadband;
   1045 		if (raw_ir != NULL)
   1046 			*raw_ir = ir;
   1047 	}
   1048 	return (error);
   1049 }
   1050