1 /*
2 * Copyright (c) 2020 Richard Osterloh
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "vcnl4040.h"
8 #include <zephyr/logging/log.h>
9
10 LOG_MODULE_DECLARE(vcnl4040, CONFIG_SENSOR_LOG_LEVEL);
11
vcnl4040_handle_cb(struct vcnl4040_data * data)12 static void vcnl4040_handle_cb(struct vcnl4040_data *data)
13 {
14 const struct vcnl4040_config *config = data->dev->config;
15
16 gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_DISABLE);
17
18 #if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
19 k_sem_give(&data->trig_sem);
20 #elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
21 k_work_submit(&data->work);
22 #endif
23 }
24
vcnl4040_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pin_mask)25 static void vcnl4040_gpio_callback(const struct device *dev,
26 struct gpio_callback *cb,
27 uint32_t pin_mask)
28 {
29 struct vcnl4040_data *data =
30 CONTAINER_OF(cb, struct vcnl4040_data, gpio_cb);
31 const struct vcnl4040_config *config = data->dev->config;
32
33 if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) {
34 return;
35 }
36
37 vcnl4040_handle_cb(data);
38 }
39
vcnl4040_handle_proxy_int(const struct device * dev)40 static int vcnl4040_handle_proxy_int(const struct device *dev)
41 {
42 struct vcnl4040_data *data = dev->data;
43
44 if (data->proxy_handler != NULL) {
45 data->proxy_handler(dev, data->proxy_trigger);
46 }
47
48 return 0;
49 }
50
vcnl4040_handle_als_int(const struct device * dev)51 static int vcnl4040_handle_als_int(const struct device *dev)
52 {
53 struct vcnl4040_data *data = dev->data;
54
55 if (data->als_handler != NULL) {
56 data->als_handler(dev, data->als_trigger);
57 }
58
59 return 0;
60 }
61
vcnl4040_handle_int(const struct device * dev)62 static void vcnl4040_handle_int(const struct device *dev)
63 {
64 const struct vcnl4040_config *config = dev->config;
65 struct vcnl4040_data *data = dev->data;
66 uint16_t int_source;
67
68 if (vcnl4040_read(dev, VCNL4040_REG_INT_FLAG, &int_source)) {
69 LOG_ERR("Could not read interrupt source");
70 int_source = 0U;
71 }
72
73 data->int_type = int_source >> 8;
74
75 if (data->int_type == VCNL4040_PROXIMITY_AWAY ||
76 data->int_type == VCNL4040_PROXIMITY_CLOSE) {
77 vcnl4040_handle_proxy_int(dev);
78 } else if (data->int_type == VCNL4040_AMBIENT_HIGH ||
79 data->int_type == VCNL4040_AMBIENT_LOW) {
80 vcnl4040_handle_als_int(dev);
81 } else {
82 LOG_ERR("Unknown interrupt source %d", data->int_type);
83 }
84
85 gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
86 }
87
88 #ifdef CONFIG_VCNL4040_TRIGGER_OWN_THREAD
vcnl4040_thread_main(void * p1,void * p2,void * p3)89 static void vcnl4040_thread_main(void *p1, void *p2, void *p3)
90 {
91 ARG_UNUSED(p2);
92 ARG_UNUSED(p3);
93
94 struct vcnl4040_data *data = p1;
95
96 while (true) {
97 k_sem_take(&data->trig_sem, K_FOREVER);
98 vcnl4040_handle_int(data->dev);
99 }
100 }
101 #endif
102
103 #ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
vcnl4040_work_handler(struct k_work * work)104 static void vcnl4040_work_handler(struct k_work *work)
105 {
106 struct vcnl4040_data *data =
107 CONTAINER_OF(work, struct vcnl4040_data, work);
108
109 vcnl4040_handle_int(data->dev);
110 }
111 #endif
112
vcnl4040_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)113 int vcnl4040_attr_set(const struct device *dev,
114 enum sensor_channel chan,
115 enum sensor_attribute attr,
116 const struct sensor_value *val)
117 {
118 struct vcnl4040_data *data = dev->data;
119 const struct vcnl4040_config *config = dev->config;
120 int ret = 0;
121
122 if (config->int_gpio.port == NULL) {
123 return -ENOTSUP;
124 }
125
126 k_mutex_lock(&data->mutex, K_FOREVER);
127
128 if (chan == SENSOR_CHAN_PROX) {
129 if (attr == SENSOR_ATTR_UPPER_THRESH) {
130 if (vcnl4040_write(dev, VCNL4040_REG_PS_THDH,
131 (uint16_t)val->val1)) {
132 ret = -EIO;
133 goto exit;
134 }
135
136 ret = 0;
137 goto exit;
138 }
139 if (attr == SENSOR_ATTR_LOWER_THRESH) {
140 if (vcnl4040_write(dev, VCNL4040_REG_PS_THDL,
141 (uint16_t)val->val1)) {
142 ret = -EIO;
143 goto exit;
144 }
145
146 ret = 0;
147 goto exit;
148 }
149 }
150 #ifdef CONFIG_VCNL4040_ENABLE_ALS
151 if (chan == SENSOR_CHAN_LIGHT) {
152 if (attr == SENSOR_ATTR_UPPER_THRESH) {
153 if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDH,
154 (uint16_t)val->val1)) {
155 ret = -EIO;
156 goto exit;
157 }
158
159 ret = 0;
160 goto exit;
161 }
162 if (attr == SENSOR_ATTR_LOWER_THRESH) {
163 if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDL,
164 (uint16_t)val->val1)) {
165 ret = -EIO;
166 goto exit;
167 }
168
169 ret = 0;
170 goto exit;
171 }
172 }
173 #endif
174 ret = -ENOTSUP;
175 exit:
176 k_mutex_unlock(&data->mutex);
177
178 return ret;
179 }
180
vcnl4040_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)181 int vcnl4040_trigger_set(const struct device *dev,
182 const struct sensor_trigger *trig,
183 sensor_trigger_handler_t handler)
184 {
185 const struct vcnl4040_config *config = dev->config;
186 struct vcnl4040_data *data = dev->data;
187 uint16_t conf;
188 int ret = 0;
189
190 if (config->int_gpio.port == NULL) {
191 return -ENOTSUP;
192 }
193
194 k_mutex_lock(&data->mutex, K_FOREVER);
195
196 switch (trig->type) {
197 case SENSOR_TRIG_THRESHOLD:
198 if (trig->chan == SENSOR_CHAN_PROX) {
199 if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
200 ret = -EIO;
201 goto exit;
202 }
203
204 /* Set interrupt bits 1:0 of high register */
205 conf |= config->proxy_type << 8;
206
207 if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
208 ret = -EIO;
209 goto exit;
210 }
211
212 data->proxy_handler = handler;
213 data->proxy_trigger = trig;
214 } else if (trig->chan == SENSOR_CHAN_LIGHT) {
215 #ifdef CONFIG_VCNL4040_ENABLE_ALS
216 if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
217 ret = -EIO;
218 goto exit;
219 }
220
221 /* ALS interrupt enable */
222 conf |= VCNL4040_ALS_INT_EN_MASK;
223
224 if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
225 ret = -EIO;
226 goto exit;
227 }
228
229 data->als_handler = handler;
230 data->als_trigger = trig;
231 #else
232 ret = -ENOTSUP;
233 goto exit;
234 #endif
235 } else {
236 ret = -ENOTSUP;
237 goto exit;
238 }
239 break;
240 default:
241 LOG_ERR("Unsupported sensor trigger");
242 ret = -ENOTSUP;
243 goto exit;
244 }
245 exit:
246 k_mutex_unlock(&data->mutex);
247
248 return ret;
249 }
250
vcnl4040_trigger_init(const struct device * dev)251 int vcnl4040_trigger_init(const struct device *dev)
252 {
253 const struct vcnl4040_config *config = dev->config;
254 struct vcnl4040_data *data = dev->data;
255
256 data->dev = dev;
257
258 if (config->int_gpio.port == NULL) {
259 LOG_DBG("instance '%s' doesn't support trigger mode", dev->name);
260 return 0;
261 }
262
263 /* Get the GPIO device */
264 if (!gpio_is_ready_dt(&config->int_gpio)) {
265 LOG_ERR("%s: device %s is not ready", dev->name,
266 config->int_gpio.port->name);
267 return -ENODEV;
268 }
269
270 #if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
271 k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
272 k_thread_create(&data->thread, data->thread_stack,
273 CONFIG_VCNL4040_THREAD_STACK_SIZE,
274 vcnl4040_thread_main,
275 data, NULL, NULL,
276 K_PRIO_COOP(CONFIG_VCNL4040_THREAD_PRIORITY),
277 0, K_NO_WAIT);
278 k_thread_name_set(&data->thread, "VCNL4040 trigger");
279 #elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
280 data->work.handler = vcnl4040_work_handler;
281 #endif
282
283 gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
284
285 gpio_init_callback(&data->gpio_cb, vcnl4040_gpio_callback,
286 BIT(config->int_gpio.pin));
287
288 if (gpio_add_callback(config->int_gpio.port, &data->gpio_cb) < 0) {
289 LOG_ERR("Failed to set gpio callback");
290 return -EIO;
291 }
292
293 gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
294
295 if (gpio_pin_get_dt(&config->int_gpio) > 0) {
296 vcnl4040_handle_cb(data);
297 }
298
299 return 0;
300 }
301