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