1 /* Copyright (c) 2024 Daniel Kampert
2 * Author: Daniel Kampert <DanielKampert@kampis-Elektroecke.de>
3 */
4
5 #include <zephyr/device.h>
6 #include <zephyr/devicetree.h>
7 #include <zephyr/drivers/i2c.h>
8 #include <zephyr/drivers/sensor.h>
9 #include <zephyr/pm/device_runtime.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/util.h>
13
14 #define APDS9306_REGISTER_MAIN_CTRL 0x00
15 #define APDS9306_REGISTER_ALS_MEAS_RATE 0x04
16 #define APDS9306_REGISTER_ALS_GAIN 0x05
17 #define APDS9306_REGISTER_PART_ID 0x06
18 #define APDS9306_REGISTER_MAIN_STATUS 0x07
19 #define APDS9306_REGISTER_CLEAR_DATA_0 0x0A
20 #define APDS9306_REGISTER_CLEAR_DATA_1 0x0B
21 #define APDS9306_REGISTER_CLEAR_DATA_2 0x0C
22 #define APDS9306_REGISTER_ALS_DATA_0 0x0D
23 #define APDS9306_REGISTER_ALS_DATA_1 0x0E
24 #define APDS9306_REGISTER_ALS_DATA_2 0x0F
25 #define APDS9306_REGISTER_INT_CFG 0x19
26 #define APDS9306_REGISTER_INT_PERSISTENCE 0x1A
27 #define APDS9306_REGISTER_ALS_THRES_UP_0 0x21
28 #define APDS9306_REGISTER_ALS_THRES_UP_1 0x22
29 #define APDS9306_REGISTER_ALS_THRES_UP_2 0x23
30 #define APDS9306_REGISTER_ALS_THRES_LOW_0 0x24
31 #define APDS9306_REGISTER_ALS_THRES_LOW_1 0x25
32 #define APDS9306_REGISTER_ALS_THRES_LOW_2 0x26
33 #define APDS9306_REGISTER_ALS_THRES_VAR 0x27
34
35 #define ADPS9306_BIT_ALS_EN BIT(0x01)
36 #define ADPS9306_BIT_ALS_DATA_STATUS BIT(0x03)
37 #define APDS9306_BIT_SW_RESET BIT(0x04)
38 #define ADPS9306_BIT_ALS_INTERRUPT_STATUS BIT(0x03)
39 #define APDS9306_BIT_POWER_ON_STATUS BIT(0x05)
40
41 #define APDS_9306_065_CHIP_ID 0xB3
42 #define APDS_9306_CHIP_ID 0xB1
43
44 #define DT_DRV_COMPAT avago_apds9306
45
46 LOG_MODULE_REGISTER(avago_apds9306, CONFIG_SENSOR_LOG_LEVEL);
47
48 struct apds9306_data {
49 uint32_t light;
50 };
51
52 struct apds9306_config {
53 struct i2c_dt_spec i2c;
54 uint8_t resolution;
55 uint16_t frequency;
56 uint8_t gain;
57 };
58
59 struct apds9306_worker_item_t {
60 struct k_work_delayable dwork;
61 const struct device *dev;
62 } apds9306_worker_item;
63
apds9306_get_time_for_resolution(uint8_t value)64 static uint32_t apds9306_get_time_for_resolution(uint8_t value)
65 {
66 switch (value) {
67 case 0:
68 return 400;
69 case 1:
70 return 200;
71 case 2:
72 return 100;
73 case 3:
74 return 50;
75 case 4:
76 return 25;
77 case 5:
78 return 4;
79 default:
80 return 100;
81 }
82 }
83
apds9306_enable(const struct device * dev)84 static int apds9306_enable(const struct device *dev)
85 {
86 const struct apds9306_config *config = dev->config;
87
88 return i2c_reg_update_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
89 ADPS9306_BIT_ALS_EN, ADPS9306_BIT_ALS_EN);
90 }
91
apds9306_standby(const struct device * dev)92 static int apds9306_standby(const struct device *dev)
93 {
94 const struct apds9306_config *config = dev->config;
95
96 return i2c_reg_update_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
97 ADPS9306_BIT_ALS_EN, 0x00);
98 }
99
apds9306_worker(struct k_work * p_work)100 static void apds9306_worker(struct k_work *p_work)
101 {
102 uint8_t buffer[3];
103 uint8_t reg;
104 struct k_work_delayable *dwork = k_work_delayable_from_work(p_work);
105 struct apds9306_worker_item_t *item =
106 CONTAINER_OF(dwork, struct apds9306_worker_item_t, dwork);
107 struct apds9306_data *data = item->dev->data;
108 const struct apds9306_config *config = item->dev->config;
109
110 if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_STATUS, &buffer[0])) {
111 LOG_ERR("Failed to read ALS status!");
112 return;
113 }
114
115 if (!(buffer[0] & ADPS9306_BIT_ALS_DATA_STATUS)) {
116 LOG_DBG("No data ready!");
117 return;
118 }
119
120 if (apds9306_standby(item->dev) != 0) {
121 LOG_ERR("Can not disable ALS!");
122 return;
123 }
124
125 reg = APDS9306_REGISTER_ALS_DATA_0;
126 if (i2c_write_read_dt(&config->i2c, ®, sizeof(reg), &buffer, sizeof(buffer)) < 0) {
127 return;
128 }
129
130 data->light = sys_get_le24(buffer);
131
132 LOG_DBG("Last measurement: %u", data->light);
133 }
134
apds9306_attr_set(const struct device * dev,enum sensor_channel channel,enum sensor_attribute attribute,const struct sensor_value * value)135 static int apds9306_attr_set(const struct device *dev, enum sensor_channel channel,
136 enum sensor_attribute attribute, const struct sensor_value *value)
137 {
138 uint8_t reg;
139 uint8_t mask;
140 uint8_t temp;
141 const struct apds9306_config *config = dev->config;
142
143 if (channel != SENSOR_CHAN_LIGHT) {
144 return -ENOTSUP;
145 }
146
147 if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
148 reg = APDS9306_REGISTER_ALS_MEAS_RATE;
149 mask = GENMASK(2, 0);
150 temp = FIELD_PREP(0x07, value->val1);
151 } else if (attribute == SENSOR_ATTR_GAIN) {
152 reg = APDS9306_REGISTER_ALS_GAIN;
153 mask = GENMASK(2, 0);
154 temp = FIELD_PREP(0x07, value->val1);
155 } else if (attribute == SENSOR_ATTR_RESOLUTION) {
156 reg = APDS9306_REGISTER_ALS_MEAS_RATE;
157 mask = GENMASK(7, 4);
158 temp = FIELD_PREP(0x07, value->val1) << 0x04;
159 } else {
160 return -ENOTSUP;
161 }
162
163 if (i2c_reg_update_byte_dt(&config->i2c, reg, mask, temp)) {
164 LOG_ERR("Failed to set sensor attribute!");
165 return -EFAULT;
166 }
167
168 return 0;
169 }
170
apds9306_attr_get(const struct device * dev,enum sensor_channel channel,enum sensor_attribute attribute,struct sensor_value * value)171 static int apds9306_attr_get(const struct device *dev, enum sensor_channel channel,
172 enum sensor_attribute attribute, struct sensor_value *value)
173 {
174 uint8_t mask;
175 uint8_t temp;
176 uint8_t reg;
177 const struct apds9306_config *config = dev->config;
178
179 if (channel != SENSOR_CHAN_LIGHT) {
180 return -ENOTSUP;
181 }
182
183 if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
184 reg = APDS9306_REGISTER_ALS_MEAS_RATE;
185 mask = 0x00;
186 } else if (attribute == SENSOR_ATTR_GAIN) {
187 reg = APDS9306_REGISTER_ALS_GAIN;
188 mask = 0x00;
189 } else if (attribute == SENSOR_ATTR_RESOLUTION) {
190 reg = APDS9306_REGISTER_ALS_MEAS_RATE;
191 mask = 0x04;
192 } else {
193 return -ENOTSUP;
194 }
195
196 if (i2c_reg_read_byte_dt(&config->i2c, reg, &temp)) {
197 LOG_ERR("Failed to read sensor attribute!");
198 return -EFAULT;
199 }
200
201 value->val1 = (temp >> mask) & 0x07;
202 value->val2 = 0;
203
204 return 0;
205 }
206
apds9306_sample_fetch(const struct device * dev,enum sensor_channel channel)207 static int apds9306_sample_fetch(const struct device *dev, enum sensor_channel channel)
208 {
209 uint8_t buffer;
210 uint8_t resolution;
211 uint16_t delay;
212 const struct apds9306_config *config = dev->config;
213
214 if ((channel != SENSOR_CHAN_ALL) && (channel != SENSOR_CHAN_LIGHT)) {
215 return -ENOTSUP;
216 }
217
218 LOG_DBG("Start a new measurement...");
219 if (apds9306_enable(dev) != 0) {
220 LOG_ERR("Can not enable ALS!");
221 return -EFAULT;
222 }
223
224 /* Get the measurement resolution. */
225 if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, &buffer)) {
226 LOG_ERR("Failed reading resolution");
227 return -EFAULT;
228 }
229
230 /* Convert the resolution into a delay time and wait for the result. */
231 resolution = (buffer >> 4) & 0x07;
232 delay = apds9306_get_time_for_resolution(resolution);
233 LOG_DBG("Measurement resolution: %u", resolution);
234 LOG_DBG("Wait for %u ms", delay);
235
236 /* We add a bit more delay to cover the startup time etc. */
237 if (!k_work_delayable_is_pending(&apds9306_worker_item.dwork)) {
238 LOG_DBG("Schedule new work");
239
240 apds9306_worker_item.dev = dev;
241 k_work_init_delayable(&apds9306_worker_item.dwork, apds9306_worker);
242 k_work_schedule(&apds9306_worker_item.dwork, K_MSEC(delay + 100));
243 } else {
244 LOG_DBG("Work pending. Wait for completion.");
245 }
246
247 return 0;
248 }
249
apds9306_channel_get(const struct device * dev,enum sensor_channel channel,struct sensor_value * value)250 static int apds9306_channel_get(const struct device *dev, enum sensor_channel channel,
251 struct sensor_value *value)
252 {
253 struct apds9306_data *data = dev->data;
254
255 if (channel != SENSOR_CHAN_LIGHT) {
256 return -ENOTSUP;
257 }
258
259 value->val1 = data->light;
260 value->val2 = 0;
261
262 return 0;
263 }
264
apds9306_sensor_setup(const struct device * dev)265 static int apds9306_sensor_setup(const struct device *dev)
266 {
267 uint32_t now;
268 uint8_t temp;
269 const struct apds9306_config *config = dev->config;
270
271 /* Wait for the device to become ready after a possible power cycle. */
272 now = k_uptime_get_32();
273 do {
274 if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_STATUS, &temp)) {
275 LOG_ERR("Failed reading sensor status!");
276 return -EFAULT;
277 }
278
279 /* We wait 100 ms maximum for the device to become ready. */
280 if ((k_uptime_get_32() - now) > 100) {
281 LOG_ERR("Sensor timeout!");
282 return -EFAULT;
283 }
284
285 k_msleep(10);
286 } while (temp & APDS9306_BIT_POWER_ON_STATUS);
287
288 if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &temp)) {
289 LOG_ERR("Failed reading chip id!");
290 return -EFAULT;
291 }
292
293 if ((temp != APDS_9306_CHIP_ID) && (temp != APDS_9306_065_CHIP_ID)) {
294 LOG_ERR("Invalid chip id! Found 0x%X!", temp);
295 return -EFAULT;
296 }
297
298 if (temp == APDS_9306_CHIP_ID) {
299 LOG_DBG("APDS-9306 found!");
300 } else if (temp == APDS_9306_065_CHIP_ID) {
301 LOG_DBG("APDS-9306-065 found!");
302 }
303
304 /* Reset the sensor. */
305 if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
306 APDS9306_BIT_SW_RESET)) {
307 LOG_ERR("Can not reset the sensor!");
308 return -EFAULT;
309 }
310 k_msleep(10);
311
312 /* Perform a dummy read to avoid bus errors after the reset. See */
313 /* https://lore.kernel.org/lkml/ab1d9746-4d23-efcc-0ee1-d2b8c634becd@tweaklogic.com/ */
314 if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &temp)) {
315 LOG_ERR("Failed reading chip id!");
316 return -EFAULT;
317 }
318
319 return 0;
320 }
321
apds9306_init(const struct device * dev)322 static int apds9306_init(const struct device *dev)
323 {
324 uint8_t value;
325 const struct apds9306_config *config = dev->config;
326
327 LOG_DBG("Start to initialize APDS9306...");
328
329 if (!i2c_is_ready_dt(&config->i2c)) {
330 LOG_ERR("Bus device is not ready!");
331 return -EINVAL;
332 }
333
334 if (apds9306_sensor_setup(dev)) {
335 LOG_ERR("Failed to setup device!");
336 return -EFAULT;
337 }
338
339 value = ((config->resolution & 0x07) << 4) | (config->frequency & 0x0F);
340 LOG_DBG("Write configuration 0x%x to register 0x%x", value,
341 APDS9306_REGISTER_ALS_MEAS_RATE);
342 if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, value)) {
343 return -EFAULT;
344 }
345
346 value = config->gain;
347 LOG_DBG("Write configuration 0x%x to register 0x%x", value, APDS9306_REGISTER_ALS_GAIN);
348 if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_GAIN, value)) {
349 return -EFAULT;
350 }
351
352 LOG_DBG("APDS9306 initialization successful!");
353
354 return 0;
355 }
356
357 static DEVICE_API(sensor, apds9306_driver_api) = {
358 .attr_set = apds9306_attr_set,
359 .attr_get = apds9306_attr_get,
360 .sample_fetch = apds9306_sample_fetch,
361 .channel_get = apds9306_channel_get,
362 };
363
364 #define APDS9306(inst) \
365 static struct apds9306_data apds9306_data_##inst; \
366 static const struct apds9306_config apds9306_config_##inst = { \
367 .i2c = I2C_DT_SPEC_INST_GET(inst), \
368 .resolution = DT_INST_PROP(inst, resolution), \
369 .gain = DT_INST_PROP(inst, gain), \
370 .frequency = DT_INST_PROP(inst, frequency), \
371 }; \
372 \
373 SENSOR_DEVICE_DT_INST_DEFINE(inst, apds9306_init, NULL, &apds9306_data_##inst, \
374 &apds9306_config_##inst, POST_KERNEL, \
375 CONFIG_SENSOR_INIT_PRIORITY, &apds9306_driver_api);
376
377 DT_INST_FOREACH_STATUS_OKAY(APDS9306)
378