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