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_sensor_setup(const struct device * dev)124 static int apds9253_sensor_setup(const struct device *dev)
125 {
126 const struct apds9253_config *config = dev->config;
127 uint8_t chip_id;
128
129 if (i2c_reg_read_byte_dt(&config->i2c, APDS9253_PART_ID, &chip_id)) {
130 LOG_ERR("Failed reading chip id");
131 return -EIO;
132 }
133
134 if ((chip_id & APDS9253_PART_ID_ID_MASK) != APDS9253_DEVICE_PART_ID) {
135 LOG_ERR("Invalid chip id 0x%x", chip_id);
136 return -EIO;
137 }
138
139 if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_GAIN_REG, APDS9253_LS_GAIN_MASK,
140 (config->ls_gain & APDS9253_LS_GAIN_MASK))) {
141 LOG_ERR("Light sensor gain is not set");
142 return -EIO;
143 }
144
145 if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_MEAS_RATE_REG,
146 APDS9253_LS_MEAS_RATE_RES_MASK,
147 (config->ls_resolution & APDS9253_LS_MEAS_RATE_RES_MASK))) {
148 LOG_ERR("Light sensor resolution is not set");
149 return -EIO;
150 }
151
152 if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_LS_MEAS_RATE_REG,
153 APDS9253_LS_MEAS_RATE_MES_MASK,
154 (config->ls_rate & APDS9253_LS_MEAS_RATE_MES_MASK))) {
155 LOG_ERR("Light sensor rate is not set");
156 return -EIO;
157 }
158
159 if (i2c_reg_update_byte_dt(&config->i2c, APDS9253_MAIN_CTRL_REG,
160 APDS9253_MAIN_CTRL_RGB_MODE, APDS9253_MAIN_CTRL_RGB_MODE)) {
161 LOG_ERR("Enable RGB mode failed");
162 return -EIO;
163 }
164
165 return 0;
166 }
167
apds9253_init_interrupt(const struct device * dev)168 static int apds9253_init_interrupt(const struct device *dev)
169 {
170 const struct apds9253_config *config = dev->config;
171 struct apds9253_data *drv_data = dev->data;
172 int ret = 0;
173
174 if (!gpio_is_ready_dt(&config->int_gpio)) {
175 LOG_ERR("%s: device %s is not ready", dev->name, config->int_gpio.port->name);
176 return -ENODEV;
177 }
178
179 ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
180 if (!ret) {
181 LOG_ERR("Failed to configure gpio direction");
182 return ret;
183 }
184
185 gpio_init_callback(&drv_data->gpio_cb, apds9253_gpio_callback, BIT(config->int_gpio.pin));
186
187 if (gpio_add_callback(config->int_gpio.port, &drv_data->gpio_cb) < 0) {
188 LOG_ERR("Failed to set gpio callback!");
189 return -EIO;
190 }
191
192 drv_data->dev = dev;
193
194 k_sem_init(&drv_data->data_sem, 0, K_SEM_MAX_LIMIT);
195 apds9253_setup_int(config, true);
196
197 if (gpio_pin_get_dt(&config->int_gpio) > 0) {
198 apds9253_handle_cb(drv_data);
199 }
200
201 return 0;
202 }
203
apds9253_init(const struct device * dev)204 static int apds9253_init(const struct device *dev)
205 {
206 const struct apds9253_config *config = dev->config;
207
208 /* Initialize time 500 us */
209 k_msleep(1);
210
211 if (!i2c_is_ready_dt(&config->i2c)) {
212 LOG_ERR("Bus device is not ready");
213 return -EINVAL;
214 }
215
216 if (apds9253_sensor_setup(dev) < 0) {
217 LOG_ERR("Failed to setup device!");
218 return -EIO;
219 }
220
221 if (config->interrupt_enabled && apds9253_init_interrupt(dev) < 0) {
222 LOG_ERR("Failed to initialize interrupt!");
223 return -EIO;
224 }
225
226 return 0;
227 }
228
229 static DEVICE_API(sensor, apds9253_driver_api) = {
230 .sample_fetch = &apds9253_sample_fetch,
231 .channel_get = &apds9253_channel_get,
232 };
233
234 #define APDS9253_INIT(n) \
235 static struct apds9253_data apds9253_data_##n = { \
236 .sample_crgb = {0}, \
237 .pdata = 0U, \
238 }; \
239 \
240 static const struct apds9253_config apds9253_config_##n = { \
241 .i2c = I2C_DT_SPEC_INST_GET(n), \
242 .interrupt_enabled = DT_INST_NODE_HAS_PROP(n, int_gpios), \
243 .int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {}), \
244 .ls_rate = DT_INST_PROP(n, rate), \
245 .ls_resolution = DT_INST_PROP(n, resolution), \
246 .ls_gain = DT_INST_PROP(n, gain), \
247 }; \
248 \
249 SENSOR_DEVICE_DT_INST_DEFINE(n, apds9253_init, NULL, &apds9253_data_##n, \
250 &apds9253_config_##n, POST_KERNEL, \
251 CONFIG_SENSOR_INIT_PRIORITY, &apds9253_driver_api);
252
253 DT_INST_FOREACH_STATUS_OKAY(APDS9253_INIT)
254