1 /* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
2  *
3  * Copyright (c) 2023 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/lsm6dsv16x.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_lsm6dsv16x
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 "lsm6dsv16x.h"
19 #include "lsm6dsv16x_rtio.h"
20 
21 LOG_MODULE_DECLARE(LSM6DSV16X, CONFIG_SENSOR_LOG_LEVEL);
22 
23 /**
24  * lsm6dsv16x_enable_xl_int - XL enable selected int pin to generate interrupt
25  */
lsm6dsv16x_enable_xl_int(const struct device * dev,int enable)26 static int lsm6dsv16x_enable_xl_int(const struct device *dev, int enable)
27 {
28 	const struct lsm6dsv16x_config *cfg = dev->config;
29 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
30 	int ret;
31 
32 	if (enable) {
33 		int16_t buf[3];
34 
35 		/* dummy read: re-trigger interrupt */
36 		lsm6dsv16x_acceleration_raw_get(ctx, buf);
37 	}
38 
39 	/* set interrupt */
40 	if ((cfg->drdy_pin == 1) || (ON_I3C_BUS(cfg) && (!I3C_INT_PIN(cfg)))) {
41 		lsm6dsv16x_pin_int_route_t val = {};
42 
43 		ret = lsm6dsv16x_pin_int1_route_get(ctx, &val);
44 		if (ret < 0) {
45 			LOG_ERR("pint_int1_route_get error");
46 			return ret;
47 		}
48 
49 		val.drdy_xl = 1;
50 
51 		ret = lsm6dsv16x_pin_int1_route_set(ctx, &val);
52 	} else {
53 		lsm6dsv16x_pin_int_route_t val = {};
54 
55 		ret = lsm6dsv16x_pin_int2_route_get(ctx, &val);
56 		if (ret < 0) {
57 			LOG_ERR("pint_int2_route_get error");
58 			return ret;
59 		}
60 
61 		val.drdy_xl = 1;
62 
63 		ret = lsm6dsv16x_pin_int2_route_set(ctx, &val);
64 	}
65 
66 	return ret;
67 }
68 
69 /**
70  * lsm6dsv16x_enable_g_int - Gyro enable selected int pin to generate interrupt
71  */
lsm6dsv16x_enable_g_int(const struct device * dev,int enable)72 static int lsm6dsv16x_enable_g_int(const struct device *dev, int enable)
73 {
74 	const struct lsm6dsv16x_config *cfg = dev->config;
75 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
76 	int ret;
77 
78 	if (enable) {
79 		int16_t buf[3];
80 
81 		/* dummy read: re-trigger interrupt */
82 		lsm6dsv16x_angular_rate_raw_get(ctx, buf);
83 	}
84 
85 	/* set interrupt */
86 	if ((cfg->drdy_pin == 1) || (ON_I3C_BUS(cfg) && (!I3C_INT_PIN(cfg)))) {
87 		lsm6dsv16x_pin_int_route_t val = {};
88 
89 		ret = lsm6dsv16x_pin_int1_route_get(ctx, &val);
90 		if (ret < 0) {
91 			LOG_ERR("pint_int1_route_get error");
92 			return ret;
93 		}
94 
95 		val.drdy_g = 1;
96 
97 		ret = lsm6dsv16x_pin_int1_route_set(ctx, &val);
98 	} else {
99 		lsm6dsv16x_pin_int_route_t val = {};
100 
101 		ret = lsm6dsv16x_pin_int2_route_get(ctx, &val);
102 		if (ret < 0) {
103 			LOG_ERR("pint_int2_route_get error");
104 			return ret;
105 		}
106 
107 		val.drdy_g = 1;
108 
109 		ret = lsm6dsv16x_pin_int2_route_set(ctx, &val);
110 	}
111 
112 	return ret;
113 }
114 
115 /**
116  * lsm6dsv16x_enable_wake_int - Enable selected int pin to generate wakeup interrupt
117  */
lsm6dsv16x_enable_wake_int(const struct device * dev,int enable)118 static int lsm6dsv16x_enable_wake_int(const struct device *dev, int enable)
119 {
120 	const struct lsm6dsv16x_config *cfg = dev->config;
121 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
122 	int ret;
123 	lsm6dsv16x_interrupt_mode_t int_mode;
124 
125 	int_mode.enable = enable ? 1 : 0;
126 	int_mode.lir = !cfg->drdy_pulsed;
127 	ret = lsm6dsv16x_interrupt_enable_set(ctx, int_mode);
128 	if (ret < 0) {
129 		LOG_ERR("interrupt_enable_set error");
130 		return ret;
131 	}
132 
133 	if ((cfg->drdy_pin == 1) || ON_I3C_BUS(cfg)) {
134 		lsm6dsv16x_pin_int_route_t val;
135 
136 		ret = lsm6dsv16x_pin_int1_route_get(ctx, &val);
137 		if (ret < 0) {
138 			LOG_ERR("pint_int1_route_get error");
139 			return ret;
140 		}
141 
142 		val.wakeup = enable ? 1 : 0;
143 
144 		ret = lsm6dsv16x_pin_int1_route_set(ctx, &val);
145 	} else {
146 		lsm6dsv16x_pin_int_route_t val;
147 
148 		ret = lsm6dsv16x_pin_int2_route_get(ctx, &val);
149 		if (ret < 0) {
150 			LOG_ERR("pint_int2_route_get error");
151 			return ret;
152 		}
153 
154 		val.wakeup = enable ? 1 : 0;
155 
156 		ret = lsm6dsv16x_pin_int2_route_set(ctx, &val);
157 	}
158 
159 	return ret;
160 }
161 
162 /**
163  * lsm6dsv16x_trigger_set - link external trigger to event data ready
164  */
lsm6dsv16x_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)165 int lsm6dsv16x_trigger_set(const struct device *dev,
166 			  const struct sensor_trigger *trig,
167 			  sensor_trigger_handler_t handler)
168 {
169 	const struct lsm6dsv16x_config *cfg = dev->config;
170 	struct lsm6dsv16x_data *lsm6dsv16x = dev->data;
171 	int ret = 0;
172 
173 	if (!cfg->trig_enabled) {
174 		LOG_ERR("trigger_set op not supported");
175 		return -ENOTSUP;
176 	}
177 
178 	if (trig == NULL) {
179 		LOG_ERR("no trigger");
180 		return -EINVAL;
181 	}
182 
183 	if (!lsm6dsv16x_is_active(dev)) {
184 		return -EBUSY;
185 	}
186 
187 	switch (trig->type) {
188 	case SENSOR_TRIG_DATA_READY:
189 		if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
190 			lsm6dsv16x->handler_drdy_acc = handler;
191 			lsm6dsv16x->trig_drdy_acc = trig;
192 			if (handler) {
193 				lsm6dsv16x_enable_xl_int(dev, LSM6DSV16X_EN_BIT);
194 			} else {
195 				lsm6dsv16x_enable_xl_int(dev, LSM6DSV16X_DIS_BIT);
196 			}
197 		} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
198 			lsm6dsv16x->handler_drdy_gyr = handler;
199 			lsm6dsv16x->trig_drdy_gyr = trig;
200 			if (handler) {
201 				lsm6dsv16x_enable_g_int(dev, LSM6DSV16X_EN_BIT);
202 			} else {
203 				lsm6dsv16x_enable_g_int(dev, LSM6DSV16X_DIS_BIT);
204 			}
205 		}
206 		break;
207 	case SENSOR_TRIG_DELTA:
208 		if (trig->chan != SENSOR_CHAN_ACCEL_XYZ) {
209 			return -ENOTSUP;
210 		}
211 		lsm6dsv16x->handler_wakeup = handler;
212 		lsm6dsv16x->trig_wakeup = trig;
213 		if (handler) {
214 			lsm6dsv16x_enable_wake_int(dev, LSM6DSV16X_EN_BIT);
215 		} else {
216 			lsm6dsv16x_enable_wake_int(dev, LSM6DSV16X_DIS_BIT);
217 		}
218 		break;
219 	default:
220 		ret = -ENOTSUP;
221 		break;
222 	}
223 
224 	return ret;
225 }
226 
227 #if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) || \
228 	defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD)
229 /**
230  * lsm6dsv16x_handle_interrupt - handle the drdy event
231  * read data and call handler if registered any
232  */
lsm6dsv16x_handle_interrupt(const struct device * dev)233 static void lsm6dsv16x_handle_interrupt(const struct device *dev)
234 {
235 	struct lsm6dsv16x_data *lsm6dsv16x = dev->data;
236 	const struct lsm6dsv16x_config *cfg = dev->config;
237 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
238 	lsm6dsv16x_data_ready_t status;
239 	lsm6dsv16x_all_int_src_t all_int_src;
240 	int ret;
241 
242 	while (1) {
243 		/* When using I3C IBI interrupt the status register is already automatically
244 		 * read (clearing the interrupt condition), so we can skip the extra bus
245 		 * transaction for FIFO stream case.
246 		 */
247 		if (ON_I3C_BUS(cfg) && !I3C_INT_PIN(cfg) && IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) {
248 			break;
249 		}
250 
251 		if (lsm6dsv16x_flag_data_ready_get(ctx, &status) < 0) {
252 			LOG_DBG("failed reading status reg");
253 			return;
254 		}
255 
256 		ret = lsm6dsv16x_read_reg(ctx, LSM6DSV16X_ALL_INT_SRC, (uint8_t *)&all_int_src, 1);
257 		if (ret < 0) {
258 			LOG_DBG("failed reading all_int_src reg");
259 			return;
260 		}
261 
262 		if (((status.drdy_xl == 0) && (status.drdy_gy == 0) && (all_int_src.wu_ia == 0)) ||
263 		    IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) {
264 			break;
265 		}
266 
267 		if ((status.drdy_xl) && (lsm6dsv16x->handler_drdy_acc != NULL)) {
268 			lsm6dsv16x->handler_drdy_acc(dev, lsm6dsv16x->trig_drdy_acc);
269 		}
270 
271 		if ((status.drdy_gy) && (lsm6dsv16x->handler_drdy_gyr != NULL)) {
272 			lsm6dsv16x->handler_drdy_gyr(dev, lsm6dsv16x->trig_drdy_gyr);
273 		}
274 
275 		if ((all_int_src.wu_ia) && lsm6dsv16x->handler_wakeup != NULL) {
276 			lsm6dsv16x->handler_wakeup(dev, lsm6dsv16x->trig_wakeup);
277 		}
278 	}
279 
280 	if (!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) {
281 		ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio,
282 						GPIO_INT_EDGE_TO_ACTIVE);
283 		if (ret < 0) {
284 			LOG_ERR("%s: Not able to configure pin_int", dev->name);
285 		}
286 	}
287 }
288 #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD || CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD */
289 
lsm6dsv16x_intr_callback(struct lsm6dsv16x_data * lsm6dsv16x)290 static void lsm6dsv16x_intr_callback(struct lsm6dsv16x_data *lsm6dsv16x)
291 {
292 #if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD)
293 	k_sem_give(&lsm6dsv16x->intr_sem);
294 #elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD)
295 	k_work_submit(&lsm6dsv16x->work);
296 #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */
297 	if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) {
298 		lsm6dsv16x_stream_irq_handler(lsm6dsv16x->dev);
299 	}
300 }
301 
lsm6dsv16x_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)302 static void lsm6dsv16x_gpio_callback(const struct device *dev,
303 				    struct gpio_callback *cb, uint32_t pins)
304 {
305 	struct lsm6dsv16x_data *lsm6dsv16x =
306 		CONTAINER_OF(cb, struct lsm6dsv16x_data, gpio_cb);
307 	int ret;
308 
309 	ARG_UNUSED(pins);
310 
311 	ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_DISABLE);
312 	if (ret < 0) {
313 		LOG_ERR("%s: Not able to configure pin_int", dev->name);
314 	}
315 
316 	lsm6dsv16x_intr_callback(lsm6dsv16x);
317 }
318 
319 #ifdef CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD
lsm6dsv16x_thread(void * p1,void * p2,void * p3)320 static void lsm6dsv16x_thread(void *p1, void *p2, void *p3)
321 {
322 	ARG_UNUSED(p2);
323 	ARG_UNUSED(p3);
324 
325 	struct lsm6dsv16x_data *lsm6dsv16x = p1;
326 
327 	while (1) {
328 		k_sem_take(&lsm6dsv16x->intr_sem, K_FOREVER);
329 		lsm6dsv16x_handle_interrupt(lsm6dsv16x->dev);
330 	}
331 }
332 #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */
333 
334 #ifdef CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD
lsm6dsv16x_work_cb(struct k_work * work)335 static void lsm6dsv16x_work_cb(struct k_work *work)
336 {
337 	struct lsm6dsv16x_data *lsm6dsv16x =
338 		CONTAINER_OF(work, struct lsm6dsv16x_data, work);
339 
340 	lsm6dsv16x_handle_interrupt(lsm6dsv16x->dev);
341 }
342 #endif /* CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD */
343 
344 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
lsm6dsv16x_ibi_cb(struct i3c_device_desc * target,struct i3c_ibi_payload * payload)345 static int lsm6dsv16x_ibi_cb(struct i3c_device_desc *target,
346 			  struct i3c_ibi_payload *payload)
347 {
348 	const struct device *dev = target->dev;
349 	struct lsm6dsv16x_data *lsm6dsv16x = dev->data;
350 
351 	/*
352 	 * The IBI Payload consist of the following 10 bytes :
353 	 * 1st byte: MDB
354 	 * - MDB[0]: FIFO interrupts (FIFO_WTM_IA, FIFO_OVR_IA, FIFO_FULL_IA, CONTER_BDR_IA)
355 	 * - MDB[1]: Physical interrupts (XLDS, GDA, TDA, XLDA_OIS, GDA_OIS)
356 	 * - MDB[2]: Basic interrupts (SLEEP_CHANGE_IA, D6D_IA, DOUBLE_TAP, SINGLE_TAP, WU_IA,
357 	 *           FF_IA)
358 	 * - MDB[3]: SHUB DRDY (SENS_HUB_ENDOP)
359 	 * - MDB[4]: Advanced Function interrupt group
360 	 * - MDB[7:5]: 3'b000: Vendor Definied
361 	 *             3'b100: Timing Information
362 	 * 2nd byte: FIFO_STATUS1
363 	 * 3rd byte: FIFO_STATUS2
364 	 * 4th byte: ALL_INT_SRC
365 	 * 5th byte: STATUS_REG
366 	 * 6th byte: STATUS_REG_OIS
367 	 * 7th byte: STATUS_MASTER_MAIN
368 	 * 8th byte: EMB_FUNC_STATUS
369 	 * 9th byte: FSM_STATUS
370 	 * 10th byte: MLC_STATUS
371 	 */
372 	if (payload->payload_len != sizeof(lsm6dsv16x->ibi_payload)) {
373 		LOG_ERR("Invalid IBI payload length");
374 		return -EINVAL;
375 	}
376 
377 	memcpy(&lsm6dsv16x->ibi_payload, payload->payload, payload->payload_len);
378 
379 	lsm6dsv16x_intr_callback(lsm6dsv16x);
380 
381 	return 0;
382 }
383 #endif
384 
lsm6dsv16x_init_interrupt(const struct device * dev)385 int lsm6dsv16x_init_interrupt(const struct device *dev)
386 {
387 	struct lsm6dsv16x_data *lsm6dsv16x = dev->data;
388 	const struct lsm6dsv16x_config *cfg = dev->config;
389 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
390 	int ret;
391 
392 	lsm6dsv16x->drdy_gpio = (cfg->drdy_pin == 1) ?
393 			(struct gpio_dt_spec *)&cfg->int1_gpio :
394 			(struct gpio_dt_spec *)&cfg->int2_gpio;
395 
396 	/* setup data ready gpio interrupt (INT1 or INT2) */
397 	if ((!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) && !gpio_is_ready_dt(lsm6dsv16x->drdy_gpio)) {
398 		LOG_ERR("Cannot get pointer to drdy_gpio device");
399 		return -EINVAL;
400 	}
401 
402 	if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) {
403 		lsm6dsv16x_stream_irq_handler(lsm6dsv16x->dev);
404 	}
405 
406 #if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD)
407 	k_sem_init(&lsm6dsv16x->intr_sem, 0, K_SEM_MAX_LIMIT);
408 
409 	k_thread_create(&lsm6dsv16x->thread, lsm6dsv16x->thread_stack,
410 			CONFIG_LSM6DSV16X_THREAD_STACK_SIZE,
411 			lsm6dsv16x_thread, lsm6dsv16x,
412 			NULL, NULL, K_PRIO_COOP(CONFIG_LSM6DSV16X_THREAD_PRIORITY),
413 			0, K_NO_WAIT);
414 	k_thread_name_set(&lsm6dsv16x->thread, "lsm6dsv16x");
415 #elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD)
416 	lsm6dsv16x->work.handler = lsm6dsv16x_work_cb;
417 #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */
418 
419 	if (!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) {
420 		ret = gpio_pin_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INPUT);
421 		if (ret < 0) {
422 			LOG_DBG("Could not configure gpio");
423 			return ret;
424 		}
425 
426 		gpio_init_callback(&lsm6dsv16x->gpio_cb,
427 				lsm6dsv16x_gpio_callback,
428 				BIT(lsm6dsv16x->drdy_gpio->pin));
429 
430 		if (gpio_add_callback(lsm6dsv16x->drdy_gpio->port, &lsm6dsv16x->gpio_cb) < 0) {
431 			LOG_DBG("Could not set gpio callback");
432 			return -EIO;
433 		}
434 
435 	}
436 
437 	/* set data ready mode on int1/int2/tir */
438 	LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed);
439 	lsm6dsv16x_data_ready_mode_t mode = cfg->drdy_pulsed ? LSM6DSV16X_DRDY_PULSED :
440 								LSM6DSV16X_DRDY_LATCHED;
441 
442 
443 	ret = lsm6dsv16x_data_ready_mode_set(ctx, mode);
444 	if (ret < 0) {
445 		LOG_ERR("drdy_pulsed config error %d", (int)cfg->drdy_pulsed);
446 		return ret;
447 	}
448 
449 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
450 	if (ON_I3C_BUS(cfg)) {
451 		if (I3C_INT_PIN(cfg)) {
452 			/* Enable INT Pins when using I3C */
453 			ret = lsm6dsv16x_i3c_int_en_set(ctx, I3C_INT_PIN(cfg));
454 			if (ret < 0) {
455 				LOG_ERR("failed to enable int pin for I3C %d", ret);
456 				return ret;
457 			}
458 
459 			ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio,
460 							      GPIO_INT_EDGE_TO_ACTIVE);
461 			if (ret < 0) {
462 				LOG_ERR("Could not configure gpio interrupt");
463 				return ret;
464 			}
465 		} else {
466 			/* I3C IBI does not utilize GPIO interrupt. */
467 			lsm6dsv16x->i3c_dev->ibi_cb = lsm6dsv16x_ibi_cb;
468 
469 			/*
470 			 * Set IBI availability time, this is the time that the sensor
471 			 * will wait for inactivity before it is okay to generate an IBI TIR.
472 			 *
473 			 * NOTE: There is a bug in the API and the Documentation where
474 			 * the defines for the values are incorrect. The correct values are:
475 			 * 0 = 50us
476 			 * 1 = 2us
477 			 * 2 = 1ms
478 			 * 3 = 25ms
479 			 */
480 			ret = lsm6dsv16x_i3c_ibi_time_set(ctx, cfg->bus_act_sel);
481 			if (ret < 0) {
482 				LOG_ERR("failed to set ibi available time %d", ret);
483 				return -EIO;
484 			}
485 
486 			if (i3c_ibi_enable(lsm6dsv16x->i3c_dev) != 0) {
487 				LOG_ERR("Could not enable I3C IBI");
488 				return -EIO;
489 			}
490 		}
491 
492 		return 0;
493 	}
494 #endif
495 
496 	return gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio,
497 					       GPIO_INT_EDGE_TO_ACTIVE);
498 }
499