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