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