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