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