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