1 /* ST Microelectronics IIS2ICLX 2-axis accelerometer 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/iis2iclx.pdf
9 */
10
11 #define DT_DRV_COMPAT st_iis2iclx
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 "iis2iclx.h"
19
20 LOG_MODULE_DECLARE(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL);
21
22 #if defined(CONFIG_IIS2ICLX_ENABLE_TEMP)
23 /**
24 * iis2iclx_enable_t_int - TEMP enable selected int pin to generate interrupt
25 */
iis2iclx_enable_t_int(const struct device * dev,int enable)26 static int iis2iclx_enable_t_int(const struct device *dev, int enable)
27 {
28 const struct iis2iclx_config *cfg = dev->config;
29 iis2iclx_pin_int2_route_t int2_route;
30
31 if (enable) {
32 int16_t buf;
33
34 /* dummy read: re-trigger interrupt */
35 iis2iclx_temperature_raw_get((stmdev_ctx_t *)&cfg->ctx, &buf);
36 }
37
38 /* set interrupt (TEMP DRDY interrupt is only on INT2) */
39 if (cfg->int_pin == 1) {
40 return -EIO;
41 }
42
43 iis2iclx_read_reg((stmdev_ctx_t *)&cfg->ctx, IIS2ICLX_INT2_CTRL,
44 (uint8_t *)&int2_route.int2_ctrl, 1);
45 int2_route.int2_ctrl.int2_drdy_temp = enable;
46 return iis2iclx_write_reg((stmdev_ctx_t *)&cfg->ctx, IIS2ICLX_INT2_CTRL,
47 (uint8_t *)&int2_route.int2_ctrl, 1);
48 }
49 #endif
50
51 /**
52 * iis2iclx_enable_xl_int - XL enable selected int pin to generate interrupt
53 */
iis2iclx_enable_xl_int(const struct device * dev,int enable)54 static int iis2iclx_enable_xl_int(const struct device *dev, int enable)
55 {
56 const struct iis2iclx_config *cfg = dev->config;
57
58 if (enable) {
59 int16_t buf[3];
60
61 /* dummy read: re-trigger interrupt */
62 iis2iclx_acceleration_raw_get((stmdev_ctx_t *)&cfg->ctx, buf);
63 }
64
65 /* set interrupt */
66 if (cfg->int_pin == 1) {
67 iis2iclx_pin_int1_route_t int1_route;
68
69 iis2iclx_read_reg((stmdev_ctx_t *)&cfg->ctx, IIS2ICLX_INT1_CTRL,
70 (uint8_t *)&int1_route.int1_ctrl, 1);
71
72 int1_route.int1_ctrl.int1_drdy_xl = enable;
73 return iis2iclx_write_reg((stmdev_ctx_t *)&cfg->ctx,
74 IIS2ICLX_INT1_CTRL,
75 (uint8_t *)&int1_route.int1_ctrl, 1);
76 } else {
77 iis2iclx_pin_int2_route_t int2_route;
78
79 iis2iclx_read_reg((stmdev_ctx_t *)&cfg->ctx, IIS2ICLX_INT2_CTRL,
80 (uint8_t *)&int2_route.int2_ctrl, 1);
81 int2_route.int2_ctrl.int2_drdy_xl = enable;
82 return iis2iclx_write_reg((stmdev_ctx_t *)&cfg->ctx,
83 IIS2ICLX_INT2_CTRL,
84 (uint8_t *)&int2_route.int2_ctrl, 1);
85 }
86 }
87
88 /**
89 * iis2iclx_trigger_set - link external trigger to event data ready
90 */
iis2iclx_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)91 int iis2iclx_trigger_set(const struct device *dev,
92 const struct sensor_trigger *trig,
93 sensor_trigger_handler_t handler)
94 {
95 const struct iis2iclx_config *cfg = dev->config;
96 struct iis2iclx_data *iis2iclx = dev->data;
97
98 if (!cfg->trig_enabled) {
99 LOG_ERR("trigger_set op not supported");
100 return -ENOTSUP;
101 }
102
103 if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
104 iis2iclx->handler_drdy_acc = handler;
105 iis2iclx->trig_drdy_acc = trig;
106 if (handler) {
107 return iis2iclx_enable_xl_int(dev, IIS2ICLX_EN_BIT);
108 } else {
109 return iis2iclx_enable_xl_int(dev, IIS2ICLX_DIS_BIT);
110 }
111 }
112 #if defined(CONFIG_IIS2ICLX_ENABLE_TEMP)
113 else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
114 iis2iclx->handler_drdy_temp = handler;
115 iis2iclx->trig_drdy_temp = trig;
116 if (handler) {
117 return iis2iclx_enable_t_int(dev, IIS2ICLX_EN_BIT);
118 } else {
119 return iis2iclx_enable_t_int(dev, IIS2ICLX_DIS_BIT);
120 }
121 }
122 #endif
123
124 return -ENOTSUP;
125 }
126
127 /**
128 * iis2iclx_handle_interrupt - handle the drdy event
129 * read data and call handler if registered any
130 */
iis2iclx_handle_interrupt(const struct device * dev)131 static void iis2iclx_handle_interrupt(const struct device *dev)
132 {
133 struct iis2iclx_data *iis2iclx = dev->data;
134 const struct iis2iclx_config *cfg = dev->config;
135 iis2iclx_status_reg_t status;
136
137 while (1) {
138 if (iis2iclx_status_reg_get((stmdev_ctx_t *)&cfg->ctx,
139 &status) < 0) {
140 LOG_DBG("failed reading status reg");
141 return;
142 }
143
144 if (status.xlda == 0
145 #if defined(CONFIG_IIS2ICLX_ENABLE_TEMP)
146 && status.tda == 0
147 #endif
148 ) {
149 break;
150 }
151
152 if ((status.xlda) && (iis2iclx->handler_drdy_acc != NULL)) {
153 iis2iclx->handler_drdy_acc(dev, iis2iclx->trig_drdy_acc);
154 }
155
156 #if defined(CONFIG_IIS2ICLX_ENABLE_TEMP)
157 if ((status.tda) && (iis2iclx->handler_drdy_temp != NULL)) {
158 iis2iclx->handler_drdy_temp(dev, iis2iclx->trig_drdy_temp);
159 }
160 #endif
161 }
162
163 gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
164 GPIO_INT_EDGE_TO_ACTIVE);
165 }
166
iis2iclx_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)167 static void iis2iclx_gpio_callback(const struct device *dev,
168 struct gpio_callback *cb, uint32_t pins)
169 {
170 struct iis2iclx_data *iis2iclx =
171 CONTAINER_OF(cb, struct iis2iclx_data, gpio_cb);
172 const struct iis2iclx_config *cfg = iis2iclx->dev->config;
173
174 ARG_UNUSED(pins);
175
176 gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, GPIO_INT_DISABLE);
177
178 #if defined(CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD)
179 k_sem_give(&iis2iclx->gpio_sem);
180 #elif defined(CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD)
181 k_work_submit(&iis2iclx->work);
182 #endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */
183 }
184
185 #ifdef CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD
iis2iclx_thread(void * p1,void * p2,void * p3)186 static void iis2iclx_thread(void *p1, void *p2, void *p3)
187 {
188 ARG_UNUSED(p2);
189 ARG_UNUSED(p3);
190
191 struct iis2iclx_data *iis2iclx = p1;
192
193 while (1) {
194 k_sem_take(&iis2iclx->gpio_sem, K_FOREVER);
195 iis2iclx_handle_interrupt(iis2iclx->dev);
196 }
197 }
198 #endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */
199
200 #ifdef CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD
iis2iclx_work_cb(struct k_work * work)201 static void iis2iclx_work_cb(struct k_work *work)
202 {
203 struct iis2iclx_data *iis2iclx =
204 CONTAINER_OF(work, struct iis2iclx_data, work);
205
206 iis2iclx_handle_interrupt(iis2iclx->dev);
207 }
208 #endif /* CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD */
209
iis2iclx_init_interrupt(const struct device * dev)210 int iis2iclx_init_interrupt(const struct device *dev)
211 {
212 struct iis2iclx_data *iis2iclx = dev->data;
213 const struct iis2iclx_config *cfg = dev->config;
214 int ret;
215
216 /* setup data ready gpio interrupt (INT1 or INT2) */
217 if (!gpio_is_ready_dt(&cfg->gpio_drdy)) {
218 LOG_ERR("Cannot get pointer to drdy_gpio device");
219 return -EINVAL;
220 }
221
222 #if defined(CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD)
223 k_sem_init(&iis2iclx->gpio_sem, 0, K_SEM_MAX_LIMIT);
224
225 k_thread_create(&iis2iclx->thread, iis2iclx->thread_stack,
226 CONFIG_IIS2ICLX_THREAD_STACK_SIZE,
227 iis2iclx_thread,
228 iis2iclx, NULL, NULL,
229 K_PRIO_COOP(CONFIG_IIS2ICLX_THREAD_PRIORITY),
230 0, K_NO_WAIT);
231 #elif defined(CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD)
232 iis2iclx->work.handler = iis2iclx_work_cb;
233 #endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */
234
235 ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT);
236 if (ret < 0) {
237 LOG_ERR("Could not configure gpio");
238 return ret;
239 }
240
241 gpio_init_callback(&iis2iclx->gpio_cb,
242 iis2iclx_gpio_callback,
243 BIT(cfg->gpio_drdy.pin));
244
245 if (gpio_add_callback(cfg->gpio_drdy.port, &iis2iclx->gpio_cb) < 0) {
246 LOG_ERR("Could not set gpio callback");
247 return -EIO;
248 }
249
250 /* enable interrupt on int1/int2 in pulse mode */
251 if (iis2iclx_int_notification_set((stmdev_ctx_t *)&cfg->ctx,
252 IIS2ICLX_ALL_INT_PULSED) < 0) {
253 LOG_ERR("Could not set pulse mode");
254 return -EIO;
255 }
256
257 if (gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
258 GPIO_INT_EDGE_TO_ACTIVE) < 0) {
259 LOG_ERR("Could not configure interrupt");
260 return -EIO;
261 }
262
263 return 0;
264 }
265