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