1 /*
2  * Copyright (c) 2017 Intel Corporation
3  * Copyright (c) 2018 Phytec Messtechnik GmbH
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT avago_apds9253
9 
10 /* @file
11  * @brief driver for APDS9253 ALS/RGB/
12  */
13 
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/drivers/i2c.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/logging/log.h>
18 
19 #include "apds9253.h"
20 
21 #define BYTES_PER_VALUE   3
22 #define VALUES_PER_SAMPLE 4
23 
24 LOG_MODULE_REGISTER(APDS9253, CONFIG_SENSOR_LOG_LEVEL);
25 
apds9253_setup_int(const struct apds9253_config * cfg,bool enable)26 static inline void apds9253_setup_int(const struct apds9253_config *cfg, bool enable)
27 {
28 	gpio_flags_t flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
29 
30 	gpio_pin_interrupt_configure_dt(&cfg->int_gpio, flags);
31 }
32 
apds9253_handle_cb(struct apds9253_data * drv_data)33 static void apds9253_handle_cb(struct apds9253_data *drv_data)
34 {
35 	apds9253_setup_int(drv_data->dev->config, false);
36 }
37 
apds9253_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)38 static void apds9253_gpio_callback(const struct device *dev, struct gpio_callback *cb,
39 				   uint32_t pins)
40 {
41 	struct apds9253_data *drv_data = CONTAINER_OF(cb, struct apds9253_data, gpio_cb);
42 
43 	apds9253_handle_cb(drv_data);
44 }
45 
get_value_from_buf(int idx,const uint8_t * buf)46 static uint32_t get_value_from_buf(int idx, const uint8_t *buf)
47 {
48 	size_t offset = BYTES_PER_VALUE * idx;
49 
50 	return sys_get_le24(&buf[offset]);
51 }
52 
apds9253_sample_fetch(const struct device * dev,enum sensor_channel chan)53 static int apds9253_sample_fetch(const struct device *dev, enum sensor_channel chan)
54 {
55 	const struct apds9253_config *config = dev->config;
56 	struct apds9253_data *data = dev->data;
57 	uint8_t tmp;
58 	uint8_t buf[BYTES_PER_VALUE * VALUES_PER_SAMPLE];
59 
60 	if (chan != SENSOR_CHAN_ALL) {
61 		LOG_ERR("Unsupported sensor channel");
62 		return -ENOTSUP;
63 	}
64 
65 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_MAIN_CTRL_REG, APDS9253_MAIN_CTRL_LS_EN,
66 				   APDS9253_MAIN_CTRL_LS_EN)) {
67 		LOG_ERR("Power on bit not set.");
68 		return -EIO;
69 	}
70 
71 	if (config->interrupt_enabled) {
72 		k_sem_take(&data->data_sem, K_FOREVER);
73 	}
74 
75 	if (i2c_reg_read_byte_dt(&config->i2c, APDS9253_MAIN_STATUS_REG, &tmp)) {
76 		return -EIO;
77 	}
78 
79 	LOG_DBG("status: 0x%x", tmp);
80 
81 	if (tmp & APDS9253_MAIN_STATUS_LS_STATUS) {
82 		if (i2c_burst_read_dt(&config->i2c, APDS9253_LS_DATA_BASE, buf, sizeof(buf))) {
83 			return -EIO;
84 		}
85 
86 		for (int i = 0; i < VALUES_PER_SAMPLE; ++i) {
87 			data->sample_crgb[i] = get_value_from_buf(i, buf);
88 		}
89 
90 		LOG_DBG("IR 0x%x GREEN 0x%x BLUE 0x%x RED 0x%x\n", data->sample_crgb[0],
91 			data->sample_crgb[1], data->sample_crgb[2], data->sample_crgb[3]);
92 	}
93 
94 	return 0;
95 }
96 
apds9253_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)97 static int apds9253_channel_get(const struct device *dev, enum sensor_channel chan,
98 				struct sensor_value *val)
99 {
100 	struct apds9253_data *data = dev->data;
101 
102 	val->val2 = 0;
103 
104 	switch (chan) {
105 	case SENSOR_CHAN_IR:
106 		val->val1 = sys_le32_to_cpu(data->sample_crgb[0]);
107 		break;
108 	case SENSOR_CHAN_GREEN:
109 		val->val1 = sys_le32_to_cpu(data->sample_crgb[1]);
110 		break;
111 	case SENSOR_CHAN_BLUE:
112 		val->val1 = sys_le32_to_cpu(data->sample_crgb[2]);
113 		break;
114 	case SENSOR_CHAN_RED:
115 		val->val1 = sys_le32_to_cpu(data->sample_crgb[3]);
116 		break;
117 	default:
118 		return -ENOTSUP;
119 	}
120 
121 	return 0;
122 }
123 
apds9253_attr_set_gain(const struct device * dev,uint8_t gain)124 static int apds9253_attr_set_gain(const struct device *dev, uint8_t gain)
125 {
126 	const struct apds9253_config *config = dev->config;
127 	struct apds9253_data *drv_data = dev->data;
128 	uint8_t value;
129 
130 	if (drv_data->gain == gain) {
131 		return 0;
132 	}
133 
134 	static uint8_t value_map[] = {
135 		APDS9253_LS_GAIN_RANGE_1, APDS9253_LS_GAIN_RANGE_3,  APDS9253_LS_GAIN_RANGE_6,
136 		APDS9253_LS_GAIN_RANGE_9, APDS9253_LS_GAIN_RANGE_18,
137 	};
138 
139 	if (gain < APDS9253_LS_GAIN_RANGE_1 || gain > APDS9253_LS_GAIN_RANGE_18) {
140 		return -EINVAL;
141 	}
142 	value = value_map[gain];
143 
144 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_GAIN_REG, APDS9253_LS_GAIN_MASK,
145 				   (value & APDS9253_LS_GAIN_MASK))) {
146 		LOG_ERR("Not able to set light, sensor gain is not set");
147 		return -EIO;
148 	}
149 
150 	drv_data->gain = gain;
151 
152 	return 0;
153 }
154 
apds9253_attr_set_sampl_freq(const struct device * dev,const struct sensor_value * sampl_freq)155 static int apds9253_attr_set_sampl_freq(const struct device *dev,
156 					const struct sensor_value *sampl_freq)
157 {
158 	const struct apds9253_config *config = dev->config;
159 	struct apds9253_data *drv_data = dev->data;
160 	uint8_t period_val;
161 	uint32_t freq_mhz = sampl_freq->val1 * 1000 + (sampl_freq->val2 / 1000);
162 
163 	if (freq_mhz >= 40000) {
164 		period_val = APDS9253_LS_MEAS_RATE_MES_25MS;
165 	} else if (freq_mhz >= 20000) {
166 		period_val = APDS9253_LS_MEAS_RATE_MES_50MS;
167 	} else if (freq_mhz >= 10000) {
168 		period_val = APDS9253_LS_MEAS_RATE_MES_100MS;
169 	} else if (freq_mhz >= 5000) {
170 		period_val = APDS9253_LS_MEAS_RATE_MES_200MS;
171 	} else if (freq_mhz >= 2000) {
172 		period_val = APDS9253_LS_MEAS_RATE_MES_500MS;
173 	} else if (freq_mhz >= 1000) {
174 		period_val = APDS9253_LS_MEAS_RATE_MES_1000MS;
175 	} else if (freq_mhz >= 500) {
176 		period_val = APDS9253_LS_MEAS_RATE_MES_2000MS;
177 	} else {
178 		LOG_INF("Frequency below minimum range, setting to minimum supported value: "
179 			"0.5Hz.");
180 		period_val = APDS9253_LS_MEAS_RATE_MES_2000MS;
181 	}
182 
183 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_MEAS_RATE_REG,
184 				   APDS9253_LS_MEAS_RATE_MES_MASK,
185 				   (period_val & APDS9253_LS_MEAS_RATE_MES_MASK))) {
186 		LOG_ERR("Not able to set light sensor measurement rate is not set");
187 		return -EIO;
188 	}
189 
190 	drv_data->meas_rate_mes = period_val;
191 
192 	return 0;
193 }
194 
apds9253_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)195 static int apds9253_attr_set(const struct device *dev, enum sensor_channel chan,
196 			     enum sensor_attribute attr, const struct sensor_value *val)
197 {
198 	switch (attr) {
199 	case SENSOR_ATTR_GAIN:
200 		return apds9253_attr_set_gain(dev, val->val1);
201 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
202 		return apds9253_attr_set_sampl_freq(dev, val);
203 	default:
204 		LOG_DBG("Sensor attribute not supported.");
205 		return -ENOTSUP;
206 	}
207 
208 	return 0;
209 }
210 
apds9253_sensor_setup(const struct device * dev)211 static int apds9253_sensor_setup(const struct device *dev)
212 {
213 	const struct apds9253_config *config = dev->config;
214 	struct apds9253_data *drv_data = dev->data;
215 	uint8_t chip_id;
216 
217 	if (i2c_reg_read_byte_dt(&config->i2c, APDS9253_PART_ID, &chip_id)) {
218 		LOG_ERR("Failed reading chip id");
219 		return -EIO;
220 	}
221 
222 	if ((chip_id & APDS9253_PART_ID_ID_MASK) != APDS9253_DEVICE_PART_ID) {
223 		LOG_ERR("Invalid chip id 0x%x", chip_id);
224 		return -EIO;
225 	}
226 
227 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_GAIN_REG, APDS9253_LS_GAIN_MASK,
228 				   (config->ls_gain & APDS9253_LS_GAIN_MASK))) {
229 		LOG_ERR("Light sensor gain is not set");
230 		return -EIO;
231 	}
232 
233 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_MEAS_RATE_REG,
234 				   APDS9253_LS_MEAS_RATE_RES_MASK,
235 				   (config->ls_resolution & APDS9253_LS_MEAS_RATE_RES_MASK))) {
236 		LOG_ERR("Light sensor resolution is not set");
237 		return -EIO;
238 	}
239 
240 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_MEAS_RATE_REG,
241 				   APDS9253_LS_MEAS_RATE_MES_MASK,
242 				   (config->ls_rate & APDS9253_LS_MEAS_RATE_MES_MASK))) {
243 		LOG_ERR("Light sensor rate is not set");
244 		return -EIO;
245 	}
246 
247 	if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_MAIN_CTRL_REG,
248 				   APDS9253_MAIN_CTRL_RGB_MODE, APDS9253_MAIN_CTRL_RGB_MODE)) {
249 		LOG_ERR("Enable RGB mode failed");
250 		return -EIO;
251 	}
252 
253 	drv_data->gain = config->ls_gain;
254 	drv_data->meas_rate_mes = config->ls_rate;
255 
256 	return 0;
257 }
258 
apds9253_init_interrupt(const struct device * dev)259 static int apds9253_init_interrupt(const struct device *dev)
260 {
261 	const struct apds9253_config *config = dev->config;
262 	struct apds9253_data *drv_data = dev->data;
263 	int ret = 0;
264 
265 	if (!gpio_is_ready_dt(&config->int_gpio)) {
266 		LOG_ERR("%s: device %s is not ready", dev->name, config->int_gpio.port->name);
267 		return -ENODEV;
268 	}
269 
270 	ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
271 	if (!ret) {
272 		LOG_ERR("Failed to configure gpio direction");
273 		return ret;
274 	}
275 
276 	gpio_init_callback(&drv_data->gpio_cb, apds9253_gpio_callback, BIT(config->int_gpio.pin));
277 
278 	if (gpio_add_callback(config->int_gpio.port, &drv_data->gpio_cb) < 0) {
279 		LOG_ERR("Failed to set gpio callback!");
280 		return -EIO;
281 	}
282 
283 	drv_data->dev = dev;
284 
285 	k_sem_init(&drv_data->data_sem, 0, K_SEM_MAX_LIMIT);
286 	apds9253_setup_int(config, true);
287 
288 	if (gpio_pin_get_dt(&config->int_gpio) > 0) {
289 		apds9253_handle_cb(drv_data);
290 	}
291 
292 	return 0;
293 }
294 
apds9253_init(const struct device * dev)295 static int apds9253_init(const struct device *dev)
296 {
297 	const struct apds9253_config *config = dev->config;
298 
299 	/* Initialize time 500 us */
300 	k_msleep(1);
301 
302 	if (!i2c_is_ready_dt(&config->i2c)) {
303 		LOG_ERR("Bus device is not ready");
304 		return -EINVAL;
305 	}
306 
307 	if (apds9253_sensor_setup(dev) < 0) {
308 		LOG_ERR("Failed to setup device!");
309 		return -EIO;
310 	}
311 
312 	if (config->interrupt_enabled && apds9253_init_interrupt(dev) < 0) {
313 		LOG_ERR("Failed to initialize interrupt!");
314 		return -EIO;
315 	}
316 
317 	return 0;
318 }
319 
320 static DEVICE_API(sensor, apds9253_driver_api) = {
321 	.sample_fetch = &apds9253_sample_fetch,
322 	.channel_get = &apds9253_channel_get,
323 	.attr_set = &apds9253_attr_set,
324 };
325 
326 #define APDS9253_INIT(n)                                                                           \
327 	static struct apds9253_data apds9253_data_##n = {                                          \
328 		.sample_crgb = {0},                                                                \
329 		.pdata = 0U,                                                                       \
330 	};                                                                                         \
331                                                                                                    \
332 	static const struct apds9253_config apds9253_config_##n = {                                \
333 		.i2c = I2C_DT_SPEC_INST_GET(n),                                                    \
334 		.interrupt_enabled = DT_INST_NODE_HAS_PROP(n, int_gpios),                          \
335 		.int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {}),                            \
336 		.ls_rate = DT_INST_PROP(n, rate),                                                  \
337 		.ls_resolution = DT_INST_PROP(n, resolution),                                      \
338 		.ls_gain = DT_INST_PROP(n, gain),                                                  \
339 	};                                                                                         \
340                                                                                                    \
341 	SENSOR_DEVICE_DT_INST_DEFINE(n, apds9253_init, NULL, &apds9253_data_##n,                   \
342 				     &apds9253_config_##n, POST_KERNEL,                            \
343 				     CONFIG_SENSOR_INIT_PRIORITY, &apds9253_driver_api);
344 
345 DT_INST_FOREACH_STATUS_OKAY(APDS9253_INIT)
346