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