1 /* ST Microelectronics ISM330DHCX 6-axis IMU sensor 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/ism330dhcx.pdf
9 */
10
11 #define DT_DRV_COMPAT st_ism330dhcx
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 "ism330dhcx.h"
19
20 LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
21
22 #if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
23 /**
24 * ism330dhcx_enable_t_int - TEMP enable selected int pin to generate interrupt
25 */
ism330dhcx_enable_t_int(const struct device * dev,int enable)26 static int ism330dhcx_enable_t_int(const struct device *dev, int enable)
27 {
28 const struct ism330dhcx_config *cfg = dev->config;
29 struct ism330dhcx_data *ism330dhcx = dev->data;
30 ism330dhcx_pin_int2_route_t int2_route;
31
32 if (enable) {
33 int16_t buf;
34
35 /* dummy read: re-trigger interrupt */
36 ism330dhcx_temperature_raw_get(ism330dhcx->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 ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
45 (uint8_t *)&int2_route.int2_ctrl, 1);
46 int2_route.int2_ctrl.int2_drdy_temp = enable;
47 return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
48 (uint8_t *)&int2_route.int2_ctrl, 1);
49 }
50 #endif
51
52 /**
53 * ism330dhcx_enable_xl_int - XL enable selected int pin to generate interrupt
54 */
ism330dhcx_enable_xl_int(const struct device * dev,int enable)55 static int ism330dhcx_enable_xl_int(const struct device *dev, int enable)
56 {
57 const struct ism330dhcx_config *cfg = dev->config;
58 struct ism330dhcx_data *ism330dhcx = dev->data;
59
60 if (enable) {
61 int16_t buf[3];
62
63 /* dummy read: re-trigger interrupt */
64 ism330dhcx_acceleration_raw_get(ism330dhcx->ctx, buf);
65 }
66
67 /* set interrupt */
68 if (cfg->int_pin == 1) {
69 ism330dhcx_pin_int1_route_t int1_route;
70
71 ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
72 (uint8_t *)&int1_route.int1_ctrl, 1);
73
74 int1_route.int1_ctrl.int1_drdy_xl = enable;
75 return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
76 (uint8_t *)&int1_route.int1_ctrl, 1);
77 } else {
78 ism330dhcx_pin_int2_route_t int2_route;
79
80 ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
81 (uint8_t *)&int2_route.int2_ctrl, 1);
82 int2_route.int2_ctrl.int2_drdy_xl = enable;
83 return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
84 (uint8_t *)&int2_route.int2_ctrl, 1);
85 }
86 }
87
88 /**
89 * ism330dhcx_enable_g_int - Gyro enable selected int pin to generate interrupt
90 */
ism330dhcx_enable_g_int(const struct device * dev,int enable)91 static int ism330dhcx_enable_g_int(const struct device *dev, int enable)
92 {
93 const struct ism330dhcx_config *cfg = dev->config;
94 struct ism330dhcx_data *ism330dhcx = dev->data;
95
96 if (enable) {
97 int16_t buf[3];
98
99 /* dummy read: re-trigger interrupt */
100 ism330dhcx_angular_rate_raw_get(ism330dhcx->ctx, buf);
101 }
102
103 /* set interrupt */
104 if (cfg->int_pin == 1) {
105 ism330dhcx_pin_int1_route_t int1_route;
106
107 ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
108 (uint8_t *)&int1_route.int1_ctrl, 1);
109 int1_route.int1_ctrl.int1_drdy_g = enable;
110 return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
111 (uint8_t *)&int1_route.int1_ctrl, 1);
112 } else {
113 ism330dhcx_pin_int2_route_t int2_route;
114
115 ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
116 (uint8_t *)&int2_route.int2_ctrl, 1);
117 int2_route.int2_ctrl.int2_drdy_g = enable;
118 return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
119 (uint8_t *)&int2_route.int2_ctrl, 1);
120 }
121 }
122
123 /**
124 * ism330dhcx_trigger_set - link external trigger to event data ready
125 */
ism330dhcx_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)126 int ism330dhcx_trigger_set(const struct device *dev,
127 const struct sensor_trigger *trig,
128 sensor_trigger_handler_t handler)
129 {
130 struct ism330dhcx_data *ism330dhcx = dev->data;
131 const struct ism330dhcx_config *cfg = dev->config;
132
133 if (!cfg->drdy_gpio.port) {
134 return -ENOTSUP;
135 }
136
137 if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
138 ism330dhcx->handler_drdy_acc = handler;
139 ism330dhcx->trig_drdy_acc = trig;
140 if (handler) {
141 return ism330dhcx_enable_xl_int(dev, ISM330DHCX_EN_BIT);
142 } else {
143 return ism330dhcx_enable_xl_int(dev, ISM330DHCX_DIS_BIT);
144 }
145 } else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
146 ism330dhcx->handler_drdy_gyr = handler;
147 ism330dhcx->trig_drdy_gyr = trig;
148 if (handler) {
149 return ism330dhcx_enable_g_int(dev, ISM330DHCX_EN_BIT);
150 } else {
151 return ism330dhcx_enable_g_int(dev, ISM330DHCX_DIS_BIT);
152 }
153 }
154 #if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
155 else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
156 ism330dhcx->handler_drdy_temp = handler;
157 ism330dhcx->trig_drdy_temp = trig;
158 if (handler) {
159 return ism330dhcx_enable_t_int(dev, ISM330DHCX_EN_BIT);
160 } else {
161 return ism330dhcx_enable_t_int(dev, ISM330DHCX_DIS_BIT);
162 }
163 }
164 #endif
165
166 return -ENOTSUP;
167 }
168
169 /**
170 * ism330dhcx_handle_interrupt - handle the drdy event
171 * read data and call handler if registered any
172 */
ism330dhcx_handle_interrupt(const struct device * dev)173 static void ism330dhcx_handle_interrupt(const struct device *dev)
174 {
175 struct ism330dhcx_data *ism330dhcx = dev->data;
176 const struct ism330dhcx_config *cfg = dev->config;
177 ism330dhcx_status_reg_t status;
178
179 while (1) {
180 if (ism330dhcx_status_reg_get(ism330dhcx->ctx, &status) < 0) {
181 LOG_DBG("failed reading status reg");
182 return;
183 }
184
185 if ((status.xlda == 0) && (status.gda == 0)
186 #if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
187 && (status.tda == 0)
188 #endif
189 ) {
190 break;
191 }
192
193 if ((status.xlda) && (ism330dhcx->handler_drdy_acc != NULL)) {
194 ism330dhcx->handler_drdy_acc(dev, ism330dhcx->trig_drdy_acc);
195 }
196
197 if ((status.gda) && (ism330dhcx->handler_drdy_gyr != NULL)) {
198 ism330dhcx->handler_drdy_gyr(dev, ism330dhcx->trig_drdy_gyr);
199 }
200
201 #if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
202 if ((status.tda) && (ism330dhcx->handler_drdy_temp != NULL)) {
203 ism330dhcx->handler_drdy_temp(dev, ism330dhcx->trig_drdy_temp);
204 }
205 #endif
206 }
207
208 gpio_pin_interrupt_configure_dt(&cfg->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE);
209 }
210
ism330dhcx_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)211 static void ism330dhcx_gpio_callback(const struct device *dev,
212 struct gpio_callback *cb, uint32_t pins)
213 {
214 struct ism330dhcx_data *ism330dhcx =
215 CONTAINER_OF(cb, struct ism330dhcx_data, gpio_cb);
216 const struct ism330dhcx_config *cfg = ism330dhcx->dev->config;
217
218 ARG_UNUSED(pins);
219
220 gpio_pin_interrupt_configure_dt(&cfg->drdy_gpio, GPIO_INT_DISABLE);
221
222 #if defined(CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD)
223 k_sem_give(&ism330dhcx->gpio_sem);
224 #elif defined(CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD)
225 k_work_submit(&ism330dhcx->work);
226 #endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
227 }
228
229 #ifdef CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD
ism330dhcx_thread(void * p1,void * p2,void * p3)230 static void ism330dhcx_thread(void *p1, void *p2, void *p3)
231 {
232 ARG_UNUSED(p2);
233 ARG_UNUSED(p3);
234
235 struct ism330dhcx_data *ism330dhcx = p1;
236
237 while (1) {
238 k_sem_take(&ism330dhcx->gpio_sem, K_FOREVER);
239 ism330dhcx_handle_interrupt(ism330dhcx->dev);
240 }
241 }
242 #endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
243
244 #ifdef CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD
ism330dhcx_work_cb(struct k_work * work)245 static void ism330dhcx_work_cb(struct k_work *work)
246 {
247 struct ism330dhcx_data *ism330dhcx =
248 CONTAINER_OF(work, struct ism330dhcx_data, work);
249
250 ism330dhcx_handle_interrupt(ism330dhcx->dev);
251 }
252 #endif /* CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD */
253
ism330dhcx_init_interrupt(const struct device * dev)254 int ism330dhcx_init_interrupt(const struct device *dev)
255 {
256 struct ism330dhcx_data *ism330dhcx = dev->data;
257 const struct ism330dhcx_config *cfg = dev->config;
258 int ret;
259
260 if (!gpio_is_ready_dt(&cfg->drdy_gpio)) {
261 LOG_ERR("GPIO device not ready");
262 return -ENODEV;
263 }
264
265 #if defined(CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD)
266 k_sem_init(&ism330dhcx->gpio_sem, 0, K_SEM_MAX_LIMIT);
267
268 k_thread_create(&ism330dhcx->thread, ism330dhcx->thread_stack,
269 CONFIG_ISM330DHCX_THREAD_STACK_SIZE,
270 ism330dhcx_thread,
271 ism330dhcx, NULL, NULL,
272 K_PRIO_COOP(CONFIG_ISM330DHCX_THREAD_PRIORITY),
273 0, K_NO_WAIT);
274 #elif defined(CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD)
275 ism330dhcx->work.handler = ism330dhcx_work_cb;
276 #endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
277
278 ret = gpio_pin_configure_dt(&cfg->drdy_gpio, GPIO_INPUT);
279 if (ret < 0) {
280 LOG_ERR("Could not configure gpio");
281 return ret;
282 }
283
284 gpio_init_callback(&ism330dhcx->gpio_cb, ism330dhcx_gpio_callback, BIT(cfg->drdy_gpio.pin));
285
286 if (gpio_add_callback(cfg->drdy_gpio.port, &ism330dhcx->gpio_cb) < 0) {
287 LOG_ERR("Could not set gpio callback");
288 return -EIO;
289 }
290
291 /* enable interrupt on int1/int2 in pulse mode */
292 if (ism330dhcx_data_ready_mode_set(ism330dhcx->ctx,
293 ISM330DHCX_DRDY_PULSED) < 0) {
294 LOG_ERR("Could not set pulse mode");
295 return -EIO;
296 }
297
298 return gpio_pin_interrupt_configure_dt(&cfg->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE);
299 }
300