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, &reg, 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