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