1 /*
2 * Copyright (c) 2023, Gustavo Silva
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ams_tsl2561
8
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/devicetree.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/__assert.h>
16
17 LOG_MODULE_REGISTER(TSL2561, CONFIG_SENSOR_LOG_LEVEL);
18
19 #define TSL2561_CHIP_ID 0x05
20
21 #define TSL2561_GAIN_1X 0x00
22 #define TSL2561_GAIN_16X 0x01
23
24 #define TSL2561_INTEGRATION_13MS 0x00
25 #define TSL2561_INTEGRATION_101MS 0x01
26 #define TSL2561_INTEGRATION_402MS 0x02
27
28 /* Register set */
29 #define TSL2561_REG_CONTROL 0x00
30 #define TSL2561_REG_TIMING 0x01
31 #define TSL2561_REG_THRESHLOWLOW 0x02
32 #define TSL2561_REG_THRESHLOWHIGH 0x03
33 #define TSL2561_REG_THRESHHIGHLOW 0x04
34 #define TSL2561_REG_THRESHHIGHHIGH 0x05
35 #define TSL2561_REG_INTERRUPT 0x06
36 #define TSL2561_REG_ID 0x0A
37 #define TSL2561_REG_DATA0LOW 0x0C
38 #define TSL2561_REG_DATA0HIGH 0x0D
39 #define TSL2561_REG_DATA1LOW 0x0E
40 #define TSL2561_REG_DATA1HIGH 0x0F
41
42 /* Command register fields */
43 #define TSL2561_COMMAND_CMD BIT(7)
44 #define TSL2561_COMMAND_WORD BIT(5)
45
46 /* Control register fields */
47 #define TSL2561_CONTROL_POWER_UP 0x03
48 #define TSL2561_CONTROL_POWER_DOWN 0x00
49
50 /* Timing register fields */
51 #define TSL2561_TIMING_GAIN BIT(4)
52 #define TSL2561_TIMING_INTEG GENMASK(1, 0)
53
54 /* ID register part number mask */
55 #define TSL2561_ID_PARTNO GENMASK(7, 4)
56
57 /* Lux calculation constants */
58 #define TSL2561_LUX_SCALE 14U
59 #define TSL2561_RATIO_SCALE 9U
60 #define TSL2561_CH_SCALE 10U
61 #define TSL2561_CHSCALE_TINT0 0x7517
62 #define TSL2561_CHSCALE_TINT1 0x0FE7
63
64 #define TSL2561_LUX_K1T 0X0040 /* 0.125 * 2^RATIO_SCALE */
65 #define TSL2561_LUX_B1T 0X01F2 /* 0.0304 * 2^LUX_SCALE */
66 #define TSL2561_LUX_M1T 0X01BE /* 0.0272 * 2^LUX_SCALE */
67 #define TSL2561_LUX_K2T 0X0080 /* 0.250 * 2^RATIO_SCALE */
68 #define TSL2561_LUX_B2T 0X0214 /* 0.0325 * 2^LUX_SCALE */
69 #define TSL2561_LUX_M2T 0X02D1 /* 0.0440 * 2^LUX_SCALE */
70 #define TSL2561_LUX_K3T 0X00C0 /* 0.375 * 2^RATIO_SCALE */
71 #define TSL2561_LUX_B3T 0X023F /* 0.0351 * 2^LUX_SCALE */
72 #define TSL2561_LUX_M3T 0X037B /* 0.0544 * 2^LUX_SCALE */
73 #define TSL2561_LUX_K4T 0X0100 /* 0.50 * 2^RATIO_SCALE */
74 #define TSL2561_LUX_B4T 0X0270 /* 0.0381 * 2^LUX_SCALE */
75 #define TSL2561_LUX_M4T 0X03FE /* 0.0624 * 2^LUX_SCALE */
76 #define TSL2561_LUX_K5T 0X0138 /* 0.61 * 2^RATIO_SCALE */
77 #define TSL2561_LUX_B5T 0X016F /* 0.0224 * 2^LUX_SCALE */
78 #define TSL2561_LUX_M5T 0X01FC /* 0.0310 * 2^LUX_SCALE */
79 #define TSL2561_LUX_K6T 0X019A /* 0.80 * 2^RATIO_SCALE */
80 #define TSL2561_LUX_B6T 0X00D2 /* 0.0128 * 2^LUX_SCALE */
81 #define TSL2561_LUX_M6T 0X00FB /* 0.0153 * 2^LUX_SCALE */
82 #define TSL2561_LUX_K7T 0X029A /* 1.3 * 2^RATIO_SCALE */
83 #define TSL2561_LUX_B7T 0X0018 /* 0.00146 * 2^LUX_SCALE */
84 #define TSL2561_LUX_M7T 0X0012 /* 0.00112 * 2^LUX_SCALE */
85 #define TSL2561_LUX_K8T 0X029A /* 1.3 * 2^RATIO_SCALE */
86 #define TSL2561_LUX_B8T 0X0000 /* 0.000 * 2^LUX_SCALE */
87 #define TSL2561_LUX_M8T 0X0000 /* 0.000 * 2^LUX_SCALE */
88
89 struct tsl2561_config {
90 struct i2c_dt_spec i2c;
91 uint16_t integration_time;
92 uint8_t gain;
93 };
94
95 struct tsl2561_data {
96 uint16_t ch0;
97 uint16_t ch1;
98 uint32_t ch_scale;
99 };
100
tsl2561_reg_read(const struct device * dev,uint8_t reg,uint8_t * buf,uint8_t size)101 static int tsl2561_reg_read(const struct device *dev, uint8_t reg, uint8_t *buf, uint8_t size)
102 {
103 int ret;
104 const struct tsl2561_config *config = dev->config;
105 uint8_t cmd = (TSL2561_COMMAND_CMD | TSL2561_COMMAND_WORD | reg);
106
107 ret = i2c_write_read_dt(&config->i2c, &cmd, 1U, buf, size);
108 if (ret < 0) {
109 LOG_ERR("Failed reading register 0x%02x", reg);
110 return ret;
111 }
112
113 return 0;
114 }
115
tsl2561_reg_write(const struct device * dev,uint8_t reg,uint8_t val)116 static int tsl2561_reg_write(const struct device *dev, uint8_t reg, uint8_t val)
117 {
118 int ret;
119 const struct tsl2561_config *config = dev->config;
120 uint8_t buf[2];
121
122 buf[0] = (TSL2561_COMMAND_CMD | TSL2561_COMMAND_WORD | reg);
123 buf[1] = val;
124
125 ret = i2c_write_dt(&config->i2c, buf, 2U);
126 if (ret < 0) {
127 LOG_ERR("Failed writing register 0x%02x", reg);
128 return ret;
129 }
130
131 return 0;
132 }
133
tsl2561_sample_fetch(const struct device * dev,enum sensor_channel chan)134 static int tsl2561_sample_fetch(const struct device *dev, enum sensor_channel chan)
135 {
136 const struct tsl2561_config *config = dev->config;
137 struct tsl2561_data *data = dev->data;
138 uint8_t bytes[2];
139 int ret;
140
141 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
142 LOG_ERR("Unsupported sensor channel");
143 return -ENOTSUP;
144 }
145
146 ret = tsl2561_reg_write(dev, TSL2561_REG_CONTROL, TSL2561_CONTROL_POWER_UP);
147 if (ret < 0) {
148 LOG_ERR("Failed to power up device");
149 return ret;
150 }
151
152 /* Short sleep after power up. Not in the datasheet, but found by trial and error */
153 k_msleep(5);
154
155 k_msleep(config->integration_time);
156
157 /* Read data register's lower and upper bytes consecutively */
158 ret = tsl2561_reg_read(dev, TSL2561_REG_DATA0LOW, bytes, 2U);
159 if (ret < 0) {
160 LOG_ERR("Failed reading channel0 data");
161 return ret;
162 }
163 data->ch0 = bytes[1] << 8 | bytes[0];
164
165 ret = tsl2561_reg_read(dev, TSL2561_REG_DATA1LOW, bytes, 2U);
166 if (ret < 0) {
167 LOG_ERR("Failed reading channel1 data");
168 return ret;
169 }
170 data->ch1 = bytes[1] << 8 | bytes[0];
171
172 ret = tsl2561_reg_write(dev, TSL2561_REG_CONTROL, TSL2561_CONTROL_POWER_DOWN);
173 if (ret < 0) {
174 LOG_ERR("Failed to power down device");
175 return ret;
176 }
177
178 LOG_DBG("channel0: 0x%x; channel1: 0x%x", data->ch0, data->ch1);
179
180 return 0;
181 }
182
tsl2561_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)183 static int tsl2561_channel_get(const struct device *dev, enum sensor_channel chan,
184 struct sensor_value *val)
185 {
186 struct tsl2561_data *data = dev->data;
187 uint32_t channel0;
188 uint32_t channel1;
189
190 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
191 return -ENOTSUP;
192 }
193
194 channel0 = (data->ch0 * data->ch_scale) >> TSL2561_CH_SCALE;
195 channel1 = (data->ch1 * data->ch_scale) >> TSL2561_CH_SCALE;
196
197 uint32_t ratio1 = 0;
198
199 if (channel0 != 0) {
200 ratio1 = (channel1 << (TSL2561_RATIO_SCALE + 1)) / channel0;
201 }
202
203 /* Round the ratio value */
204 uint32_t ratio = (ratio1 + 1) >> 1;
205
206 uint32_t b = 0;
207 uint32_t m = 0;
208
209 if (ratio <= TSL2561_LUX_K1T) {
210 b = TSL2561_LUX_B1T;
211 m = TSL2561_LUX_M1T;
212 } else if (ratio <= TSL2561_LUX_K2T) {
213 b = TSL2561_LUX_B2T;
214 m = TSL2561_LUX_M2T;
215 } else if (ratio <= TSL2561_LUX_K3T) {
216 b = TSL2561_LUX_B3T;
217 m = TSL2561_LUX_M3T;
218 } else if (ratio <= TSL2561_LUX_K4T) {
219 b = TSL2561_LUX_B4T;
220 m = TSL2561_LUX_M4T;
221 } else if (ratio <= TSL2561_LUX_K5T) {
222 b = TSL2561_LUX_B5T;
223 m = TSL2561_LUX_M5T;
224 } else if (ratio <= TSL2561_LUX_K6T) {
225 b = TSL2561_LUX_B6T;
226 m = TSL2561_LUX_M6T;
227 } else if (ratio <= TSL2561_LUX_K7T) {
228 b = TSL2561_LUX_B7T;
229 m = TSL2561_LUX_M7T;
230 } else if (ratio > TSL2561_LUX_K8T) {
231 b = TSL2561_LUX_B8T;
232 m = TSL2561_LUX_M8T;
233 }
234
235 int32_t tmp = ((channel0 * b) - (channel1 * m));
236
237 /* Round LSB (2^(LUX_SCALE−1)) */
238 tmp += (1 << (TSL2561_LUX_SCALE - 1));
239
240 /* Strip off fractional portion */
241 val->val1 = tmp >> TSL2561_LUX_SCALE;
242
243 val->val2 = 0;
244
245 return 0;
246 }
247
248 static const struct sensor_driver_api tsl2561_driver_api = {
249 .sample_fetch = tsl2561_sample_fetch,
250 .channel_get = tsl2561_channel_get
251 };
252
tsl2561_sensor_setup(const struct device * dev)253 static int tsl2561_sensor_setup(const struct device *dev)
254 {
255 const struct tsl2561_config *config = dev->config;
256 struct tsl2561_data *data = dev->data;
257 uint8_t timing_reg;
258 uint8_t chip_id;
259 uint8_t tmp;
260 int ret;
261
262 ret = tsl2561_reg_read(dev, TSL2561_REG_ID, &chip_id, 1U);
263 if (ret < 0) {
264 LOG_ERR("Failed reading chip ID");
265 return ret;
266 }
267
268 if (FIELD_GET(TSL2561_ID_PARTNO, chip_id) != TSL2561_CHIP_ID) {
269 LOG_ERR("Chip ID is invalid! Device @%02x is not TSL2561!", config->i2c.addr);
270 return -EIO;
271 }
272
273 switch (config->integration_time) {
274 case 13:
275 tmp = TSL2561_INTEGRATION_13MS;
276 data->ch_scale = TSL2561_CHSCALE_TINT0;
277 break;
278 case 101:
279 tmp = TSL2561_INTEGRATION_101MS;
280 data->ch_scale = TSL2561_CHSCALE_TINT1;
281 break;
282 case 402:
283 tmp = TSL2561_INTEGRATION_402MS;
284 data->ch_scale = (1 << TSL2561_CH_SCALE);
285 break;
286 default:
287 LOG_ERR("Invalid integration time");
288 return -EINVAL;
289 }
290
291 timing_reg = TSL2561_TIMING_INTEG & tmp;
292
293 switch (config->gain) {
294 case 1:
295 tmp = TSL2561_GAIN_1X;
296 data->ch_scale = data->ch_scale << 4;
297 break;
298 case 16:
299 tmp = TSL2561_GAIN_16X;
300 break;
301 default:
302 LOG_ERR("Invalid ADC gain");
303 return -EINVAL;
304 }
305
306 timing_reg |= FIELD_PREP(TSL2561_TIMING_GAIN, tmp);
307
308 ret = tsl2561_reg_write(dev, TSL2561_REG_TIMING, timing_reg);
309 if (ret < 0) {
310 LOG_ERR("Failed setting timing register");
311 return ret;
312 }
313
314 return 0;
315 }
316
tsl2561_init(const struct device * dev)317 static int tsl2561_init(const struct device *dev)
318 {
319 const struct tsl2561_config *config = dev->config;
320 int ret;
321
322 if (!i2c_is_ready_dt(&config->i2c)) {
323 LOG_ERR("I2C dev %s not ready", config->i2c.bus->name);
324 return -ENODEV;
325 }
326
327 ret = tsl2561_sensor_setup(dev);
328 if (ret < 0) {
329 LOG_ERR("Failed to configure device");
330 return ret;
331 }
332
333 return 0;
334 }
335
336 #define TSL2561_INIT_INST(n) \
337 static struct tsl2561_data tsl2561_data_##n; \
338 static const struct tsl2561_config tsl2561_config_##n = { \
339 .i2c = I2C_DT_SPEC_INST_GET(n), \
340 .integration_time = DT_INST_PROP(n, integration_time), \
341 .gain = DT_INST_PROP(n, gain)}; \
342 SENSOR_DEVICE_DT_INST_DEFINE(n, tsl2561_init, NULL, &tsl2561_data_##n, \
343 &tsl2561_config_##n, POST_KERNEL, \
344 CONFIG_SENSOR_INIT_PRIORITY, &tsl2561_driver_api);
345
346 DT_INST_FOREACH_STATUS_OKAY(TSL2561_INIT_INST)
347