tsllux.c revision 1.4 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