1 /*
2 * Copyright (c) 2023 Kurtis Dinelle
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ams_tsl2591
8
9 #include <zephyr/device.h>
10 #include <zephyr/pm/device.h>
11 #include <zephyr/sys/byteorder.h>
12 #include "tsl2591.h"
13
14 LOG_MODULE_REGISTER(TSL2591, CONFIG_SENSOR_LOG_LEVEL);
15
tsl2591_reg_read(const struct device * dev,uint8_t reg,uint8_t * buf,uint8_t size)16 static int tsl2591_reg_read(const struct device *dev, uint8_t reg, uint8_t *buf, uint8_t size)
17 {
18 const struct tsl2591_config *config = dev->config;
19 uint8_t cmd = TSL2591_NORMAL_CMD | reg;
20
21 return i2c_write_read_dt(&config->i2c, &cmd, 1U, buf, size);
22 }
23
tsl2591_reg_write(const struct device * dev,uint8_t reg,uint8_t val)24 static int tsl2591_reg_write(const struct device *dev, uint8_t reg, uint8_t val)
25 {
26 const struct tsl2591_config *config = dev->config;
27 uint8_t cmd[2] = {TSL2591_NORMAL_CMD | reg, val};
28
29 return i2c_write_dt(&config->i2c, cmd, 2U);
30 }
31
tsl2591_reg_update(const struct device * dev,uint8_t reg,uint8_t mask,uint8_t val)32 int tsl2591_reg_update(const struct device *dev, uint8_t reg, uint8_t mask, uint8_t val)
33 {
34 uint8_t old_value, new_value;
35 int ret;
36
37 ret = tsl2591_reg_read(dev, reg, &old_value, 1U);
38 if (ret < 0) {
39 return ret;
40 }
41
42 new_value = (old_value & ~mask) | (val & mask);
43 if (new_value == old_value) {
44 return 0;
45 }
46
47 return tsl2591_reg_write(dev, reg, new_value);
48 }
49
tsl2591_sample_fetch(const struct device * dev,enum sensor_channel chan)50 static int tsl2591_sample_fetch(const struct device *dev, enum sensor_channel chan)
51 {
52 struct tsl2591_data *data = dev->data;
53 uint8_t als_data[4];
54 int ret;
55
56 #ifdef CONFIG_TSL2591_FETCH_WAIT
57 uint8_t status;
58
59 ret = tsl2591_reg_read(dev, TSL2591_REG_STATUS, &status, 1U);
60 if (ret < 0) {
61 LOG_ERR("Failed to read status register");
62 return ret;
63 }
64
65 /* Check if ALS has completed an integration cycle since AEN asserted.
66 * If not, sleep for the duration of an integration cycle to ensure valid reading.
67 */
68 if (!(status & TSL2591_AVALID_MASK)) {
69 k_msleep((data->atime / 100) * TSL2591_MAX_TIME_STEP);
70 }
71
72 /* Reassert AEN to determine if next reading is valid */
73 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_AEN_MASK, TSL2591_AEN_OFF);
74 if (ret < 0) {
75 LOG_ERR("Failed to disable ALS");
76 return ret;
77 }
78
79 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_AEN_MASK, TSL2591_AEN_ON);
80 if (ret < 0) {
81 LOG_ERR("Failed to re-enable ALS");
82 return ret;
83 }
84 #endif
85
86 switch (chan) {
87 case SENSOR_CHAN_ALL:
88 ret = tsl2591_reg_read(dev, TSL2591_REG_C0DATAL, als_data, 4U);
89 if (ret < 0) {
90 LOG_ERR("Failed to read ALS data");
91 return ret;
92 }
93
94 data->vis_count = sys_get_le16(als_data);
95 data->ir_count = sys_get_le16(als_data + 2);
96 break;
97 case SENSOR_CHAN_LIGHT:
98 ret = tsl2591_reg_read(dev, TSL2591_REG_C0DATAL, als_data, 2U);
99 if (ret < 0) {
100 LOG_ERR("Failed to read ALS visible light data");
101 return ret;
102 }
103
104 data->vis_count = sys_get_le16(als_data);
105 break;
106 case SENSOR_CHAN_IR:
107 ret = tsl2591_reg_read(dev, TSL2591_REG_C1DATAL, als_data, 2U);
108 if (ret < 0) {
109 LOG_ERR("Failed to read ALS infrared data");
110 return ret;
111 }
112
113 data->ir_count = sys_get_le16(als_data);
114 break;
115 default:
116 LOG_ERR("Unsupported sensor channel");
117 return -ENOTSUP;
118 }
119
120 #ifdef CONFIG_TSL2591_WARN_SATURATED
121 uint16_t max_count = data->atime == 100 ? TSL2591_MAX_ADC_100 : TSL2591_MAX_ADC;
122 bool vis_saturated = (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT) &&
123 (data->vis_count >= max_count);
124 bool ir_saturated = (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_IR) &&
125 (data->ir_count >= max_count);
126 if (vis_saturated || ir_saturated) {
127 LOG_WRN("Sensor ADC potentially saturated, reading may be invalid");
128 return -EOVERFLOW;
129 }
130 #endif
131
132 return 0;
133 }
134
tsl2591_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)135 static int tsl2591_channel_get(const struct device *dev, enum sensor_channel chan,
136 struct sensor_value *val)
137 {
138 const struct tsl2591_data *data = dev->data;
139 int64_t cpl = (int64_t)data->atime * (int64_t)data->again;
140 int64_t strength;
141
142 /* Unfortunately, datasheet does not provide a lux conversion formula for this particular
143 * device. There is still ongoing discussion about the proper formula, though this
144 * implementation uses a slightly modified version of the Adafruit library formula:
145 * https://github.com/adafruit/Adafruit_TSL2591_Library/
146 *
147 * Since the device relies on both visible and IR readings to calculate lux,
148 * read SENSOR_CHAN_ALL to get a closer approximation of lux. Reading SENSOR_CHAN_LIGHT or
149 * SENSOR_CHAN_IR individually can be more closely thought of as relative strength
150 * as opposed to true lux.
151 */
152 switch (chan) {
153 case SENSOR_CHAN_ALL:
154 if (data->vis_count > 0) {
155 cpl *= 1000000;
156 strength =
157 (data->vis_count - data->ir_count) *
158 (1000000 - (((int64_t)data->ir_count * 1000000) / data->vis_count));
159 } else {
160 strength = 0;
161 }
162 break;
163 case SENSOR_CHAN_LIGHT:
164 strength = data->vis_count;
165 break;
166 case SENSOR_CHAN_IR:
167 strength = data->ir_count;
168 break;
169 default:
170 LOG_ERR("Unsupported sensor channel");
171 return -ENOTSUP;
172 }
173
174 strength *= TSL2591_LUX_DF;
175 val->val1 = strength / cpl;
176 val->val2 = ((strength % cpl) * 1000000) / cpl;
177
178 return 0;
179 }
180
181 #ifdef CONFIG_TSL2591_TRIGGER
tsl2591_set_threshold(const struct device * dev,enum sensor_attribute attr,const struct sensor_value * val)182 static int tsl2591_set_threshold(const struct device *dev, enum sensor_attribute attr,
183 const struct sensor_value *val)
184 {
185 const struct tsl2591_data *data = dev->data;
186 const struct tsl2591_config *config = dev->config;
187 uint64_t cpl;
188 uint32_t raw;
189 uint16_t thld;
190 uint8_t thld_reg;
191 uint8_t cmd[3];
192 int ret;
193
194 /* Convert from relative strength of visible light to raw value */
195 cpl = data->atime * data->again;
196 raw = ((val->val1 * cpl) / TSL2591_LUX_DF) +
197 ((val->val2 * cpl) / (1000000U * TSL2591_LUX_DF));
198
199 if (raw > TSL2591_MAX_ADC) {
200 LOG_ERR("Given value would overflow threshold register");
201 return -EOVERFLOW;
202 }
203
204 thld = sys_cpu_to_le16(raw);
205 thld_reg = attr == SENSOR_ATTR_LOWER_THRESH ? TSL2591_REG_AILTL : TSL2591_REG_AIHTL;
206
207 cmd[0] = TSL2591_NORMAL_CMD | thld_reg;
208 bytecpy(cmd + 1, &thld, 2U);
209
210 ret = i2c_write_dt(&config->i2c, cmd, 3U);
211 if (ret < 0) {
212 LOG_ERR("Failed to set interrupt threshold");
213 }
214
215 return ret;
216 }
217
tsl2591_set_persist(const struct device * dev,int32_t persist_filter)218 static int tsl2591_set_persist(const struct device *dev, int32_t persist_filter)
219 {
220 uint8_t persist_mode;
221 int ret;
222
223 switch (persist_filter) {
224 case 0:
225 persist_mode = TSL2591_PERSIST_EVERY;
226 break;
227 case 1:
228 persist_mode = TSL2591_PERSIST_1;
229 break;
230 case 2:
231 persist_mode = TSL2591_PERSIST_2;
232 break;
233 case 3:
234 persist_mode = TSL2591_PERSIST_3;
235 break;
236 case 5:
237 persist_mode = TSL2591_PERSIST_5;
238 break;
239 case 10:
240 persist_mode = TSL2591_PERSIST_10;
241 break;
242 case 15:
243 persist_mode = TSL2591_PERSIST_15;
244 break;
245 case 20:
246 persist_mode = TSL2591_PERSIST_20;
247 break;
248 case 25:
249 persist_mode = TSL2591_PERSIST_25;
250 break;
251 case 30:
252 persist_mode = TSL2591_PERSIST_30;
253 break;
254 case 35:
255 persist_mode = TSL2591_PERSIST_35;
256 break;
257 case 40:
258 persist_mode = TSL2591_PERSIST_40;
259 break;
260 case 45:
261 persist_mode = TSL2591_PERSIST_45;
262 break;
263 case 50:
264 persist_mode = TSL2591_PERSIST_50;
265 break;
266 case 55:
267 persist_mode = TSL2591_PERSIST_55;
268 break;
269 case 60:
270 persist_mode = TSL2591_PERSIST_60;
271 break;
272 default:
273 LOG_ERR("Invalid persist filter");
274 return -EINVAL;
275 }
276
277 ret = tsl2591_reg_write(dev, TSL2591_REG_PERSIST, persist_mode);
278 if (ret < 0) {
279 LOG_ERR("Failed to set persist filter");
280 }
281
282 return ret;
283 }
284 #endif
285
tsl2591_set_gain(const struct device * dev,enum sensor_gain_tsl2591 gain)286 static int tsl2591_set_gain(const struct device *dev, enum sensor_gain_tsl2591 gain)
287 {
288 struct tsl2591_data *data = dev->data;
289 uint8_t gain_mode;
290 int ret;
291
292 switch (gain) {
293 case TSL2591_SENSOR_GAIN_LOW:
294 data->again = TSL2591_GAIN_SCALE_LOW;
295 gain_mode = TSL2591_GAIN_MODE_LOW;
296 break;
297 case TSL2591_SENSOR_GAIN_MED:
298 data->again = TSL2591_GAIN_SCALE_MED;
299 gain_mode = TSL2591_GAIN_MODE_MED;
300 break;
301 case TSL2591_SENSOR_GAIN_HIGH:
302 data->again = TSL2591_GAIN_SCALE_HIGH;
303 gain_mode = TSL2591_GAIN_MODE_HIGH;
304 break;
305 case TSL2591_SENSOR_GAIN_MAX:
306 data->again = TSL2591_GAIN_SCALE_MAX;
307 gain_mode = TSL2591_GAIN_MODE_MAX;
308 break;
309 default:
310 LOG_ERR("Invalid gain mode");
311 return -EINVAL;
312 }
313
314 ret = tsl2591_reg_update(dev, TSL2591_REG_CONFIG, TSL2591_AGAIN_MASK, gain_mode);
315 if (ret < 0) {
316 LOG_ERR("Failed to set gain mode");
317 }
318
319 return ret;
320 }
321
tsl2591_set_integration(const struct device * dev,int32_t integration_time)322 static int tsl2591_set_integration(const struct device *dev, int32_t integration_time)
323 {
324 struct tsl2591_data *data = dev->data;
325 uint8_t atime_mode;
326 int ret;
327
328 switch (integration_time) {
329 case 100:
330 atime_mode = TSL2591_INTEGRATION_100MS;
331 break;
332 case 200:
333 atime_mode = TSL2591_INTEGRATION_200MS;
334 break;
335 case 300:
336 atime_mode = TSL2591_INTEGRATION_300MS;
337 break;
338 case 400:
339 atime_mode = TSL2591_INTEGRATION_400MS;
340 break;
341 case 500:
342 atime_mode = TSL2591_INTEGRATION_500MS;
343 break;
344 case 600:
345 atime_mode = TSL2591_INTEGRATION_600MS;
346 break;
347 default:
348 LOG_ERR("Invalid integration time");
349 return -EINVAL;
350 }
351
352 ret = tsl2591_reg_update(dev, TSL2591_REG_CONFIG, TSL2591_ATIME_MASK, atime_mode);
353 if (ret < 0) {
354 LOG_ERR("Failed to set integration time");
355 return ret;
356 }
357
358 data->atime = integration_time;
359
360 return 0;
361 }
362
tsl2591_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)363 static int tsl2591_attr_set(const struct device *dev, enum sensor_channel chan,
364 enum sensor_attribute attr, const struct sensor_value *val)
365 {
366 const struct tsl2591_data *data = dev->data;
367 int ret;
368
369 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_POWER_MASK, TSL2591_POWER_OFF);
370 if (ret < 0) {
371 LOG_ERR("Unable to power down device");
372 return ret;
373 }
374
375 #ifdef CONFIG_TSL2591_TRIGGER
376 if (attr == SENSOR_ATTR_UPPER_THRESH || attr == SENSOR_ATTR_LOWER_THRESH) {
377 if (chan == SENSOR_CHAN_LIGHT) {
378 ret = tsl2591_set_threshold(dev, attr, val);
379 } else {
380 LOG_ERR("Attribute not supported for channel");
381 ret = -ENOTSUP;
382 }
383 goto exit;
384 }
385 #endif
386
387 switch ((enum sensor_attribute_tsl2591)attr) {
388 case SENSOR_ATTR_GAIN_MODE:
389 ret = tsl2591_set_gain(dev, (enum sensor_gain_tsl2591)val->val1);
390 break;
391 case SENSOR_ATTR_INTEGRATION_TIME:
392 ret = tsl2591_set_integration(dev, val->val1);
393 break;
394
395 #ifdef CONFIG_TSL2591_TRIGGER
396 case SENSOR_ATTR_INT_PERSIST:
397 ret = tsl2591_set_persist(dev, val->val1);
398 break;
399 #endif
400 default:
401 LOG_ERR("Invalid sensor attribute");
402 ret = -EINVAL;
403 goto exit; /* So the compiler doesn't warn if triggers not enabled */
404 }
405
406 exit:
407 if (data->powered_on) {
408 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_POWER_MASK,
409 TSL2591_POWER_ON);
410 }
411
412 return ret;
413 }
414
tsl2591_setup(const struct device * dev)415 static int tsl2591_setup(const struct device *dev)
416 {
417 struct tsl2591_data *data = dev->data;
418 uint8_t device_id;
419 int ret;
420
421 ret = tsl2591_reg_write(dev, TSL2591_REG_CONFIG, TSL2591_SRESET);
422 if (ret < 0) {
423 LOG_ERR("Failed to reset device");
424 return ret;
425 }
426
427 ret = tsl2591_reg_read(dev, TSL2591_REG_ID, &device_id, 1U);
428 if (ret < 0) {
429 LOG_ERR("Failed to read device ID");
430 return ret;
431 }
432
433 if (device_id != TSL2591_DEV_ID) {
434 LOG_ERR("Device with ID 0x%02x is not supported", device_id);
435 return -ENOTSUP;
436 }
437
438 /* Set initial values to match sensor values on reset */
439 data->again = TSL2591_GAIN_SCALE_LOW;
440 data->atime = 100U;
441
442 ret = tsl2591_reg_write(dev, TSL2591_REG_ENABLE, TSL2591_POWER_ON);
443 if (ret < 0) {
444 LOG_ERR("Failed to perform initial power up of device");
445 return ret;
446 }
447
448 data->powered_on = true;
449
450 return 0;
451 }
452
tsl2591_init(const struct device * dev)453 static int tsl2591_init(const struct device *dev)
454 {
455 const struct tsl2591_config *config = dev->config;
456 int ret;
457
458 if (!i2c_is_ready_dt(&config->i2c)) {
459 LOG_ERR("I2C dev %s not ready", config->i2c.bus->name);
460 return -ENODEV;
461 }
462
463 ret = tsl2591_setup(dev);
464 if (ret < 0) {
465 LOG_ERR("Failed to setup device");
466 return ret;
467 }
468
469 #ifdef CONFIG_TSL2591_TRIGGER
470 ret = tsl2591_initialize_int(dev);
471 if (ret < 0) {
472 LOG_ERR("Failed to initialize interrupt!");
473 return ret;
474 }
475 #endif
476
477 return 0;
478 }
479
480 static DEVICE_API(sensor, tsl2591_driver_api) = {
481 #ifdef CONFIG_TSL2591_TRIGGER
482 .trigger_set = tsl2591_trigger_set,
483 #endif
484 .attr_set = tsl2591_attr_set,
485 .sample_fetch = tsl2591_sample_fetch,
486 .channel_get = tsl2591_channel_get};
487
488 #ifdef CONFIG_PM_DEVICE
tsl2591_pm_action(const struct device * dev,enum pm_device_action action)489 static int tsl2591_pm_action(const struct device *dev, enum pm_device_action action)
490 {
491 struct tsl2591_data *data = dev->data;
492 int ret;
493
494 switch (action) {
495 case PM_DEVICE_ACTION_RESUME:
496 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_POWER_MASK,
497 TSL2591_POWER_ON);
498 if (ret < 0) {
499 LOG_ERR("Failed to power on device");
500 return ret;
501 }
502
503 data->powered_on = true;
504 break;
505 case PM_DEVICE_ACTION_SUSPEND:
506 ret = tsl2591_reg_update(dev, TSL2591_REG_ENABLE, TSL2591_POWER_MASK,
507 TSL2591_POWER_OFF);
508 if (ret < 0) {
509 LOG_ERR("Failed to power off device");
510 return ret;
511 }
512
513 data->powered_on = false;
514 break;
515 default:
516 LOG_ERR("Unsupported PM action");
517 return -ENOTSUP;
518 }
519
520 return 0;
521 }
522 #endif
523
524 #define TSL2591_INIT_INST(n) \
525 static struct tsl2591_data tsl2591_data_##n; \
526 static const struct tsl2591_config tsl2591_config_##n = { \
527 .i2c = I2C_DT_SPEC_INST_GET(n), \
528 IF_ENABLED(CONFIG_TSL2591_TRIGGER, \
529 (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),))}; \
530 PM_DEVICE_DT_INST_DEFINE(n, tsl2591_pm_action); \
531 SENSOR_DEVICE_DT_INST_DEFINE(n, tsl2591_init, PM_DEVICE_DT_INST_GET(n), &tsl2591_data_##n, \
532 &tsl2591_config_##n, POST_KERNEL, \
533 CONFIG_SENSOR_INIT_PRIORITY, &tsl2591_driver_api);
534
535 DT_INST_FOREACH_STATUS_OKAY(TSL2591_INIT_INST)
536