1 /* ST Microelectronics LSM6DSO 6-axis IMU sensor driver
2  *
3  * Copyright (c) 2019 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/lsm6dso.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_lsm6dso
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 "lsm6dso.h"
19 
20 LOG_MODULE_DECLARE(LSM6DSO, CONFIG_SENSOR_LOG_LEVEL);
21 
22 #if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
23 /**
24  * lsm6dso_enable_t_int - TEMP enable selected int pin to generate interrupt
25  */
lsm6dso_enable_t_int(const struct device * dev,int enable)26 static int lsm6dso_enable_t_int(const struct device *dev, int enable)
27 {
28 	const struct lsm6dso_config *cfg = dev->config;
29 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
30 	lsm6dso_int2_ctrl_t int2_ctrl;
31 
32 	if (enable) {
33 		int16_t buf;
34 
35 		/* dummy read: re-trigger interrupt */
36 		lsm6dso_temperature_raw_get(ctx, &buf);
37 	}
38 
39 	/* set interrupt (TEMP DRDY interrupt is only on INT2) */
40 	if (cfg->int_pin == 1) {
41 		return -EIO;
42 	}
43 
44 	lsm6dso_read_reg(ctx, LSM6DSO_INT2_CTRL, (uint8_t *)&int2_ctrl, 1);
45 	int2_ctrl.int2_drdy_temp = enable;
46 	return lsm6dso_write_reg(ctx, LSM6DSO_INT2_CTRL,
47 				 (uint8_t *)&int2_ctrl, 1);
48 }
49 #endif
50 
51 /**
52  * lsm6dso_enable_xl_int - XL enable selected int pin to generate interrupt
53  */
lsm6dso_enable_xl_int(const struct device * dev,int enable)54 static int lsm6dso_enable_xl_int(const struct device *dev, int enable)
55 {
56 	const struct lsm6dso_config *cfg = dev->config;
57 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
58 
59 	if (enable) {
60 		int16_t buf[3];
61 
62 		/* dummy read: re-trigger interrupt */
63 		lsm6dso_acceleration_raw_get(ctx, buf);
64 	}
65 
66 	/* set interrupt */
67 	if (cfg->int_pin == 1) {
68 		lsm6dso_int1_ctrl_t int1_ctrl;
69 
70 		lsm6dso_read_reg(ctx, LSM6DSO_INT1_CTRL,
71 				 (uint8_t *)&int1_ctrl, 1);
72 
73 		int1_ctrl.int1_drdy_xl = enable;
74 		return lsm6dso_write_reg(ctx, LSM6DSO_INT1_CTRL,
75 					 (uint8_t *)&int1_ctrl, 1);
76 	} else {
77 		lsm6dso_int2_ctrl_t int2_ctrl;
78 
79 		lsm6dso_read_reg(ctx, LSM6DSO_INT2_CTRL,
80 				 (uint8_t *)&int2_ctrl, 1);
81 		int2_ctrl.int2_drdy_xl = enable;
82 		return lsm6dso_write_reg(ctx, LSM6DSO_INT2_CTRL,
83 					 (uint8_t *)&int2_ctrl, 1);
84 	}
85 }
86 
87 /**
88  * lsm6dso_enable_g_int - Gyro enable selected int pin to generate interrupt
89  */
lsm6dso_enable_g_int(const struct device * dev,int enable)90 static int lsm6dso_enable_g_int(const struct device *dev, int enable)
91 {
92 	const struct lsm6dso_config *cfg = dev->config;
93 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
94 
95 	if (enable) {
96 		int16_t buf[3];
97 
98 		/* dummy read: re-trigger interrupt */
99 		lsm6dso_angular_rate_raw_get(ctx, buf);
100 	}
101 
102 	/* set interrupt */
103 	if (cfg->int_pin == 1) {
104 		lsm6dso_int1_ctrl_t int1_ctrl;
105 
106 		lsm6dso_read_reg(ctx, LSM6DSO_INT1_CTRL,
107 				 (uint8_t *)&int1_ctrl, 1);
108 		int1_ctrl.int1_drdy_g = enable;
109 		return lsm6dso_write_reg(ctx, LSM6DSO_INT1_CTRL,
110 					 (uint8_t *)&int1_ctrl, 1);
111 	} else {
112 		lsm6dso_int2_ctrl_t int2_ctrl;
113 
114 		lsm6dso_read_reg(ctx, LSM6DSO_INT2_CTRL,
115 				 (uint8_t *)&int2_ctrl, 1);
116 		int2_ctrl.int2_drdy_g = enable;
117 		return lsm6dso_write_reg(ctx, LSM6DSO_INT2_CTRL,
118 					 (uint8_t *)&int2_ctrl, 1);
119 	}
120 }
121 
122 /**
123  * lsm6dso_trigger_set - link external trigger to event data ready
124  */
lsm6dso_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)125 int lsm6dso_trigger_set(const struct device *dev,
126 			  const struct sensor_trigger *trig,
127 			  sensor_trigger_handler_t handler)
128 {
129 	const struct lsm6dso_config *cfg = dev->config;
130 	struct lsm6dso_data *lsm6dso = dev->data;
131 
132 	if (!cfg->trig_enabled) {
133 		LOG_ERR("trigger_set op not supported");
134 		return -ENOTSUP;
135 	}
136 
137 	if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
138 		lsm6dso->handler_drdy_acc = handler;
139 		lsm6dso->trig_drdy_acc = trig;
140 		if (handler) {
141 			return lsm6dso_enable_xl_int(dev, LSM6DSO_EN_BIT);
142 		} else {
143 			return lsm6dso_enable_xl_int(dev, LSM6DSO_DIS_BIT);
144 		}
145 	} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
146 		lsm6dso->handler_drdy_gyr = handler;
147 		lsm6dso->trig_drdy_gyr = trig;
148 		if (handler) {
149 			return lsm6dso_enable_g_int(dev, LSM6DSO_EN_BIT);
150 		} else {
151 			return lsm6dso_enable_g_int(dev, LSM6DSO_DIS_BIT);
152 		}
153 	}
154 #if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
155 	else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
156 		lsm6dso->handler_drdy_temp = handler;
157 		lsm6dso->trig_drdy_temp = trig;
158 		if (handler) {
159 			return lsm6dso_enable_t_int(dev, LSM6DSO_EN_BIT);
160 		} else {
161 			return lsm6dso_enable_t_int(dev, LSM6DSO_DIS_BIT);
162 		}
163 	}
164 #endif
165 
166 	return -ENOTSUP;
167 }
168 
169 /**
170  * lsm6dso_handle_interrupt - handle the drdy event
171  * read data and call handler if registered any
172  */
lsm6dso_handle_interrupt(const struct device * dev)173 static void lsm6dso_handle_interrupt(const struct device *dev)
174 {
175 	struct lsm6dso_data *lsm6dso = dev->data;
176 	const struct lsm6dso_config *cfg = dev->config;
177 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
178 	lsm6dso_status_reg_t status;
179 
180 	while (1) {
181 		if (lsm6dso_status_reg_get(ctx, &status) < 0) {
182 			LOG_DBG("failed reading status reg");
183 			return;
184 		}
185 
186 		if ((status.xlda == 0) && (status.gda == 0)
187 #if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
188 					&& (status.tda == 0)
189 #endif
190 					) {
191 			break;
192 		}
193 
194 		if ((status.xlda) && (lsm6dso->handler_drdy_acc != NULL)) {
195 			lsm6dso->handler_drdy_acc(dev, lsm6dso->trig_drdy_acc);
196 		}
197 
198 		if ((status.gda) && (lsm6dso->handler_drdy_gyr != NULL)) {
199 			lsm6dso->handler_drdy_gyr(dev, lsm6dso->trig_drdy_gyr);
200 		}
201 
202 #if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
203 		if ((status.tda) && (lsm6dso->handler_drdy_temp != NULL)) {
204 			lsm6dso->handler_drdy_temp(dev, lsm6dso->trig_drdy_temp);
205 		}
206 #endif
207 	}
208 
209 	gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
210 					GPIO_INT_EDGE_TO_ACTIVE);
211 }
212 
lsm6dso_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)213 static void lsm6dso_gpio_callback(const struct device *dev,
214 				    struct gpio_callback *cb, uint32_t pins)
215 {
216 	struct lsm6dso_data *lsm6dso =
217 		CONTAINER_OF(cb, struct lsm6dso_data, gpio_cb);
218 	const struct lsm6dso_config *cfg = lsm6dso->dev->config;
219 
220 	ARG_UNUSED(pins);
221 
222 	gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, GPIO_INT_DISABLE);
223 
224 #if defined(CONFIG_LSM6DSO_TRIGGER_OWN_THREAD)
225 	k_sem_give(&lsm6dso->gpio_sem);
226 #elif defined(CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD)
227 	k_work_submit(&lsm6dso->work);
228 #endif /* CONFIG_LSM6DSO_TRIGGER_OWN_THREAD */
229 }
230 
231 #ifdef CONFIG_LSM6DSO_TRIGGER_OWN_THREAD
lsm6dso_thread(void * p1,void * p2,void * p3)232 static void lsm6dso_thread(void *p1, void *p2, void *p3)
233 {
234 	ARG_UNUSED(p2);
235 	ARG_UNUSED(p3);
236 
237 	struct lsm6dso_data *lsm6dso = p1;
238 
239 	while (1) {
240 		k_sem_take(&lsm6dso->gpio_sem, K_FOREVER);
241 		lsm6dso_handle_interrupt(lsm6dso->dev);
242 	}
243 }
244 #endif /* CONFIG_LSM6DSO_TRIGGER_OWN_THREAD */
245 
246 #ifdef CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD
lsm6dso_work_cb(struct k_work * work)247 static void lsm6dso_work_cb(struct k_work *work)
248 {
249 	struct lsm6dso_data *lsm6dso =
250 		CONTAINER_OF(work, struct lsm6dso_data, work);
251 
252 	lsm6dso_handle_interrupt(lsm6dso->dev);
253 }
254 #endif /* CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD */
255 
lsm6dso_init_interrupt(const struct device * dev)256 int lsm6dso_init_interrupt(const struct device *dev)
257 {
258 	struct lsm6dso_data *lsm6dso = dev->data;
259 	const struct lsm6dso_config *cfg = dev->config;
260 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
261 	int ret;
262 
263 	/* setup data ready gpio interrupt (INT1 or INT2) */
264 	if (!gpio_is_ready_dt(&cfg->gpio_drdy)) {
265 		LOG_ERR("Cannot get pointer to drdy_gpio device");
266 		return -EINVAL;
267 	}
268 
269 #if defined(CONFIG_LSM6DSO_TRIGGER_OWN_THREAD)
270 	k_sem_init(&lsm6dso->gpio_sem, 0, K_SEM_MAX_LIMIT);
271 
272 	k_thread_create(&lsm6dso->thread, lsm6dso->thread_stack,
273 			CONFIG_LSM6DSO_THREAD_STACK_SIZE,
274 			lsm6dso_thread, lsm6dso,
275 			NULL, NULL, K_PRIO_COOP(CONFIG_LSM6DSO_THREAD_PRIORITY),
276 			0, K_NO_WAIT);
277 	k_thread_name_set(&lsm6dso->thread, "lsm6dso");
278 #elif defined(CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD)
279 	lsm6dso->work.handler = lsm6dso_work_cb;
280 #endif /* CONFIG_LSM6DSO_TRIGGER_OWN_THREAD */
281 
282 	ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT);
283 	if (ret < 0) {
284 		LOG_DBG("Could not configure gpio");
285 		return ret;
286 	}
287 
288 	gpio_init_callback(&lsm6dso->gpio_cb,
289 			   lsm6dso_gpio_callback,
290 			   BIT(cfg->gpio_drdy.pin));
291 
292 	if (gpio_add_callback(cfg->gpio_drdy.port, &lsm6dso->gpio_cb) < 0) {
293 		LOG_DBG("Could not set gpio callback");
294 		return -EIO;
295 	}
296 
297 
298 	/* set data ready mode on int1/int2 */
299 	LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed);
300 	lsm6dso_dataready_pulsed_t mode = cfg->drdy_pulsed ? LSM6DSO_DRDY_PULSED :
301 							     LSM6DSO_DRDY_LATCHED;
302 
303 	ret = lsm6dso_data_ready_mode_set(ctx, mode);
304 	if (ret < 0) {
305 		LOG_ERR("drdy_pulsed config error %d", (int)cfg->drdy_pulsed);
306 		return ret;
307 	}
308 
309 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
310 					       GPIO_INT_EDGE_TO_ACTIVE);
311 }
312