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(struct vcnl4040_data * data)89 static void vcnl4040_thread_main(struct vcnl4040_data *data)
90 {
91 while (true) {
92 k_sem_take(&data->trig_sem, K_FOREVER);
93 vcnl4040_handle_int(data->dev);
94 }
95 }
96 #endif
97
98 #ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
vcnl4040_work_handler(struct k_work * work)99 static void vcnl4040_work_handler(struct k_work *work)
100 {
101 struct vcnl4040_data *data =
102 CONTAINER_OF(work, struct vcnl4040_data, work);
103
104 vcnl4040_handle_int(data->dev);
105 }
106 #endif
107
vcnl4040_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)108 int vcnl4040_attr_set(const struct device *dev,
109 enum sensor_channel chan,
110 enum sensor_attribute attr,
111 const struct sensor_value *val)
112 {
113 struct vcnl4040_data *data = dev->data;
114 const struct vcnl4040_config *config = dev->config;
115 int ret = 0;
116
117 if (config->int_gpio.port == NULL) {
118 return -ENOTSUP;
119 }
120
121 k_mutex_lock(&data->mutex, K_FOREVER);
122
123 if (chan == SENSOR_CHAN_PROX) {
124 if (attr == SENSOR_ATTR_UPPER_THRESH) {
125 if (vcnl4040_write(dev, VCNL4040_REG_PS_THDH,
126 (uint16_t)val->val1)) {
127 ret = -EIO;
128 goto exit;
129 }
130
131 ret = 0;
132 goto exit;
133 }
134 if (attr == SENSOR_ATTR_LOWER_THRESH) {
135 if (vcnl4040_write(dev, VCNL4040_REG_PS_THDL,
136 (uint16_t)val->val1)) {
137 ret = -EIO;
138 goto exit;
139 }
140
141 ret = 0;
142 goto exit;
143 }
144 }
145 #ifdef CONFIG_VCNL4040_ENABLE_ALS
146 if (chan == SENSOR_CHAN_LIGHT) {
147 if (attr == SENSOR_ATTR_UPPER_THRESH) {
148 if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDH,
149 (uint16_t)val->val1)) {
150 ret = -EIO;
151 goto exit;
152 }
153
154 ret = 0;
155 goto exit;
156 }
157 if (attr == SENSOR_ATTR_LOWER_THRESH) {
158 if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDL,
159 (uint16_t)val->val1)) {
160 ret = -EIO;
161 goto exit;
162 }
163
164 ret = 0;
165 goto exit;
166 }
167 }
168 #endif
169 ret = -ENOTSUP;
170 exit:
171 k_mutex_unlock(&data->mutex);
172
173 return ret;
174 }
175
vcnl4040_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)176 int vcnl4040_trigger_set(const struct device *dev,
177 const struct sensor_trigger *trig,
178 sensor_trigger_handler_t handler)
179 {
180 const struct vcnl4040_config *config = dev->config;
181 struct vcnl4040_data *data = dev->data;
182 uint16_t conf;
183 int ret = 0;
184
185 if (config->int_gpio.port == NULL) {
186 return -ENOTSUP;
187 }
188
189 k_mutex_lock(&data->mutex, K_FOREVER);
190
191 switch (trig->type) {
192 case SENSOR_TRIG_THRESHOLD:
193 if (trig->chan == SENSOR_CHAN_PROX) {
194 if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
195 ret = -EIO;
196 goto exit;
197 }
198
199 /* Set interrupt bits 1:0 of high register */
200 conf |= config->proxy_type << 8;
201
202 if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
203 ret = -EIO;
204 goto exit;
205 }
206
207 data->proxy_handler = handler;
208 data->proxy_trigger = trig;
209 } else if (trig->chan == SENSOR_CHAN_LIGHT) {
210 #ifdef CONFIG_VCNL4040_ENABLE_ALS
211 if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
212 ret = -EIO;
213 goto exit;
214 }
215
216 /* ALS interrupt enable */
217 conf |= VCNL4040_ALS_INT_EN_MASK;
218
219 if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
220 ret = -EIO;
221 goto exit;
222 }
223
224 data->als_handler = handler;
225 data->als_trigger = trig;
226 #else
227 ret = -ENOTSUP;
228 goto exit;
229 #endif
230 } else {
231 ret = -ENOTSUP;
232 goto exit;
233 }
234 break;
235 default:
236 LOG_ERR("Unsupported sensor trigger");
237 ret = -ENOTSUP;
238 goto exit;
239 }
240 exit:
241 k_mutex_unlock(&data->mutex);
242
243 return ret;
244 }
245
vcnl4040_trigger_init(const struct device * dev)246 int vcnl4040_trigger_init(const struct device *dev)
247 {
248 const struct vcnl4040_config *config = dev->config;
249 struct vcnl4040_data *data = dev->data;
250
251 data->dev = dev;
252
253 if (config->int_gpio.port == NULL) {
254 LOG_DBG("instance '%s' doesn't support trigger mode", dev->name);
255 return 0;
256 }
257
258 /* Get the GPIO device */
259 if (!gpio_is_ready_dt(&config->int_gpio)) {
260 LOG_ERR("%s: device %s is not ready", dev->name,
261 config->int_gpio.port->name);
262 return -ENODEV;
263 }
264
265 #if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
266 k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
267 k_thread_create(&data->thread, data->thread_stack,
268 CONFIG_VCNL4040_THREAD_STACK_SIZE,
269 (k_thread_entry_t)vcnl4040_thread_main,
270 data, NULL, NULL,
271 K_PRIO_COOP(CONFIG_VCNL4040_THREAD_PRIORITY),
272 0, K_NO_WAIT);
273 k_thread_name_set(&data->thread, "VCNL4040 trigger");
274 #elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
275 data->work.handler = vcnl4040_work_handler;
276 #endif
277
278 gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
279
280 gpio_init_callback(&data->gpio_cb, vcnl4040_gpio_callback,
281 BIT(config->int_gpio.pin));
282
283 if (gpio_add_callback(config->int_gpio.port, &data->gpio_cb) < 0) {
284 LOG_ERR("Failed to set gpio callback");
285 return -EIO;
286 }
287
288 gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
289
290 if (gpio_pin_get_dt(&config->int_gpio) > 0) {
291 vcnl4040_handle_cb(data);
292 }
293
294 return 0;
295 }
296