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