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