1 /*
2  * Copyright (c) 2016 Freescale Semiconductor, Inc.
3  * Copyright (c) 2018 Phytec Messtechnik GmbH
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT nxp_fxos8700
9 
10 #include "fxos8700.h"
11 #include <zephyr/logging/log.h>
12 
13 LOG_MODULE_DECLARE(FXOS8700, CONFIG_SENSOR_LOG_LEVEL);
14 
fxos8700_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pin_mask)15 static void fxos8700_gpio_callback(const struct device *dev,
16 				   struct gpio_callback *cb,
17 				   uint32_t pin_mask)
18 {
19 	struct fxos8700_data *data =
20 		CONTAINER_OF(cb, struct fxos8700_data, gpio_cb);
21 	const struct fxos8700_config *config = data->dev->config;
22 
23 	if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) {
24 		return;
25 	}
26 
27 	gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_DISABLE);
28 
29 #if defined(CONFIG_FXOS8700_TRIGGER_OWN_THREAD)
30 	k_sem_give(&data->trig_sem);
31 #elif defined(CONFIG_FXOS8700_TRIGGER_GLOBAL_THREAD)
32 	k_work_submit(&data->work);
33 #endif
34 }
35 
fxos8700_handle_drdy_int(const struct device * dev)36 static int fxos8700_handle_drdy_int(const struct device *dev)
37 {
38 	struct fxos8700_data *data = dev->data;
39 
40 	if (data->drdy_handler) {
41 		data->drdy_handler(dev, data->drdy_trig);
42 	}
43 
44 	return 0;
45 }
46 
47 #ifdef CONFIG_FXOS8700_PULSE
fxos8700_handle_pulse_int(const struct device * dev)48 static int fxos8700_handle_pulse_int(const struct device *dev)
49 {
50 	const struct fxos8700_config *config = dev->config;
51 	struct fxos8700_data *data = dev->data;
52 	sensor_trigger_handler_t handler = NULL;
53 	const struct sensor_trigger *trig = NULL;
54 	uint8_t pulse_source;
55 
56 	k_sem_take(&data->sem, K_FOREVER);
57 
58 	if (config->ops->byte_read(dev, FXOS8700_REG_PULSE_SRC,
59 				   &pulse_source)) {
60 		LOG_ERR("Could not read pulse source");
61 	}
62 
63 	k_sem_give(&data->sem);
64 
65 	if (pulse_source & FXOS8700_PULSE_SRC_DPE) {
66 		handler = data->double_tap_handler;
67 		trig = data->double_tap_trig;
68 	} else {
69 		handler = data->tap_handler;
70 		trig = data->tap_trig;
71 	}
72 
73 	if (handler) {
74 		handler(dev, trig);
75 	}
76 
77 	return 0;
78 }
79 #endif
80 
81 #ifdef CONFIG_FXOS8700_MOTION
fxos8700_handle_motion_int(const struct device * dev)82 static int fxos8700_handle_motion_int(const struct device *dev)
83 {
84 	const struct fxos8700_config *config = dev->config;
85 	struct fxos8700_data *data = dev->data;
86 	sensor_trigger_handler_t handler = data->motion_handler;
87 	uint8_t motion_source;
88 
89 	k_sem_take(&data->sem, K_FOREVER);
90 
91 	if (config->ops->byte_read(dev, FXOS8700_REG_FF_MT_SRC,
92 				   &motion_source)) {
93 		LOG_ERR("Could not read pulse source");
94 	}
95 
96 	k_sem_give(&data->sem);
97 
98 	if (handler) {
99 		LOG_DBG("FF_MT_SRC 0x%x", motion_source);
100 		handler(dev, data->motion_trig);
101 	}
102 
103 	return 0;
104 }
105 #endif
106 
107 #ifdef CONFIG_FXOS8700_MAG_VECM
fxos8700_handle_m_vecm_int(const struct device * dev)108 static int fxos8700_handle_m_vecm_int(const struct device *dev)
109 {
110 	struct fxos8700_data *data = dev->data;
111 
112 	if (data->m_vecm_handler) {
113 		data->m_vecm_handler(dev, data->m_vecm_trig);
114 	}
115 
116 	return 0;
117 }
118 #endif
119 
fxos8700_handle_int(const struct device * dev)120 static void fxos8700_handle_int(const struct device *dev)
121 {
122 	const struct fxos8700_config *config = dev->config;
123 	struct fxos8700_data *data = dev->data;
124 	uint8_t int_source;
125 
126 	/* Interrupt status register */
127 	k_sem_take(&data->sem, K_FOREVER);
128 
129 	if (config->ops->byte_read(dev, FXOS8700_REG_INT_SOURCE,
130 				   &int_source)) {
131 		LOG_ERR("Could not read interrupt source");
132 		int_source = 0U;
133 	}
134 
135 	k_sem_give(&data->sem);
136 
137 	if (int_source & FXOS8700_DRDY_MASK) {
138 		fxos8700_handle_drdy_int(dev);
139 	}
140 #ifdef CONFIG_FXOS8700_PULSE
141 	if (int_source & FXOS8700_PULSE_MASK) {
142 		fxos8700_handle_pulse_int(dev);
143 	}
144 #endif
145 #ifdef CONFIG_FXOS8700_MOTION
146 	if (int_source & FXOS8700_MOTION_MASK) {
147 		fxos8700_handle_motion_int(dev);
148 	}
149 #endif
150 #ifdef CONFIG_FXOS8700_MAG_VECM
151 	/* Magnetometer interrupt source register */
152 	k_sem_take(&data->sem, K_FOREVER);
153 
154 	if (config->ops->byte_read(dev, FXOS8700_REG_M_INT_SRC,
155 				   &int_source)) {
156 		LOG_ERR("Could not read magnetometer interrupt source");
157 		int_source = 0U;
158 	}
159 
160 	k_sem_give(&data->sem);
161 
162 	if (int_source & FXOS8700_VECM_MASK) {
163 		fxos8700_handle_m_vecm_int(dev);
164 	}
165 #endif
166 
167 	gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
168 }
169 
170 #ifdef CONFIG_FXOS8700_TRIGGER_OWN_THREAD
fxos8700_thread_main(void * p1,void * p2,void * p3)171 static void fxos8700_thread_main(void *p1, void *p2, void *p3)
172 {
173 	ARG_UNUSED(p2);
174 	ARG_UNUSED(p3);
175 
176 	struct fxos8700_data *data = p1;
177 
178 	while (true) {
179 		k_sem_take(&data->trig_sem, K_FOREVER);
180 		fxos8700_handle_int(data->dev);
181 	}
182 }
183 #endif
184 
185 #ifdef CONFIG_FXOS8700_TRIGGER_GLOBAL_THREAD
fxos8700_work_handler(struct k_work * work)186 static void fxos8700_work_handler(struct k_work *work)
187 {
188 	struct fxos8700_data *data =
189 		CONTAINER_OF(work, struct fxos8700_data, work);
190 
191 	fxos8700_handle_int(data->dev);
192 }
193 #endif
194 
fxos8700_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)195 int fxos8700_trigger_set(const struct device *dev,
196 			 const struct sensor_trigger *trig,
197 			 sensor_trigger_handler_t handler)
198 {
199 	const struct fxos8700_config *config = dev->config;
200 	struct fxos8700_data *data = dev->data;
201 	enum fxos8700_power power = FXOS8700_POWER_STANDBY;
202 	uint8_t mask;
203 	int ret = 0;
204 
205 	k_sem_take(&data->sem, K_FOREVER);
206 
207 	switch (trig->type) {
208 	case SENSOR_TRIG_DATA_READY:
209 		mask = FXOS8700_DRDY_MASK;
210 		data->drdy_handler = handler;
211 		data->drdy_trig = trig;
212 		break;
213 #ifdef CONFIG_FXOS8700_PULSE
214 	case SENSOR_TRIG_TAP:
215 		mask = FXOS8700_PULSE_MASK;
216 		data->tap_handler = handler;
217 		data->tap_trig = trig;
218 		break;
219 	case SENSOR_TRIG_DOUBLE_TAP:
220 		mask = FXOS8700_PULSE_MASK;
221 		data->double_tap_handler = handler;
222 		data->double_tap_trig = trig;
223 		break;
224 #endif
225 #ifdef CONFIG_FXOS8700_MOTION
226 	case SENSOR_TRIG_DELTA:
227 		mask = FXOS8700_MOTION_MASK;
228 		data->motion_handler = handler;
229 		data->motion_trig = trig;
230 		break;
231 #endif
232 #ifdef CONFIG_FXOS8700_MAG_VECM
233 	case FXOS8700_TRIG_M_VECM:
234 		mask = FXOS8700_VECM_MASK;
235 		data->m_vecm_handler = handler;
236 		data->m_vecm_trig = trig;
237 		break;
238 #endif
239 	default:
240 		LOG_ERR("Unsupported sensor trigger");
241 		ret = -ENOTSUP;
242 		goto exit;
243 	}
244 
245 	/* The sensor must be in standby mode when writing the configuration
246 	 * registers, therefore get the current power mode so we can restore it
247 	 * later.
248 	 */
249 	if (fxos8700_get_power(dev, &power)) {
250 		LOG_ERR("Could not get power mode");
251 		ret = -EIO;
252 		goto exit;
253 	}
254 
255 	/* Put the sensor in standby mode */
256 	if (fxos8700_set_power(dev, FXOS8700_POWER_STANDBY)) {
257 		LOG_ERR("Could not set standby mode");
258 		ret = -EIO;
259 		goto exit;
260 	}
261 
262 	/* Configure the sensor interrupt */
263 	if (config->ops->reg_field_update(dev, FXOS8700_REG_CTRLREG4, mask,
264 					  handler ? mask : 0)) {
265 		LOG_ERR("Could not configure interrupt");
266 		ret = -EIO;
267 		goto exit;
268 	}
269 
270 	/* Restore the previous power mode */
271 	if (fxos8700_set_power(dev, power)) {
272 		LOG_ERR("Could not restore power mode");
273 		ret = -EIO;
274 		goto exit;
275 	}
276 
277 exit:
278 	k_sem_give(&data->sem);
279 
280 	return ret;
281 }
282 
283 #ifdef CONFIG_FXOS8700_PULSE
fxos8700_pulse_init(const struct device * dev)284 static int fxos8700_pulse_init(const struct device *dev)
285 {
286 	const struct fxos8700_config *config = dev->config;
287 
288 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_CFG,
289 				    config->pulse_cfg)) {
290 		return -EIO;
291 	}
292 
293 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSX,
294 				    config->pulse_ths[0])) {
295 		return -EIO;
296 	}
297 
298 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSY,
299 				    config->pulse_ths[1])) {
300 		return -EIO;
301 	}
302 
303 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSZ,
304 				    config->pulse_ths[2])) {
305 		return -EIO;
306 	}
307 
308 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_TMLT,
309 				    config->pulse_tmlt)) {
310 		return -EIO;
311 	}
312 
313 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_LTCY,
314 				    config->pulse_ltcy)) {
315 		return -EIO;
316 	}
317 
318 	if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_WIND,
319 				    config->pulse_wind)) {
320 		return -EIO;
321 	}
322 
323 	return 0;
324 }
325 #endif
326 
327 #ifdef CONFIG_FXOS8700_MOTION
fxos8700_motion_init(const struct device * dev)328 static int fxos8700_motion_init(const struct device *dev)
329 {
330 	const struct fxos8700_config *config = dev->config;
331 
332 	/* Set Mode 4, Motion detection with ELE = 1, OAE = 1 */
333 	if (config->ops->byte_write(dev,
334 				    FXOS8700_REG_FF_MT_CFG,
335 				    FXOS8700_FF_MT_CFG_ELE |
336 				    FXOS8700_FF_MT_CFG_OAE |
337 				    FXOS8700_FF_MT_CFG_ZEFE |
338 				    FXOS8700_FF_MT_CFG_YEFE |
339 				    FXOS8700_FF_MT_CFG_XEFE)) {
340 		return -EIO;
341 	}
342 
343 	/* Set motion threshold to maximimum */
344 	if (config->ops->byte_write(dev, FXOS8700_REG_FF_MT_THS,
345 				    FXOS8700_REG_FF_MT_THS)) {
346 		return -EIO;
347 	}
348 
349 	return 0;
350 }
351 #endif
352 
353 #ifdef CONFIG_FXOS8700_MAG_VECM
fxos8700_m_vecm_init(const struct device * dev)354 static int fxos8700_m_vecm_init(const struct device *dev)
355 {
356 	const struct fxos8700_config *config = dev->config;
357 	uint8_t m_vecm_cfg = config->mag_vecm_cfg;
358 
359 	/* Route the interrupt to INT1 pin */
360 #if CONFIG_FXOS8700_MAG_VECM_INT1
361 	m_vecm_cfg |= FXOS8700_MAG_VECM_INT1_MASK;
362 #endif
363 
364 	/* Set magnetic vector-magnitude function */
365 	if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_CFG,
366 				    m_vecm_cfg)) {
367 		LOG_ERR("Could not set magnetic vector-magnitude function");
368 		return -EIO;
369 	}
370 
371 	/* Set magnetic vector-magnitude function threshold values:
372 	 * handle both MSB and LSB registers
373 	 */
374 	if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_THS_MSB,
375 				    config->mag_vecm_ths[0])) {
376 		LOG_ERR("Could not set magnetic vector-magnitude function threshold MSB value");
377 		return -EIO;
378 	}
379 
380 	if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_THS_LSB,
381 				    config->mag_vecm_ths[1])) {
382 		LOG_ERR("Could not set magnetic vector-magnitude function threshold LSB value");
383 		return -EIO;
384 	}
385 
386 	return 0;
387 }
388 #endif
389 
fxos8700_trigger_init(const struct device * dev)390 int fxos8700_trigger_init(const struct device *dev)
391 {
392 	const struct fxos8700_config *config = dev->config;
393 	struct fxos8700_data *data = dev->data;
394 	uint8_t ctrl_reg5;
395 	int ret;
396 
397 	data->dev = dev;
398 
399 #if defined(CONFIG_FXOS8700_TRIGGER_OWN_THREAD)
400 	k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
401 	k_thread_create(&data->thread, data->thread_stack,
402 			CONFIG_FXOS8700_THREAD_STACK_SIZE,
403 			fxos8700_thread_main,
404 			data, NULL, NULL,
405 			K_PRIO_COOP(CONFIG_FXOS8700_THREAD_PRIORITY),
406 			0, K_NO_WAIT);
407 #elif defined(CONFIG_FXOS8700_TRIGGER_GLOBAL_THREAD)
408 	data->work.handler = fxos8700_work_handler;
409 #endif
410 
411 	/* Route the interrupts to INT1/INT2 pins */
412 	ctrl_reg5 = 0U;
413 #if CONFIG_FXOS8700_DRDY_INT1
414 	ctrl_reg5 |= FXOS8700_DRDY_MASK;
415 #endif
416 #if CONFIG_FXOS8700_PULSE_INT1
417 	ctrl_reg5 |= FXOS8700_PULSE_MASK;
418 #endif
419 #if CONFIG_FXOS8700_MOTION_INT1
420 	ctrl_reg5 |= FXOS8700_MOTION_MASK;
421 #endif
422 
423 	if (config->ops->byte_write(dev, FXOS8700_REG_CTRLREG5,
424 				    ctrl_reg5)) {
425 		LOG_ERR("Could not configure interrupt pin routing");
426 		return -EIO;
427 	}
428 
429 #ifdef CONFIG_FXOS8700_PULSE
430 	if (fxos8700_pulse_init(dev)) {
431 		LOG_ERR("Could not configure pulse");
432 		return -EIO;
433 	}
434 #endif
435 #ifdef CONFIG_FXOS8700_MOTION
436 	if (fxos8700_motion_init(dev)) {
437 		LOG_ERR("Could not configure motion");
438 		return -EIO;
439 	}
440 #endif
441 #ifdef CONFIG_FXOS8700_MAG_VECM
442 	if (fxos8700_m_vecm_init(dev)) {
443 		LOG_ERR("Could not configure magnetic vector-magnitude");
444 		return -EIO;
445 	}
446 #endif
447 
448 	if (!gpio_is_ready_dt(&config->int_gpio)) {
449 		LOG_ERR("GPIO device not ready");
450 		return -ENODEV;
451 	}
452 
453 	ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
454 	if (ret < 0) {
455 		return ret;
456 	}
457 
458 	gpio_init_callback(&data->gpio_cb, fxos8700_gpio_callback,
459 			   BIT(config->int_gpio.pin));
460 
461 	ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb);
462 	if (ret < 0) {
463 		return ret;
464 	}
465 
466 	ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
467 	if (ret < 0) {
468 		return ret;
469 	}
470 
471 	return 0;
472 }
473