1 /*
2 * Copyright (c) 2025 Andreas Klinger
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT vishay_veml6031
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/pm/device.h>
15 #include <zephyr/sys/byteorder.h>
16
17 #include <zephyr/drivers/sensor/veml6031.h>
18
19 LOG_MODULE_REGISTER(VEML6031, CONFIG_SENSOR_LOG_LEVEL);
20
21 /*
22 * ID code of device
23 */
24 #define VEML6031_DEFAULT_ID 0x01
25
26 /*
27 * Bit mask to check for data ready in single measurement.
28 */
29 #define VEML6031_ALS_AF_DATA_READY BIT(3)
30
31 /*
32 * Maximum value of ALS data which also means that the sensor is in saturation
33 * and that the measured value might be wrong.
34 * In such a case the user program should reduce one or more of the following
35 * attributes to get a relyable value:
36 * gain
37 * integration time
38 * effective photodiode size
39 *
40 */
41 #define VEML6031_ALS_DATA_OVERFLOW 0xFFFF
42
43 /*
44 * 16-bit command register addresses
45 */
46 #define VEML6031_CMDCODE_ALS_CONF_0 0x00
47 #define VEML6031_CMDCODE_ALS_CONF_1 0x01
48 #define VEML6031_CMDCODE_ALS_WH_L 0x04
49 #define VEML6031_CMDCODE_ALS_WH_H 0x05
50 #define VEML6031_CMDCODE_ALS_WL_L 0x06
51 #define VEML6031_CMDCODE_ALS_WL_H 0x07
52 #define VEML6031_CMDCODE_ALS_DATA_L 0x10
53 #define VEML6031_CMDCODE_ALS_DATA_H 0x11
54 #define VEML6031_CMDCODE_IR_DATA_L 0x12
55 #define VEML6031_CMDCODE_IR_DATA_H 0x13
56 #define VEML6031_CMDCODE_ID_L 0x14
57 #define VEML6031_CMDCODE_ID_H 0x15
58 #define VEML6031_CMDCODE_ALS_INT 0x17
59
60 /*
61 * ALS integration time struct.
62 */
63 struct veml6031_it_data {
64 enum veml6031_it num;
65 uint8_t val;
66 int us;
67 };
68
69 /*
70 * ALS integration time setting values.
71 *
72 * The enumerators of <tt>enum veml6031_it</tt> provide
73 * indices into this array to get the related value for the
74 * ALS_IT configuration bits.
75 */
76 static const struct veml6031_it_data veml6031_it_values[] = {
77 {VEML6031_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */
78 {VEML6031_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */
79 {VEML6031_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */
80 {VEML6031_IT_25, 0x03, 25000}, /* 25 - 0b0011 */
81 {VEML6031_IT_50, 0x04, 50000}, /* 50 - 0b0100 */
82 {VEML6031_IT_100, 0x05, 100000}, /* 100 - 0b0101 */
83 {VEML6031_IT_200, 0x06, 200000}, /* 200 - 0b0110 */
84 {VEML6031_IT_400, 0x07, 400000}, /* 400 - 0b0111 */
85 };
86
87 /*
88 * Resolution matrix for values to convert between data provided
89 * by the sensor ("counts") and lux.
90 *
91 * These values depend on the current size, gain and integration time settings.
92 * The enumerators of <tt>enum veml6031_div4</tt>, <tt>enum veml6031_gain</tt>
93 * and <tt>enum veml6031_als_it</tt> are used for indices into this matrix.
94 */
95 static const float
96 veml6031_resolution[VEML6031_DIV4_COUNT][VEML6031_GAIN_COUNT][VEML6031_IT_COUNT] = {
97 /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */
98 /* size 4/4 */
99 {
100 {0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, 0.0136f,
101 0.0068f}, /* Gain 1 */
102 {0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f, 0.0136f, 0.0068f,
103 0.0034f}, /* Gain 2 */
104 {1.3188f, 0.6504f, 0.3297f, 0.1648f, 0.0824f, 0.0412f, 0.0206f,
105 0.0103f}, /* Gain 0.66 */
106 {1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f,
107 0.0136f}, /* Gain 0.5 */
108 },
109 {
110 /* size 1/4 */
111 {3.4816f, 1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f,
112 0.0272f}, /* Gain 1 */
113 {1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f, 0.0544f, 0.0272f,
114 0.0136f}, /* Gain 2 */
115 {5.2752f, 2.6376f, 1.3188f, 0.6594f, 0.3297f, 0.1648f, 0.0824f,
116 0.0412f}, /* Gain 0.66 */
117 {6.9632f, 3.4816f, 1.7408f, 0.8704f, 0.4352f, 0.2176f, 0.1088f,
118 0.0544f}, /* Gain 0.5 */
119 },
120 };
121
122 struct veml6031_config {
123 struct i2c_dt_spec bus;
124 };
125
126 struct veml6031_data {
127 uint8_t sd; /* Band gap and LDO shutdown */
128 uint8_t int_en; /* ALS interrupt enable */
129 uint8_t trig; /* ALS active force trigger */
130 uint8_t af; /* Active force mode */
131 uint8_t ir_sd; /* ALS and IR channel shutdown */
132 uint8_t cal; /* Power on ready */
133 enum veml6031_div4 div4; /* effective photodiode size */
134 enum veml6031_gain gain; /* gain selection */
135 enum veml6031_it itim; /* ALS integration time */
136 enum veml6031_pers pers; /* ALS persistens protect */
137 uint16_t thresh_high;
138 uint16_t thresh_low;
139 uint16_t als_data;
140 uint32_t als_lux;
141 uint16_t ir_data;
142 uint32_t int_flags;
143 };
144
veml6031_sleep_by_integration_time(const struct veml6031_data * data)145 static void veml6031_sleep_by_integration_time(const struct veml6031_data *data)
146 {
147 k_sleep(K_USEC(veml6031_it_values[data->itim].us));
148 }
149
veml6031_check_gain(const struct sensor_value * val)150 static int veml6031_check_gain(const struct sensor_value *val)
151 {
152 return val->val1 >= VEML6031_GAIN_1 && val->val1 <= VEML6031_GAIN_0_5;
153 }
154
veml6031_check_it(const struct sensor_value * val)155 static int veml6031_check_it(const struct sensor_value *val)
156 {
157 return val->val1 >= VEML6031_IT_3_125 && val->val1 <= VEML6031_IT_400;
158 }
159
veml6031_check_div4(const struct sensor_value * val)160 static int veml6031_check_div4(const struct sensor_value *val)
161 {
162 return val->val1 >= VEML6031_SIZE_4_4 && val->val1 <= VEML6031_SIZE_1_4;
163 }
164
veml6031_check_pers(const struct sensor_value * val)165 static int veml6031_check_pers(const struct sensor_value *val)
166 {
167 return val->val1 >= VEML6031_PERS_1 && val->val1 <= VEML6031_PERS_8;
168 }
169
veml6031_read(const struct device * dev,uint8_t cmd,uint8_t * data)170 static int veml6031_read(const struct device *dev, uint8_t cmd, uint8_t *data)
171 {
172 const struct veml6031_config *conf = dev->config;
173
174 uint8_t recv_buf;
175 int ret;
176
177 ret = i2c_reg_read_byte_dt(&conf->bus, cmd, &recv_buf);
178 if (ret < 0) {
179 return ret;
180 }
181
182 *data = recv_buf;
183
184 return 0;
185 }
186
veml6031_read16(const struct device * dev,uint8_t cmd,uint8_t * data)187 static int veml6031_read16(const struct device *dev, uint8_t cmd, uint8_t *data)
188 {
189 const struct veml6031_config *conf = dev->config;
190 int ret;
191
192 ret = i2c_burst_read_dt(&conf->bus, cmd, data, 2);
193 if (ret < 0) {
194 return ret;
195 }
196
197 return 0;
198 }
199
veml6031_write16(const struct device * dev,uint8_t cmd,uint8_t * data)200 static int veml6031_write16(const struct device *dev, uint8_t cmd, uint8_t *data)
201 {
202 const struct veml6031_config *conf = dev->config;
203
204 return i2c_burst_write_dt(&conf->bus, cmd, data, 2);
205 }
206
veml6031_write_conf(const struct device * dev)207 static int veml6031_write_conf(const struct device *dev)
208 {
209 int ret;
210 struct veml6031_data *data = dev->data;
211 uint8_t conf[2] = {0, 0};
212
213 /* Bits 7 -> ALS and IR channel shutdown */
214 conf[1] |= data->ir_sd << 7;
215 /* Bits 6 -> Effective photodiode size */
216 conf[1] |= data->div4 << 6;
217 /* Bit 5 -> reserved */
218 /* Bits 4:3 -> Gain selection */
219 conf[1] |= data->gain << 3;
220 /* Bits 2:1 -> ALS persistence protect number */
221 conf[1] |= data->pers << 1;
222 /* Bit 0 -> Power on ready */
223 if (data->cal) {
224 conf[1] |= BIT(0);
225 }
226 /* Bit 7 -> reserved, have to be 0 */
227 /* Bits 6:4 -> integration time (ALS_IT) */
228 conf[0] |= data->itim << 4;
229 /* Bit 3 -> Active force mode enable */
230 if (data->af) {
231 conf[0] |= BIT(3);
232 }
233 /* Bit 2 -> ALS active force trigger */
234 if (data->trig) {
235 conf[0] |= BIT(2);
236 }
237 /* Bit 1 -> ALS interrupt enable */
238 if (data->int_en) {
239 conf[0] |= BIT(1);
240 }
241 /* Bit 0 -> shut down setting (SD) */
242 if (data->sd) {
243 conf[0] |= BIT(0);
244 }
245
246 ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_CONF_0, conf);
247 if (ret) {
248 LOG_ERR("Error while writing conf[0] ret: %d", ret);
249 return ret;
250 }
251
252 return 0;
253 }
254
veml6031_write_thresh_high(const struct device * dev)255 static int veml6031_write_thresh_high(const struct device *dev)
256 {
257 int ret;
258 const struct veml6031_data *data = dev->data;
259 uint8_t val[2];
260
261 val[0] = data->thresh_high & 0xFF;
262 val[1] = data->thresh_high >> 8;
263
264 LOG_DBG("Writing high threshold counts: %d", data->thresh_high);
265 ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_WH_L, val);
266 if (ret) {
267 return ret;
268 }
269
270 return 0;
271 }
272
veml6031_write_thresh_low(const struct device * dev)273 static int veml6031_write_thresh_low(const struct device *dev)
274 {
275 int ret;
276 const struct veml6031_data *data = dev->data;
277 uint8_t val[2];
278
279 val[0] = data->thresh_low & 0xFF;
280 val[1] = data->thresh_low >> 8;
281
282 LOG_DBG("Writing low threshold counts: %d", data->thresh_low);
283 ret = veml6031_write16(dev, VEML6031_CMDCODE_ALS_WL_L, val);
284 if (ret) {
285 return ret;
286 }
287
288 return 0;
289 }
290
veml6031_fetch(const struct device * dev)291 static int veml6031_fetch(const struct device *dev)
292 {
293 struct veml6031_data *data = dev->data;
294 int ret;
295
296 ret = veml6031_read16(dev, VEML6031_CMDCODE_ALS_DATA_L, (uint8_t *)&data->als_data);
297 if (ret < 0) {
298 return ret;
299 }
300 data->als_data = sys_le16_to_cpu(data->als_data);
301
302 ret = veml6031_read16(dev, VEML6031_CMDCODE_IR_DATA_L, (uint8_t *)&data->ir_data);
303 if (ret < 0) {
304 return ret;
305 }
306 data->ir_data = sys_le16_to_cpu(data->ir_data);
307
308 data->als_lux = data->als_data * veml6031_resolution[data->div4][data->gain][data->itim];
309
310 LOG_DBG("Read ALS measurement: counts=%d, lux=%d ir=%d", data->als_data, data->als_lux,
311 data->ir_data);
312
313 if (data->als_data == VEML6031_ALS_DATA_OVERFLOW) {
314 return -E2BIG;
315 }
316
317 return 0;
318 }
319
veml6031_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)320 static int veml6031_attr_set(const struct device *dev, enum sensor_channel chan,
321 enum sensor_attribute attr, const struct sensor_value *val)
322 {
323 struct veml6031_data *data = dev->data;
324
325 if (chan != SENSOR_CHAN_LIGHT) {
326 return -ENOTSUP;
327 }
328
329 /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */
330 switch ((int)attr) {
331 case SENSOR_ATTR_VEML6031_IT:
332 if (veml6031_check_it(val)) {
333 data->itim = (enum veml6031_it)val->val1;
334 } else {
335 return -EINVAL;
336 }
337 break;
338 case SENSOR_ATTR_VEML6031_DIV4:
339 if (veml6031_check_div4(val)) {
340 data->div4 = (enum veml6031_div4)val->val1;
341 } else {
342 return -EINVAL;
343 }
344 break;
345 case SENSOR_ATTR_VEML6031_GAIN:
346 if (veml6031_check_gain(val)) {
347 data->gain = (enum veml6031_gain)val->val1;
348 } else {
349 return -EINVAL;
350 }
351 break;
352 case SENSOR_ATTR_VEML6031_PERS:
353 if (veml6031_check_pers(val)) {
354 data->pers = (enum veml6031_pers)val->val1;
355 } else {
356 return -EINVAL;
357 }
358 break;
359 case SENSOR_ATTR_LOWER_THRESH:
360 data->thresh_low =
361 val->val1 / veml6031_resolution[data->div4][data->gain][data->itim];
362 return veml6031_write_thresh_low(dev);
363 case SENSOR_ATTR_UPPER_THRESH:
364 data->thresh_high =
365 val->val1 / veml6031_resolution[data->div4][data->gain][data->itim];
366 return veml6031_write_thresh_high(dev);
367 default:
368 return -ENOTSUP;
369 }
370
371 return 0;
372 }
373
veml6031_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)374 static int veml6031_attr_get(const struct device *dev, enum sensor_channel chan,
375 enum sensor_attribute attr, struct sensor_value *val)
376 {
377 struct veml6031_data *data = dev->data;
378
379 if (chan != SENSOR_CHAN_LIGHT) {
380 return -ENOTSUP;
381 }
382
383 /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */
384 switch ((int)attr) {
385 case SENSOR_ATTR_VEML6031_IT:
386 val->val1 = data->itim;
387 break;
388 case SENSOR_ATTR_VEML6031_DIV4:
389 val->val1 = data->div4;
390 break;
391 case SENSOR_ATTR_VEML6031_GAIN:
392 val->val1 = data->gain;
393 break;
394 case SENSOR_ATTR_VEML6031_PERS:
395 val->val1 = data->pers;
396 break;
397 case SENSOR_ATTR_LOWER_THRESH:
398 val->val1 = data->thresh_low;
399 break;
400 case SENSOR_ATTR_UPPER_THRESH:
401 val->val1 = data->thresh_high;
402 break;
403 default:
404 return -ENOTSUP;
405 }
406
407 val->val2 = 0;
408
409 return 0;
410 }
411
veml6031_perform_single_measurement(const struct device * dev)412 static int veml6031_perform_single_measurement(const struct device *dev)
413 {
414 struct veml6031_data *data = dev->data;
415 int ret;
416 uint8_t val;
417 int cnt = 0;
418
419 data->ir_sd = 0;
420 data->cal = 1;
421 data->af = 1;
422 data->trig = 1;
423 data->int_en = 0;
424 data->sd = 0;
425
426 ret = veml6031_write_conf(dev);
427 if (ret) {
428 return ret;
429 }
430
431 ret = veml6031_read(dev, VEML6031_CMDCODE_ALS_INT, &val);
432
433 veml6031_sleep_by_integration_time(data);
434
435 while (1) {
436 ret = veml6031_read(dev, VEML6031_CMDCODE_ALS_INT, &val);
437 if (ret) {
438 return ret;
439 }
440
441 if (val & VEML6031_ALS_AF_DATA_READY) {
442 break;
443 }
444
445 k_sleep(K_MSEC(1));
446
447 cnt++;
448 }
449
450 LOG_DBG("read VEML6031_CMDCODE_ALS_INT: %02X (%d)", val, cnt);
451
452 return 0;
453 }
454
veml6031_sample_fetch(const struct device * dev,enum sensor_channel chan)455 static int veml6031_sample_fetch(const struct device *dev, enum sensor_channel chan)
456 {
457 int ret;
458
459 /* Start sensor for new measurement */
460 if (chan == SENSOR_CHAN_LIGHT || chan == SENSOR_CHAN_ALL) {
461 ret = veml6031_perform_single_measurement(dev);
462 if (ret < 0) {
463 return ret;
464 }
465
466 return veml6031_fetch(dev);
467 } else {
468 return -ENOTSUP;
469 }
470
471 return 0;
472 }
473
veml6031_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)474 static int veml6031_channel_get(const struct device *dev, enum sensor_channel chan,
475 struct sensor_value *val)
476 {
477 struct veml6031_data *data = dev->data;
478
479 switch ((int)chan) {
480 case SENSOR_CHAN_LIGHT:
481 val->val1 = data->als_lux;
482 break;
483 case SENSOR_CHAN_VEML6031_ALS_RAW_COUNTS:
484 val->val1 = data->als_data;
485 break;
486 case SENSOR_CHAN_VEML6031_IR_RAW_COUNTS:
487 val->val1 = data->ir_data;
488 break;
489 default:
490 return -ENOTSUP;
491 }
492
493 val->val2 = 0;
494
495 return 0;
496 }
497
498 #ifdef CONFIG_PM_DEVICE
499
veml6031_set_shutdown_flag(const struct device * dev,uint8_t new_val)500 static int veml6031_set_shutdown_flag(const struct device *dev, uint8_t new_val)
501 {
502 struct veml6031_data *data = dev->data;
503 uint8_t prev_sd;
504 uint8_t prev_ir_sd;
505 int ret;
506
507 prev_sd = data->sd;
508 prev_ir_sd = data->ir_sd;
509 data->sd = new_val;
510 data->ir_sd = new_val;
511
512 ret = veml6031_write_conf(dev);
513 if (ret < 0) {
514 data->ir_sd = prev_ir_sd;
515 data->sd = prev_sd;
516 }
517 return ret;
518 }
519
veml6031_pm_action(const struct device * dev,enum pm_device_action action)520 static int veml6031_pm_action(const struct device *dev, enum pm_device_action action)
521 {
522 struct veml6031_data *data = dev->data;
523
524 if (!data->sd) {
525 switch (action) {
526 case PM_DEVICE_ACTION_SUSPEND:
527 return veml6031_set_shutdown_flag(dev, 1);
528
529 case PM_DEVICE_ACTION_RESUME:
530 return veml6031_set_shutdown_flag(dev, 0);
531
532 default:
533 return -ENOTSUP;
534 }
535 }
536
537 return 0;
538 }
539
540 #endif /* CONFIG_PM_DEVICE */
541
veml6031_init(const struct device * dev)542 static int veml6031_init(const struct device *dev)
543 {
544 const struct veml6031_config *conf = dev->config;
545 int ret;
546 uint8_t val8;
547
548 if (!i2c_is_ready_dt(&conf->bus)) {
549 LOG_ERR("VEML device not ready");
550 return -ENODEV;
551 }
552
553 ret = veml6031_read(dev, VEML6031_CMDCODE_ID_L, &val8);
554 if (ret < 0) {
555 LOG_ERR("Error while reading ID low. ret: %d", ret);
556 return ret;
557 }
558 if (val8 != VEML6031_DEFAULT_ID) {
559 LOG_ERR("Device ID wrong: %d", val8);
560 return -EIO;
561 }
562 ret = veml6031_read(dev, VEML6031_CMDCODE_ID_H, &val8);
563 if (ret < 0) {
564 LOG_ERR("Error while reading ID high. ret: %d", ret);
565 return ret;
566 }
567 LOG_DBG("veml6031 found package: %02d address: %02X version: %3s", val8 >> 6,
568 val8 >> 4 & 0x03 ? 0x10 : 0x29, val8 & 0x0F ? "XXX" : "A01");
569
570 /* Initialize sensor configuration */
571 ret = veml6031_write_thresh_low(dev);
572 if (ret < 0) {
573 LOG_ERR("Error while writing thresh low. ret: %d", ret);
574 return ret;
575 }
576
577 ret = veml6031_write_thresh_high(dev);
578 if (ret < 0) {
579 LOG_ERR("Error while writing thresh high. ret: %d", ret);
580 return ret;
581 }
582
583 ret = veml6031_write_conf(dev);
584 if (ret < 0) {
585 LOG_ERR("Error while writing conf. ret: %d", ret);
586 return ret;
587 }
588
589 return 0;
590 }
591
592 static DEVICE_API(sensor, veml6031_api) = {
593 .sample_fetch = veml6031_sample_fetch,
594 .channel_get = veml6031_channel_get,
595 .attr_set = veml6031_attr_set,
596 .attr_get = veml6031_attr_get,
597 };
598
599 #define VEML6031_INIT(n) \
600 static struct veml6031_data veml6031_data_##n = {.trig = 1, \
601 .af = 1, \
602 .div4 = VEML6031_SIZE_4_4, \
603 .gain = VEML6031_GAIN_1, \
604 .itim = VEML6031_IT_100, \
605 .pers = VEML6031_PERS_1, \
606 .thresh_high = 0xFFFF}; \
607 \
608 static const struct veml6031_config veml6031_config_##n = { \
609 .bus = I2C_DT_SPEC_INST_GET(n)}; \
610 \
611 PM_DEVICE_DT_INST_DEFINE(n, veml6031_pm_action); \
612 \
613 SENSOR_DEVICE_DT_INST_DEFINE(n, veml6031_init, PM_DEVICE_DT_INST_GET(n), \
614 &veml6031_data_##n, &veml6031_config_##n, POST_KERNEL, \
615 CONFIG_SENSOR_INIT_PRIORITY, &veml6031_api);
616
617 DT_INST_FOREACH_STATUS_OKAY(VEML6031_INIT)
618