1 /*
2  * Copyright (c) 2022 T-Mobile USA, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ams_tsl2540
8 
9 #include "tsl2540.h"
10 
11 #include <stdlib.h>
12 
13 #include <zephyr/logging/log.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/pm/device.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/sys/util.h>
19 
20 #define TSL2540_INTEGRATION_TIME_MS	(2.81)
21 #define TSL2540_DEVICE_FACTOR		(53.0)
22 
23 #define FIXED_ATTENUATION_TO_DBL(x)	(x * 0.00001)
24 
25 LOG_MODULE_REGISTER(tsl2540, CONFIG_SENSOR_LOG_LEVEL);
26 
tsl2540_sample_fetch(const struct device * dev,enum sensor_channel chan)27 static int tsl2540_sample_fetch(const struct device *dev, enum sensor_channel chan)
28 {
29 	const struct tsl2540_config *cfg = dev->config;
30 	struct tsl2540_data *data = dev->data;
31 	int ret = 0;
32 
33 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT ||
34 			chan == SENSOR_CHAN_IR);
35 	k_sem_take(&data->sem, K_FOREVER);
36 
37 	if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT) {
38 		uint16_t le16_buffer;
39 
40 		ret = i2c_burst_read_dt(&cfg->i2c_spec, TSL2540_REG_VIS_LOW,
41 					(uint8_t *)&le16_buffer, sizeof(le16_buffer));
42 		if (ret) {
43 			LOG_ERR("Could not fetch ambient light (visible)");
44 			k_sem_give(&data->sem);
45 			return -EIO;
46 		}
47 
48 		data->count_vis = sys_le16_to_cpu(le16_buffer);
49 	}
50 
51 	if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_IR) {
52 		uint16_t le16_buffer;
53 
54 		ret = i2c_burst_read_dt(&cfg->i2c_spec, TSL2540_REG_IR_LOW, (uint8_t *)&le16_buffer,
55 					sizeof(le16_buffer));
56 		if (ret) {
57 			LOG_ERR("Could not fetch ambient light (IR)");
58 			k_sem_give(&data->sem);
59 			return -EIO;
60 		}
61 
62 		data->count_ir = sys_le16_to_cpu(le16_buffer);
63 	}
64 
65 	k_sem_give(&data->sem);
66 
67 	return ret;
68 }
69 
tsl2540_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)70 static int tsl2540_channel_get(const struct device *dev, enum sensor_channel chan,
71 			       struct sensor_value *val)
72 {
73 	const struct tsl2540_config *cfg = dev->config;
74 	struct tsl2540_data *data = dev->data;
75 	int ret = 0;
76 	double cpl;
77 	double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
78 	double glass_ir_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_ir_attenuation);
79 
80 	k_sem_take(&data->sem, K_FOREVER);
81 
82 	cpl = (data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS;
83 	cpl *= data->again;
84 
85 	switch (chan) {
86 	case SENSOR_CHAN_LIGHT:
87 		sensor_value_from_double(val, data->count_vis / cpl *
88 					 TSL2540_DEVICE_FACTOR * glass_attenuation);
89 		break;
90 	case SENSOR_CHAN_IR:
91 		sensor_value_from_double(val, data->count_ir / cpl *
92 					 TSL2540_DEVICE_FACTOR * glass_ir_attenuation);
93 		break;
94 	default:
95 		ret = -ENOTSUP;
96 	}
97 
98 	k_sem_give(&data->sem);
99 
100 	return ret;
101 }
102 
tsl2540_attr_set_gain(const struct device * dev,enum sensor_gain_tsl2540 gain)103 static int tsl2540_attr_set_gain(const struct device *dev, enum sensor_gain_tsl2540 gain)
104 {
105 	const struct tsl2540_config *cfg = dev->config;
106 	struct tsl2540_data *data = dev->data;
107 	uint8_t value = 0;
108 	double again = 0.0;
109 
110 	switch (gain) {
111 	case TSL2540_SENSOR_GAIN_1_2:
112 		value = TSL2540_CFG1_G1_2;
113 		again = TSL2540_AGAIN_S1_2;
114 		break;
115 	case TSL2540_SENSOR_GAIN_1:
116 		value = TSL2540_CFG1_G1;
117 		again = TSL2540_AGAIN_S1;
118 		break;
119 	case TSL2540_SENSOR_GAIN_4:
120 		value = TSL2540_CFG1_G4;
121 		again = TSL2540_AGAIN_S4;
122 		break;
123 	case TSL2540_SENSOR_GAIN_16:
124 		value = TSL2540_CFG1_G16;
125 		again = TSL2540_AGAIN_S16;
126 		break;
127 	case TSL2540_SENSOR_GAIN_64:
128 		value = TSL2540_CFG1_G64;
129 		again = TSL2540_AGAIN_S64;
130 		break;
131 	case TSL2540_SENSOR_GAIN_128:
132 		value = TSL2540_CFG1_G128;
133 		again = TSL2540_CFG2_G128;
134 		break;
135 	}
136 
137 	if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_CFG_1, value) < 0) {
138 		return -EIO;
139 	}
140 
141 	if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_CFG_2, value) < 0) {
142 		return -EIO;
143 	}
144 
145 	data->again = again;
146 
147 	return 0;
148 }
149 
tsl2540_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)150 static int tsl2540_attr_set(const struct device *dev, enum sensor_channel chan,
151 				enum sensor_attribute attr, const struct sensor_value *val)
152 {
153 	const struct tsl2540_config *cfg = dev->config;
154 	struct tsl2540_data *data = dev->data;
155 	int ret = 0;
156 	uint8_t temp;
157 	double it;
158 
159 	if ((chan != SENSOR_CHAN_IR) & (chan != SENSOR_CHAN_LIGHT)) {
160 		return -ENOTSUP;
161 	}
162 
163 	k_sem_take(&data->sem, K_FOREVER);
164 
165 	ret = i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR, TSL2540_ENABLE_MASK &
166 				    ~TSL2540_ENABLE_CONF);
167 	if (ret) {
168 		k_sem_give(&data->sem);
169 		return ret;
170 	}
171 
172 #if CONFIG_TSL2540_TRIGGER
173 	if (chan == SENSOR_CHAN_LIGHT) {
174 		if (attr == SENSOR_ATTR_UPPER_THRESH) {
175 			double cpl;
176 			uint16_t thld, le16_buffer;
177 			double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
178 
179 			cpl = ((data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS);
180 			cpl *= data->again;
181 			cpl /= (TSL2540_DEVICE_FACTOR * glass_attenuation);
182 			thld = sensor_value_to_double(val) * cpl;
183 			LOG_DBG("attr: %d, cpl: %g, thld: %x\n", attr, cpl, thld);
184 
185 			le16_buffer = sys_cpu_to_le16(thld);
186 			ret = i2c_burst_write_dt(
187 				&((const struct tsl2540_config *)dev->config)->i2c_spec,
188 				TSL2540_REG_AIHT_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer));
189 
190 			goto exit;
191 		}
192 		if (attr == SENSOR_ATTR_LOWER_THRESH) {
193 			double cpl;
194 			uint16_t thld, le16_buffer;
195 			double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
196 
197 			cpl = ((data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS);
198 			cpl *= data->again;
199 			cpl /= (TSL2540_DEVICE_FACTOR * glass_attenuation);
200 			thld = sensor_value_to_double(val) * cpl;
201 			LOG_DBG("attr: %d, cpl: %g, thld: %x\n", attr, cpl, thld);
202 
203 			le16_buffer = sys_cpu_to_le16(sys_cpu_to_le16(thld));
204 
205 			ret = i2c_burst_write_dt(
206 				&((const struct tsl2540_config *)dev->config)->i2c_spec,
207 				TSL2540_REG_AILT_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer));
208 
209 			goto exit;
210 		}
211 
212 	}
213 #endif /* CONFIG_TSL2540_TRIGGER */
214 
215 	if (attr == SENSOR_ATTR_GAIN) {
216 		tsl2540_attr_set_gain(dev, (enum sensor_gain_tsl2540)val->val1);
217 		goto exit;
218 	}
219 
220 	switch ((enum sensor_attribute_tsl2540)attr) {
221 	case SENSOR_ATTR_INT_APERS:
222 		temp = (uint8_t)val->val1;
223 
224 		if (temp > 15) {
225 			ret = -EINVAL;
226 			goto exit;
227 		}
228 
229 		if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_PERS, temp)) {
230 			ret = -EIO;
231 			goto exit;
232 		}
233 		break;
234 	case SENSOR_ATTR_INTEGRATION_TIME:
235 		it = sensor_value_to_double(val);
236 		it /= TSL2540_INTEGRATION_TIME_MS;
237 		if (it < 1 || it > 256) {
238 			ret = -EINVAL;
239 			goto exit;
240 		}
241 		it -= 1;
242 		temp = (uint8_t)it;
243 		if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_ATIME, temp)) {
244 			ret = -EIO;
245 			goto exit;
246 		}
247 
248 		data->integration_time = temp;
249 		ret = 0;
250 		break;
251 	case SENSOR_ATTR_TSL2540_SHUTDOWN_MODE:
252 		data->enable_mode = TSL2540_ENABLE_DISABLE;
253 		ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
254 						TSL2540_CFG3_CONF);
255 		break;
256 	case SENSOR_ATTR_TSL2540_CONTINUOUS_MODE:
257 		data->enable_mode = TSL2540_ENABLE_CONF;
258 		ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
259 						TSL2540_CFG3_CONF);
260 		break;
261 	case SENSOR_ATTR_TSL2540_CONTINUOUS_NO_WAIT_MODE:
262 		data->enable_mode = TSL2540_ENABLE_AEN_PON;
263 		ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
264 						TSL2540_CFG3_DFLT);
265 		break;
266 	}
267 
268 exit:
269 	i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR, TSL2540_ENABLE_MASK,
270 				data->enable_mode);
271 
272 	k_sem_give(&data->sem);
273 
274 	return ret;
275 }
276 
tsl2540_setup(const struct device * dev)277 static int tsl2540_setup(const struct device *dev)
278 {
279 	struct sensor_value integration_time;
280 
281 	/* Set ALS integration time */
282 	tsl2540_attr_set(dev, (enum sensor_channel)SENSOR_CHAN_LIGHT,
283 			 (enum sensor_attribute)SENSOR_ATTR_GAIN,
284 			 &(struct sensor_value){.val1 = TSL2540_SENSOR_GAIN_1_2, .val2 = 0});
285 
286 	sensor_value_from_double(&integration_time, 500.0);
287 	tsl2540_attr_set(dev, (enum sensor_channel)SENSOR_CHAN_LIGHT,
288 			 (enum sensor_attribute)SENSOR_ATTR_INTEGRATION_TIME, &integration_time);
289 
290 	return 0;
291 }
292 
tsl2540_init(const struct device * dev)293 static int tsl2540_init(const struct device *dev)
294 {
295 	const struct tsl2540_config *cfg = dev->config;
296 	struct tsl2540_data *data = dev->data;
297 	int ret;
298 
299 	data->enable_mode = TSL2540_ENABLE_DISABLE;
300 
301 	k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
302 
303 	if (!i2c_is_ready_dt(&cfg->i2c_spec)) {
304 		LOG_ERR("I2C dev %s not ready", cfg->i2c_spec.bus->name);
305 		return -ENODEV;
306 	}
307 
308 	ret = i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_PERS, 1);
309 	if (ret) {
310 		LOG_ERR("Failed to setup interrupt persistence filter");
311 		return ret;
312 	}
313 
314 	ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
315 				     TSL2540_CFG3_DFLT);
316 	if (ret) {
317 		LOG_ERR("Failed to set configuration");
318 		return ret;
319 	}
320 
321 	if (tsl2540_setup(dev)) {
322 		LOG_ERR("Failed to setup ambient light functionality");
323 		return -EIO;
324 	}
325 
326 #if CONFIG_TSL2540_TRIGGER
327 	if (tsl2540_trigger_init(dev)) {
328 		LOG_ERR("Could not initialize interrupts");
329 		return -EIO;
330 	}
331 #endif
332 
333 	LOG_DBG("Init complete");
334 
335 	return 0;
336 }
337 
338 static DEVICE_API(sensor, tsl2540_driver_api) = {
339 	.sample_fetch = tsl2540_sample_fetch,
340 	.channel_get = tsl2540_channel_get,
341 	.attr_set = tsl2540_attr_set,
342 #ifdef CONFIG_TSL2540_TRIGGER
343 	.trigger_set = tsl2540_trigger_set,
344 #endif
345 };
346 
347 #ifdef CONFIG_PM_DEVICE
tsl2540_pm_action(const struct device * dev,enum pm_device_action action)348 static int tsl2540_pm_action(const struct device *dev, enum pm_device_action action)
349 {
350 
351 	const struct tsl2540_config *cfg = dev->config;
352 	struct tsl2540_data *data = dev->data;
353 	int ret = 0;
354 
355 	switch (action) {
356 	case PM_DEVICE_ACTION_RESUME:
357 		ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR,
358 					    TSL2540_ENABLE_MASK, data->enable_mode);
359 		break;
360 	case PM_DEVICE_ACTION_SUSPEND:
361 		ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR,
362 					    TSL2540_ENABLE_MASK, TSL2540_ENABLE_DISABLE);
363 		break;
364 	default:
365 		return -ENOTSUP;
366 	}
367 
368 	return ret;
369 }
370 #endif
371 
372 #define TSL2540_GLASS_ATTEN(inst)						\
373 	.glass_attenuation = DT_INST_PROP(inst, glass_attenuation),		\
374 	.glass_ir_attenuation = DT_INST_PROP(inst, glass_ir_attenuation),	\
375 
376 #define TSL2540_DEFINE(inst)									\
377 	static struct tsl2540_data tsl2540_prv_data_##inst;					\
378 	static const struct tsl2540_config tsl2540_config_##inst = {				\
379 		.i2c_spec = I2C_DT_SPEC_INST_GET(inst),						\
380 		IF_ENABLED(CONFIG_TSL2540_TRIGGER,						\
381 		(.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),))				\
382 		TSL2540_GLASS_ATTEN(inst)							\
383 	};											\
384 	PM_DEVICE_DT_INST_DEFINE(inst, tsl2540_pm_action);					\
385 	SENSOR_DEVICE_DT_INST_DEFINE(inst, &tsl2540_init, PM_DEVICE_DT_INST_GET(inst),		\
386 				&tsl2540_prv_data_##inst, &tsl2540_config_##inst, POST_KERNEL,	\
387 				CONFIG_SENSOR_INIT_PRIORITY, &tsl2540_driver_api);
388 
389 DT_INST_FOREACH_STATUS_OKAY(TSL2540_DEFINE)
390