mpu6050: Motion Libraries: Add kernel files for Invensense MPU6050.
Robert Collins [Fri, 2 Dec 2011 00:18:19 +0000 (16:18 -0800)]
Invensense kernel files for MPU6050. MPL version 4.1.1.

Bug 825602

Signed-off-by: Robert Collins <rcollins@nvidia.com>
Reviewed-on: http://git-master/r/70372
(cherry picked from commit 8852d46c35632f960aa1753097bb9d1c448d3602)

Change-Id: I1660a52debaaae39af94199a08e1f3eb88f08b6e
Signed-off-by: Pritesh Raithatha <praithatha@nvidia.com>
Reviewed-on: http://git-master/r/82716
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Tested-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-by: Robert Collins <rcollins@nvidia.com>

23 files changed:
drivers/misc/Makefile
drivers/misc/inv_mpu/Kconfig
drivers/misc/inv_mpu/Makefile
drivers/misc/inv_mpu/accel/mpu6050.c [new file with mode: 0644]
drivers/misc/inv_mpu/compass/Kconfig
drivers/misc/inv_mpu/compass/Makefile
drivers/misc/inv_mpu/compass/ak89xx.c [new file with mode: 0644]
drivers/misc/inv_mpu/mldl_cfg.h
drivers/misc/inv_mpu/mpu3050/Makefile [new file with mode: 0644]
drivers/misc/inv_mpu/mpu3050/mldl_cfg.c [copied from drivers/misc/inv_mpu/mldl_cfg.c with 100% similarity]
drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c [copied from drivers/misc/inv_mpu/mldl_print_cfg.c with 100% similarity]
drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c [copied from drivers/misc/inv_mpu/mlsl-kernel.c with 100% similarity]
drivers/misc/inv_mpu/mpu3050/mpu-dev.c [copied from drivers/misc/inv_mpu/mpu-dev.c with 99% similarity]
drivers/misc/inv_mpu/mpu6050/Makefile [new file with mode: 0644]
drivers/misc/inv_mpu/mpu6050/mldl_cfg.c [moved from drivers/misc/inv_mpu/mldl_cfg.c with 70% similarity]
drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c [moved from drivers/misc/inv_mpu/mldl_print_cfg.c with 98% similarity]
drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c [moved from drivers/misc/inv_mpu/mlsl-kernel.c with 99% similarity]
drivers/misc/inv_mpu/mpu6050/mpu-dev.c [moved from drivers/misc/inv_mpu/mpu-dev.c with 94% similarity]
drivers/misc/inv_mpu/mpu6050b1.h [new file with mode: 0644]
drivers/misc/inv_mpu/mpuirq.c
drivers/misc/inv_mpu/mpuirq.h
drivers/misc/inv_mpu/slaveirq.c
include/linux/mpu.h

index a8f6ab7..5b3728e 100644 (file)
@@ -54,7 +54,7 @@ obj-$(CONFIG_APANIC)          += apanic.o
 obj-$(CONFIG_SENSORS_AK8975)   += akm8975.o
 obj-$(CONFIG_SENSORS_NCT1008)  += nct1008.o
 obj-$(CONFIG_BCM4329_RFKILL)   += bcm4329_rfkill.o
-obj-$(CONFIG_MPU_SENSORS_MPU3050)      += inv_mpu/
+obj-$(CONFIG_INV_SENSORS)      += inv_mpu/
 obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o
 obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/
 obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o
index 53c7c20..4c231b5 100644 (file)
@@ -6,7 +6,7 @@ config MPU_SENSORS_TIMERIRQ
          or timer threads.  Reading from this device returns the same type of
          information as reading from the MPU and slave IRQ's.
 
-menuconfig: INV_SENSORS
+menuconfig INV_SENSORS
        tristate "Motion Processing Unit"
        depends on I2C
        default y
@@ -14,12 +14,12 @@ menuconfig: INV_SENSORS
 if INV_SENSORS
 
 choice
-       prompt "MPU Master"
+       tristate "MPU Master"
        depends on I2C && INV_SENSORS
        default MPU_SENSORS_MPU3050
 
 config MPU_SENSORS_MPU3050
-       bool "MPU3050"
+       tristate "MPU3050"
        depends on I2C
        select MPU_SENSORS_MPU3050_GYRO
        help
@@ -28,7 +28,7 @@ config MPU_SENSORS_MPU3050
          will be called mpu3050.
 
 config MPU_SENSORS_MPU6050A2
-       bool "MPU6050A2"
+       tristate "MPU6050A2"
        depends on I2C
        select MPU_SENSORS_MPU6050_GYRO
        help
@@ -37,9 +37,9 @@ config MPU_SENSORS_MPU6050A2
          will be called mpu6050a2.
 
 config MPU_SENSORS_MPU6050B1
-       bool "MPU6050B1"
-       select MPU_SENSORS_MPU6050_GYRO
+       tristate "MPU6050B1"
        depends on I2C
+       select MPU_SENSORS_MPU6050_GYRO
        help
          If you say yes here you get support for the MPU6050 Gyroscope driver
          This driver can also be built as a module.  If so, the module
@@ -47,11 +47,6 @@ config MPU_SENSORS_MPU6050B1
 
 endchoice
 
-choice
-       prompt "Gyroscope Type"
-       depends on I2C && INV_SENSORS
-       default MPU_SENSORS_MPU3050_GYRO
-
 config MPU_SENSORS_MPU3050_GYRO
        bool "MPU3050 built in gyroscope"
        depends on MPU_SENSORS_MPU3050
@@ -60,8 +55,6 @@ config MPU_SENSORS_MPU6050_GYRO
        bool "MPU6050 built in gyroscope"
        depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
 
-endchoice
-
 source "drivers/misc/inv_mpu/accel/Kconfig"
 source "drivers/misc/inv_mpu/compass/Kconfig"
 source "drivers/misc/inv_mpu/pressure/Kconfig"
index 248648f..f10c684 100644 (file)
@@ -3,43 +3,16 @@
 #
 #
 
-# MPU
-ifdef CONFIG_MPU_SENSORS_MPU3050
-INV_MODULE_NAME := mpu3050
-endif
-
-ifdef CONFIG_MPU_SENSORS_MPU6050A2
-INV_MODULE_NAME := mpu6050
-endif
-
-ifdef CONFIG_MPU_SENSORS_MPU6050B1
-INV_MODULE_NAME := mpu6050
-endif
-
-obj-$(CONFIG_INV_SENSORS)      += $(INV_MODULE_NAME).o
-
-$(INV_MODULE_NAME)-objs += mpuirq.o
-$(INV_MODULE_NAME)-objs += slaveirq.o
-$(INV_MODULE_NAME)-objs += mpu-dev.o
-$(INV_MODULE_NAME)-objs += mlsl-kernel.o
-$(INV_MODULE_NAME)-objs += mldl_cfg.o
-$(INV_MODULE_NAME)-objs += mldl_print_cfg.o
-
-ifdef CONFIG_MPU_SENSORS_MPU6050A2
-$(INV_MODULE_NAME)-objs += accel/mpu6050.o
-endif
-
-ifdef CONFIG_MPU_SENSORS_MPU6050B1
-$(INV_MODULE_NAME)-objs += accel/mpu6050.o
-endif
-
 EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
 EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
 EXTRA_CFLAGS += -DINV_CACHE_DMP=1
 
 obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o
+obj-$(CONFIG_INV_SENSORS) += mpuirq.o slaveirq.o
 
-obj-y                  += accel/
-obj-y                  += compass/
-obj-y                  += pressure/
+obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050/
+obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050/
 
+obj-y += accel/
+obj-y += compass/
+obj-y += pressure/
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.c b/drivers/misc/inv_mpu/accel/mpu6050.c
new file mode 100644 (file)
index 0000000..c5bb678
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+       $License:
+       Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+       $
+ */
+
+/**
+ *  @addtogroup ACCELDL
+ *  @brief      Provides the interface to setup and handle an accelerometer.
+ *
+ *  @{
+ *      @file   mpu6050.c
+ *      @brief  Accelerometer setup and handling methods for Invensense MPU6050
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mpu6050b1.h"
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+struct mpu6050_config {
+       unsigned int odr;               /**< output data rate 1/1000 Hz */
+       unsigned int fsr;               /**< full scale range mg */
+       unsigned int ths;               /**< mot/no-mot thseshold mg */
+       unsigned int dur;               /**< mot/no-mot duration ms */
+       unsigned int irq_type;          /**< irq type */
+};
+
+struct mpu6050_private_data {
+       struct mpu6050_config suspend;
+       struct mpu6050_config resume;
+       struct mldl_cfg *mldl_cfg_ref;
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int mpu6050_set_mldl_cfg_ref(void *mlsl_handle,
+                       struct ext_slave_platform_data *pdata,
+                       struct mldl_cfg *mldl_cfg_ref)
+{
+       struct mpu6050_private_data *private_data =
+                       (struct mpu6050_private_data *)pdata->private_data;
+       private_data->mldl_cfg_ref = mldl_cfg_ref;
+       return 0;
+}
+static int mpu6050_set_lp_mode(void *mlsl_handle,
+                       struct ext_slave_platform_data *pdata,
+                       unsigned char lpa_freq)
+{
+       unsigned char b = 0;
+       /* Reducing the duration setting for lp mode */
+       b = 1;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_ACCEL_MOT_DUR, b);
+       /* Setting the cycle bit and LPA wake up freq */
+       inv_serial_read(mlsl_handle, pdata->address, MPUREG_PWR_MGMT_1, 1,
+                       &b);
+       b |= BIT_CYCLE | BIT_PD_PTAT;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_PWR_MGMT_1,
+                               b);
+       inv_serial_read(mlsl_handle, pdata->address,
+                       MPUREG_PWR_MGMT_2, 1, &b);
+       b |= lpa_freq & BITS_LPA_WAKE_CTRL;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_PWR_MGMT_2, b);
+
+       return INV_SUCCESS;
+}
+
+static int mpu6050_set_fp_mode(void *mlsl_handle,
+                               struct ext_slave_platform_data *pdata)
+{
+       unsigned char b;
+       struct mpu6050_private_data *private_data =
+                       (struct mpu6050_private_data *)pdata->private_data;
+       /* Resetting the cycle bit and LPA wake up freq */
+       inv_serial_read(mlsl_handle, pdata->address,
+                       MPUREG_PWR_MGMT_1, 1, &b);
+       b &= ~BIT_CYCLE & ~BIT_PD_PTAT;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_PWR_MGMT_1, b);
+       inv_serial_read(mlsl_handle, pdata->address,
+                       MPUREG_PWR_MGMT_2, 1, &b);
+       b &= ~BITS_LPA_WAKE_CTRL;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_PWR_MGMT_2, b);
+       /* Resetting the duration setting for fp mode */
+       b = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
+       inv_serial_single_write(mlsl_handle, pdata->address,
+                               MPUREG_ACCEL_MOT_DUR, b);
+
+       return INV_SUCCESS;
+}
+/**
+ * Record the odr for use in computing duration values.
+ *
+ * @param config Config to set, suspend or resume structure
+ * @param odr output data rate in 1/1000 hz
+ */
+static int mpu6050_set_odr(void *mlsl_handle,
+                         struct ext_slave_platform_data *pdata,
+                         struct mpu6050_config *config, long apply, long odr)
+{
+       int result;
+       unsigned char b;
+       unsigned char lpa_freq = 1; /* Default value */
+       long base;
+       int total_divider;
+       struct mpu6050_private_data *private_data =
+                       (struct mpu6050_private_data *)pdata->private_data;
+       struct mldl_cfg *mldl_cfg_ref =
+                       (struct mldl_cfg *)private_data->mldl_cfg_ref;
+
+       if (mldl_cfg_ref) {
+               base = 1000 *
+                       inv_mpu_get_sampling_rate_hz(mldl_cfg_ref->mpu_gyro_cfg)
+                       * (mldl_cfg_ref->mpu_gyro_cfg->divider + 1);
+       } else {
+               /* have no reference to mldl_cfg => assume base rate is 1000 */
+               base = 1000000L;
+       }
+
+       if (odr != 0) {
+               total_divider = (base / odr) - 1;
+               /* final odr MAY be different from requested odr due to
+                  integer truncation */
+               config->odr = base / (total_divider + 1);
+       } else {
+               config->odr = 0;
+               return 0;
+       }
+
+       /* if the DMP and/or gyros are on, don't set the ODR =>
+          the DMP/gyro mldl_cfg->divider setting will handle it */
+       if (apply
+           && (mldl_cfg_ref &&
+           !(mldl_cfg_ref->inv_mpu_cfg->requested_sensors &
+                   INV_DMP_PROCESSOR))) {
+               result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                       MPUREG_SMPLRT_DIV,
+                                       (unsigned char)total_divider);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               MPL_LOGI("ODR : %d mHz\n", config->odr);
+       }
+       /* Decide whether to put accel in LP mode or pull out of LP mode
+          based on the odr. */
+       switch (odr) {
+       case 1000:
+               lpa_freq = BITS_LPA_WAKE_1HZ;
+               break;
+       case 2000:
+               lpa_freq = BITS_LPA_WAKE_2HZ;
+               break;
+       case 10000:
+               lpa_freq = BITS_LPA_WAKE_10HZ;
+               break;
+       case 40000:
+               lpa_freq = BITS_LPA_WAKE_40HZ;
+               break;
+       default:
+               inv_serial_read(mlsl_handle, pdata->address,
+                               MPUREG_PWR_MGMT_1, 1, &b);
+               b &= BIT_CYCLE;
+               if (b == BIT_CYCLE) {
+                       MPL_LOGI(" Accel LP - > FP mode. \n ");
+                       mpu6050_set_fp_mode(mlsl_handle, pdata);
+               }
+       }
+       /* If lpa_freq default value was changed, set into LP mode */
+       if (lpa_freq != 1) {
+               MPL_LOGI(" Accel FP - > LP mode. \n ");
+               mpu6050_set_lp_mode(mlsl_handle, pdata, lpa_freq);
+       }
+       return 0;
+}
+
+static int mpu6050_set_fsr(void *mlsl_handle,
+                         struct ext_slave_platform_data *pdata,
+                         struct mpu6050_config *config, long apply, long fsr)
+{
+       unsigned char fsr_mask;
+       int result;
+
+       if (fsr <= 2000) {
+               config->fsr = 2000;
+               fsr_mask = 0x00;
+       } else if (fsr <= 4000) {
+               config->fsr = 4000;
+               fsr_mask = 0x08;
+       } else if (fsr <= 8000) {
+               config->fsr = 8000;
+               fsr_mask = 0x10;
+       } else { /* fsr = [8001, oo) */
+               config->fsr = 16000;
+               fsr_mask = 0x18;
+       }
+
+       if (apply) {
+               unsigned char reg;
+               result = inv_serial_read(mlsl_handle, pdata->address,
+                                        MPUREG_ACCEL_CONFIG, 1, &reg);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                                MPUREG_ACCEL_CONFIG,
+                                                reg | fsr_mask);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               MPL_LOGV("FSR: %d\n", config->fsr);
+       }
+       return 0;
+}
+
+static int mpu6050_set_irq(void *mlsl_handle,
+                         struct ext_slave_platform_data *pdata,
+                         struct mpu6050_config *config, long apply,
+                         long irq_type)
+{
+       int result;
+       unsigned char reg_int_cfg;
+
+       switch (irq_type) {
+       case MPU_SLAVE_IRQ_TYPE_DATA_READY:
+               config->irq_type = irq_type;
+               reg_int_cfg = BIT_RAW_RDY_EN;
+               break;
+       /* todo: add MOTION, NO_MOTION, and FREEFALL */
+       case MPU_SLAVE_IRQ_TYPE_NONE:
+               /* Do nothing, not even set the interrupt because it is
+                  shared with the gyro */
+               config->irq_type = irq_type;
+               return 0;
+       default:
+               return INV_ERROR_INVALID_PARAMETER;
+       }
+
+       if (apply) {
+               result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                                MPUREG_INT_ENABLE,
+                                                reg_int_cfg);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               MPL_LOGV("irq_type: %d\n", config->irq_type);
+       }
+
+       return 0;
+}
+
+static int mpu6050_set_ths(void *mlsl_handle,
+                         struct ext_slave_platform_data *slave,
+                         struct mpu6050_config *config, long apply, long ths)
+{
+       if (ths < 0)
+               ths = 0;
+
+       config->ths = ths;
+       MPL_LOGV("THS: %d\n", config->ths);
+       return 0;
+}
+
+static int mpu6050_set_dur(void *mlsl_handle,
+                         struct ext_slave_platform_data *slave,
+                         struct mpu6050_config *config, long apply, long dur)
+{
+       if (dur < 0)
+               dur = 0;
+
+       config->dur = dur;
+       MPL_LOGV("DUR: %d\n", config->dur);
+       return 0;
+}
+
+
+static int mpu6050_init(void *mlsl_handle,
+                      struct ext_slave_descr *slave,
+                      struct ext_slave_platform_data *pdata)
+{
+       int result;
+       struct mpu6050_private_data *private_data;
+
+
+       private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+
+       if (!private_data)
+               return INV_ERROR_MEMORY_EXAUSTED;
+
+       pdata->private_data = private_data;
+
+       result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
+                                false, 0);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
+                                false, 200000);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+                                false, 2000);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
+                                false, 2000);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
+                                false, MPU_SLAVE_IRQ_TYPE_NONE);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
+                                false, MPU_SLAVE_IRQ_TYPE_NONE);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend,
+                                false, 80);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume,
+                                false, 40);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend,
+                                false, 1000);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume,
+                                false, 2540);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       return 0;
+}
+
+static int mpu6050_exit(void *mlsl_handle,
+                      struct ext_slave_descr *slave,
+                      struct ext_slave_platform_data *pdata)
+{
+       kfree(pdata->private_data);
+       pdata->private_data = NULL;
+       return 0;
+}
+
+static int mpu6050_suspend(void *mlsl_handle,
+                          struct ext_slave_descr *slave,
+                          struct ext_slave_platform_data *pdata)
+{
+       unsigned char reg;
+       int result;
+       struct mpu6050_private_data *private_data =
+                       (struct mpu6050_private_data *)pdata->private_data;
+
+       result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
+                               true, private_data->suspend.odr);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
+                               true, private_data->suspend.irq_type);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       result = inv_serial_read(mlsl_handle, pdata->address,
+                                MPUREG_PWR_MGMT_2, 1, &reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
+
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        MPUREG_PWR_MGMT_2, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       return 0;
+}
+
+static int mpu6050_resume(void *mlsl_handle,
+                         struct ext_slave_descr *slave,
+                         struct ext_slave_platform_data *pdata)
+{
+       int result;
+       unsigned char reg;
+       struct mpu6050_private_data *private_data =
+               (struct mpu6050_private_data *)pdata->private_data;
+
+       result = inv_serial_read(mlsl_handle, pdata->address,
+                                MPUREG_PWR_MGMT_1, 1, &reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       if (reg & BIT_SLEEP) {
+               result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                       MPUREG_PWR_MGMT_1, reg & ~BIT_SLEEP);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+       }
+       msleep(2);
+
+       result = inv_serial_read(mlsl_handle, pdata->address,
+                       MPUREG_PWR_MGMT_2, 1, &reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                      MPUREG_PWR_MGMT_2, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       /* settings */
+
+       result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
+                                true, private_data->resume.fsr);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
+                                true, private_data->resume.odr);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
+                                true, private_data->resume.irq_type);
+
+       /* motion, no_motion */
+       /* TODO : port these in their respective _set_thrs and _set_dur
+                 functions and use the APPLY paremeter to apply just like
+                 _set_odr, _set_irq, and _set_fsr. */
+       reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB;
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        MPUREG_ACCEL_MOT_THR, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       reg = (unsigned char)
+           ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths);
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        MPUREG_ACCEL_ZRMOT_THR, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        MPUREG_ACCEL_MOT_DUR, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB;
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        MPUREG_ACCEL_ZRMOT_DUR, reg);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       return 0;
+}
+
+static int mpu6050_read(void *mlsl_handle,
+                       struct ext_slave_descr *slave,
+                       struct ext_slave_platform_data *pdata,
+                       unsigned char *data)
+{
+       int result;
+       result = inv_serial_read(mlsl_handle, pdata->address,
+                                slave->read_reg, slave->read_len, data);
+       return result;
+}
+
+static int mpu6050_config(void *mlsl_handle,
+                        struct ext_slave_descr *slave,
+                        struct ext_slave_platform_data *pdata,
+                        struct ext_slave_config *data)
+{
+       struct mpu6050_private_data *private_data =
+               (struct mpu6050_private_data *)pdata->private_data;
+       if (!data->data)
+               return INV_ERROR_INVALID_PARAMETER;
+
+       switch (data->key) {
+       case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+               return mpu6050_set_odr(mlsl_handle, pdata,
+                                     &private_data->suspend,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_ODR_RESUME:
+               return mpu6050_set_odr(mlsl_handle, pdata,
+                                     &private_data->resume,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+               return mpu6050_set_fsr(mlsl_handle, pdata,
+                                     &private_data->suspend,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_FSR_RESUME:
+               return mpu6050_set_fsr(mlsl_handle, pdata,
+                                     &private_data->resume,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_MOT_THS:
+               return mpu6050_set_ths(mlsl_handle, pdata,
+                                     &private_data->suspend,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_NMOT_THS:
+               return mpu6050_set_ths(mlsl_handle, pdata,
+                                     &private_data->resume,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_MOT_DUR:
+               return mpu6050_set_dur(mlsl_handle, pdata,
+                                     &private_data->suspend,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_NMOT_DUR:
+               return mpu6050_set_dur(mlsl_handle, pdata,
+                                     &private_data->resume,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+               return mpu6050_set_irq(mlsl_handle, pdata,
+                                     &private_data->suspend,
+                                     data->apply, *((long *)data->data));
+               break;
+       case MPU_SLAVE_CONFIG_IRQ_RESUME:
+               return mpu6050_set_irq(mlsl_handle, pdata,
+                                     &private_data->resume,
+                                     data->apply, *((long *)data->data));
+       case MPU_SLAVE_CONFIG_INTERNAL_REFERENCE:
+               return mpu6050_set_mldl_cfg_ref(mlsl_handle, pdata,
+                                              (struct mldl_cfg *)data->data);
+               break;
+
+       default:
+               return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+       };
+
+       return 0;
+}
+
+static int mpu6050_get_config(void *mlsl_handle,
+                            struct ext_slave_descr *slave,
+                            struct ext_slave_platform_data *pdata,
+                            struct ext_slave_config *data)
+{
+       struct mpu6050_private_data *private_data =
+               (struct mpu6050_private_data *)pdata->private_data;
+       if (!data->data)
+               return INV_ERROR_INVALID_PARAMETER;
+
+       switch (data->key) {
+       case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->suspend.odr;
+               break;
+       case MPU_SLAVE_CONFIG_ODR_RESUME:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->resume.odr;
+               break;
+       case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->suspend.fsr;
+               break;
+       case MPU_SLAVE_CONFIG_FSR_RESUME:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->resume.fsr;
+               break;
+       case MPU_SLAVE_CONFIG_MOT_THS:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->suspend.ths;
+               break;
+       case MPU_SLAVE_CONFIG_NMOT_THS:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->resume.ths;
+               break;
+       case MPU_SLAVE_CONFIG_MOT_DUR:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->suspend.dur;
+               break;
+       case MPU_SLAVE_CONFIG_NMOT_DUR:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->resume.dur;
+               break;
+       case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->suspend.irq_type;
+               break;
+       case MPU_SLAVE_CONFIG_IRQ_RESUME:
+               (*(unsigned long *)data->data) =
+                   (unsigned long)private_data->resume.irq_type;
+               break;
+       default:
+               return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+       };
+
+       return 0;
+}
+
+static struct ext_slave_descr mpu6050_descr = {
+       .init             = mpu6050_init,
+       .exit             = mpu6050_exit,
+       .suspend          = mpu6050_suspend,
+       .resume           = mpu6050_resume,
+       .read             = mpu6050_read,
+       .config           = mpu6050_config,
+       .get_config       = mpu6050_get_config,
+       .name             = "mpu6050",
+       .type             = EXT_SLAVE_TYPE_ACCEL,
+       .id               = ACCEL_ID_MPU6050,
+       .read_reg         = 0x3B,
+       .read_len         = 6,
+       .endian           = EXT_SLAVE_BIG_ENDIAN,
+       .range            = {2, 0},
+       .trigger          = NULL,
+};
+
+struct ext_slave_descr *mpu6050_get_slave_descr(void)
+{
+       return &mpu6050_descr;
+}
+
+/**
+ *  @}
+ */
index 6c4c13a..7e6bac8 100644 (file)
@@ -9,6 +9,15 @@ menuconfig INV_SENSORS_COMPASS
 
 if INV_SENSORS_COMPASS
 
+config MPU_SENSORS_AK8963
+        tristate "AKM ak8963"
+        help
+          This enables support for the AKM ak8963 compass
+          This support is for integration with the MPU3050 or MPU6050 gyroscope
+          device driver.  Only one compass can be registered at a time.
+          Specifying more that one compass in the board file will result
+          in runtime errors.
+
 config MPU_SENSORS_AK8975
        tristate "AKM ak8975"
        help
index aa8aa6a..d76ce1b 100644 (file)
@@ -28,6 +28,9 @@ inv_mpu_hscdtd002b-objs +=    hscdtd002b.o
 obj-$(CONFIG_MPU_SENSORS_HSCDTD004A) += inv_mpu_hscdtd004a.o
 inv_mpu_hscdtd004a-objs +=     hscdtd004a.o
 
+obj-$(CONFIG_MPU_SENSORS_AK8963) += inv_mpu_ak89xx.o
+inv_mpu_ak89xx-objs +=  ak89xx.o
+
 obj-$(CONFIG_MPU_SENSORS_AK8975) += inv_mpu_ak8975.o
 inv_mpu_ak8975-objs += ak8975.o
 
diff --git a/drivers/misc/inv_mpu/compass/ak89xx.c b/drivers/misc/inv_mpu/compass/ak89xx.c
new file mode 100644 (file)
index 0000000..d15b0b8
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+       $License:
+       Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+       $
+ */
+
+/**
+ *  @addtogroup COMPASSDL
+ *
+ *  @{
+ *      @file   ak89xx.c
+ *      @brief  Magnetometer setup and handling methods for the AKM
+ *              compass devices.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define AK89XX_REG_ST1     (0x02)
+#define AK89XX_REG_HXL     (0x03)
+#define AK89XX_REG_ST2     (0x09)
+
+#define AK89XX_REG_CNTL    (0x0A)
+#define AK89XX_REG_ASAX    (0x10)
+#define AK89XX_REG_ASAY    (0x11)
+#define AK89XX_REG_ASAZ    (0x12)
+
+#define AK89XX_CNTL_MODE_POWER_DOWN        (0x00)
+#define AK89XX_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
+#define AK89XX_CNTL_MODE_FUSE_ROM_ACCESS    (0x0f)
+
+/* -------------------------------------------------------------------------- */
+struct ak89xx_config {
+       char asa[COMPASS_NUM_AXES];     /* axis sensitivity adjustment */
+};
+
+struct ak89xx_private_data {
+       struct ak89xx_config init;
+};
+
+/* -------------------------------------------------------------------------- */
+static int ak89xx_init(void *mlsl_handle,
+                      struct ext_slave_descr *slave,
+                      struct ext_slave_platform_data *pdata)
+{
+       int result;
+       unsigned char serial_data[COMPASS_NUM_AXES];
+
+       struct ak89xx_private_data *private_data;
+       private_data = (struct ak89xx_private_data *)
+           kzalloc(sizeof(struct ak89xx_private_data), GFP_KERNEL);
+
+       if (!private_data)
+               return INV_ERROR_MEMORY_EXAUSTED;
+
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        AK89XX_REG_CNTL,
+                                        AK89XX_CNTL_MODE_POWER_DOWN);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       /* Wait at least 100us */
+       udelay(100);
+
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        AK89XX_REG_CNTL,
+                                        AK89XX_CNTL_MODE_FUSE_ROM_ACCESS);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       /* Wait at least 200us */
+       udelay(200);
+
+       result = inv_serial_read(mlsl_handle, pdata->address,
+                                AK89XX_REG_ASAX,
+                                COMPASS_NUM_AXES, serial_data);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       pdata->private_data = private_data;
+
+       private_data->init.asa[0] = serial_data[0];
+       private_data->init.asa[1] = serial_data[1];
+       private_data->init.asa[2] = serial_data[2];
+
+       result = inv_serial_single_write(mlsl_handle, pdata->address,
+                                        AK89XX_REG_CNTL,
+                                        AK89XX_CNTL_MODE_POWER_DOWN);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       udelay(100);
+       return INV_SUCCESS;
+}
+
+static int ak89xx_exit(void *mlsl_handle,
+                      struct ext_slave_descr *slave,
+                      struct ext_slave_platform_data *pdata)
+{
+       kfree(pdata->private_data);
+       return INV_SUCCESS;
+}
+
+static int ak89xx_suspend(void *mlsl_handle,
+                  struct ext_slave_descr *slave,
+                  struct ext_slave_platform_data *pdata)
+{
+       int result = INV_SUCCESS;
+       result =
+           inv_serial_single_write(mlsl_handle, pdata->address,
+                                   AK89XX_REG_CNTL,
+                                   AK89XX_CNTL_MODE_POWER_DOWN);
+       msleep(1);              /* wait at least 100us */
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       return result;
+}
+
+static int ak89xx_resume(void *mlsl_handle,
+                 struct ext_slave_descr *slave,
+                 struct ext_slave_platform_data *pdata)
+{
+       int result = INV_SUCCESS;
+       result =
+           inv_serial_single_write(mlsl_handle, pdata->address,
+                                   AK89XX_REG_CNTL,
+                                   AK89XX_CNTL_MODE_SINGLE_MEASUREMENT);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+       return result;
+}
+
+static int ak89xx_read(void *mlsl_handle,
+               struct ext_slave_descr *slave,
+               struct ext_slave_platform_data *pdata, unsigned char *data)
+{
+       unsigned char regs[8];
+       unsigned char *stat = &regs[0];
+       unsigned char *stat2 = &regs[7];
+       int result = INV_SUCCESS;
+       int status = INV_SUCCESS;
+
+       result =
+           inv_serial_read(mlsl_handle, pdata->address, AK89XX_REG_ST1,
+                           8, regs);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
+
+       /* Always return the data and the status registers */
+       memcpy(data, &regs[1], 6);
+       data[6] = regs[0];
+       data[7] = regs[7];
+
+       /*
+        * ST : data ready -
+        * Measurement has been completed and data is ready to be read.
+        */
+       if (*stat & 0x01)
+               status = INV_SUCCESS;
+
+       /*
+        * ST2 : data error -
+        * occurs when data read is started outside of a readable period;
+        * data read would not be correct.
+        * Valid in continuous measurement mode only.
+        * In single measurement mode this error should not occour but we
+        * stil account for it and return an error, since the data would be
+        * corrupted.
+        * DERR bit is self-clearing when ST2 register is read.
+        *
+        * This bit is always zero on the AK8963.
+        */
+       if (*stat2 & 0x04)
+               status = INV_ERROR_COMPASS_DATA_ERROR;
+       /*
+        * ST2 : overflow -
+        * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
+        * This is likely to happen in presence of an external magnetic
+        * disturbance; it indicates, the sensor data is incorrect and should
+        * be ignored.
+        * An error is returned.
+        * HOFL bit clears when a new measurement starts.
+        */
+       if (*stat2 & 0x08)
+               status = INV_ERROR_COMPASS_DATA_OVERFLOW;
+       /*
+        * ST : overrun -
+        * the previous sample was not fetched and lost.
+        * Valid in continuous measurement mode only.
+        * In single measurement mode this error should not occour and we
+        * don't consider this condition an error.
+        * DOR bit is self-clearing when ST2 or any meas. data register is
+        * read.
+        */
+       if (*stat & 0x02) {
+               /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
+               status = INV_SUCCESS;
+       }
+
+       /*
+        * trigger next measurement if:
+        *    - stat is non zero;
+        *    - if stat is zero and stat2 is non zero.
+        * Won't trigger if data is not ready and there was no error.
+        */
+       if (*stat != 0x00 || *stat2 != 0x00) {
+               result = inv_serial_single_write(
+                   mlsl_handle, pdata->address,
+                   AK89XX_REG_CNTL, AK89XX_CNTL_MODE_SINGLE_MEASUREMENT);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+       }
+
+       return status;
+}
+
+static int ak89xx_config(void *mlsl_handle,
+                        struct ext_slave_descr *slave,
+                        struct ext_slave_platform_data *pdata,
+                        struct ext_slave_config *data)
+{
+       int result;
+       if (!data->data)
+               return INV_ERROR_INVALID_PARAMETER;
+
+       switch (data->key) {
+       case MPU_SLAVE_WRITE_REGISTERS:
+               result = inv_serial_write(mlsl_handle, pdata->address,
+                                         data->len,
+                                         (unsigned char *)data->data);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               break;
+       case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+       case MPU_SLAVE_CONFIG_ODR_RESUME:
+       case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+       case MPU_SLAVE_CONFIG_FSR_RESUME:
+       case MPU_SLAVE_CONFIG_MOT_THS:
+       case MPU_SLAVE_CONFIG_NMOT_THS:
+       case MPU_SLAVE_CONFIG_MOT_DUR:
+       case MPU_SLAVE_CONFIG_NMOT_DUR:
+       case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+       case MPU_SLAVE_CONFIG_IRQ_RESUME:
+       default:
+               return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+       };
+
+       return INV_SUCCESS;
+}
+
+static int ak89xx_get_config(void *mlsl_handle,
+                            struct ext_slave_descr *slave,
+                            struct ext_slave_platform_data *pdata,
+                            struct ext_slave_config *data)
+{
+       struct ak89xx_private_data *private_data = pdata->private_data;
+       int result;
+       if (!data->data)
+               return INV_ERROR_INVALID_PARAMETER;
+
+       switch (data->key) {
+       case MPU_SLAVE_READ_REGISTERS:
+               {
+                       unsigned char *serial_data =
+                           (unsigned char *)data->data;
+                       result =
+                           inv_serial_read(mlsl_handle, pdata->address,
+                                           serial_data[0], data->len - 1,
+                                           &serial_data[1]);
+                       if (result) {
+                               LOG_RESULT_LOCATION(result);
+                               return result;
+                       }
+                       break;
+               }
+       case MPU_SLAVE_READ_SCALE:
+               {
+                       unsigned char *serial_data =
+                           (unsigned char *)data->data;
+                       serial_data[0] = private_data->init.asa[0];
+                       serial_data[1] = private_data->init.asa[1];
+                       serial_data[2] = private_data->init.asa[2];
+                       result = INV_SUCCESS;
+                       if (result) {
+                               LOG_RESULT_LOCATION(result);
+                               return result;
+                       }
+                       break;
+               }
+       case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+               (*(unsigned long *)data->data) = 0;
+               break;
+       case MPU_SLAVE_CONFIG_ODR_RESUME:
+               (*(unsigned long *)data->data) = 8000;
+               break;
+       case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+       case MPU_SLAVE_CONFIG_FSR_RESUME:
+       case MPU_SLAVE_CONFIG_MOT_THS:
+       case MPU_SLAVE_CONFIG_NMOT_THS:
+       case MPU_SLAVE_CONFIG_MOT_DUR:
+       case MPU_SLAVE_CONFIG_NMOT_DUR:
+       case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+       case MPU_SLAVE_CONFIG_IRQ_RESUME:
+       default:
+               return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+       };
+
+       return INV_SUCCESS;
+}
+
+static struct ext_slave_read_trigger ak89xx_read_trigger = {
+       /*.reg              = */ 0x0A,
+       /*.value            = */ 0x01
+};
+
+static struct ext_slave_descr ak89xx_descr = {
+       .init             = ak89xx_init,
+       .exit             = ak89xx_exit,
+       .suspend          = ak89xx_suspend,
+       .resume           = ak89xx_resume,
+       .read             = ak89xx_read,
+       .config           = ak89xx_config,
+       .get_config       = ak89xx_get_config,
+       .name             = "ak89xx",
+       .type             = EXT_SLAVE_TYPE_COMPASS,
+       .id               = COMPASS_ID_AK8963,
+       .read_reg         = 0x01,
+       .read_len         = 10,
+       .endian           = EXT_SLAVE_LITTLE_ENDIAN,
+       .range            = {9830, 4000},
+       .trigger          = &ak89xx_read_trigger,
+};
+
+static
+struct ext_slave_descr *ak89xx_get_slave_descr(void)
+{
+       return &ak89xx_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct ak89xx_mod_private_data {
+       struct i2c_client *client;
+       struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int ak89xx_mod_probe(struct i2c_client *client,
+                          const struct i2c_device_id *devid)
+{
+       struct ext_slave_platform_data *pdata;
+       struct ak89xx_mod_private_data *private_data;
+       int result = 0;
+
+       dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+       /* Override default values based on model. */
+       ak89xx_descr.id = devid->driver_data;
+       /* ak89xx_descr.name = devid->name; */
+       switch (ak89xx_descr.id) {
+       case COMPASS_ID_AK8963:
+               break;
+       case COMPASS_ID_AK8972:
+               ak89xx_descr.read_len = 9;
+               ak89xx_descr.range.mantissa = 39321;
+               ak89xx_descr.range.fraction = 6000;
+               break;
+       case COMPASS_ID_AK8975:
+               break;
+       default:
+               result = -EFAULT;
+               goto out_no_free;
+       }
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               result = -ENODEV;
+               goto out_no_free;
+       }
+
+       pdata = client->dev.platform_data;
+       if (!pdata) {
+               dev_err(&client->adapter->dev,
+                       "Missing platform data for slave %s\n", devid->name);
+               result = -EFAULT;
+               goto out_no_free;
+       }
+
+       private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+       if (!private_data) {
+               result = -ENOMEM;
+               goto out_no_free;
+       }
+
+       i2c_set_clientdata(client, private_data);
+       private_data->client = client;
+       private_data->pdata = pdata;
+
+       result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+                                       ak89xx_get_slave_descr);
+       if (result) {
+               dev_err(&client->adapter->dev,
+                       "Slave registration failed: %s, %d\n",
+                       devid->name, result);
+               goto out_free_memory;
+       }
+
+       return result;
+
+out_free_memory:
+       kfree(private_data);
+out_no_free:
+       dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+       return result;
+
+}
+
+static int ak89xx_mod_remove(struct i2c_client *client)
+{
+       struct ak89xx_mod_private_data *private_data =
+               i2c_get_clientdata(client);
+
+       dev_dbg(&client->adapter->dev, "%s\n", __func__);
+       inv_mpu_unregister_slave(client, private_data->pdata,
+                               ak89xx_get_slave_descr);
+
+       kfree(private_data);
+       return 0;
+}
+
+static const struct i2c_device_id ak89xx_mod_id[] = {
+       { "ak8963", COMPASS_ID_AK8963 },
+       { "ak8972", COMPASS_ID_AK8972 },
+       { "ak8975", COMPASS_ID_AK8975 },
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak89xx_mod_id);
+
+static struct i2c_driver ak89xx_mod_driver = {
+       .class = I2C_CLASS_HWMON,
+       .probe = ak89xx_mod_probe,
+       .remove = ak89xx_mod_remove,
+       .id_table = ak89xx_mod_id,
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "ak89xx_mod",
+                  },
+       .address_list = normal_i2c,
+};
+
+static int __init ak89xx_mod_init(void)
+{
+       int res = i2c_add_driver(&ak89xx_mod_driver);
+       pr_info("%s: Probe name %s\n", __func__, "ak89xx_mod");
+       if (res)
+               pr_err("%s failed\n", __func__);
+       return res;
+}
+
+static void __exit ak89xx_mod_exit(void)
+{
+       pr_info("%s\n", __func__);
+       i2c_del_driver(&ak89xx_mod_driver);
+}
+
+module_init(ak89xx_mod_init);
+module_exit(ak89xx_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate AKM AK89XX sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ak89xx_mod");
+
+/**
+ *  @}
+ */
index 1c23878..2b81b89 100644 (file)
 #include "mltypes.h"
 #include "mlsl.h"
 #include <linux/mpu.h>
+#ifdef MPU_CURRENT_BUILD_MPU6050B1
+#include "mpu6050b1.h"
+#elif defined(MPU_CURRENT_BUILD_MPU3050)
 #include "mpu3050.h"
+#endif
 
 #include "log.h"
 
diff --git a/drivers/misc/inv_mpu/mpu3050/Makefile b/drivers/misc/inv_mpu/mpu3050/Makefile
new file mode 100644 (file)
index 0000000..9e57393
--- /dev/null
@@ -0,0 +1,17 @@
+
+# Kernel makefile for motions sensors
+#
+#
+
+obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o
+
+ccflags-y := -DMPU_CURRENT_BUILD_MPU3050
+
+mpu3050-objs += mldl_cfg.o
+mpu3050-objs += mldl_print_cfg.o
+mpu3050-objs += mlsl-kernel.o
+mpu3050-objs += mpu-dev.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
+EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
+EXTRA_CFLAGS += -DINV_CACHE_DMP=1
similarity index 99%
copy from drivers/misc/inv_mpu/mpu-dev.c
copy to drivers/misc/inv_mpu/mpu3050/mpu-dev.c
index ddaf086..22a837b 100644 (file)
@@ -1059,6 +1059,7 @@ int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
        struct mldl_cfg *mldl_cfg;
        int res = 0;
        int ii = 0;
+       unsigned long irq_flags;
 
        dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
 
@@ -1138,7 +1139,12 @@ int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
        if (client->irq) {
                dev_info(&client->adapter->dev,
                         "Installing irq using %d\n", client->irq);
-               res = mpuirq_init(client, mldl_cfg);
+               if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
+                       irq_flags = IRQF_TRIGGER_FALLING;
+               else
+                       irq_flags = IRQF_TRIGGER_RISING;
+               res = mpuirq_init(client, mldl_cfg, irq_flags);
+
                if (res)
                        goto out_mpuirq_failed;
        } else {
diff --git a/drivers/misc/inv_mpu/mpu6050/Makefile b/drivers/misc/inv_mpu/mpu6050/Makefile
new file mode 100644 (file)
index 0000000..a93aa97
--- /dev/null
@@ -0,0 +1,18 @@
+
+# Kernel makefile for motions sensors
+#
+#
+
+obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050b1.o
+
+ccflags-y := -DMPU_CURRENT_BUILD_MPU6050B1
+
+mpu6050b1-objs += mldl_cfg.o
+mpu6050b1-objs += mldl_print_cfg.o
+mpu6050b1-objs += mlsl-kernel.o
+mpu6050b1-objs += mpu-dev.o
+mpu6050b1-objs += ../accel/mpu6050.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
+EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
+EXTRA_CFLAGS += -DINV_CACHE_DMP=1
similarity index 70%
rename from drivers/misc/inv_mpu/mldl_cfg.c
rename to drivers/misc/inv_mpu/mpu6050/mldl_cfg.c
index ccacc8e..22af0c2 100644 (file)
@@ -33,7 +33,7 @@
 
 #include "mldl_cfg.h"
 #include <linux/mpu.h>
-#include "mpu3050.h"
+#include "mpu6050b1.h"
 
 #include "mlsl.h"
 #include "mldl_print_cfg.h"
@@ -43,8 +43,8 @@
 
 /* -------------------------------------------------------------------------- */
 
-#define SLEEP   1
-#define WAKE_UP 0
+#define SLEEP   0
+#define WAKE_UP 7
 #define RESET   1
 #define STANDBY 1
 
@@ -154,98 +154,84 @@ static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle)
        return result;
 }
 
-
-
-static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
-                                 void *mlsl_handle, unsigned char enable)
+/**
+ *  @brief  enables/disables the I2C bypass to an external device
+ *          connected to MPU's secondary I2C bus.
+ *  @param  enable
+ *              Non-zero to enable pass through.
+ *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
+ */
+static int mpu6050b1_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
+                                   void *mlsl_handle, unsigned char enable)
 {
-       unsigned char b;
+       unsigned char reg;
        int result;
        unsigned char status = mldl_cfg->inv_mpu_state->status;
-
        if ((status & MPU_GYRO_IS_BYPASSED && enable) ||
            (!(status & MPU_GYRO_IS_BYPASSED) && !enable))
                return INV_SUCCESS;
 
        /*---- get current 'USER_CTRL' into b ----*/
-       result = inv_serial_read(
-               mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_USER_CTRL, 1, &b);
+       result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+                                MPUREG_USER_CTRL, 1, &reg);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
 
-       b &= ~BIT_AUX_IF_EN;
-
        if (!enable) {
+               /* setting int_config with the property flag BIT_BYPASS_EN
+                  should be done by the setup functions */
                result = inv_serial_single_write(
                        mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_USER_CTRL,
-                       (b | BIT_AUX_IF_EN));
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
-               }
-       } else {
-               /* Coming out of I2C is tricky due to several erratta.  Do not
-                * modify this algorithm
-                */
-               /*
-                * 1) wait for the right time and send the command to change
-                * the aux i2c slave address to an invalid address that will
-                * get nack'ed
-                *
-                * 0x00 is broadcast.  0x7F is unlikely to be used by any aux.
-                */
-               result = inv_serial_single_write(
-                       mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_AUX_SLV_ADDR, 0x7F);
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
-               }
-               /*
-                * 2) wait enough time for a nack to occur, then go into
-                *    bypass mode:
-                */
-               msleep(2);
-               result = inv_serial_single_write(
-                       mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_USER_CTRL, (b));
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
+                       MPUREG_INT_PIN_CFG,
+                       (mldl_cfg->pdata->int_config & ~(BIT_BYPASS_EN)));
+               if (!(reg & BIT_I2C_MST_EN)) {
+                       result =
+                           inv_serial_single_write(
+                                   mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+                                   MPUREG_USER_CTRL,
+                                   (reg | BIT_I2C_MST_EN));
+                       if (result) {
+                               LOG_RESULT_LOCATION(result);
+                               return result;
+                       }
                }
-               /*
-                * 3) wait for up to one MPU cycle then restore the slave
-                *    address
-                */
-               msleep(inv_mpu_get_sampling_period_us(mldl_cfg->mpu_gyro_cfg)
-                       / 1000);
-               result = inv_serial_single_write(
-                       mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_AUX_SLV_ADDR,
-                       mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]
-                               ->address);
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
+       } else if (enable) {
+               if (reg & BIT_AUX_IF_EN) {
+                       result =
+                           inv_serial_single_write(
+                                   mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+                                   MPUREG_USER_CTRL,
+                                   (reg & (~BIT_I2C_MST_EN)));
+                       if (result) {
+                               LOG_RESULT_LOCATION(result);
+                               return result;
+                       }
+                       /*****************************************
+                        * To avoid hanging the bus we must sleep until all
+                        * slave transactions have been completed.
+                        *  24 bytes max slave reads
+                        *  +1 byte possible extra write
+                        *  +4 max slave address
+                        * ---
+                        *  33 Maximum bytes
+                        *  x9 Approximate bits per byte
+                        * ---
+                        * 297 bits.
+                        * 2.97 ms minimum @ 100kbps
+                        * 0.75 ms minimum @ 400kbps.
+                        *****************************************/
+                       msleep(3);
                }
-
-               /*
-                * 4) reset the ime interface
-                */
-
                result = inv_serial_single_write(
                        mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_USER_CTRL,
-                       (b | BIT_AUX_IF_RST));
+                       MPUREG_INT_PIN_CFG,
+                       (mldl_cfg->pdata->int_config | BIT_BYPASS_EN));
                if (result) {
                        LOG_RESULT_LOCATION(result);
                        return result;
                }
-               msleep(2);
        }
        if (enable)
                mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
@@ -256,6 +242,8 @@ static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
 }
 
 
+
+
 /**
  *  @brief  enables/disables the I2C bypass to an external device
  *          connected to MPU's secondary I2C bus.
@@ -266,7 +254,7 @@ static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
 static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
                              unsigned char enable)
 {
-       return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
+       return mpu6050b1_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
 }
 
 
@@ -275,48 +263,100 @@ static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
 /* NOTE : when not indicated, product revision
          is considered an 'npp'; non production part */
 
+/* produces an unique identifier for each device based on the
+   combination of product version and product revision */
 struct prod_rev_map_t {
+       unsigned short mpl_product_key;
        unsigned char silicon_rev;
        unsigned short gyro_trim;
+       unsigned short accel_trim;
 };
 
-#define OLDEST_PROD_REV_SUPPORTED      11
+/* NOTE: product entries are in chronological order */
 static struct prod_rev_map_t prod_rev_map[] = {
-       {0, 0},
-       {MPU_SILICON_REV_A4, 131},      /* 1  A? OBSOLETED */
-       {MPU_SILICON_REV_A4, 131},      /* 2  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 3  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 4  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 5  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 6  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 7  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 8  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 9  |  */
-       {MPU_SILICON_REV_A4, 131},      /* 10 V  */
-       {MPU_SILICON_REV_B1, 131},      /* 11 B1 */
-       {MPU_SILICON_REV_B1, 131},      /* 12 |  */
-       {MPU_SILICON_REV_B1, 131},      /* 13 |  */
-       {MPU_SILICON_REV_B1, 131},      /* 14 V  */
-       {MPU_SILICON_REV_B4, 131},      /* 15 B4 */
-       {MPU_SILICON_REV_B4, 131},      /* 16 |  */
-       {MPU_SILICON_REV_B4, 131},      /* 17 |  */
-       {MPU_SILICON_REV_B4, 131},      /* 18 |  */
-       {MPU_SILICON_REV_B4, 115},      /* 19 |  */
-       {MPU_SILICON_REV_B4, 115},      /* 20 V  */
-       {MPU_SILICON_REV_B6, 131},      /* 21 B6 (B6/A9)  */
-       {MPU_SILICON_REV_B4, 115},      /* 22 B4 (B7/A10) */
-       {MPU_SILICON_REV_B6, 0},        /* 23 B6 */
-       {MPU_SILICON_REV_B6, 0},        /* 24 |  */
-       {MPU_SILICON_REV_B6, 0},        /* 25 |  */
-       {MPU_SILICON_REV_B6, 131},      /* 26 V  (B6/A11) */
+       /* prod_ver = 0 */
+       {MPL_PROD_KEY(0,  1), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  2), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  3), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  4), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  5), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  6), MPU_SILICON_REV_A2, 131, 16384},  /* (A2/C2-1) */
+       /* prod_ver = 1, forced to 0 for MPU6050 A2 */
+       {MPL_PROD_KEY(0,  7), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  8), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  9), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384},  /* (A2/D2-1) */
+       {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384},  /* (A2/D4)   */
+       /* prod_ver = 1 */
+       {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D2-1) */
+       {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D2-2) */
+       {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D2-3) */
+       {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D2-4) */
+       {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D2-5) */
+       {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/D4)   */
+       {MPL_PROD_KEY(1,  1), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-1) */
+       {MPL_PROD_KEY(1,  2), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-2) */
+       {MPL_PROD_KEY(1,  3), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-3) */
+       {MPL_PROD_KEY(1,  4), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-4) */
+       {MPL_PROD_KEY(1,  5), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-5) */
+       {MPL_PROD_KEY(1,  6), MPU_SILICON_REV_B1, 131, 16384},  /* (B1/E1-6) */
+       /* prod_ver = 2 */
+       {MPL_PROD_KEY(2,  7), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-1) */
+       {MPL_PROD_KEY(2,  8), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-2) */
+       {MPL_PROD_KEY(2,  9), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-3) */
+       {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-4) */
+       {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-5) */
+       {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E1-6) */
+       {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/D4)   */
+       /* prod_ver = 3 */
+       {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384},  /* (B2/E2)   */
+       /* prod_ver = 4 */
+       {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131,  8192},  /* (B2/F1)   */
+       {MPL_PROD_KEY(4,  1), MPU_SILICON_REV_B1, 131,  8192},  /* (B3/F1)   */
+       {MPL_PROD_KEY(4,  3), MPU_SILICON_REV_B1, 131,  8192},  /* (B4/F1)   */
+       /* prod_ver = 5 */
+       {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384},  /* (B5/E2)   */
+       /* prod_ver = 7 */
+       {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384},  /* (B5/E2)   */
+       /* prod_ver = 8 */
+       {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384},  /* (B5/E2)   */
+       /* prod_ver = 9 */
+       {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384},  /* (B5/E2)   */
+       /* prod_ver = 10 */
+       {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}  /* (B5/E2)   */
 };
 
 /**
  *  @internal
- *  @brief  Get the silicon revision ID from OTP for MPU3050.
- *          The silicon revision number is in read from OTP bank 0,
- *          ADDR6[7:2].  The corresponding ID is retrieved by lookup
- *          in a map.
+ *  @brief  Inverse lookup of the index of an MPL product key .
+ *  @param  key
+ *              the MPL product indentifier also referred to as 'key'.
+ *  @return the index position of the key in the array, -1 if not found.
+ */
+short index_of_key(unsigned short key)
+{
+       int i;
+       for (i = 0; i < NUM_OF_PROD_REVS; i++)
+               if (prod_rev_map[i].mpl_product_key == key)
+                       return (short)i;
+       return -1;
+}
+
+/**
+ *  @internal
+ *  @brief  Get the product revision and version for MPU6050 and
+ *          extract all per-part specific information.
+ *          The product version number is read from the PRODUCT_ID register in
+ *          user space register map.
+ *          The product revision number is in read from OTP bank 0, ADDR6[7:2].
+ *          These 2 numbers, combined, provide an unique key to be used to
+ *          retrieve some per-device information such as the silicon revision
+ *          and the gyro and accel sensitivity trim values.
  *
  *  @param  mldl_cfg
  *              a pointer to the mldl config data structure.
@@ -326,32 +366,33 @@ static struct prod_rev_map_t prod_rev_map[] = {
  *
  *  @return 0 on success, a non-zero error code otherwise.
  */
-static int inv_get_silicon_rev_mpu3050(
+static int inv_get_silicon_rev_mpu6050(
                struct mldl_cfg *mldl_cfg, void *mlsl_handle)
 {
        int result;
-       unsigned char index = 0x00;
+       unsigned char prod_ver = 0x00, prod_rev = 0x00;
        unsigned char bank =
            (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
-       unsigned short mem_addr = ((bank << 8) | 0x06);
+       unsigned short memAddr = ((bank << 8) | 0x06);
+       unsigned short key;
+       short index;
        struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
 
        result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                                MPUREG_PRODUCT_ID, 1,
-                                &mpu_chip_info->product_id);
+                                MPUREG_PRODUCT_ID, 1, &prod_ver);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
+       prod_ver &= 0xF;
 
-       result = inv_serial_read_mem(
-               mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-               mem_addr, 1, &index);
+       result = inv_serial_read_mem(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+                                    memAddr, 1, &prod_rev);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
-       index >>= 2;
+       prod_rev >>= 2;
 
        /* clean the prefetch and cfg user bank bits */
        result = inv_serial_single_write(
@@ -362,25 +403,34 @@ static int inv_get_silicon_rev_mpu3050(
                return result;
        }
 
-       if (index < OLDEST_PROD_REV_SUPPORTED || index >= NUM_OF_PROD_REVS) {
-               mpu_chip_info->silicon_revision = 0;
-               mpu_chip_info->gyro_sens_trim = 0;
-               MPL_LOGE("Unsupported Product Revision Detected : %d\n", index);
+       key = MPL_PROD_KEY(prod_ver, prod_rev);
+       if (key == 0) {
+               MPL_LOGE("Product id read as 0 "
+                        "indicates device is either "
+                        "incompatible or an MPU3050\n");
+               return INV_ERROR_INVALID_MODULE;
+       }
+       index = index_of_key(key);
+       if (index == -1 || index >= NUM_OF_PROD_REVS) {
+               MPL_LOGE("Unsupported product key %d in MPL\n", key);
+               return INV_ERROR_INVALID_MODULE;
+       }
+       /* check MPL is compiled for this device */
+       if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) {
+               MPL_LOGE("MPL compiled for MPU6050B1 support "
+                        "but device is not MPU6050B1 (%d)\n", key);
                return INV_ERROR_INVALID_MODULE;
        }
 
-       mpu_chip_info->product_revision = index;
+       mpu_chip_info->product_id = prod_ver;
+       mpu_chip_info->product_revision = prod_rev;
        mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev;
        mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim;
-       if (mpu_chip_info->gyro_sens_trim == 0) {
-               MPL_LOGE("gyro sensitivity trim is 0"
-                        " - unsupported non production part.\n");
-               return INV_ERROR_INVALID_MODULE;
-       }
+       mpu_chip_info->accel_sens_trim = prod_rev_map[index].accel_trim;
 
        return result;
 }
-#define inv_get_silicon_rev inv_get_silicon_rev_mpu3050
+#define inv_get_silicon_rev inv_get_silicon_rev_mpu6050
 
 
 /**
@@ -412,237 +462,116 @@ static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg,
        int result;
        unsigned char regval;
 
-       unsigned char reg;
-       unsigned char mask;
-
-       if (0 == mldl_cfg->mpu_chip_info->silicon_revision)
-               return INV_ERROR_INVALID_PARAMETER;
-
-       /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR --
-       NOTE: this is incompatible with ST accelerometers where the VDDIO
-       bit MUST be set to enable ST's internal logic to autoincrement
-       the register address on burst reads --*/
-       if ((mldl_cfg->mpu_chip_info->silicon_revision & 0xf)
-               < MPU_SILICON_REV_B6) {
-               reg = MPUREG_ACCEL_BURST_ADDR;
-               mask = 0x80;
-       } else {
-               /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 =>
-                 the mask is always 0x04 --*/
-               reg = MPUREG_FIFO_EN2;
-               mask = 0x04;
-       }
-
        result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                                reg, 1, &regval);
+                                MPUREG_YG_OFFS_TC, 1, &regval);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
-
        if (enable)
-               regval |= mask;
-       else
-               regval &= ~mask;
+               regval |= BIT_I2C_MST_VDDIO;
 
        result = inv_serial_single_write(
-               mlsl_handle, mldl_cfg->mpu_chip_info->addr, reg, regval);
+               mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+               MPUREG_YG_OFFS_TC, regval);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
-       return result;
        return INV_SUCCESS;
 }
 
 
 /**
  * @internal
- * @brief   This function controls the power management on the MPU device.
- *          The entire chip can be put to low power sleep mode, or individual
- *          gyros can be turned on/off.
- *
- *          Putting the device into sleep mode depending upon the changing needs
- *          of the associated applications is a recommended method for reducing
- *          power consuption.  It is a safe opearation in that sleep/wake up of
- *          gyros while running will not result in any interruption of data.
+ * @brief MPU6050 B1 power management functions.
+ * @param mldl_cfg
+ *          a pointer to the internal mldl_cfg data structure.
+ * @param mlsl_handle
+ *          a file handle to the serial device used to communicate
+ *          with the MPU6050 B1 device.
+ * @param reset
+ *          1 to reset hardware.
+ * @param sensors
+ *          Bitfield of sensors to leave on
  *
- *          Although it is entirely allowed to put the device into full sleep
- *          while running the DMP, it is not recomended because it will disrupt
- *          the ongoing calculations carried on inside the DMP and consequently
- *          the sensor fusion algorithm. Furthermore, while in sleep mode
- *          read & write operation from the app processor on both registers and
- *          memory are disabled and can only regained by restoring the MPU in
- *          normal power mode.
- *          Disabling any of the gyro axis will reduce the associated power
- *          consuption from the PLL but will not stop the DMP from running
- *          state.
- *
- * @param   reset
- *              Non-zero to reset the device. Note that this setting
- *              is volatile and the corresponding register bit will
- *              clear itself right after being applied.
- * @param   sleep
- *              Non-zero to put device into full sleep.
- * @param   disable_gx
- *              Non-zero to disable gyro X.
- * @param   disable_gy
- *              Non-zero to disable gyro Y.
- * @param   disable_gz
- *              Non-zero to disable gyro Z.
- *
- * @return  INV_SUCCESS if successfull; a non-zero error code otherwise.
+ * @return 0 on success, a non-zero error code on error.
  */
-static int mpu3050_pwr_mgmt(struct mldl_cfg *mldl_cfg,
-                           void *mlsl_handle,
-                           unsigned char reset,
-                           unsigned char sleep,
-                           unsigned char disable_gx,
-                           unsigned char disable_gy,
-                           unsigned char disable_gz)
+static int mpu60xx_pwr_mgmt(struct mldl_cfg *mldl_cfg,
+                                   void *mlsl_handle,
+                                   unsigned int reset, unsigned long sensors)
 {
-       unsigned char b;
+       unsigned char pwr_mgmt[2];
+       unsigned char pwr_mgmt_prev[2];
        int result;
+       int sleep = !(sensors & (INV_THREE_AXIS_GYRO | INV_THREE_AXIS_ACCEL
+                               | INV_DMP_PROCESSOR));
 
-       result =
-           inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                           MPUREG_PWR_MGM, 1, &b);
-       if (result) {
-               LOG_RESULT_LOCATION(result);
-               return result;
-       }
-
-       /* If we are awake, we need to put it in bypass before resetting */
-       if ((!(b & BIT_SLEEP)) && reset)
-               result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
-
-       /* Reset if requested */
        if (reset) {
-               MPL_LOGV("Reset MPU3050\n");
+               MPL_LOGI("Reset MPU6050 B1\n");
                result = inv_serial_single_write(
                        mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_PWR_MGM, b | BIT_H_RESET);
+                       MPUREG_PWR_MGMT_1, BIT_H_RESET);
                if (result) {
                        LOG_RESULT_LOCATION(result);
                        return result;
                }
-               msleep(5);
-               /* Some chips are awake after reset and some are asleep,
-                * check the status */
-               result = inv_serial_read(
-                       mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_PWR_MGM, 1, &b);
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
-               }
-       }
-
-       /* Update the suspended state just in case we return early */
-       if (b & BIT_SLEEP) {
-               mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED;
-               mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED;
-       } else {
-               mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED;
-               mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED;
+               mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
+               msleep(15);
        }
 
-       /* if power status match requested, nothing else's left to do */
-       if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
-           (((sleep != 0) * BIT_SLEEP) |
-            ((disable_gx != 0) * BIT_STBY_XG) |
-            ((disable_gy != 0) * BIT_STBY_YG) |
-            ((disable_gz != 0) * BIT_STBY_ZG))) {
-               return INV_SUCCESS;
+       /* NOTE : reading both PWR_MGMT_1 and PWR_MGMT_2 for efficiency because
+                 they are accessible even when the device is powered off */
+       result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+                                MPUREG_PWR_MGMT_1, 2, pwr_mgmt_prev);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
        }
 
-       /*
-        * This specific transition between states needs to be reinterpreted:
-        *    (1,1,1,1) -> (0,1,1,1) has to become
-        *    (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1)
-        * where
-        *    (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1)
-        */
-       if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
-           (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
-           && ((!sleep) && disable_gx && disable_gy && disable_gz)) {
-               result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0);
-               if (result)
-                       return result;
-               b |= BIT_SLEEP;
-               b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
-       }
+       pwr_mgmt[0] = pwr_mgmt_prev[0];
+       pwr_mgmt[1] = pwr_mgmt_prev[1];
 
-       if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) {
-               if (sleep) {
-                       result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
-                       if (result) {
-                               LOG_RESULT_LOCATION(result);
-                               return result;
-                       }
-                       b |= BIT_SLEEP;
-                       result =
-                           inv_serial_single_write(
-                                   mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                                   MPUREG_PWR_MGM, b);
-                       if (result) {
-                               LOG_RESULT_LOCATION(result);
-                               return result;
-                       }
-                       mldl_cfg->inv_mpu_state->status |=
-                               MPU_GYRO_IS_SUSPENDED;
-                       mldl_cfg->inv_mpu_state->status |=
-                               MPU_DEVICE_IS_SUSPENDED;
-               } else {
-                       b &= ~BIT_SLEEP;
-                       result =
-                           inv_serial_single_write(
-                                   mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                                   MPUREG_PWR_MGM, b);
-                       if (result) {
-                               LOG_RESULT_LOCATION(result);
-                               return result;
-                       }
-                       mldl_cfg->inv_mpu_state->status &=
-                               ~MPU_GYRO_IS_SUSPENDED;
-                       mldl_cfg->inv_mpu_state->status &=
-                               ~MPU_DEVICE_IS_SUSPENDED;
-                       msleep(5);
-               }
+       if (sleep) {
+               mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED;
+               pwr_mgmt[0] |= BIT_SLEEP;
+       } else {
+               mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED;
+               pwr_mgmt[0] &= ~BIT_SLEEP;
        }
-       /*---
-         WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE
-         1) put one axis at a time in stand-by
-         ---*/
-       if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) {
-               b ^= BIT_STBY_XG;
+       if (pwr_mgmt[0] != pwr_mgmt_prev[0]) {
                result = inv_serial_single_write(
                        mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_PWR_MGM, b);
+                       MPUREG_PWR_MGMT_1, pwr_mgmt[0]);
                if (result) {
                        LOG_RESULT_LOCATION(result);
                        return result;
                }
        }
-       if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) {
-               b ^= BIT_STBY_YG;
+
+       pwr_mgmt[1] &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
+       if (!(sensors & INV_X_GYRO))
+               pwr_mgmt[1] |= BIT_STBY_XG;
+       if (!(sensors & INV_Y_GYRO))
+               pwr_mgmt[1] |= BIT_STBY_YG;
+       if (!(sensors & INV_Z_GYRO))
+               pwr_mgmt[1] |= BIT_STBY_ZG;
+
+       if (pwr_mgmt[1] != pwr_mgmt_prev[1]) {
                result = inv_serial_single_write(
                        mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_PWR_MGM, b);
+                       MPUREG_PWR_MGMT_2, pwr_mgmt[1]);
                if (result) {
                        LOG_RESULT_LOCATION(result);
                        return result;
                }
        }
-       if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) {
-               b ^= BIT_STBY_ZG;
-               result = inv_serial_single_write(
-                       mlsl_handle, mldl_cfg->mpu_chip_info->addr,
-                       MPUREG_PWR_MGM, b);
-               if (result) {
-                       LOG_RESULT_LOCATION(result);
-                       return result;
-               }
+
+       if ((pwr_mgmt[1] & (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
+           (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) {
+               mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED;
+       } else {
+               mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED;
        }
 
        return INV_SUCCESS;
@@ -681,8 +610,32 @@ static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
                LOG_RESULT_LOCATION(result);
                return result;
        }
-       /* TODO : workarounds to be determined and implemented */
 
+       /* ERRATA:
+          workaroud to switch from any MPU_CLK_SEL_PLLGYROx to
+          MPU_CLK_SEL_INTERNAL and XGyro is powered up:
+          1) Select INT_OSC
+          2) PD XGyro
+          3) PU XGyro
+        */
+       if ((cur_clk_src == MPU_CLK_SEL_PLLGYROX
+                || cur_clk_src == MPU_CLK_SEL_PLLGYROY
+                || cur_clk_src == MPU_CLK_SEL_PLLGYROZ)
+           && mldl_cfg->mpu_gyro_cfg->clk_src == MPU_CLK_SEL_INTERNAL
+           && mldl_cfg->inv_mpu_cfg->requested_sensors & INV_X_GYRO) {
+               unsigned char first_result = INV_SUCCESS;
+               mldl_cfg->inv_mpu_cfg->requested_sensors &= ~INV_X_GYRO;
+               result = mpu60xx_pwr_mgmt(
+                       mldl_cfg, gyro_handle,
+                       false, mldl_cfg->inv_mpu_cfg->requested_sensors);
+               ERROR_CHECK_FIRST(first_result, result);
+               mldl_cfg->inv_mpu_cfg->requested_sensors |= INV_X_GYRO;
+               result = mpu60xx_pwr_mgmt(
+                       mldl_cfg, gyro_handle,
+                       false, mldl_cfg->inv_mpu_cfg->requested_sensors);
+               ERROR_CHECK_FIRST(first_result, result);
+               result = first_result;
+       }
        return result;
 }
 
@@ -708,7 +661,8 @@ static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
  *
  * returns INV_SUCCESS or non-zero error code
  */
-static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg,
+
+static int mpu_set_slave_mpu60xx(struct mldl_cfg *mldl_cfg,
                                 void *gyro_handle,
                                 struct ext_slave_descr *slave,
                                 struct ext_slave_platform_data *slave_pdata,
@@ -716,81 +670,266 @@ static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg,
 {
        int result;
        unsigned char reg;
+       /* Slave values */
        unsigned char slave_reg;
        unsigned char slave_len;
        unsigned char slave_endian;
        unsigned char slave_address;
-
-       if (slave_id != EXT_SLAVE_TYPE_ACCEL)
-               return 0;
-
-       result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
+       /* Which MPU6050 registers to use */
+       unsigned char addr_reg;
+       unsigned char reg_reg;
+       unsigned char ctrl_reg;
+       /* Which MPU6050 registers to use for the trigger */
+       unsigned char addr_trig_reg;
+       unsigned char reg_trig_reg;
+       unsigned char ctrl_trig_reg;
+
+       unsigned char bits_slave_delay = 0;
+       /* Divide down rate for the Slave, from the mpu rate */
+       unsigned char d0_trig_reg;
+       unsigned char delay_ctrl_orig;
+       unsigned char delay_ctrl;
+       long divider;
 
        if (NULL == slave || NULL == slave_pdata) {
                slave_reg = 0;
                slave_len = 0;
                slave_endian = 0;
                slave_address = 0;
-               mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0;
        } else {
                slave_reg = slave->read_reg;
                slave_len = slave->read_len;
                slave_endian = slave->endian;
                slave_address = slave_pdata->address;
-               mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 1;
-       }
+               slave_address |= BIT_I2C_READ;
+       }
+
+       switch (slave_id) {
+       case EXT_SLAVE_TYPE_ACCEL:
+               addr_reg = MPUREG_I2C_SLV1_ADDR;
+               reg_reg  = MPUREG_I2C_SLV1_REG;
+               ctrl_reg = MPUREG_I2C_SLV1_CTRL;
+               addr_trig_reg = 0;
+               reg_trig_reg  = 0;
+               ctrl_trig_reg = 0;
+               bits_slave_delay = BIT_SLV1_DLY_EN;
+               break;
+       case EXT_SLAVE_TYPE_COMPASS:
+               addr_reg = MPUREG_I2C_SLV0_ADDR;
+               reg_reg  = MPUREG_I2C_SLV0_REG;
+               ctrl_reg = MPUREG_I2C_SLV0_CTRL;
+               addr_trig_reg = MPUREG_I2C_SLV2_ADDR;
+               reg_trig_reg  = MPUREG_I2C_SLV2_REG;
+               ctrl_trig_reg = MPUREG_I2C_SLV2_CTRL;
+               d0_trig_reg   = MPUREG_I2C_SLV2_DO;
+               bits_slave_delay = BIT_SLV2_DLY_EN | BIT_SLV0_DLY_EN;
+               break;
+       case EXT_SLAVE_TYPE_PRESSURE:
+               addr_reg = MPUREG_I2C_SLV3_ADDR;
+               reg_reg  = MPUREG_I2C_SLV3_REG;
+               ctrl_reg = MPUREG_I2C_SLV3_CTRL;
+               addr_trig_reg = MPUREG_I2C_SLV4_ADDR;
+               reg_trig_reg  = MPUREG_I2C_SLV4_REG;
+               ctrl_trig_reg = MPUREG_I2C_SLV4_CTRL;
+               bits_slave_delay = BIT_SLV4_DLY_EN | BIT_SLV3_DLY_EN;
+               break;
+       default:
+               LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+               return INV_ERROR_INVALID_PARAMETER;
+       };
+
+       /* return if this slave has already been set */
+       if ((slave_address &&
+            ((mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay)
+                   == bits_slave_delay)) ||
+           (!slave_address &&
+            (mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) ==
+                   0))
+               return 0;
+
+       result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
 
        /* Address */
        result = inv_serial_single_write(gyro_handle,
                                         mldl_cfg->mpu_chip_info->addr,
-                                        MPUREG_AUX_SLV_ADDR, slave_address);
+                                        addr_reg, slave_address);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
        /* Register */
-       result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
-                                MPUREG_ACCEL_BURST_ADDR, 1, &reg);
+       result = inv_serial_single_write(gyro_handle,
+                                        mldl_cfg->mpu_chip_info->addr,
+                                        reg_reg, slave_reg);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
-       reg = ((reg & 0x80) | slave_reg);
+
+       /* Length, byte swapping, grouping & enable */
+       if (slave_len > BITS_SLV_LENG) {
+               MPL_LOGW("Limiting slave burst read length to "
+                        "the allowed maximum (15B, req. %d)\n", slave_len);
+               slave_len = BITS_SLV_LENG;
+               return INV_ERROR_INVALID_CONFIGURATION;
+       }
+       reg = slave_len;
+       if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) {
+               reg |= BIT_SLV_BYTE_SW;
+               if (slave_reg & 1)
+                       reg |= BIT_SLV_GRP;
+       }
+       if (slave_address)
+               reg |= BIT_SLV_ENABLE;
+
        result = inv_serial_single_write(gyro_handle,
                                         mldl_cfg->mpu_chip_info->addr,
-                                        MPUREG_ACCEL_BURST_ADDR, reg);
+                                        ctrl_reg, reg);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
 
-       /* Length */
-       result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
-                                MPUREG_USER_CTRL, 1, &reg);
-       if (result) {
-               LOG_RESULT_LOCATION(result);
-               return result;
+       /* Trigger */
+       if (addr_trig_reg) {
+               /* If slave address is 0 this clears the trigger */
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                addr_trig_reg,
+                                                slave_address & ~BIT_I2C_READ);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
        }
-       reg = (reg & ~BIT_AUX_RD_LENG);
-       result = inv_serial_single_write(
-               gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_USER_CTRL, reg);
+
+       if (slave && slave->trigger && reg_trig_reg) {
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                reg_trig_reg,
+                                                slave->trigger->reg);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                ctrl_trig_reg,
+                                                BIT_SLV_ENABLE | 0x01);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                d0_trig_reg,
+                                                slave->trigger->value);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+       } else if (ctrl_trig_reg) {
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                ctrl_trig_reg, 0x00);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+       }
+
+       /* Data rate */
+       if (slave) {
+               struct ext_slave_config config;
+               long data;
+               config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
+               config.len = sizeof(long);
+               config.apply = false;
+               config.data = &data;
+               if (!(slave->get_config))
+                       return INV_ERROR_INVALID_CONFIGURATION;
+
+               result = slave->get_config(NULL, slave, slave_pdata, &config);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               MPL_LOGI("Slave %d ODR: %ld Hz\n", slave_id, data / 1000);
+               divider = ((1000 * inv_mpu_get_sampling_rate_hz(
+                                   mldl_cfg->mpu_gyro_cfg))
+                       / data) - 1;
+       } else {
+               divider = 0;
+       }
+
+       result = inv_serial_read(gyro_handle,
+                               mldl_cfg->mpu_chip_info->addr,
+                               MPUREG_I2C_MST_DELAY_CTRL,
+                               1, &delay_ctrl_orig);
+       delay_ctrl = delay_ctrl_orig;
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
 
+       if (divider > 0 && divider <= MASK_I2C_MST_DLY) {
+               result = inv_serial_read(gyro_handle,
+                                        mldl_cfg->mpu_chip_info->addr,
+                                        MPUREG_I2C_SLV4_CTRL, 1, &reg);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+               if ((reg & MASK_I2C_MST_DLY) &&
+                       ((long)(reg & MASK_I2C_MST_DLY) !=
+                               (divider & MASK_I2C_MST_DLY))) {
+                       MPL_LOGW("Changing slave divider: %ld to %ld\n",
+                                (long)(reg & MASK_I2C_MST_DLY),
+                                (divider & MASK_I2C_MST_DLY));
+
+               }
+               reg |= (unsigned char)(divider & MASK_I2C_MST_DLY);
+               result = inv_serial_single_write(gyro_handle,
+                                                mldl_cfg->mpu_chip_info->addr,
+                                                MPUREG_I2C_SLV4_CTRL,
+                                                reg);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+
+               delay_ctrl |= bits_slave_delay;
+       } else {
+               delay_ctrl &= ~(bits_slave_delay);
+       }
+       if (delay_ctrl != delay_ctrl_orig) {
+               result = inv_serial_single_write(
+                       gyro_handle, mldl_cfg->mpu_chip_info->addr,
+                       MPUREG_I2C_MST_DELAY_CTRL,
+                       delay_ctrl);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       return result;
+               }
+       }
+
+       if (slave_address)
+               mldl_cfg->inv_mpu_state->i2c_slaves_enabled |=
+                       bits_slave_delay;
+       else
+               mldl_cfg->inv_mpu_state->i2c_slaves_enabled &=
+                       ~bits_slave_delay;
+
        return result;
 }
 
-
 static int mpu_set_slave(struct mldl_cfg *mldl_cfg,
                         void *gyro_handle,
                         struct ext_slave_descr *slave,
                         struct ext_slave_platform_data *slave_pdata,
                         int slave_id)
 {
-       return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave,
+       return mpu_set_slave_mpu60xx(mldl_cfg, gyro_handle, slave,
                                     slave_pdata, slave_id);
 }
 /**
@@ -876,7 +1015,7 @@ int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
                        return result;
                }
 
-#define ML_SKIP_CHECK 20
+#define ML_SKIP_CHECK 38
                for (offset = 0; offset < write_size; offset++) {
                        /* skip the register memory locations */
                        if (bank == 0 && offset < ML_SKIP_CHECK)
@@ -905,25 +1044,17 @@ static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
        unsigned char regs[7];
 
        /* Wake up the part */
-       result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, false, false,
-                                 !(sensors & INV_X_GYRO),
-                                 !(sensors & INV_Y_GYRO),
-                                 !(sensors & INV_Z_GYRO));
-
-       if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) &&
-           !mpu_was_reset(mldl_cfg, gyro_handle)) {
-               return INV_SUCCESS;
-       }
-
+       result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, sensors);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
+
+       /* Always set the INT_ENABLE and DIVIDER as the Accel Only mode for 6050
+          can set these too */
        result = inv_serial_single_write(
                gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_INT_CFG,
-               (mldl_cfg->mpu_gyro_cfg->int_config |
-                       mldl_cfg->pdata->int_config));
+               MPUREG_INT_ENABLE, (mldl_cfg->mpu_gyro_cfg->int_config));
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
@@ -935,18 +1066,34 @@ static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
                LOG_RESULT_LOCATION(result);
                return result;
        }
+
+       if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) &&
+           !mpu_was_reset(mldl_cfg, gyro_handle)) {
+               return INV_SUCCESS;
+       }
+
+       /* Configure the MPU */
+       result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+       if (result) {
+               LOG_RESULT_LOCATION(result);
+               return result;
+       }
        result = mpu_set_clock_source(gyro_handle, mldl_cfg);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
 
-       reg = DLPF_FS_SYNC_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync,
-                                mldl_cfg->mpu_gyro_cfg->full_scale,
-                                mldl_cfg->mpu_gyro_cfg->lpf);
+       reg = MPUREG_GYRO_CONFIG_VALUE(0, 0, 0,
+                                      mldl_cfg->mpu_gyro_cfg->full_scale);
+       result = inv_serial_single_write(
+               gyro_handle, mldl_cfg->mpu_chip_info->addr,
+               MPUREG_GYRO_CONFIG, reg);
+       reg = MPUREG_CONFIG_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync,
+                                 mldl_cfg->mpu_gyro_cfg->lpf);
        result = inv_serial_single_write(
                gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_DLPF_FS_SYNC, reg);
+               MPUREG_CONFIG, reg);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
@@ -974,21 +1121,24 @@ static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
 
        result = inv_serial_single_write(
                gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_XG_OFFS_TC, mldl_cfg->mpu_offsets->tc[0]);
+               MPUREG_XG_OFFS_TC,
+               ((mldl_cfg->mpu_offsets->tc[0] << 1) & BITS_XG_OFFS_TC));
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
+       regs[0] = ((mldl_cfg->mpu_offsets->tc[1] << 1) & BITS_YG_OFFS_TC);
        result = inv_serial_single_write(
                gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_YG_OFFS_TC, mldl_cfg->mpu_offsets->tc[1]);
+               MPUREG_YG_OFFS_TC, regs[0]);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
        }
        result = inv_serial_single_write(
                gyro_handle, mldl_cfg->mpu_chip_info->addr,
-               MPUREG_ZG_OFFS_TC, mldl_cfg->mpu_offsets->tc[2]);
+               MPUREG_ZG_OFFS_TC,
+               ((mldl_cfg->mpu_offsets->tc[2] << 1) & BITS_ZG_OFFS_TC));
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
@@ -1252,8 +1402,9 @@ int inv_mpu_open(struct mldl_cfg *mldl_cfg,
         * Take the DMP out of sleep, and
         * read the product_id, sillicon rev and whoami
         */
-       mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
-       result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, RESET, 0, 0, 0, 0);
+       mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
+       result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, true,
+                                 INV_THREE_AXIS_GYRO);
        if (result) {
                LOG_RESULT_LOCATION(result);
                return result;
@@ -1301,9 +1452,13 @@ int inv_mpu_open(struct mldl_cfg *mldl_cfg,
                return result;
        }
 
+       for (ii = 0; ii < GYRO_NUM_AXES; ii++) {
+               mldl_cfg->mpu_offsets->tc[ii] =
+                   (mldl_cfg->mpu_offsets->tc[ii] & BITS_XG_OFFS_TC) >> 1;
+       }
 
 #if INV_CACHE_DMP != 0
-       result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP, 0, 0, 0);
+       result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, 0);
 #endif
        if (result) {
                LOG_RESULT_LOCATION(result);
@@ -1581,12 +1736,8 @@ int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
        /* Gyro */
        if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] &&
            !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) {
-               result = mpu3050_pwr_mgmt(
-                       mldl_cfg, gyro_handle, 0,
-                       suspend_dmp && suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE],
-                       (unsigned char)(sensors & INV_X_GYRO),
-                       (unsigned char)(sensors & INV_Y_GYRO),
-                       (unsigned char)(sensors & INV_Z_GYRO));
+               result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false,
+                                       ((~sensors) & INV_ALL_SENSORS));
                if (result) {
                        LOG_RESULT_LOCATION(result);
                        return result;
similarity index 98%
rename from drivers/misc/inv_mpu/mldl_print_cfg.c
rename to drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c
index e2b8d30..7379a17 100644 (file)
@@ -75,6 +75,7 @@ void mldl_print_cfg(struct mldl_cfg *mldl_cfg)
        MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision);
        MPL_LOGV("product_id       = %02x\n", mpu_chip_info->product_id);
        MPL_LOGV("gyro_sens_trim   = %02x\n", mpu_chip_info->gyro_sens_trim);
+       MPL_LOGV("accel_sens_trim  = %02x\n", mpu_chip_info->accel_sens_trim);
 
        MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors);
        MPL_LOGV("ignore_system_suspend= %04x\n",
similarity index 99%
rename from drivers/misc/inv_mpu/mlsl-kernel.c
rename to drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c
index 19adf51..f1c228f 100644 (file)
@@ -20,7 +20,7 @@
 #include "mlsl.h"
 #include <linux/i2c.h>
 #include "log.h"
-#include "mpu3050.h"
+#include "mpu6050b1.h"
 
 static int inv_i2c_write(struct i2c_adapter *i2c_adap,
                            unsigned char address,
similarity index 94%
rename from drivers/misc/inv_mpu/mpu-dev.c
rename to drivers/misc/inv_mpu/mpu6050/mpu-dev.c
index ddaf086..e171dc2 100644 (file)
@@ -49,6 +49,7 @@
 #include "mldl_cfg.h"
 #include <linux/mpu.h>
 
+#include "accel/mpu6050.h"
 
 /* Platform data for the MPU */
 struct mpu_private_data {
@@ -970,12 +971,34 @@ int inv_mpu_register_slave(struct module *slave_module,
                }
        }
 
+       if (slave_descr->type == EXT_SLAVE_TYPE_ACCEL &&
+           slave_descr->id == ACCEL_ID_MPU6050 &&
+           slave_descr->config) {
+               /* pass a reference to the mldl_cfg data
+                  structure to the mpu6050 accel "class" */
+               struct ext_slave_config config;
+               config.key = MPU_SLAVE_CONFIG_INTERNAL_REFERENCE;
+               config.len = sizeof(struct mldl_cfg *);
+               config.apply = true;
+               config.data = mldl_cfg;
+               result = slave_descr->config(
+                       slave_client->adapter, slave_descr,
+                       slave_pdata, &config);
+               if (result) {
+                       LOG_RESULT_LOCATION(result);
+                       goto out_slavedescr_exit;
+               }
+       }
        pdata_slave[slave_descr->type] = slave_pdata;
        mpu->slave_modules[slave_descr->type] = slave_module;
        mldl_cfg->slave[slave_descr->type] = slave_descr;
 
        goto out_unlock_mutex;
 
+out_slavedescr_exit:
+       if (slave_descr->exit)
+               slave_descr->exit(slave_client->adapter,
+                                 slave_descr, slave_pdata);
 out_unlock_mutex:
        mutex_unlock(&mpu->mutex);
 
@@ -1059,6 +1082,7 @@ int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
        struct mldl_cfg *mldl_cfg;
        int res = 0;
        int ii = 0;
+       unsigned long irq_flags;
 
        dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
 
@@ -1138,15 +1162,45 @@ int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
        if (client->irq) {
                dev_info(&client->adapter->dev,
                         "Installing irq using %d\n", client->irq);
-               res = mpuirq_init(client, mldl_cfg);
+               if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
+                       irq_flags = IRQF_TRIGGER_FALLING;
+               else
+                       irq_flags = IRQF_TRIGGER_RISING;
+               res = mpuirq_init(client, mldl_cfg, irq_flags);
+
                if (res)
                        goto out_mpuirq_failed;
        } else {
                dev_WARN(&client->adapter->dev,
                         "Missing %s IRQ\n", MPU_NAME);
        }
+       if (!strcmp(mpu_id[1].name, devid->name)) {
+               /* Special case to re-use the inv_mpu_register_slave */
+               struct ext_slave_platform_data *slave_pdata;
+               slave_pdata = kzalloc(sizeof(*slave_pdata), GFP_KERNEL);
+               if (!slave_pdata) {
+                       res = -ENOMEM;
+                       goto out_slave_pdata_kzalloc_failed;
+               }
+               slave_pdata->bus = EXT_SLAVE_BUS_PRIMARY;
+               for (ii = 0; ii < 9; ii++)
+                       slave_pdata->orientation[ii] = pdata->orientation[ii];
+               res = inv_mpu_register_slave(
+                       NULL, client,
+                       slave_pdata,
+                       mpu6050_get_slave_descr);
+               if (res) {
+                       /* if inv_mpu_register_slave fails there are no pointer
+                          references to the memory allocated to slave_pdata */
+                       kfree(slave_pdata);
+                       goto out_slave_pdata_kzalloc_failed;
+               }
+       }
        return res;
 
+out_slave_pdata_kzalloc_failed:
+       if (client->irq)
+               mpuirq_exit();
 out_mpuirq_failed:
        misc_deregister(&mpu->dev);
 out_misc_register_failed:
@@ -1190,6 +1244,17 @@ static int mpu_remove(struct i2c_client *client)
                slave_adapter[EXT_SLAVE_TYPE_COMPASS],
                slave_adapter[EXT_SLAVE_TYPE_PRESSURE]);
 
+       if (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL] &&
+               (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL]->id ==
+                       ACCEL_ID_MPU6050)) {
+               struct ext_slave_platform_data *slave_pdata =
+                       mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL];
+               inv_mpu_unregister_slave(
+                       client,
+                       mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+                       mpu6050_get_slave_descr);
+               kfree(slave_pdata);
+       }
 
        if (client->irq)
                mpuirq_exit();
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h
new file mode 100644 (file)
index 0000000..686f6e5
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+       $License:
+       Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+       $
+ */
+
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ *      @file     mpu6050.h
+ *      @brief
+ */
+
+#ifndef __MPU_H_
+#error Do not include this file directly.  Include mpu.h instead.
+#endif
+
+#ifndef __MPU6050B1_H_
+#define __MPU6050B1_H_
+
+
+#define MPU_NAME "mpu6050B1"
+#define DEFAULT_MPU_SLAVEADDR          0x68
+
+/*==== MPU6050B1 REGISTER SET ====*/
+enum {
+       MPUREG_XG_OFFS_TC = 0,                  /* 0x00,   0 */
+       MPUREG_YG_OFFS_TC,                      /* 0x01,   1 */
+       MPUREG_ZG_OFFS_TC,                      /* 0x02,   2 */
+       MPUREG_X_FINE_GAIN,                     /* 0x03,   3 */
+       MPUREG_Y_FINE_GAIN,                     /* 0x04,   4 */
+       MPUREG_Z_FINE_GAIN,                     /* 0x05,   5 */
+       MPUREG_XA_OFFS_H,                       /* 0x06,   6 */
+       MPUREG_XA_OFFS_L,                       /* 0x07,   7 */
+       MPUREG_YA_OFFS_H,                       /* 0x08,   8 */
+       MPUREG_YA_OFFS_L,                       /* 0x09,   9 */
+       MPUREG_ZA_OFFS_H,                       /* 0x0a,  10 */
+       MPUREG_ZA_OFFS_L,                       /* 0x0B,  11 */
+       MPUREG_PRODUCT_ID,                      /* 0x0c,  12 */
+       MPUREG_0D_RSVD,                         /* 0x0d,  13 */
+       MPUREG_0E_RSVD,                         /* 0x0e,  14 */
+       MPUREG_0F_RSVD,                         /* 0x0f,  15 */
+       MPUREG_10_RSVD,                         /* 0x00,  16 */
+       MPUREG_11_RSVD,                         /* 0x11,  17 */
+       MPUREG_12_RSVD,                         /* 0x12,  18 */
+       MPUREG_XG_OFFS_USRH,                    /* 0x13,  19 */
+       MPUREG_XG_OFFS_USRL,                    /* 0x14,  20 */
+       MPUREG_YG_OFFS_USRH,                    /* 0x15,  21 */
+       MPUREG_YG_OFFS_USRL,                    /* 0x16,  22 */
+       MPUREG_ZG_OFFS_USRH,                    /* 0x17,  23 */
+       MPUREG_ZG_OFFS_USRL,                    /* 0x18,  24 */
+       MPUREG_SMPLRT_DIV,                      /* 0x19,  25 */
+       MPUREG_CONFIG,                          /* 0x1A,  26 */
+       MPUREG_GYRO_CONFIG,                     /* 0x1b,  27 */
+       MPUREG_ACCEL_CONFIG,                    /* 0x1c,  28 */
+       MPUREG_ACCEL_FF_THR,                    /* 0x1d,  29 */
+       MPUREG_ACCEL_FF_DUR,                    /* 0x1e,  30 */
+       MPUREG_ACCEL_MOT_THR,                   /* 0x1f,  31 */
+       MPUREG_ACCEL_MOT_DUR,                   /* 0x20,  32 */
+       MPUREG_ACCEL_ZRMOT_THR,                 /* 0x21,  33 */
+       MPUREG_ACCEL_ZRMOT_DUR,                 /* 0x22,  34 */
+       MPUREG_FIFO_EN,                         /* 0x23,  35 */
+       MPUREG_I2C_MST_CTRL,                    /* 0x24,  36 */
+       MPUREG_I2C_SLV0_ADDR,                   /* 0x25,  37 */
+       MPUREG_I2C_SLV0_REG,                    /* 0x26,  38 */
+       MPUREG_I2C_SLV0_CTRL,                   /* 0x27,  39 */
+       MPUREG_I2C_SLV1_ADDR,                   /* 0x28,  40 */
+       MPUREG_I2C_SLV1_REG,                    /* 0x29,  41 */
+       MPUREG_I2C_SLV1_CTRL,                   /* 0x2a,  42 */
+       MPUREG_I2C_SLV2_ADDR,                   /* 0x2B,  43 */
+       MPUREG_I2C_SLV2_REG,                    /* 0x2c,  44 */
+       MPUREG_I2C_SLV2_CTRL,                   /* 0x2d,  45 */
+       MPUREG_I2C_SLV3_ADDR,                   /* 0x2E,  46 */
+       MPUREG_I2C_SLV3_REG,                    /* 0x2f,  47 */
+       MPUREG_I2C_SLV3_CTRL,                   /* 0x30,  48 */
+       MPUREG_I2C_SLV4_ADDR,                   /* 0x31,  49 */
+       MPUREG_I2C_SLV4_REG,                    /* 0x32,  50 */
+       MPUREG_I2C_SLV4_DO,                     /* 0x33,  51 */
+       MPUREG_I2C_SLV4_CTRL,                   /* 0x34,  52 */
+       MPUREG_I2C_SLV4_DI,                     /* 0x35,  53 */
+       MPUREG_I2C_MST_STATUS,                  /* 0x36,  54 */
+       MPUREG_INT_PIN_CFG,                     /* 0x37,  55 */
+       MPUREG_INT_ENABLE,                      /* 0x38,  56 */
+       MPUREG_DMP_INT_STATUS,                  /* 0x39,  57 */
+       MPUREG_INT_STATUS,                      /* 0x3A,  58 */
+       MPUREG_ACCEL_XOUT_H,                    /* 0x3B,  59 */
+       MPUREG_ACCEL_XOUT_L,                    /* 0x3c,  60 */
+       MPUREG_ACCEL_YOUT_H,                    /* 0x3d,  61 */
+       MPUREG_ACCEL_YOUT_L,                    /* 0x3e,  62 */
+       MPUREG_ACCEL_ZOUT_H,                    /* 0x3f,  63 */
+       MPUREG_ACCEL_ZOUT_L,                    /* 0x40,  64 */
+       MPUREG_TEMP_OUT_H,                      /* 0x41,  65 */
+       MPUREG_TEMP_OUT_L,                      /* 0x42,  66 */
+       MPUREG_GYRO_XOUT_H,                     /* 0x43,  67 */
+       MPUREG_GYRO_XOUT_L,                     /* 0x44,  68 */
+       MPUREG_GYRO_YOUT_H,                     /* 0x45,  69 */
+       MPUREG_GYRO_YOUT_L,                     /* 0x46,  70 */
+       MPUREG_GYRO_ZOUT_H,                     /* 0x47,  71 */
+       MPUREG_GYRO_ZOUT_L,                     /* 0x48,  72 */
+       MPUREG_EXT_SLV_SENS_DATA_00,            /* 0x49,  73 */
+       MPUREG_EXT_SLV_SENS_DATA_01,            /* 0x4a,  74 */
+       MPUREG_EXT_SLV_SENS_DATA_02,            /* 0x4b,  75 */
+       MPUREG_EXT_SLV_SENS_DATA_03,            /* 0x4c,  76 */
+       MPUREG_EXT_SLV_SENS_DATA_04,            /* 0x4d,  77 */
+       MPUREG_EXT_SLV_SENS_DATA_05,            /* 0x4e,  78 */
+       MPUREG_EXT_SLV_SENS_DATA_06,            /* 0x4F,  79 */
+       MPUREG_EXT_SLV_SENS_DATA_07,            /* 0x50,  80 */
+       MPUREG_EXT_SLV_SENS_DATA_08,            /* 0x51,  81 */
+       MPUREG_EXT_SLV_SENS_DATA_09,            /* 0x52,  82 */
+       MPUREG_EXT_SLV_SENS_DATA_10,            /* 0x53,  83 */
+       MPUREG_EXT_SLV_SENS_DATA_11,            /* 0x54,  84 */
+       MPUREG_EXT_SLV_SENS_DATA_12,            /* 0x55,  85 */
+       MPUREG_EXT_SLV_SENS_DATA_13,            /* 0x56,  86 */
+       MPUREG_EXT_SLV_SENS_DATA_14,            /* 0x57,  87 */
+       MPUREG_EXT_SLV_SENS_DATA_15,            /* 0x58,  88 */
+       MPUREG_EXT_SLV_SENS_DATA_16,            /* 0x59,  89 */
+       MPUREG_EXT_SLV_SENS_DATA_17,            /* 0x5a,  90 */
+       MPUREG_EXT_SLV_SENS_DATA_18,            /* 0x5B,  91 */
+       MPUREG_EXT_SLV_SENS_DATA_19,            /* 0x5c,  92 */
+       MPUREG_EXT_SLV_SENS_DATA_20,            /* 0x5d,  93 */
+       MPUREG_EXT_SLV_SENS_DATA_21,            /* 0x5e,  94 */
+       MPUREG_EXT_SLV_SENS_DATA_22,            /* 0x5f,  95 */
+       MPUREG_EXT_SLV_SENS_DATA_23,            /* 0x60,  96 */
+       MPUREG_ACCEL_INTEL_STATUS,              /* 0x61,  97 */
+       MPUREG_62_RSVD,                         /* 0x62,  98 */
+       MPUREG_I2C_SLV0_DO,                     /* 0x63,  99 */
+       MPUREG_I2C_SLV1_DO,                     /* 0x64, 100 */
+       MPUREG_I2C_SLV2_DO,                     /* 0x65, 101 */
+       MPUREG_I2C_SLV3_DO,                     /* 0x66, 102 */
+       MPUREG_I2C_MST_DELAY_CTRL,              /* 0x67, 103 */
+       MPUREG_SIGNAL_PATH_RESET,               /* 0x68, 104 */
+       MPUREG_ACCEL_INTEL_CTRL,                /* 0x69, 105 */
+       MPUREG_USER_CTRL,                       /* 0x6A, 106 */
+       MPUREG_PWR_MGMT_1,                      /* 0x6B, 107 */
+       MPUREG_PWR_MGMT_2,                      /* 0x6C, 108 */
+       MPUREG_BANK_SEL,                        /* 0x6D, 109 */
+       MPUREG_MEM_START_ADDR,                  /* 0x6E, 100 */
+       MPUREG_MEM_R_W,                         /* 0x6F, 111 */
+       MPUREG_DMP_CFG_1,                       /* 0x70, 112 */
+       MPUREG_DMP_CFG_2,                       /* 0x71, 113 */
+       MPUREG_FIFO_COUNTH,                     /* 0x72, 114 */
+       MPUREG_FIFO_COUNTL,                     /* 0x73, 115 */
+       MPUREG_FIFO_R_W,                        /* 0x74, 116 */
+       MPUREG_WHOAMI,                          /* 0x75, 117 */
+
+       NUM_OF_MPU_REGISTERS                    /* = 0x76, 118 */
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+       MEM_RAM_BANK_0 = 0,
+       MEM_RAM_BANK_1,
+       MEM_RAM_BANK_2,
+       MEM_RAM_BANK_3,
+       MEM_RAM_BANK_4,
+       MEM_RAM_BANK_5,
+       MEM_RAM_BANK_6,
+       MEM_RAM_BANK_7,
+       MEM_RAM_BANK_8,
+       MEM_RAM_BANK_9,
+       MEM_RAM_BANK_10,
+       MEM_RAM_BANK_11,
+       MPU_MEM_NUM_RAM_BANKS,
+       MPU_MEM_OTP_BANK_0 = 16
+};
+
+
+/*==== MPU6050B1 parameters ====*/
+
+#define NUM_REGS               (NUM_OF_MPU_REGISTERS)
+#define START_SENS_REGS                (0x3B)
+#define NUM_SENS_REGS          (0x60 - START_SENS_REGS + 1)
+
+/*---- MPU Memory ----*/
+#define NUM_BANKS              (MPU_MEM_NUM_RAM_BANKS)
+#define BANK_SIZE              (256)
+#define MEM_SIZE               (NUM_BANKS * BANK_SIZE)
+#define MPU_MEM_BANK_SIZE      (BANK_SIZE)     /*alternative name */
+
+#define FIFO_HW_SIZE           (1024)
+
+#define NUM_EXT_SLAVES         (4)
+
+
+/*==== BITS FOR MPU6050B1 ====*/
+/*---- MPU6050B1 'XG_OFFS_TC' register (0, 1, 2) ----*/
+#define BIT_PU_SLEEP_MODE                      0x80
+#define BITS_XG_OFFS_TC                                0x7E
+#define BIT_OTP_BNK_VLD                                0x01
+
+#define BIT_I2C_MST_VDDIO                      0x80
+#define BITS_YG_OFFS_TC                                0x7E
+#define BITS_ZG_OFFS_TC                                0x7E
+/*---- MPU6050B1 'FIFO_EN' register (23) ----*/
+#define        BIT_TEMP_OUT                            0x80
+#define        BIT_GYRO_XOUT                           0x40
+#define        BIT_GYRO_YOUT                           0x20
+#define        BIT_GYRO_ZOUT                           0x10
+#define        BIT_ACCEL                               0x08
+#define        BIT_SLV_2                               0x04
+#define        BIT_SLV_1                               0x02
+#define        BIT_SLV_0                               0x01
+/*---- MPU6050B1 'CONFIG' register (1A) ----*/
+/*NONE                                         0xC0 */
+#define        BITS_EXT_SYNC_SET                       0x38
+#define        BITS_DLPF_CFG                           0x07
+/*---- MPU6050B1 'GYRO_CONFIG' register (1B) ----*/
+/* voluntarily modified label from BITS_FS_SEL to
+ * BITS_GYRO_FS_SEL to avoid confusion with MPU
+ */
+#define BITS_GYRO_FS_SEL                       0x18
+/*NONE                                         0x07 */
+/*---- MPU6050B1 'ACCEL_CONFIG' register (1C) ----*/
+#define BITS_ACCEL_FS_SEL                      0x18
+#define BITS_ACCEL_HPF                         0x07
+/*---- MPU6050B1 'I2C_MST_CTRL' register (24) ----*/
+#define BIT_MULT_MST_EN                                0x80
+#define BIT_WAIT_FOR_ES                                0x40
+#define BIT_SLV_3_FIFO_EN                      0x20
+#define BIT_I2C_MST_PSR                                0x10
+#define BITS_I2C_MST_CLK                       0x0F
+/*---- MPU6050B1 'I2C_SLV?_ADDR' register (27,2A,2D,30) ----*/
+#define BIT_I2C_READ                           0x80
+#define BIT_I2C_WRITE                          0x00
+#define BITS_I2C_ADDR                          0x7F
+/*---- MPU6050B1 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/
+#define BIT_SLV_ENABLE                         0x80
+#define BIT_SLV_BYTE_SW                                0x40
+#define BIT_SLV_REG_DIS                                0x20
+#define BIT_SLV_GRP                            0x10
+#define BITS_SLV_LENG                          0x0F
+/*---- MPU6050B1 'I2C_SLV4_ADDR' register (31) ----*/
+#define BIT_I2C_SLV4_RNW                       0x80
+/*---- MPU6050B1 'I2C_SLV4_CTRL' register (34) ----*/
+#define BIT_I2C_SLV4_EN                                0x80
+#define BIT_SLV4_DONE_INT_EN                   0x40
+#define BIT_SLV4_REG_DIS                       0x20
+#define MASK_I2C_MST_DLY                       0x1F
+/*---- MPU6050B1 'I2C_MST_STATUS' register (36) ----*/
+#define BIT_PASS_THROUGH                       0x80
+#define BIT_I2C_SLV4_DONE                      0x40
+#define BIT_I2C_LOST_ARB                       0x20
+#define BIT_I2C_SLV4_NACK                      0x10
+#define BIT_I2C_SLV3_NACK                      0x08
+#define BIT_I2C_SLV2_NACK                      0x04
+#define BIT_I2C_SLV1_NACK                      0x02
+#define BIT_I2C_SLV0_NACK                      0x01
+/*---- MPU6050B1 'INT_PIN_CFG' register (37) ----*/
+#define        BIT_ACTL                                0x80
+#define BIT_ACTL_LOW                           0x80
+#define BIT_ACTL_HIGH                          0x00
+#define        BIT_OPEN                                0x40
+#define        BIT_LATCH_INT_EN                        0x20
+#define        BIT_INT_ANYRD_2CLEAR                    0x10
+#define        BIT_ACTL_FSYNC                          0x08
+#define        BIT_FSYNC_INT_EN                        0x04
+#define        BIT_BYPASS_EN                           0x02
+#define        BIT_CLKOUT_EN                           0x01
+/*---- MPU6050B1 'INT_ENABLE' register (38) ----*/
+#define        BIT_FF_EN                               0x80
+#define        BIT_MOT_EN                              0x40
+#define        BIT_ZMOT_EN                             0x20
+#define        BIT_FIFO_OVERFLOW_EN                    0x10
+#define BIT_I2C_MST_INT_EN                     0x08
+#define        BIT_PLL_RDY_EN                          0x04
+#define BIT_DMP_INT_EN                         0x02
+#define        BIT_RAW_RDY_EN                          0x01
+/*---- MPU6050B1 'DMP_INT_STATUS' register (39) ----*/
+/*NONE                                         0x80 */
+/*NONE                                         0x40 */
+#define        BIT_DMP_INT_5                           0x20
+#define        BIT_DMP_INT_4                           0x10
+#define        BIT_DMP_INT_3                           0x08
+#define        BIT_DMP_INT_2                           0x04
+#define        BIT_DMP_INT_1                           0x02
+#define        BIT_DMP_INT_0                           0x01
+/*---- MPU6050B1 'INT_STATUS' register (3A) ----*/
+#define        BIT_FF_INT                              0x80
+#define        BIT_MOT_INT                             0x40
+#define        BIT_ZMOT_INT                            0x20
+#define        BIT_FIFO_OVERFLOW_INT                   0x10
+#define        BIT_I2C_MST_INT                         0x08
+#define        BIT_PLL_RDY_INT                         0x04
+#define BIT_DMP_INT                            0x02
+#define        BIT_RAW_DATA_RDY_INT                    0x01
+/*---- MPU6050B1 'MPUREG_I2C_MST_DELAY_CTRL' register (0x67) ----*/
+#define        BIT_DELAY_ES_SHADOW                     0x80
+#define        BIT_SLV4_DLY_EN                         0x10
+#define        BIT_SLV3_DLY_EN                         0x08
+#define        BIT_SLV2_DLY_EN                         0x04
+#define        BIT_SLV1_DLY_EN                         0x02
+#define        BIT_SLV0_DLY_EN                         0x01
+/*---- MPU6050B1 'BANK_SEL' register (6D) ----*/
+#define        BIT_PRFTCH_EN                           0x40
+#define        BIT_CFG_USER_BANK                       0x20
+#define        BITS_MEM_SEL                            0x1f
+/*---- MPU6050B1 'USER_CTRL' register (6A) ----*/
+#define        BIT_DMP_EN                              0x80
+#define        BIT_FIFO_EN                             0x40
+#define        BIT_I2C_MST_EN                          0x20
+#define        BIT_I2C_IF_DIS                          0x10
+#define        BIT_DMP_RST                             0x08
+#define        BIT_FIFO_RST                            0x04
+#define        BIT_I2C_MST_RST                         0x02
+#define        BIT_SIG_COND_RST                        0x01
+/*---- MPU6050B1 'PWR_MGMT_1' register (6B) ----*/
+#define        BIT_H_RESET                             0x80
+#define        BIT_SLEEP                               0x40
+#define        BIT_CYCLE                               0x20
+#define BIT_PD_PTAT                            0x08
+#define BITS_CLKSEL                            0x07
+/*---- MPU6050B1 'PWR_MGMT_2' register (6C) ----*/
+#define        BITS_LPA_WAKE_CTRL                      0xC0
+#define        BITS_LPA_WAKE_1HZ                       0x00
+#define        BITS_LPA_WAKE_2HZ                       0x40
+#define        BITS_LPA_WAKE_10HZ                      0x80
+#define        BITS_LPA_WAKE_40HZ                      0xC0
+#define        BIT_STBY_XA                             0x20
+#define        BIT_STBY_YA                             0x10
+#define        BIT_STBY_ZA                             0x08
+#define        BIT_STBY_XG                             0x04
+#define        BIT_STBY_YG                             0x02
+#define        BIT_STBY_ZG                             0x01
+
+#define ACCEL_MOT_THR_LSB (32) /* mg */
+#define ACCEL_MOT_DUR_LSB (1)
+#define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255)
+#define ACCEL_ZRMOT_DUR_LSB (64)
+
+/*----------------------------------------------------------------------------*/
+/*---- Alternative names to take care of conflicts with current mpu3050.h ----*/
+/*----------------------------------------------------------------------------*/
+
+/*-- registers --*/
+#define MPUREG_DLPF_FS_SYNC    MPUREG_CONFIG                   /* 0x1A */
+
+#define MPUREG_PWR_MGM         MPUREG_PWR_MGMT_1               /* 0x6B */
+#define MPUREG_FIFO_EN1                MPUREG_FIFO_EN                  /* 0x23 */
+#define MPUREG_INT_CFG         MPUREG_INT_ENABLE               /* 0x38 */
+#define MPUREG_X_OFFS_USRH     MPUREG_XG_OFFS_USRH             /* 0x13 */
+#define MPUREG_WHO_AM_I                MPUREG_WHOAMI                   /* 0x75 */
+#define MPUREG_23_RSVD         MPUREG_EXT_SLV_SENS_DATA_00     /* 0x49 */
+
+/*-- bits --*/
+/* 'USER_CTRL' register */
+#define BIT_AUX_IF_EN          BIT_I2C_MST_EN
+#define BIT_AUX_RD_LENG                BIT_I2C_MST_EN
+#define BIT_IME_IF_RST         BIT_I2C_MST_RST
+#define BIT_GYRO_RST           BIT_SIG_COND_RST
+/* 'INT_ENABLE' register */
+#define BIT_RAW_RDY            BIT_RAW_DATA_RDY_INT
+#define BIT_MPU_RDY_EN         BIT_PLL_RDY_EN
+/* 'INT_STATUS' register */
+#define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT
+
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2             1       /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1             2       /* MPU6050B1 Device */
+
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5                105
+#define MPU_PRODUCT_KEY_B2_F1          431
+
+/*---- structure containing control variables used by MLDL ----*/
+/*---- MPU clock source settings ----*/
+/*---- MPU filter selections ----*/
+enum mpu_filter {
+       MPU_FILTER_256HZ_NOLPF2 = 0,
+       MPU_FILTER_188HZ,
+       MPU_FILTER_98HZ,
+       MPU_FILTER_42HZ,
+       MPU_FILTER_20HZ,
+       MPU_FILTER_10HZ,
+       MPU_FILTER_5HZ,
+       MPU_FILTER_2100HZ_NOLPF,
+       NUM_MPU_FILTER
+};
+
+enum mpu_fullscale {
+       MPU_FS_250DPS = 0,
+       MPU_FS_500DPS,
+       MPU_FS_1000DPS,
+       MPU_FS_2000DPS,
+       NUM_MPU_FS
+};
+
+enum mpu_clock_sel {
+       MPU_CLK_SEL_INTERNAL = 0,
+       MPU_CLK_SEL_PLLGYROX,
+       MPU_CLK_SEL_PLLGYROY,
+       MPU_CLK_SEL_PLLGYROZ,
+       MPU_CLK_SEL_PLLEXT32K,
+       MPU_CLK_SEL_PLLEXT19M,
+       MPU_CLK_SEL_RESERVED,
+       MPU_CLK_SEL_STOP,
+       NUM_CLK_SEL
+};
+
+enum mpu_ext_sync {
+       MPU_EXT_SYNC_NONE = 0,
+       MPU_EXT_SYNC_TEMP,
+       MPU_EXT_SYNC_GYROX,
+       MPU_EXT_SYNC_GYROY,
+       MPU_EXT_SYNC_GYROZ,
+       MPU_EXT_SYNC_ACCELX,
+       MPU_EXT_SYNC_ACCELY,
+       MPU_EXT_SYNC_ACCELZ,
+       NUM_MPU_EXT_SYNC
+};
+
+#define MPUREG_CONFIG_VALUE(ext_sync, lpf) \
+       ((ext_sync << 3) | lpf)
+
+#define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \
+       ((x_st ? 0x80 : 0) |                            \
+        (y_st ? 0x70 : 0) |                            \
+        (z_st ? 0x60 : 0) |                            \
+        (full_scale << 3))
+
+#endif                         /* __MPU6050_H_ */
index dfd87cd..d8b721e 100644 (file)
@@ -193,7 +193,8 @@ static struct miscdevice mpuirq_device = {
        .fops = &mpuirq_fops,
 };
 
-int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg)
+int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg,
+               unsigned long irq_flags)
 {
 
        int res;
@@ -211,15 +212,9 @@ int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg)
        mpuirq_dev_data.dev = &mpuirq_device;
 
        if (mpuirq_dev_data.irq) {
-               unsigned long flags;
-               if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
-                       flags = IRQF_TRIGGER_FALLING;
-               else
-                       flags = IRQF_TRIGGER_RISING;
-
-               flags |= IRQF_SHARED;
+               irq_flags |= IRQF_SHARED;
                res =
-                   request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags,
+                   request_irq(mpuirq_dev_data.irq, mpuirq_handler, irq_flags,
                                interface, &mpuirq_dev_data.irq);
                if (res) {
                        dev_err(&mpu_client->adapter->dev,
@@ -241,6 +236,7 @@ int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg)
 
        return res;
 }
+EXPORT_SYMBOL(mpuirq_init);
 
 void mpuirq_exit(void)
 {
@@ -252,6 +248,7 @@ void mpuirq_exit(void)
 
        return;
 }
+EXPORT_SYMBOL(mpuirq_exit);
 
 module_param(interface, charp, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(interface, "The Interface name");
index 3348071..073867d 100644 (file)
@@ -31,6 +31,7 @@
 #define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long)
 
 void mpuirq_exit(void);
-int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg);
+int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg,
+               unsigned long irq_flags);
 
 #endif
index 95e690e..22cfa4e 100644 (file)
@@ -249,6 +249,7 @@ out_request_irq:
 
        return res;
 }
+EXPORT_SYMBOL(slaveirq_init);
 
 void slaveirq_exit(struct ext_slave_platform_data *pdata)
 {
@@ -264,3 +265,4 @@ void slaveirq_exit(struct ext_slave_platform_data *pdata)
        kfree(pdata->irq_data);
        pdata->irq_data = NULL;
 }
+EXPORT_SYMBOL(slaveirq_exit);
index fd66ba0..1977b38 100644 (file)
@@ -158,6 +158,7 @@ enum ext_slave_id {
        ACCEL_ID_MPU6050,
 
        COMPASS_ID_AK8975,
+       COMPASS_ID_AK8963,
        COMPASS_ID_AK8972,
        COMPASS_ID_AMI30X,
        COMPASS_ID_AMI306,