1 /* ST Microelectronics IIS2DLPC 3-axis accelerometer driver
2 *
3 * Copyright (c) 2020 STMicroelectronics
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Datasheet:
8 * https://www.st.com/resource/en/datasheet/iis2dlpc.pdf
9 */
10
11 #define DT_DRV_COMPAT st_iis2dlpc
12
13 #include <zephyr/kernel.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/logging/log.h>
17
18 #include "iis2dlpc.h"
19
20 LOG_MODULE_DECLARE(IIS2DLPC, CONFIG_SENSOR_LOG_LEVEL);
21
22 /**
23 * iis2dlpc_enable_int - enable selected int pin to generate interrupt
24 */
iis2dlpc_enable_int(const struct device * dev,enum sensor_trigger_type type,int enable)25 static int iis2dlpc_enable_int(const struct device *dev,
26 enum sensor_trigger_type type, int enable)
27 {
28 const struct iis2dlpc_config *cfg = dev->config;
29 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
30 iis2dlpc_reg_t int_route;
31
32 switch (type) {
33 case SENSOR_TRIG_DATA_READY:
34 if (cfg->drdy_int == 1) {
35 /* set interrupt for pin INT1 */
36 iis2dlpc_pin_int1_route_get(ctx,
37 &int_route.ctrl4_int1_pad_ctrl);
38 int_route.ctrl4_int1_pad_ctrl.int1_drdy = enable;
39
40 return iis2dlpc_pin_int1_route_set(ctx,
41 &int_route.ctrl4_int1_pad_ctrl);
42 } else {
43 /* set interrupt for pin INT2 */
44 iis2dlpc_pin_int2_route_get(ctx,
45 &int_route.ctrl5_int2_pad_ctrl);
46 int_route.ctrl5_int2_pad_ctrl.int2_drdy = enable;
47
48 return iis2dlpc_pin_int2_route_set(ctx,
49 &int_route.ctrl5_int2_pad_ctrl);
50 }
51 break;
52 #ifdef CONFIG_IIS2DLPC_TAP
53 case SENSOR_TRIG_TAP:
54 /* set interrupt for pin INT1 */
55 iis2dlpc_pin_int1_route_get(ctx,
56 &int_route.ctrl4_int1_pad_ctrl);
57 int_route.ctrl4_int1_pad_ctrl.int1_single_tap = enable;
58
59 return iis2dlpc_pin_int1_route_set(ctx,
60 &int_route.ctrl4_int1_pad_ctrl);
61 case SENSOR_TRIG_DOUBLE_TAP:
62 /* set interrupt for pin INT1 */
63 iis2dlpc_pin_int1_route_get(ctx,
64 &int_route.ctrl4_int1_pad_ctrl);
65 int_route.ctrl4_int1_pad_ctrl.int1_tap = enable;
66
67 return iis2dlpc_pin_int1_route_set(ctx,
68 &int_route.ctrl4_int1_pad_ctrl);
69 #endif /* CONFIG_IIS2DLPC_TAP */
70 #ifdef CONFIG_IIS2DLPC_ACTIVITY
71 case SENSOR_TRIG_DELTA:
72 /* set interrupt for pin INT1 */
73 iis2dlpc_pin_int1_route_get(ctx,
74 &int_route.ctrl4_int1_pad_ctrl);
75 int_route.ctrl4_int1_pad_ctrl.int1_wu = enable;
76
77 iis2dlpc_act_mode_set(ctx, enable ? IIS2DLPC_DETECT_ACT_INACT
78 : IIS2DLPC_NO_DETECTION);
79
80 return iis2dlpc_pin_int1_route_set(ctx,
81 &int_route.ctrl4_int1_pad_ctrl);
82 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
83 default:
84 LOG_ERR("Unsupported trigger interrupt route %d", type);
85 return -ENOTSUP;
86 }
87 }
88
89 /**
90 * iis2dlpc_trigger_set - link external trigger to event data ready
91 */
iis2dlpc_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)92 int iis2dlpc_trigger_set(const struct device *dev,
93 const struct sensor_trigger *trig,
94 sensor_trigger_handler_t handler)
95 {
96 const struct iis2dlpc_config *cfg = dev->config;
97 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
98 struct iis2dlpc_data *iis2dlpc = dev->data;
99 int16_t raw[3];
100 int state = (handler != NULL) ? PROPERTY_ENABLE : PROPERTY_DISABLE;
101
102 switch (trig->type) {
103 case SENSOR_TRIG_DATA_READY:
104 iis2dlpc->drdy_handler = handler;
105 iis2dlpc->drdy_trig = trig;
106 if (state) {
107 /* dummy read: re-trigger interrupt */
108 iis2dlpc_acceleration_raw_get(ctx, raw);
109 }
110 return iis2dlpc_enable_int(dev, SENSOR_TRIG_DATA_READY, state);
111 #ifdef CONFIG_IIS2DLPC_TAP
112 case SENSOR_TRIG_TAP:
113 iis2dlpc->tap_handler = handler;
114 iis2dlpc->tap_trig = trig;
115 return iis2dlpc_enable_int(dev, SENSOR_TRIG_TAP, state);
116 case SENSOR_TRIG_DOUBLE_TAP:
117 iis2dlpc->double_tap_handler = handler;
118 iis2dlpc->double_tap_trig = trig;
119 return iis2dlpc_enable_int(dev, SENSOR_TRIG_DOUBLE_TAP, state);
120 #endif /* CONFIG_IIS2DLPC_TAP */
121 #ifdef CONFIG_IIS2DLPC_ACTIVITY
122 case SENSOR_TRIG_DELTA:
123 iis2dlpc->activity_handler = handler;
124 iis2dlpc->activity_trig = trig;
125 return iis2dlpc_enable_int(dev, SENSOR_TRIG_DELTA, state);
126 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
127 default:
128 LOG_ERR("Unsupported sensor trigger");
129 return -ENOTSUP;
130 }
131 }
132
iis2dlpc_handle_drdy_int(const struct device * dev)133 static int iis2dlpc_handle_drdy_int(const struct device *dev)
134 {
135 struct iis2dlpc_data *data = dev->data;
136
137 if (data->drdy_handler) {
138 data->drdy_handler(dev, data->drdy_trig);
139 }
140
141 return 0;
142 }
143
144 #ifdef CONFIG_IIS2DLPC_ACTIVITY
iis2dlpc_handle_activity_int(const struct device * dev)145 static int iis2dlpc_handle_activity_int(const struct device *dev)
146 {
147 struct iis2dlpc_data *data = dev->data;
148 sensor_trigger_handler_t handler = data->activity_handler;
149
150 if (handler) {
151 handler(dev, data->activity_trig);
152 }
153
154 return 0;
155 }
156 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
157
158 #ifdef CONFIG_IIS2DLPC_TAP
iis2dlpc_handle_single_tap_int(const struct device * dev)159 static int iis2dlpc_handle_single_tap_int(const struct device *dev)
160 {
161 struct iis2dlpc_data *data = dev->data;
162 sensor_trigger_handler_t handler = data->tap_handler;
163
164 if (handler) {
165 handler(dev, data->tap_trig);
166 }
167
168 return 0;
169 }
170
iis2dlpc_handle_double_tap_int(const struct device * dev)171 static int iis2dlpc_handle_double_tap_int(const struct device *dev)
172 {
173 struct iis2dlpc_data *data = dev->data;
174 sensor_trigger_handler_t handler = data->double_tap_handler;
175
176 if (handler) {
177 handler(dev, data->double_tap_trig);
178 }
179
180 return 0;
181 }
182 #endif /* CONFIG_IIS2DLPC_TAP */
183
184 /**
185 * iis2dlpc_handle_interrupt - handle the drdy event
186 * read data and call handler if registered any
187 */
iis2dlpc_handle_interrupt(const struct device * dev)188 static void iis2dlpc_handle_interrupt(const struct device *dev)
189 {
190 const struct iis2dlpc_config *cfg = dev->config;
191 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
192 iis2dlpc_all_sources_t sources;
193
194 iis2dlpc_all_sources_get(ctx, &sources);
195
196 if (sources.status_dup.drdy) {
197 iis2dlpc_handle_drdy_int(dev);
198 }
199 #ifdef CONFIG_IIS2DLPC_TAP
200 if (sources.status_dup.single_tap) {
201 iis2dlpc_handle_single_tap_int(dev);
202 }
203 if (sources.status_dup.double_tap) {
204 iis2dlpc_handle_double_tap_int(dev);
205 }
206 #endif /* CONFIG_IIS2DLPC_TAP */
207 #ifdef CONFIG_IIS2DLPC_ACTIVITY
208 if (sources.all_int_src.wu_ia) {
209 iis2dlpc_handle_activity_int(dev);
210 }
211 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
212
213 gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
214 GPIO_INT_EDGE_TO_ACTIVE);
215 }
216
iis2dlpc_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)217 static void iis2dlpc_gpio_callback(const struct device *dev,
218 struct gpio_callback *cb, uint32_t pins)
219 {
220 struct iis2dlpc_data *iis2dlpc =
221 CONTAINER_OF(cb, struct iis2dlpc_data, gpio_cb);
222 const struct iis2dlpc_config *cfg = iis2dlpc->dev->config;
223
224 ARG_UNUSED(pins);
225
226 gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, GPIO_INT_DISABLE);
227
228 #if defined(CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD)
229 k_sem_give(&iis2dlpc->gpio_sem);
230 #elif defined(CONFIG_IIS2DLPC_TRIGGER_GLOBAL_THREAD)
231 k_work_submit(&iis2dlpc->work);
232 #endif /* CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD */
233 }
234
235 #ifdef CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD
iis2dlpc_thread(void * p1,void * p2,void * p3)236 static void iis2dlpc_thread(void *p1, void *p2, void *p3)
237 {
238 ARG_UNUSED(p2);
239 ARG_UNUSED(p3);
240
241 struct iis2dlpc_data *iis2dlpc = p1;
242
243 while (1) {
244 k_sem_take(&iis2dlpc->gpio_sem, K_FOREVER);
245 iis2dlpc_handle_interrupt(iis2dlpc->dev);
246 }
247 }
248 #endif /* CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD */
249
250 #ifdef CONFIG_IIS2DLPC_TRIGGER_GLOBAL_THREAD
iis2dlpc_work_cb(struct k_work * work)251 static void iis2dlpc_work_cb(struct k_work *work)
252 {
253 struct iis2dlpc_data *iis2dlpc =
254 CONTAINER_OF(work, struct iis2dlpc_data, work);
255
256 iis2dlpc_handle_interrupt(iis2dlpc->dev);
257 }
258 #endif /* CONFIG_IIS2DLPC_TRIGGER_GLOBAL_THREAD */
259
iis2dlpc_init_interrupt(const struct device * dev)260 int iis2dlpc_init_interrupt(const struct device *dev)
261 {
262 struct iis2dlpc_data *iis2dlpc = dev->data;
263 const struct iis2dlpc_config *cfg = dev->config;
264 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
265 int ret;
266
267 /* setup data ready gpio interrupt (INT1 or INT2) */
268 if (!gpio_is_ready_dt(&cfg->gpio_drdy)) {
269 LOG_ERR("Cannot get pointer to drdy_gpio device");
270 return -EINVAL;
271 }
272
273 #if defined(CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD)
274 k_sem_init(&iis2dlpc->gpio_sem, 0, K_SEM_MAX_LIMIT);
275
276 k_thread_create(&iis2dlpc->thread, iis2dlpc->thread_stack,
277 CONFIG_IIS2DLPC_THREAD_STACK_SIZE,
278 iis2dlpc_thread, iis2dlpc,
279 NULL, NULL, K_PRIO_COOP(CONFIG_IIS2DLPC_THREAD_PRIORITY),
280 0, K_NO_WAIT);
281 #elif defined(CONFIG_IIS2DLPC_TRIGGER_GLOBAL_THREAD)
282 iis2dlpc->work.handler = iis2dlpc_work_cb;
283 #endif /* CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD */
284
285 ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT);
286 if (ret < 0) {
287 LOG_ERR("Could not configure gpio");
288 return ret;
289 }
290
291 gpio_init_callback(&iis2dlpc->gpio_cb,
292 iis2dlpc_gpio_callback,
293 BIT(cfg->gpio_drdy.pin));
294
295 if (gpio_add_callback(cfg->gpio_drdy.port, &iis2dlpc->gpio_cb) < 0) {
296 LOG_ERR("Could not set gpio callback");
297 return -EIO;
298 }
299
300 /* enable interrupt on int1/int2 in pulse mode */
301 if (iis2dlpc_int_notification_set(ctx, IIS2DLPC_INT_PULSED)) {
302 LOG_ERR("Could not set pulse mode");
303 return -EIO;
304 }
305
306 return gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
307 GPIO_INT_EDGE_TO_ACTIVE);
308 }
309