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