1 /* ST Microelectronics LSM6DSO16IS 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/lsm6dso16is.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_lsm6dso16is
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 "lsm6dso16is.h"
19 
20 LOG_MODULE_DECLARE(LSM6DSO16IS, CONFIG_SENSOR_LOG_LEVEL);
21 
22 #if defined(CONFIG_LSM6DSO16IS_ENABLE_TEMP)
23 /**
24  * lsm6dso16is_enable_t_int - TEMP enable selected int pin to generate interrupt
25  */
lsm6dso16is_enable_t_int(const struct device * dev,int enable)26 static int lsm6dso16is_enable_t_int(const struct device *dev, int enable)
27 {
28 	const struct lsm6dso16is_config *cfg = dev->config;
29 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
30 	lsm6dso16is_pin_int2_route_t val;
31 	int ret;
32 
33 	if (enable) {
34 		int16_t buf;
35 
36 		/* dummy read: re-trigger interrupt */
37 		lsm6dso16is_temperature_raw_get(ctx, &buf);
38 	}
39 
40 	/* set interrupt (TEMP DRDY interrupt is only on INT2) */
41 	if (cfg->drdy_pin == 1) {
42 		return -EIO;
43 	}
44 
45 	ret = lsm6dso16is_pin_int2_route_get(ctx, &val);
46 	if (ret < 0) {
47 		LOG_ERR("pint_int2_route_get error");
48 		return ret;
49 	}
50 
51 	val.drdy_temp = 1;
52 
53 	return lsm6dso16is_pin_int2_route_set(ctx, val);
54 }
55 #endif
56 
57 /**
58  * lsm6dso16is_enable_xl_int - XL enable selected int pin to generate interrupt
59  */
lsm6dso16is_enable_xl_int(const struct device * dev,int enable)60 static int lsm6dso16is_enable_xl_int(const struct device *dev, int enable)
61 {
62 	const struct lsm6dso16is_config *cfg = dev->config;
63 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
64 	int ret;
65 
66 	if (enable) {
67 		int16_t buf[3];
68 
69 		/* dummy read: re-trigger interrupt */
70 		lsm6dso16is_acceleration_raw_get(ctx, buf);
71 	}
72 
73 	/* set interrupt */
74 	if (cfg->drdy_pin == 1) {
75 		lsm6dso16is_pin_int1_route_t val;
76 
77 		ret = lsm6dso16is_pin_int1_route_get(ctx, &val);
78 		if (ret < 0) {
79 			LOG_ERR("pint_int1_route_get error");
80 			return ret;
81 		}
82 
83 		val.drdy_xl = 1;
84 
85 		ret = lsm6dso16is_pin_int1_route_set(ctx, val);
86 	} else {
87 		lsm6dso16is_pin_int2_route_t val;
88 
89 		ret = lsm6dso16is_pin_int2_route_get(ctx, &val);
90 		if (ret < 0) {
91 			LOG_ERR("pint_int2_route_get error");
92 			return ret;
93 		}
94 
95 		val.drdy_xl = 1;
96 
97 		ret = lsm6dso16is_pin_int2_route_set(ctx, val);
98 	}
99 
100 	return ret;
101 }
102 
103 /**
104  * lsm6dso16is_enable_g_int - Gyro enable selected int pin to generate interrupt
105  */
lsm6dso16is_enable_g_int(const struct device * dev,int enable)106 static int lsm6dso16is_enable_g_int(const struct device *dev, int enable)
107 {
108 	const struct lsm6dso16is_config *cfg = dev->config;
109 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
110 	int ret;
111 
112 	if (enable) {
113 		int16_t buf[3];
114 
115 		/* dummy read: re-trigger interrupt */
116 		lsm6dso16is_angular_rate_raw_get(ctx, buf);
117 	}
118 
119 	/* set interrupt */
120 	if (cfg->drdy_pin == 1) {
121 		lsm6dso16is_pin_int1_route_t val;
122 
123 		ret = lsm6dso16is_pin_int1_route_get(ctx, &val);
124 		if (ret < 0) {
125 			LOG_ERR("pint_int1_route_get error");
126 			return ret;
127 		}
128 
129 		val.drdy_gy = 1;
130 
131 		ret = lsm6dso16is_pin_int1_route_set(ctx, val);
132 	} else {
133 		lsm6dso16is_pin_int2_route_t val;
134 
135 		ret = lsm6dso16is_pin_int2_route_get(ctx, &val);
136 		if (ret < 0) {
137 			LOG_ERR("pint_int2_route_get error");
138 			return ret;
139 		}
140 
141 		val.drdy_gy = 1;
142 
143 		ret = lsm6dso16is_pin_int2_route_set(ctx, val);
144 	}
145 
146 	return ret;
147 }
148 
149 /**
150  * lsm6dso16is_trigger_set - link external trigger to event data ready
151  */
lsm6dso16is_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)152 int lsm6dso16is_trigger_set(const struct device *dev,
153 			  const struct sensor_trigger *trig,
154 			  sensor_trigger_handler_t handler)
155 {
156 	const struct lsm6dso16is_config *cfg = dev->config;
157 	struct lsm6dso16is_data *lsm6dso16is = dev->data;
158 
159 	if (!cfg->trig_enabled) {
160 		LOG_ERR("trigger_set op not supported");
161 		return -ENOTSUP;
162 	}
163 
164 	if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
165 		lsm6dso16is->handler_drdy_acc = handler;
166 		lsm6dso16is->trig_drdy_acc = trig;
167 		if (handler) {
168 			return lsm6dso16is_enable_xl_int(dev, LSM6DSO16IS_EN_BIT);
169 		} else {
170 			return lsm6dso16is_enable_xl_int(dev, LSM6DSO16IS_DIS_BIT);
171 		}
172 	} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
173 		lsm6dso16is->handler_drdy_gyr = handler;
174 		lsm6dso16is->trig_drdy_gyr = trig;
175 		if (handler) {
176 			return lsm6dso16is_enable_g_int(dev, LSM6DSO16IS_EN_BIT);
177 		} else {
178 			return lsm6dso16is_enable_g_int(dev, LSM6DSO16IS_DIS_BIT);
179 		}
180 	}
181 #if defined(CONFIG_LSM6DSO16IS_ENABLE_TEMP)
182 	else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
183 		lsm6dso16is->handler_drdy_temp = handler;
184 		lsm6dso16is->trig_drdy_temp = trig;
185 		if (handler) {
186 			return lsm6dso16is_enable_t_int(dev, LSM6DSO16IS_EN_BIT);
187 		} else {
188 			return lsm6dso16is_enable_t_int(dev, LSM6DSO16IS_DIS_BIT);
189 		}
190 	}
191 #endif
192 
193 	return -ENOTSUP;
194 }
195 
196 /**
197  * lsm6dso16is_handle_interrupt - handle the drdy event
198  * read data and call handler if registered any
199  */
lsm6dso16is_handle_interrupt(const struct device * dev)200 static void lsm6dso16is_handle_interrupt(const struct device *dev)
201 {
202 	struct lsm6dso16is_data *lsm6dso16is = dev->data;
203 	const struct lsm6dso16is_config *cfg = dev->config;
204 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
205 	lsm6dso16is_status_reg_t status;
206 
207 	while (1) {
208 		if (lsm6dso16is_status_reg_get(ctx, &status) < 0) {
209 			LOG_DBG("failed reading status reg");
210 			return;
211 		}
212 
213 		if ((status.xlda == 0) && (status.gda == 0)
214 #if defined(CONFIG_LSM6DSO16IS_ENABLE_TEMP)
215 					&& (status.tda == 0)
216 #endif
217 					) {
218 			break;
219 		}
220 
221 		if ((status.xlda) && (lsm6dso16is->handler_drdy_acc != NULL)) {
222 			lsm6dso16is->handler_drdy_acc(dev, lsm6dso16is->trig_drdy_acc);
223 		}
224 
225 		if ((status.gda) && (lsm6dso16is->handler_drdy_gyr != NULL)) {
226 			lsm6dso16is->handler_drdy_gyr(dev, lsm6dso16is->trig_drdy_gyr);
227 		}
228 
229 #if defined(CONFIG_LSM6DSO16IS_ENABLE_TEMP)
230 		if ((status.tda) && (lsm6dso16is->handler_drdy_temp != NULL)) {
231 			lsm6dso16is->handler_drdy_temp(dev, lsm6dso16is->trig_drdy_temp);
232 		}
233 #endif
234 	}
235 
236 	gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
237 					GPIO_INT_EDGE_TO_ACTIVE);
238 }
239 
lsm6dso16is_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)240 static void lsm6dso16is_gpio_callback(const struct device *dev,
241 				    struct gpio_callback *cb, uint32_t pins)
242 {
243 	struct lsm6dso16is_data *lsm6dso16is =
244 		CONTAINER_OF(cb, struct lsm6dso16is_data, gpio_cb);
245 	const struct lsm6dso16is_config *cfg = lsm6dso16is->dev->config;
246 
247 	ARG_UNUSED(pins);
248 
249 	gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, GPIO_INT_DISABLE);
250 
251 #if defined(CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD)
252 	k_sem_give(&lsm6dso16is->gpio_sem);
253 #elif defined(CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD)
254 	k_work_submit(&lsm6dso16is->work);
255 #endif /* CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD */
256 }
257 
258 #ifdef CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD
lsm6dso16is_thread(void * p1,void * p2,void * p3)259 static void lsm6dso16is_thread(void *p1, void *p2, void *p3)
260 {
261 	ARG_UNUSED(p2);
262 	ARG_UNUSED(p3);
263 
264 	struct lsm6dso16is_data *lsm6dso16is = p1;
265 
266 	while (1) {
267 		k_sem_take(&lsm6dso16is->gpio_sem, K_FOREVER);
268 		lsm6dso16is_handle_interrupt(lsm6dso16is->dev);
269 	}
270 }
271 #endif /* CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD */
272 
273 #ifdef CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD
lsm6dso16is_work_cb(struct k_work * work)274 static void lsm6dso16is_work_cb(struct k_work *work)
275 {
276 	struct lsm6dso16is_data *lsm6dso16is =
277 		CONTAINER_OF(work, struct lsm6dso16is_data, work);
278 
279 	lsm6dso16is_handle_interrupt(lsm6dso16is->dev);
280 }
281 #endif /* CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD */
282 
lsm6dso16is_init_interrupt(const struct device * dev)283 int lsm6dso16is_init_interrupt(const struct device *dev)
284 {
285 	struct lsm6dso16is_data *lsm6dso16is = dev->data;
286 	const struct lsm6dso16is_config *cfg = dev->config;
287 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
288 	int ret;
289 
290 	/* setup data ready gpio interrupt (INT1 or INT2) */
291 	if (!gpio_is_ready_dt(&cfg->gpio_drdy)) {
292 		LOG_ERR("Cannot get pointer to drdy_gpio device");
293 		return -EINVAL;
294 	}
295 
296 #if defined(CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD)
297 	k_sem_init(&lsm6dso16is->gpio_sem, 0, K_SEM_MAX_LIMIT);
298 
299 	k_thread_create(&lsm6dso16is->thread, lsm6dso16is->thread_stack,
300 			CONFIG_LSM6DSO16IS_THREAD_STACK_SIZE,
301 			lsm6dso16is_thread, lsm6dso16is,
302 			NULL, NULL, K_PRIO_COOP(CONFIG_LSM6DSO16IS_THREAD_PRIORITY),
303 			0, K_NO_WAIT);
304 	k_thread_name_set(&lsm6dso16is->thread, "lsm6dso16is");
305 #elif defined(CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD)
306 	lsm6dso16is->work.handler = lsm6dso16is_work_cb;
307 #endif /* CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD */
308 
309 	ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT);
310 	if (ret < 0) {
311 		LOG_DBG("Could not configure gpio");
312 		return ret;
313 	}
314 
315 	gpio_init_callback(&lsm6dso16is->gpio_cb,
316 			   lsm6dso16is_gpio_callback,
317 			   BIT(cfg->gpio_drdy.pin));
318 
319 	if (gpio_add_callback(cfg->gpio_drdy.port, &lsm6dso16is->gpio_cb) < 0) {
320 		LOG_DBG("Could not set gpio callback");
321 		return -EIO;
322 	}
323 
324 
325 	/* set data ready mode on int1/int2 */
326 	LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed);
327 	lsm6dso16is_data_ready_mode_t mode = cfg->drdy_pulsed ? LSM6DSO16IS_DRDY_PULSED :
328 							     LSM6DSO16IS_DRDY_LATCHED;
329 
330 	ret = lsm6dso16is_data_ready_mode_set(ctx, mode);
331 	if (ret < 0) {
332 		LOG_ERR("drdy_pulsed config error %d", (int)cfg->drdy_pulsed);
333 		return ret;
334 	}
335 
336 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
337 					       GPIO_INT_EDGE_TO_ACTIVE);
338 }
339