3 Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * @brief Provides the interface to setup and handle an accelerometer.
26 * @brief Accelerometer setup and handling methods for Invensense MPU6050
29 /* -------------------------------------------------------------------------- */
31 #include <linux/i2c.h>
32 #include <linux/module.h>
33 #include <linux/moduleparam.h>
34 #include <linux/kernel.h>
35 #include <linux/errno.h>
36 #include <linux/delay.h>
37 #include <linux/slab.h>
41 #include <linux/mpu.h>
42 #include "mpu6050b1.h"
46 #define MPL_LOG_TAG "MPL-acc"
48 /* -------------------------------------------------------------------------- */
50 struct mpu6050_config {
51 unsigned int odr; /**< output data rate 1/1000 Hz */
52 unsigned int fsr; /**< full scale range mg */
53 unsigned int ths; /**< mot/no-mot thseshold mg */
54 unsigned int dur; /**< mot/no-mot duration ms */
55 unsigned int irq_type; /**< irq type */
58 struct mpu6050_private_data {
59 struct mpu6050_config suspend;
60 struct mpu6050_config resume;
61 struct mldl_cfg *mldl_cfg_ref;
64 /* -------------------------------------------------------------------------- */
66 static int mpu6050_set_mldl_cfg_ref(void *mlsl_handle,
67 struct ext_slave_platform_data *pdata,
68 struct mldl_cfg *mldl_cfg_ref)
70 struct mpu6050_private_data *private_data =
71 (struct mpu6050_private_data *)pdata->private_data;
72 private_data->mldl_cfg_ref = mldl_cfg_ref;
75 static int mpu6050_set_lp_mode(void *mlsl_handle,
76 struct ext_slave_platform_data *pdata,
77 unsigned char lpa_freq)
80 /* Reducing the duration setting for lp mode */
82 inv_serial_single_write(mlsl_handle, pdata->address,
83 MPUREG_ACCEL_MOT_DUR, b);
84 /* Setting the cycle bit and LPA wake up freq */
85 inv_serial_read(mlsl_handle, pdata->address, MPUREG_PWR_MGMT_1, 1,
87 b |= BIT_CYCLE | BIT_PD_PTAT;
88 inv_serial_single_write(mlsl_handle, pdata->address,
91 inv_serial_read(mlsl_handle, pdata->address,
92 MPUREG_PWR_MGMT_2, 1, &b);
93 b |= lpa_freq & BITS_LPA_WAKE_CTRL;
94 inv_serial_single_write(mlsl_handle, pdata->address,
95 MPUREG_PWR_MGMT_2, b);
100 static int mpu6050_set_fp_mode(void *mlsl_handle,
101 struct ext_slave_platform_data *pdata)
104 struct mpu6050_private_data *private_data =
105 (struct mpu6050_private_data *)pdata->private_data;
106 /* Resetting the cycle bit and LPA wake up freq */
107 inv_serial_read(mlsl_handle, pdata->address,
108 MPUREG_PWR_MGMT_1, 1, &b);
109 b &= ~BIT_CYCLE & ~BIT_PD_PTAT;
110 inv_serial_single_write(mlsl_handle, pdata->address,
111 MPUREG_PWR_MGMT_1, b);
112 inv_serial_read(mlsl_handle, pdata->address,
113 MPUREG_PWR_MGMT_2, 1, &b);
114 b &= ~BITS_LPA_WAKE_CTRL;
115 inv_serial_single_write(mlsl_handle, pdata->address,
116 MPUREG_PWR_MGMT_2, b);
117 /* Resetting the duration setting for fp mode */
118 b = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
119 inv_serial_single_write(mlsl_handle, pdata->address,
120 MPUREG_ACCEL_MOT_DUR, b);
125 * Record the odr for use in computing duration values.
127 * @param config Config to set, suspend or resume structure
128 * @param odr output data rate in 1/1000 hz
130 static int mpu6050_set_odr(void *mlsl_handle,
131 struct ext_slave_platform_data *pdata,
132 struct mpu6050_config *config, long apply, long odr)
136 unsigned char lpa_freq = 1; /* Default value */
139 struct mpu6050_private_data *private_data =
140 (struct mpu6050_private_data *)pdata->private_data;
141 struct mldl_cfg *mldl_cfg_ref =
142 (struct mldl_cfg *)private_data->mldl_cfg_ref;
146 inv_mpu_get_sampling_rate_hz(mldl_cfg_ref->mpu_gyro_cfg)
147 * (mldl_cfg_ref->mpu_gyro_cfg->divider + 1);
149 /* have no reference to mldl_cfg => assume base rate is 1000 */
154 total_divider = (base / odr) - 1;
155 /* final odr MAY be different from requested odr due to
156 integer truncation */
157 config->odr = base / (total_divider + 1);
163 /* if the DMP and/or gyros are on, don't set the ODR =>
164 the DMP/gyro mldl_cfg->divider setting will handle it */
167 !(mldl_cfg_ref->inv_mpu_cfg->requested_sensors &
168 INV_DMP_PROCESSOR))) {
169 result = inv_serial_single_write(mlsl_handle, pdata->address,
171 (unsigned char)total_divider);
173 LOG_RESULT_LOCATION(result);
176 MPL_LOGI("ODR : %d mHz\n", config->odr);
178 /* Decide whether to put accel in LP mode or pull out of LP mode
182 lpa_freq = BITS_LPA_WAKE_1HZ;
185 lpa_freq = BITS_LPA_WAKE_2HZ;
188 lpa_freq = BITS_LPA_WAKE_10HZ;
191 lpa_freq = BITS_LPA_WAKE_40HZ;
194 inv_serial_read(mlsl_handle, pdata->address,
195 MPUREG_PWR_MGMT_1, 1, &b);
197 if (b == BIT_CYCLE) {
198 MPL_LOGI(" Accel LP - > FP mode. \n ");
199 mpu6050_set_fp_mode(mlsl_handle, pdata);
202 /* If lpa_freq default value was changed, set into LP mode */
204 MPL_LOGI(" Accel FP - > LP mode. \n ");
205 mpu6050_set_lp_mode(mlsl_handle, pdata, lpa_freq);
210 static int mpu6050_set_fsr(void *mlsl_handle,
211 struct ext_slave_platform_data *pdata,
212 struct mpu6050_config *config, long apply, long fsr)
214 unsigned char fsr_mask;
220 } else if (fsr <= 4000) {
223 } else if (fsr <= 8000) {
226 } else { /* fsr = [8001, oo) */
233 result = inv_serial_read(mlsl_handle, pdata->address,
234 MPUREG_ACCEL_CONFIG, 1, ®);
236 LOG_RESULT_LOCATION(result);
239 result = inv_serial_single_write(mlsl_handle, pdata->address,
243 LOG_RESULT_LOCATION(result);
246 MPL_LOGV("FSR: %d\n", config->fsr);
251 static int mpu6050_set_irq(void *mlsl_handle,
252 struct ext_slave_platform_data *pdata,
253 struct mpu6050_config *config, long apply,
257 /* HACK, no need for interrupts for MPU6050 accel
258 - use of soft interrupt is required */
261 case MPU_SLAVE_IRQ_TYPE_DATA_READY:
262 config->irq_type = irq_type;
263 reg_int_cfg = BIT_RAW_RDY_EN;
265 /* todo: add MOTION, NO_MOTION, and FREEFALL */
266 case MPU_SLAVE_IRQ_TYPE_NONE:
267 /* Do nothing, not even set the interrupt because it is
268 shared with the gyro */
269 config->irq_type = irq_type;
272 return INV_ERROR_INVALID_PARAMETER;
276 result = inv_serial_single_write(mlsl_handle, pdata->address,
280 LOG_RESULT_LOCATION(result);
283 MPL_LOGV("irq_type: %d\n", config->irq_type);
290 static int mpu6050_set_ths(void *mlsl_handle,
291 struct ext_slave_platform_data *slave,
292 struct mpu6050_config *config, long apply, long ths)
298 MPL_LOGV("THS: %d\n", config->ths);
302 static int mpu6050_set_dur(void *mlsl_handle,
303 struct ext_slave_platform_data *slave,
304 struct mpu6050_config *config, long apply, long dur)
310 MPL_LOGV("DUR: %d\n", config->dur);
315 static int mpu6050_init(void *mlsl_handle,
316 struct ext_slave_descr *slave,
317 struct ext_slave_platform_data *pdata)
320 struct mpu6050_private_data *private_data;
323 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
326 return INV_ERROR_MEMORY_EXAUSTED;
328 pdata->private_data = private_data;
330 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
333 LOG_RESULT_LOCATION(result);
336 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
339 LOG_RESULT_LOCATION(result);
342 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend,
345 LOG_RESULT_LOCATION(result);
348 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
351 LOG_RESULT_LOCATION(result);
355 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
356 false, MPU_SLAVE_IRQ_TYPE_NONE);
358 LOG_RESULT_LOCATION(result);
361 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
362 false, MPU_SLAVE_IRQ_TYPE_NONE);
364 LOG_RESULT_LOCATION(result);
368 result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend,
371 LOG_RESULT_LOCATION(result);
374 result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume,
377 LOG_RESULT_LOCATION(result);
380 result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend,
383 LOG_RESULT_LOCATION(result);
386 result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume,
389 LOG_RESULT_LOCATION(result);
396 static int mpu6050_exit(void *mlsl_handle,
397 struct ext_slave_descr *slave,
398 struct ext_slave_platform_data *pdata)
400 kfree(pdata->private_data);
401 pdata->private_data = NULL;
405 static int mpu6050_suspend(void *mlsl_handle,
406 struct ext_slave_descr *slave,
407 struct ext_slave_platform_data *pdata)
411 struct mpu6050_private_data *private_data =
412 (struct mpu6050_private_data *)pdata->private_data;
414 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
415 true, private_data->suspend.odr);
417 LOG_RESULT_LOCATION(result);
421 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
422 true, private_data->suspend.irq_type);
424 LOG_RESULT_LOCATION(result);
428 result = inv_serial_read(mlsl_handle, pdata->address,
429 MPUREG_PWR_MGMT_2, 1, ®);
431 LOG_RESULT_LOCATION(result);
434 reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
436 result = inv_serial_single_write(mlsl_handle, pdata->address,
437 MPUREG_PWR_MGMT_2, reg);
439 LOG_RESULT_LOCATION(result);
446 static int mpu6050_resume(void *mlsl_handle,
447 struct ext_slave_descr *slave,
448 struct ext_slave_platform_data *pdata)
452 struct mpu6050_private_data *private_data =
453 (struct mpu6050_private_data *)pdata->private_data;
455 result = inv_serial_read(mlsl_handle, pdata->address,
456 MPUREG_PWR_MGMT_1, 1, ®);
458 LOG_RESULT_LOCATION(result);
462 if (reg & BIT_SLEEP) {
463 result = inv_serial_single_write(mlsl_handle, pdata->address,
464 MPUREG_PWR_MGMT_1, reg & ~BIT_SLEEP);
466 LOG_RESULT_LOCATION(result);
472 result = inv_serial_read(mlsl_handle, pdata->address,
473 MPUREG_PWR_MGMT_2, 1, ®);
475 LOG_RESULT_LOCATION(result);
478 reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
479 result = inv_serial_single_write(mlsl_handle, pdata->address,
480 MPUREG_PWR_MGMT_2, reg);
482 LOG_RESULT_LOCATION(result);
488 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
489 true, private_data->resume.fsr);
491 LOG_RESULT_LOCATION(result);
494 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
495 true, private_data->resume.odr);
497 LOG_RESULT_LOCATION(result);
500 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
501 true, private_data->resume.irq_type);
503 /* motion, no_motion */
504 /* TODO : port these in their respective _set_thrs and _set_dur
505 functions and use the APPLY paremeter to apply just like
506 _set_odr, _set_irq, and _set_fsr. */
507 reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB;
508 result = inv_serial_single_write(mlsl_handle, pdata->address,
509 MPUREG_ACCEL_MOT_THR, reg);
511 LOG_RESULT_LOCATION(result);
514 reg = (unsigned char)
515 ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths);
516 result = inv_serial_single_write(mlsl_handle, pdata->address,
517 MPUREG_ACCEL_ZRMOT_THR, reg);
519 LOG_RESULT_LOCATION(result);
522 reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
523 result = inv_serial_single_write(mlsl_handle, pdata->address,
524 MPUREG_ACCEL_MOT_DUR, reg);
526 LOG_RESULT_LOCATION(result);
529 reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB;
530 result = inv_serial_single_write(mlsl_handle, pdata->address,
531 MPUREG_ACCEL_ZRMOT_DUR, reg);
533 LOG_RESULT_LOCATION(result);
539 static int mpu6050_read(void *mlsl_handle,
540 struct ext_slave_descr *slave,
541 struct ext_slave_platform_data *pdata,
545 result = inv_serial_read(mlsl_handle, pdata->address,
546 slave->read_reg, slave->read_len, data);
550 static int mpu6050_config(void *mlsl_handle,
551 struct ext_slave_descr *slave,
552 struct ext_slave_platform_data *pdata,
553 struct ext_slave_config *data)
555 struct mpu6050_private_data *private_data =
556 (struct mpu6050_private_data *)pdata->private_data;
558 return INV_ERROR_INVALID_PARAMETER;
561 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
562 return mpu6050_set_odr(mlsl_handle, pdata,
563 &private_data->suspend,
564 data->apply, *((long *)data->data));
565 case MPU_SLAVE_CONFIG_ODR_RESUME:
566 return mpu6050_set_odr(mlsl_handle, pdata,
567 &private_data->resume,
568 data->apply, *((long *)data->data));
569 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
570 return mpu6050_set_fsr(mlsl_handle, pdata,
571 &private_data->suspend,
572 data->apply, *((long *)data->data));
573 case MPU_SLAVE_CONFIG_FSR_RESUME:
574 return mpu6050_set_fsr(mlsl_handle, pdata,
575 &private_data->resume,
576 data->apply, *((long *)data->data));
577 case MPU_SLAVE_CONFIG_MOT_THS:
578 return mpu6050_set_ths(mlsl_handle, pdata,
579 &private_data->suspend,
580 data->apply, *((long *)data->data));
581 case MPU_SLAVE_CONFIG_NMOT_THS:
582 return mpu6050_set_ths(mlsl_handle, pdata,
583 &private_data->resume,
584 data->apply, *((long *)data->data));
585 case MPU_SLAVE_CONFIG_MOT_DUR:
586 return mpu6050_set_dur(mlsl_handle, pdata,
587 &private_data->suspend,
588 data->apply, *((long *)data->data));
589 case MPU_SLAVE_CONFIG_NMOT_DUR:
590 return mpu6050_set_dur(mlsl_handle, pdata,
591 &private_data->resume,
592 data->apply, *((long *)data->data));
593 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
594 return mpu6050_set_irq(mlsl_handle, pdata,
595 &private_data->suspend,
596 data->apply, *((long *)data->data));
598 case MPU_SLAVE_CONFIG_IRQ_RESUME:
599 return mpu6050_set_irq(mlsl_handle, pdata,
600 &private_data->resume,
601 data->apply, *((long *)data->data));
602 case MPU_SLAVE_CONFIG_INTERNAL_REFERENCE:
603 return mpu6050_set_mldl_cfg_ref(mlsl_handle, pdata,
604 (struct mldl_cfg *)data->data);
608 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
614 static int mpu6050_get_config(void *mlsl_handle,
615 struct ext_slave_descr *slave,
616 struct ext_slave_platform_data *pdata,
617 struct ext_slave_config *data)
619 struct mpu6050_private_data *private_data =
620 (struct mpu6050_private_data *)pdata->private_data;
622 return INV_ERROR_INVALID_PARAMETER;
625 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
626 (*(unsigned long *)data->data) =
627 (unsigned long)private_data->suspend.odr;
629 case MPU_SLAVE_CONFIG_ODR_RESUME:
630 (*(unsigned long *)data->data) =
631 (unsigned long)private_data->resume.odr;
633 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
634 (*(unsigned long *)data->data) =
635 (unsigned long)private_data->suspend.fsr;
637 case MPU_SLAVE_CONFIG_FSR_RESUME:
638 (*(unsigned long *)data->data) =
639 (unsigned long)private_data->resume.fsr;
641 case MPU_SLAVE_CONFIG_MOT_THS:
642 (*(unsigned long *)data->data) =
643 (unsigned long)private_data->suspend.ths;
645 case MPU_SLAVE_CONFIG_NMOT_THS:
646 (*(unsigned long *)data->data) =
647 (unsigned long)private_data->resume.ths;
649 case MPU_SLAVE_CONFIG_MOT_DUR:
650 (*(unsigned long *)data->data) =
651 (unsigned long)private_data->suspend.dur;
653 case MPU_SLAVE_CONFIG_NMOT_DUR:
654 (*(unsigned long *)data->data) =
655 (unsigned long)private_data->resume.dur;
657 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
658 (*(unsigned long *)data->data) =
659 (unsigned long)private_data->suspend.irq_type;
661 case MPU_SLAVE_CONFIG_IRQ_RESUME:
662 (*(unsigned long *)data->data) =
663 (unsigned long)private_data->resume.irq_type;
666 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
672 static struct ext_slave_descr mpu6050_descr = {
673 .init = mpu6050_init,
674 .exit = mpu6050_exit,
675 .suspend = mpu6050_suspend,
676 .resume = mpu6050_resume,
677 .read = mpu6050_read,
678 .config = mpu6050_config,
679 .get_config = mpu6050_get_config,
681 .type = EXT_SLAVE_TYPE_ACCEL,
682 .id = ACCEL_ID_MPU6050,
685 .endian = EXT_SLAVE_BIG_ENDIAN,
690 struct ext_slave_descr *mpu6050_get_slave_descr(void)
692 return &mpu6050_descr;