summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorRobert Collins <rcollins@nvidia.com>2011-12-01 16:18:19 -0800
committerSimone Willett <swillett@nvidia.com>2012-02-14 09:46:41 -0800
commit2f3c7d00ed29fdb6ef107c4b304c1464153114d6 (patch)
tree2941e994734aabddee03925477834c4cf2ea4af0 /drivers/misc
parent8f4d3050455e55864262a5d5a1e249eadca79dd0 (diff)
mpu6050: Motion Libraries: Add kernel files for Invensense MPU6050.
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>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/misc/inv_mpu/Kconfig19
-rw-r--r--drivers/misc/inv_mpu/Makefile39
-rw-r--r--drivers/misc/inv_mpu/accel/mpu6050.c695
-rw-r--r--drivers/misc/inv_mpu/compass/Kconfig9
-rw-r--r--drivers/misc/inv_mpu/compass/Makefile3
-rw-r--r--drivers/misc/inv_mpu/compass/ak89xx.c522
-rw-r--r--drivers/misc/inv_mpu/mldl_cfg.h4
-rw-r--r--drivers/misc/inv_mpu/mpu3050/Makefile17
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mldl_cfg.c (renamed from drivers/misc/inv_mpu/mldl_cfg.c)0
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c (renamed from drivers/misc/inv_mpu/mldl_print_cfg.c)0
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c (renamed from drivers/misc/inv_mpu/mlsl-kernel.c)0
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mpu-dev.c (renamed from drivers/misc/inv_mpu/mpu-dev.c)8
-rw-r--r--drivers/misc/inv_mpu/mpu6050/Makefile18
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mldl_cfg.c1916
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c138
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c420
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mpu-dev.c1309
-rw-r--r--drivers/misc/inv_mpu/mpu6050b1.h435
-rw-r--r--drivers/misc/inv_mpu/mpuirq.c15
-rw-r--r--drivers/misc/inv_mpu/mpuirq.h3
-rw-r--r--drivers/misc/inv_mpu/slaveirq.c2
22 files changed, 5516 insertions, 58 deletions
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a8f6ab7fb89d..5b3728ebcaee 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -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
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig
index 53c7c200d453..4c231b576f68 100644
--- a/drivers/misc/inv_mpu/Kconfig
+++ b/drivers/misc/inv_mpu/Kconfig
@@ -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"
diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile
index 248648f6567c..f10c6844a087 100644
--- a/drivers/misc/inv_mpu/Makefile
+++ b/drivers/misc/inv_mpu/Makefile
@@ -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
index 000000000000..c5bb6784a411
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mpu6050.c
@@ -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;
+}
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/Kconfig b/drivers/misc/inv_mpu/compass/Kconfig
index 6c4c13a6e19a..7e6bac87d31e 100644
--- a/drivers/misc/inv_mpu/compass/Kconfig
+++ b/drivers/misc/inv_mpu/compass/Kconfig
@@ -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
diff --git a/drivers/misc/inv_mpu/compass/Makefile b/drivers/misc/inv_mpu/compass/Makefile
index aa8aa6a2657b..d76ce1b06f31 100644
--- a/drivers/misc/inv_mpu/compass/Makefile
+++ b/drivers/misc/inv_mpu/compass/Makefile
@@ -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
index 000000000000..d15b0b8dbe68
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ak89xx.c
@@ -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");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/mldl_cfg.h b/drivers/misc/inv_mpu/mldl_cfg.h
index 1c23878940b7..2b81b89179d5 100644
--- a/drivers/misc/inv_mpu/mldl_cfg.h
+++ b/drivers/misc/inv_mpu/mldl_cfg.h
@@ -31,7 +31,11 @@
#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
index 000000000000..9e5739302386
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/Makefile
@@ -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
diff --git a/drivers/misc/inv_mpu/mldl_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c
index ccacc8ec0b56..ccacc8ec0b56 100644
--- a/drivers/misc/inv_mpu/mldl_cfg.c
+++ b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c
diff --git a/drivers/misc/inv_mpu/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c
index e2b8d30cebaa..e2b8d30cebaa 100644
--- a/drivers/misc/inv_mpu/mldl_print_cfg.c
+++ b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c
diff --git a/drivers/misc/inv_mpu/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c
index 19adf5182c00..19adf5182c00 100644
--- a/drivers/misc/inv_mpu/mlsl-kernel.c
+++ b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c
diff --git a/drivers/misc/inv_mpu/mpu-dev.c b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c
index ddaf0864a440..22a837b35c57 100644
--- a/drivers/misc/inv_mpu/mpu-dev.c
+++ b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c
@@ -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
index 000000000000..a93aa97a6997
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/Makefile
@@ -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
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c
new file mode 100644
index 000000000000..22af0c200985
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c
@@ -0,0 +1,1916 @@
+/*
+ $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 MLDL
+ *
+ * @{
+ * @file mldl_cfg.c
+ * @brief The Motion Library Driver Layer.
+ */
+
+/* -------------------------------------------------------------------------- */
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <stddef.h>
+
+#include "mldl_cfg.h"
+#include <linux/mpu.h>
+#include "mpu6050b1.h"
+
+#include "mlsl.h"
+#include "mldl_print_cfg.h"
+#include "log.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "mldl_cfg:"
+
+/* -------------------------------------------------------------------------- */
+
+#define SLEEP 0
+#define WAKE_UP 7
+#define RESET 1
+#define STANDBY 1
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * @brief Stop the DMP running
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle)
+{
+ unsigned char user_ctrl_reg;
+ int result;
+
+ if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)
+ return INV_SUCCESS;
+
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST;
+ user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST;
+
+ result = inv_serial_single_write(gyro_handle,
+ mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED;
+
+ return result;
+}
+
+/**
+ * @brief Starts the DMP running
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle)
+{
+ unsigned char user_ctrl_reg;
+ int result;
+
+ if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
+ mldl_cfg->mpu_gyro_cfg->dmp_enable)
+ ||
+ ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
+ !mldl_cfg->mpu_gyro_cfg->dmp_enable))
+ return INV_SUCCESS;
+
+ result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL,
+ ((user_ctrl_reg & (~BIT_FIFO_EN))
+ | BIT_FIFO_RST));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ user_ctrl_reg |= BIT_DMP_EN;
+
+ if (mldl_cfg->mpu_gyro_cfg->fifo_enable)
+ user_ctrl_reg |= BIT_FIFO_EN;
+ else
+ user_ctrl_reg &= ~BIT_FIFO_EN;
+
+ user_ctrl_reg |= BIT_DMP_RST;
+
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED;
+
+ return result;
+}
+
+/**
+ * @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 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, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ 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_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;
+ }
+ }
+ } 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);
+ }
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_INT_PIN_CFG,
+ (mldl_cfg->pdata->int_config | BIT_BYPASS_EN));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ if (enable)
+ mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
+ else
+ mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
+
+ return result;
+}
+
+
+
+
+/**
+ * @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 mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
+ unsigned char enable)
+{
+ return mpu6050b1_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
+}
+
+
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+
+/* 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;
+};
+
+/* NOTE: product entries are in chronological order */
+static struct prod_rev_map_t prod_rev_map[] = {
+ /* 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 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.
+ * @param mlsl_handle
+ * an file handle to the serial communication device the
+ * device is connected to.
+ *
+ * @return 0 on success, a non-zero error code otherwise.
+ */
+static int inv_get_silicon_rev_mpu6050(
+ struct mldl_cfg *mldl_cfg, void *mlsl_handle)
+{
+ int result;
+ 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 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, &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,
+ memAddr, 1, &prod_rev);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ prod_rev >>= 2;
+
+ /* clean the prefetch and cfg user bank bits */
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_BANK_SEL, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ 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_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;
+ mpu_chip_info->accel_sens_trim = prod_rev_map[index].accel_trim;
+
+ return result;
+}
+#define inv_get_silicon_rev inv_get_silicon_rev_mpu6050
+
+
+/**
+ * @brief Enable / Disable the use MPU's secondary I2C interface level
+ * shifters.
+ * When enabled the secondary I2C interface to which the external
+ * device is connected runs at VDD voltage (main supply).
+ * When disabled the 2nd interface runs at VDDIO voltage.
+ * See the device specification for more details.
+ *
+ * @note using this API may produce unpredictable results, depending on how
+ * the MPU and slave device are setup on the target platform.
+ * Use of this API should entirely be restricted to system
+ * integrators. Once the correct value is found, there should be no
+ * need to change the level shifter at runtime.
+ *
+ * @pre Must be called after inv_serial_start().
+ * @note Typically called before inv_dmp_open().
+ *
+ * @param[in] enable:
+ * 0 to run at VDDIO (default),
+ * 1 to run at VDD.
+ *
+ * @return INV_SUCCESS if successfull, a non-zero error code otherwise.
+ */
+static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle, unsigned char enable)
+{
+ int result;
+ unsigned char regval;
+
+ result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_YG_OFFS_TC, 1, &regval);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (enable)
+ regval |= BIT_I2C_MST_VDDIO;
+
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_YG_OFFS_TC, regval);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return INV_SUCCESS;
+}
+
+
+/**
+ * @internal
+ * @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
+ *
+ * @return 0 on success, a non-zero error code on error.
+ */
+static int mpu60xx_pwr_mgmt(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ unsigned int reset, unsigned long sensors)
+{
+ 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));
+
+ if (reset) {
+ MPL_LOGI("Reset MPU6050 B1\n");
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_PWR_MGMT_1, BIT_H_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
+ msleep(15);
+ }
+
+ /* 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;
+ }
+
+ pwr_mgmt[0] = pwr_mgmt_prev[0];
+ pwr_mgmt[1] = pwr_mgmt_prev[1];
+
+ 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;
+ }
+ if (pwr_mgmt[0] != pwr_mgmt_prev[0]) {
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_PWR_MGMT_1, pwr_mgmt[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ 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_MGMT_2, pwr_mgmt[1]);
+ 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;
+}
+
+
+/**
+ * @brief sets the clock source for the gyros.
+ * @param mldl_cfg
+ * a pointer to the struct mldl_cfg data structure.
+ * @param gyro_handle
+ * an handle to the serial device the gyro is assigned to.
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
+{
+ int result;
+ unsigned char cur_clk_src;
+ unsigned char reg;
+
+ /* clock source selection */
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_PWR_MGM, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ cur_clk_src = reg & BITS_CLKSEL;
+ reg &= ~BITS_CLKSEL;
+
+
+ result = inv_serial_single_write(
+ gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* 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;
+}
+
+/**
+ * Configures the MPU I2C Master
+ *
+ * @mldl_cfg Handle to the configuration data
+ * @gyro_handle handle to the gyro communictation interface
+ * @slave Can be Null if turning off the slave
+ * @slave_pdata Can be null if turning off the slave
+ * @slave_id enum ext_slave_type to determine which index to use
+ *
+ *
+ * This fucntion configures the slaves by:
+ * 1) Setting up the read
+ * a) Read Register
+ * b) Read Length
+ * 2) Set up the data trigger (MPU6050 only)
+ * a) Set trigger write register
+ * b) Set Trigger write value
+ * 3) Set up the divider (MPU6050 only)
+ * 4) Set the slave bypass mode depending on slave
+ *
+ * returns INV_SUCCESS or non-zero error code
+ */
+
+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,
+ int slave_id)
+{
+ int result;
+ unsigned char reg;
+ /* Slave values */
+ unsigned char slave_reg;
+ unsigned char slave_len;
+ unsigned char slave_endian;
+ unsigned char slave_address;
+ /* 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;
+ } else {
+ slave_reg = slave->read_reg;
+ slave_len = slave->read_len;
+ slave_endian = slave->endian;
+ slave_address = slave_pdata->address;
+ 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,
+ addr_reg, slave_address);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Register */
+ 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;
+ }
+
+ /* 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,
+ ctrl_reg, 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;
+ }
+ }
+
+ 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_mpu60xx(mldl_cfg, gyro_handle, slave,
+ slave_pdata, slave_id);
+}
+/**
+ * Check to see if the gyro was reset by testing a couple of registers known
+ * to change on reset.
+ *
+ * @mldl_cfg mldl configuration structure
+ * @gyro_handle handle used to communicate with the gyro
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg;
+
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_DMP_CFG_2, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg)
+ return true;
+
+ if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1)
+ return false;
+
+ /* Inconclusive assume it was reset */
+ return true;
+}
+
+
+int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
+ const unsigned char *data, int size)
+{
+ int bank, offset, write_size;
+ int result;
+ unsigned char read[MPU_MEM_BANK_SIZE];
+
+ if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) {
+#if INV_CACHE_DMP == 1
+ memcpy(mldl_cfg->mpu_ram->ram, data, size);
+ return INV_SUCCESS;
+#else
+ LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
+ return INV_ERROR_MEMORY_SET;
+#endif
+ }
+
+ if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) {
+ LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
+ return INV_ERROR_MEMORY_SET;
+ }
+ /* Write and verify memory */
+ for (bank = 0; size > 0; bank++,
+ size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ result = inv_serial_write_mem(mlsl_handle,
+ mldl_cfg->mpu_chip_info->addr,
+ ((bank << 8) | 0x00),
+ write_size,
+ data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ MPL_LOGE("Write mem error in bank %d\n", bank);
+ return result;
+ }
+ result = inv_serial_read_mem(mlsl_handle,
+ mldl_cfg->mpu_chip_info->addr,
+ ((bank << 8) | 0x00),
+ write_size,
+ read);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ MPL_LOGE("Read mem error in bank %d\n", bank);
+ return result;
+ }
+
+#define ML_SKIP_CHECK 38
+ for (offset = 0; offset < write_size; offset++) {
+ /* skip the register memory locations */
+ if (bank == 0 && offset < ML_SKIP_CHECK)
+ continue;
+ if (data[offset] != read[offset]) {
+ result = INV_ERROR_SERIAL_WRITE;
+ break;
+ }
+ }
+ if (result != INV_SUCCESS) {
+ LOG_RESULT_LOCATION(result);
+ MPL_LOGE("Read data mismatch at bank %d, offset %d\n",
+ bank, offset);
+ return result;
+ }
+ }
+ return INV_SUCCESS;
+}
+
+static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
+ unsigned long sensors)
+{
+ int result;
+ int ii;
+ unsigned char reg;
+ unsigned char regs[7];
+
+ /* Wake up the part */
+ 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_ENABLE, (mldl_cfg->mpu_gyro_cfg->int_config));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(
+ gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider);
+ if (result) {
+ 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 = 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_CONFIG, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(
+ gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(
+ gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Write and verify memory */
+#if INV_CACHE_DMP != 0
+ inv_mpu_set_firmware(mldl_cfg, gyro_handle,
+ mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length);
+#endif
+
+ result = inv_serial_single_write(
+ gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ 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, 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] << 1) & BITS_ZG_OFFS_TC));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ regs[0] = MPUREG_X_OFFS_USRH;
+ for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) {
+ regs[1 + ii * 2] =
+ (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8)
+ & 0xff;
+ regs[1 + ii * 2 + 1] =
+ (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff);
+ }
+ result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ 7, regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Configure slaves */
+ result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
+ mldl_cfg->pdata->level_shifter);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG;
+
+ return result;
+}
+
+int gyro_config(void *mlsl_handle,
+ struct mldl_cfg *mldl_cfg,
+ struct ext_slave_config *data)
+{
+ struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
+ struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
+ struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
+ int ii;
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_INT_CONFIG:
+ mpu_gyro_cfg->int_config = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_EXT_SYNC:
+ mpu_gyro_cfg->ext_sync = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_FULL_SCALE:
+ mpu_gyro_cfg->full_scale = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_LPF:
+ mpu_gyro_cfg->lpf = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_CLK_SRC:
+ mpu_gyro_cfg->clk_src = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_DIVIDER:
+ mpu_gyro_cfg->divider = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_DMP_ENABLE:
+ mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_FIFO_ENABLE:
+ mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_DMP_CFG1:
+ mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_DMP_CFG2:
+ mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_TC:
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii];
+ break;
+ case MPU_SLAVE_GYRO:
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii];
+ break;
+ case MPU_SLAVE_ADDR:
+ mpu_chip_info->addr = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_PRODUCT_REVISION:
+ mpu_chip_info->product_revision = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_SILICON_REVISION:
+ mpu_chip_info->silicon_revision = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_PRODUCT_ID:
+ mpu_chip_info->product_id = *((__u8 *)data->data);
+ break;
+ case MPU_SLAVE_GYRO_SENS_TRIM:
+ mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data);
+ break;
+ case MPU_SLAVE_ACCEL_SENS_TRIM:
+ mpu_chip_info->accel_sens_trim = *((__u16 *)data->data);
+ break;
+ case MPU_SLAVE_RAM:
+ if (data->len != mldl_cfg->mpu_ram->length)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len);
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG;
+ return INV_SUCCESS;
+}
+
+int gyro_get_config(void *mlsl_handle,
+ struct mldl_cfg *mldl_cfg,
+ struct ext_slave_config *data)
+{
+ struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
+ struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
+ struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
+ int ii;
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_INT_CONFIG:
+ *((__u8 *)data->data) = mpu_gyro_cfg->int_config;
+ break;
+ case MPU_SLAVE_EXT_SYNC:
+ *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync;
+ break;
+ case MPU_SLAVE_FULL_SCALE:
+ *((__u8 *)data->data) = mpu_gyro_cfg->full_scale;
+ break;
+ case MPU_SLAVE_LPF:
+ *((__u8 *)data->data) = mpu_gyro_cfg->lpf;
+ break;
+ case MPU_SLAVE_CLK_SRC:
+ *((__u8 *)data->data) = mpu_gyro_cfg->clk_src;
+ break;
+ case MPU_SLAVE_DIVIDER:
+ *((__u8 *)data->data) = mpu_gyro_cfg->divider;
+ break;
+ case MPU_SLAVE_DMP_ENABLE:
+ *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable;
+ break;
+ case MPU_SLAVE_FIFO_ENABLE:
+ *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable;
+ break;
+ case MPU_SLAVE_DMP_CFG1:
+ *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1;
+ break;
+ case MPU_SLAVE_DMP_CFG2:
+ *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2;
+ break;
+ case MPU_SLAVE_TC:
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii];
+ break;
+ case MPU_SLAVE_GYRO:
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii];
+ break;
+ case MPU_SLAVE_ADDR:
+ *((__u8 *)data->data) = mpu_chip_info->addr;
+ break;
+ case MPU_SLAVE_PRODUCT_REVISION:
+ *((__u8 *)data->data) = mpu_chip_info->product_revision;
+ break;
+ case MPU_SLAVE_SILICON_REVISION:
+ *((__u8 *)data->data) = mpu_chip_info->silicon_revision;
+ break;
+ case MPU_SLAVE_PRODUCT_ID:
+ *((__u8 *)data->data) = mpu_chip_info->product_id;
+ break;
+ case MPU_SLAVE_GYRO_SENS_TRIM:
+ *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim;
+ break;
+ case MPU_SLAVE_ACCEL_SENS_TRIM:
+ *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim;
+ break;
+ case MPU_SLAVE_RAM:
+ if (data->len != mldl_cfg->mpu_ram->length)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len);
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+
+/*******************************************************************************
+ *******************************************************************************
+ * Exported functions
+ *******************************************************************************
+ ******************************************************************************/
+
+/**
+ * Initializes the pdata structure to defaults.
+ *
+ * Opens the device to read silicon revision, product id and whoami.
+ *
+ * @mldl_cfg
+ * The internal device configuration data structure.
+ * @mlsl_handle
+ * The serial communication handle.
+ *
+ * @return INV_SUCCESS if silicon revision, product id and woami are supported
+ * by this software.
+ */
+int inv_mpu_open(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle, void *pressure_handle)
+{
+ int result;
+ void *slave_handle[EXT_SLAVE_NUM_TYPES];
+ int ii;
+
+ /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
+ ii = 0;
+ mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false;
+ mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN;
+ mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
+ mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ;
+ mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS;
+ mldl_cfg->mpu_gyro_cfg->divider = 4;
+ mldl_cfg->mpu_gyro_cfg->dmp_enable = 1;
+ mldl_cfg->mpu_gyro_cfg->fifo_enable = 1;
+ mldl_cfg->mpu_gyro_cfg->ext_sync = 0;
+ mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0;
+ mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0;
+ mldl_cfg->inv_mpu_state->status =
+ MPU_DMP_IS_SUSPENDED |
+ MPU_GYRO_IS_SUSPENDED |
+ MPU_ACCEL_IS_SUSPENDED |
+ MPU_COMPASS_IS_SUSPENDED |
+ MPU_PRESSURE_IS_SUSPENDED |
+ MPU_DEVICE_IS_SUSPENDED;
+ mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0;
+
+ slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
+ slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
+ slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
+ slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
+
+ if (mldl_cfg->mpu_chip_info->addr == 0) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Reset,
+ * 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 = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, true,
+ INV_THREE_AXIS_GYRO);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_get_silicon_rev(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Get the factory temperature compensation offsets */
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_XG_OFFS_TC, 1,
+ &mldl_cfg->mpu_offsets->tc[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_YG_OFFS_TC, 1,
+ &mldl_cfg->mpu_offsets->tc[1]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
+ MPUREG_ZG_OFFS_TC, 1,
+ &mldl_cfg->mpu_offsets->tc[2]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Into bypass mode before sleeping and calling the slaves init */
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
+ mldl_cfg->pdata->level_shifter);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ 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 = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, 0);
+#endif
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+
+ return result;
+
+}
+
+/**
+ * Close the mpu interface
+ *
+ * @mldl_cfg pointer to the configuration structure
+ * @mlsl_handle pointer to the serial layer handle
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+int inv_mpu_close(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle)
+{
+ return 0;
+}
+
+/**
+ * @brief resume the MPU device and all the other sensor
+ * devices from their low power state.
+ *
+ * @mldl_cfg
+ * pointer to the configuration structure
+ * @gyro_handle
+ * the main file handle to the MPU device.
+ * @accel_handle
+ * an handle to the accelerometer device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the accelerometer device operates on the same
+ * primary bus of MPU.
+ * @compass_handle
+ * an handle to the compass device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the compass device operates on the same
+ * primary bus of MPU.
+ * @pressure_handle
+ * an handle to the pressure sensor device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the pressure sensor device operates on the same
+ * primary bus of MPU.
+ * @resume_gyro
+ * whether resuming the gyroscope device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_accel
+ * whether resuming the accelerometer device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_compass
+ * whether resuming the compass device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_pressure
+ * whether resuming the pressure sensor device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @return INV_SUCCESS or a non-zero error code.
+ */
+int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors)
+{
+ int result = INV_SUCCESS;
+ int ii;
+ bool resume_slave[EXT_SLAVE_NUM_TYPES];
+ bool resume_dmp = sensors & INV_DMP_PROCESSOR;
+ void *slave_handle[EXT_SLAVE_NUM_TYPES];
+ resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
+ (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
+ resume_slave[EXT_SLAVE_TYPE_ACCEL] =
+ sensors & INV_THREE_AXIS_ACCEL;
+ resume_slave[EXT_SLAVE_TYPE_COMPASS] =
+ sensors & INV_THREE_AXIS_COMPASS;
+ resume_slave[EXT_SLAVE_TYPE_PRESSURE] =
+ sensors & INV_THREE_AXIS_PRESSURE;
+
+ slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
+ slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
+ slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
+ slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
+
+
+ mldl_print_cfg(mldl_cfg);
+
+ /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */
+ for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (resume_slave[ii] &&
+ ((!mldl_cfg->slave[ii]) ||
+ (!mldl_cfg->slave[ii]->resume))) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp)
+ && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) ||
+ (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = dmp_stop(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = gyro_resume(mldl_cfg, gyro_handle, sensors);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!mldl_cfg->slave[ii] ||
+ !mldl_cfg->pdata_slave[ii] ||
+ !resume_slave[ii] ||
+ !(mldl_cfg->inv_mpu_state->status & (1 << ii)))
+ continue;
+
+ if (EXT_SLAVE_BUS_SECONDARY ==
+ mldl_cfg->pdata_slave[ii]->bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ true);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->slave[ii]->resume(slave_handle[ii],
+ mldl_cfg->slave[ii],
+ mldl_cfg->pdata_slave[ii]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->inv_mpu_state->status &= ~(1 << ii);
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (resume_dmp &&
+ !(mldl_cfg->inv_mpu_state->status & (1 << ii)) &&
+ mldl_cfg->pdata_slave[ii] &&
+ EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) {
+ result = mpu_set_slave(mldl_cfg,
+ gyro_handle,
+ mldl_cfg->slave[ii],
+ mldl_cfg->pdata_slave[ii],
+ mldl_cfg->slave[ii]->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ }
+
+ /* Turn on the master i2c iterface if necessary */
+ if (resume_dmp) {
+ result = mpu_set_i2c_bypass(
+ mldl_cfg, gyro_handle,
+ !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Now start */
+ result = dmp_start(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->inv_mpu_cfg->requested_sensors = sensors;
+
+ return result;
+}
+
+/**
+ * @brief suspend the MPU device and all the other sensor
+ * devices into their low power state.
+ * @mldl_cfg
+ * a pointer to the struct mldl_cfg internal data
+ * structure.
+ * @gyro_handle
+ * the main file handle to the MPU device.
+ * @accel_handle
+ * an handle to the accelerometer device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the accelerometer device operates on the same
+ * primary bus of MPU.
+ * @compass_handle
+ * an handle to the compass device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the compass device operates on the same
+ * primary bus of MPU.
+ * @pressure_handle
+ * an handle to the pressure sensor device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the pressure sensor device operates on the same
+ * primary bus of MPU.
+ * @accel
+ * whether suspending the accelerometer device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @compass
+ * whether suspending the compass device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @pressure
+ * whether suspending the pressure sensor device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @return INV_SUCCESS or a non-zero error code.
+ */
+int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors)
+{
+ int result = INV_SUCCESS;
+ int ii;
+ struct ext_slave_descr **slave = mldl_cfg->slave;
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR);
+ bool suspend_slave[EXT_SLAVE_NUM_TYPES];
+ void *slave_handle[EXT_SLAVE_NUM_TYPES];
+
+ suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
+ ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO))
+ == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
+ suspend_slave[EXT_SLAVE_TYPE_ACCEL] =
+ ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL);
+ suspend_slave[EXT_SLAVE_TYPE_COMPASS] =
+ ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS);
+ suspend_slave[EXT_SLAVE_TYPE_PRESSURE] =
+ ((sensors & INV_THREE_AXIS_PRESSURE) ==
+ INV_THREE_AXIS_PRESSURE);
+
+ slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
+ slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
+ slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
+ slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
+
+ if (suspend_dmp) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = dmp_stop(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Gyro */
+ if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] &&
+ !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) {
+ result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false,
+ ((~sensors) & INV_ALL_SENSORS));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii);
+ if (!slave[ii] || !pdata_slave[ii] ||
+ is_suspended || !suspend_slave[ii])
+ continue;
+
+ if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = slave[ii]->suspend(slave_handle[ii],
+ slave[ii],
+ pdata_slave[ii]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
+ result = mpu_set_slave(mldl_cfg, gyro_handle,
+ NULL, NULL,
+ slave[ii]->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->inv_mpu_state->status |= (1 << ii);
+ }
+
+ /* Re-enable the i2c master if there are configured slaves and DMP */
+ if (!suspend_dmp) {
+ result = mpu_set_i2c_bypass(
+ mldl_cfg, gyro_handle,
+ !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS;
+
+ return result;
+}
+
+int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ int bypass_result;
+ int remain_bypassed = true;
+
+ if (NULL == slave || NULL == slave->read) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
+ remain_bypassed = false;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->read(slave_handle, slave, pdata, data);
+
+ if (!remain_bypassed) {
+ bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (bypass_result) {
+ LOG_RESULT_LOCATION(bypass_result);
+ return bypass_result;
+ }
+ }
+ return result;
+}
+
+int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ int remain_bypassed = true;
+
+ if (NULL == slave || NULL == slave->config) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
+ remain_bypassed = false;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->config(slave_handle, slave, pdata, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (!remain_bypassed) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ int remain_bypassed = true;
+
+ if (NULL == slave || NULL == slave->get_config) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
+ remain_bypassed = false;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->get_config(slave_handle, slave, pdata, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (!remain_bypassed) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c
new file mode 100644
index 000000000000..7379a1707610
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c
@@ -0,0 +1,138 @@
+/*
+ $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 MLDL
+ *
+ * @{
+ * @file mldl_print_cfg.c
+ * @brief The Motion Library Driver Layer.
+ */
+
+#include <stddef.h>
+#include "mldl_cfg.h"
+#include "mlsl.h"
+#include "linux/mpu.h"
+
+#include "log.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "mldl_print_cfg:"
+
+#undef MPL_LOG_NDEBUG
+#define MPL_LOG_NDEBUG 1
+
+void mldl_print_cfg(struct mldl_cfg *mldl_cfg)
+{
+ struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
+ struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
+ struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
+ struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg;
+ struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state;
+ struct ext_slave_descr **slave = mldl_cfg->slave;
+ struct mpu_platform_data *pdata = mldl_cfg->pdata;
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ /* mpu_gyro_cfg */
+ MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config);
+ MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync);
+ MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale);
+ MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf);
+ MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src);
+ MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider);
+ MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable);
+ MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable);
+ MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1);
+ MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2);
+ /* mpu_offsets */
+ MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]);
+ MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]);
+ MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]);
+ MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]);
+ MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]);
+ MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]);
+
+ /* mpu_chip_info */
+ MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr);
+
+ MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision);
+ 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",
+ inv_mpu_cfg->ignore_system_suspend);
+ MPL_LOGV("status = %04x\n", inv_mpu_state->status);
+ MPL_LOGV("i2c_slaves_enabled= %04x\n",
+ inv_mpu_state->i2c_slaves_enabled);
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!slave[ii])
+ continue;
+ MPL_LOGV("SLAVE %d:\n", ii);
+ MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend);
+ MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume);
+ MPL_LOGV(" read = %02x\n", (int)slave[ii]->read);
+ MPL_LOGV(" type = %02x\n", slave[ii]->type);
+ MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg);
+ MPL_LOGV(" len = %02x\n", slave[ii]->read_len);
+ MPL_LOGV(" endian = %02x\n", slave[ii]->endian);
+ MPL_LOGV(" range.mantissa= %02x\n",
+ slave[ii]->range.mantissa);
+ MPL_LOGV(" range.fraction= %02x\n",
+ slave[ii]->range.fraction);
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ continue;
+ MPL_LOGV("PDATA_SLAVE[%d]\n", ii);
+ MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq);
+ MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num);
+ MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus);
+ MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address);
+ MPL_LOGV(" orientation=\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ pdata_slave[ii]->orientation[0],
+ pdata_slave[ii]->orientation[1],
+ pdata_slave[ii]->orientation[2],
+ pdata_slave[ii]->orientation[3],
+ pdata_slave[ii]->orientation[4],
+ pdata_slave[ii]->orientation[5],
+ pdata_slave[ii]->orientation[6],
+ pdata_slave[ii]->orientation[7],
+ pdata_slave[ii]->orientation[8]);
+ }
+
+ MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config);
+ MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter);
+ MPL_LOGV("pdata->orientation =\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ pdata->orientation[0], pdata->orientation[1],
+ pdata->orientation[2], pdata->orientation[3],
+ pdata->orientation[4], pdata->orientation[5],
+ pdata->orientation[6], pdata->orientation[7],
+ pdata->orientation[8]);
+}
diff --git a/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c
new file mode 100644
index 000000000000..f1c228f48fe3
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c
@@ -0,0 +1,420 @@
+/*
+ $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/>.
+ $
+ */
+
+#include "mlsl.h"
+#include <linux/i2c.h>
+#include "log.h"
+#include "mpu6050b1.h"
+
+static int inv_i2c_write(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned int len, unsigned char const *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (!data || !i2c_adap) {
+ LOG_RESULT_LOCATION(-EINVAL);
+ return -EINVAL;
+ }
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = (unsigned char *)data;
+ msgs[0].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ LOG_RESULT_LOCATION(res);
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_write_register(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned char reg, unsigned char value)
+{
+ unsigned char data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ return inv_i2c_write(i2c_adap, address, 2, data);
+}
+
+static int inv_i2c_read(struct i2c_adapter *i2c_adap,
+ unsigned char address, unsigned char reg,
+ unsigned int len, unsigned char *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data || !i2c_adap) {
+ LOG_RESULT_LOCATION(-EINVAL);
+ return -EINVAL;
+ }
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = address;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 2);
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ LOG_RESULT_LOCATION(res);
+ return res;
+ } else
+ return 0;
+}
+
+static int mpu_memory_read(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char *data)
+{
+ unsigned char bank[2];
+ unsigned char addr[2];
+ unsigned char buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !i2c_adap) {
+ LOG_RESULT_LOCATION(-EINVAL);
+ return -EINVAL;
+ }
+
+ bank[0] = MPUREG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = MPUREG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = MPUREG_MEM_R_W;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ LOG_RESULT_LOCATION(res);
+ return res;
+ } else
+ return 0;
+}
+
+static int mpu_memory_write(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char const *data)
+{
+ unsigned char bank[2];
+ unsigned char addr[2];
+ unsigned char buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !i2c_adap) {
+ LOG_RESULT_LOCATION(-EINVAL);
+ return -EINVAL;
+ }
+ if (len >= (sizeof(buf) - 1)) {
+ LOG_RESULT_LOCATION(-ENOMEM);
+ return -ENOMEM;
+ }
+
+ bank[0] = MPUREG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = MPUREG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = MPUREG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (unsigned char *)buf;
+ msgs[2].len = len + 1;
+
+ res = i2c_transfer(i2c_adap, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ LOG_RESULT_LOCATION(res);
+ return res;
+ } else
+ return 0;
+}
+
+int inv_serial_single_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned char data)
+{
+ return inv_i2c_write_register((struct i2c_adapter *)sl_handle,
+ slave_addr, register_addr, data);
+}
+EXPORT_SYMBOL(inv_serial_single_write);
+
+int inv_serial_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ const unsigned short data_length = length - 1;
+ const unsigned char start_reg_addr = data[0];
+ unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
+ unsigned short bytes_written = 0;
+
+ while (bytes_written < data_length) {
+ unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE,
+ data_length - bytes_written);
+ if (bytes_written == 0) {
+ result = inv_i2c_write((struct i2c_adapter *)
+ sl_handle, slave_addr,
+ 1 + this_len, data);
+ } else {
+ /* manually increment register addr between chunks */
+ i2c_write[0] = start_reg_addr + bytes_written;
+ memcpy(&i2c_write[1], &data[1 + bytes_written],
+ this_len);
+ result = inv_i2c_write((struct i2c_adapter *)
+ sl_handle, slave_addr,
+ 1 + this_len, i2c_write);
+ }
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write);
+
+int inv_serial_read(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR
+ && (register_addr == MPUREG_FIFO_R_W ||
+ register_addr == MPUREG_MEM_R_W)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result = inv_i2c_read((struct i2c_adapter *)sl_handle,
+ slave_addr, register_addr + bytes_read,
+ this_len, &data[bytes_read]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_read += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read);
+
+int inv_serial_write_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ unsigned short bytes_written = 0;
+
+ if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
+ pr_err("memory read length (%d B) extends beyond its"
+ " limits (%d) if started at location %d\n", length,
+ MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_written < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
+ result = mpu_memory_write((struct i2c_adapter *)sl_handle,
+ slave_addr, mem_addr + bytes_written,
+ this_len, &data[bytes_written]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write_mem);
+
+int inv_serial_read_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
+ printk
+ ("memory read length (%d B) extends beyond its limits (%d) "
+ "if started at location %d\n", length,
+ MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result =
+ mpu_memory_read((struct i2c_adapter *)sl_handle,
+ slave_addr, mem_addr + bytes_read,
+ this_len, &data[bytes_read]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_read += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read_mem);
+
+int inv_serial_write_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
+ unsigned short bytes_written = 0;
+
+ if (length > FIFO_HW_SIZE) {
+ printk(KERN_ERR
+ "maximum fifo write length is %d\n", FIFO_HW_SIZE);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_written < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
+ i2c_write[0] = MPUREG_FIFO_R_W;
+ memcpy(&i2c_write[1], &data[bytes_written], this_len);
+ result = inv_i2c_write((struct i2c_adapter *)sl_handle,
+ slave_addr, this_len + 1, i2c_write);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write_fifo);
+
+int inv_serial_read_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if (length > FIFO_HW_SIZE) {
+ printk(KERN_ERR
+ "maximum fifo read length is %d\n", FIFO_HW_SIZE);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result = inv_i2c_read((struct i2c_adapter *)sl_handle,
+ slave_addr, MPUREG_FIFO_R_W, this_len,
+ &data[bytes_read]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ bytes_read += this_len;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read_fifo);
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/mpu6050/mpu-dev.c b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c
new file mode 100644
index 000000000000..e171dc2a7eff
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c
@@ -0,0 +1,1309 @@
+/*
+ $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/>.
+ $
+ */
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <linux/poll.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "mpuirq.h"
+#include "slaveirq.h"
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#include <linux/mpu.h>
+
+#include "accel/mpu6050.h"
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+ struct miscdevice dev;
+ struct i2c_client *client;
+
+ /* mldl_cfg data */
+ struct mldl_cfg mldl_cfg;
+ struct mpu_ram mpu_ram;
+ struct mpu_gyro_cfg mpu_gyro_cfg;
+ struct mpu_offsets mpu_offsets;
+ struct mpu_chip_info mpu_chip_info;
+ struct inv_mpu_cfg inv_mpu_cfg;
+ struct inv_mpu_state inv_mpu_state;
+
+ struct mutex mutex;
+ wait_queue_head_t mpu_event_wait;
+ struct completion completion;
+ struct timer_list timeout;
+ struct notifier_block nb;
+ struct mpuirq_data mpu_pm_event;
+ int response_timeout; /* In seconds */
+ unsigned long event;
+ int pid;
+ struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
+};
+
+struct mpu_private_data *mpu_private_data;
+
+static void mpu_pm_timeout(u_long data)
+{
+ struct mpu_private_data *mpu = (struct mpu_private_data *)data;
+ struct i2c_client *client = mpu->client;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ complete(&mpu->completion);
+}
+
+static int mpu_pm_notifier_callback(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ struct mpu_private_data *mpu =
+ container_of(nb, struct mpu_private_data, nb);
+ struct i2c_client *client = mpu->client;
+ struct timeval event_time;
+ dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
+
+ /* Prevent the file handle from being closed before we initialize
+ the completion event */
+ mutex_lock(&mpu->mutex);
+ if (!(mpu->pid) ||
+ (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
+ mutex_unlock(&mpu->mutex);
+ return NOTIFY_OK;
+ }
+
+ if (event == PM_SUSPEND_PREPARE)
+ mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
+ if (event == PM_POST_SUSPEND)
+ mpu->event = MPU_PM_EVENT_POST_SUSPEND;
+
+ do_gettimeofday(&event_time);
+ mpu->mpu_pm_event.interruptcount++;
+ mpu->mpu_pm_event.irqtime =
+ (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
+ mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
+ mpu->mpu_pm_event.data = mpu->event;
+
+ if (mpu->response_timeout > 0) {
+ mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
+ add_timer(&mpu->timeout);
+ }
+ INIT_COMPLETION(mpu->completion);
+ mutex_unlock(&mpu->mutex);
+
+ wake_up_interruptible(&mpu->mpu_event_wait);
+ wait_for_completion(&mpu->completion);
+ del_timer_sync(&mpu->timeout);
+ dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
+ return NOTIFY_OK;
+}
+
+static int mpu_dev_open(struct inode *inode, struct file *file)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ int result;
+ int ii;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
+
+ result = mutex_lock_interruptible(&mpu->mutex);
+ if (mpu->pid) {
+ mutex_unlock(&mpu->mutex);
+ return -EBUSY;
+ }
+ mpu->pid = current->pid;
+
+ /* Reset the sensors to the default */
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "%s: mutex_lock_interruptible returned %d\n",
+ __func__, result);
+ return result;
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+ __module_get(mpu->slave_modules[ii]);
+
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+/* close function - called when the "file" /dev/mpu is closed in userspace */
+static int mpu_release(struct inode *inode, struct file *file)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int result = 0;
+ int ii;
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+ mutex_lock(&mpu->mutex);
+ mldl_cfg->inv_mpu_cfg->requested_sensors = 0;
+ result = inv_mpu_suspend(mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ INV_ALL_SENSORS);
+ mpu->pid = 0;
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+ module_put(mpu->slave_modules[ii]);
+
+ mutex_unlock(&mpu->mutex);
+ complete(&mpu->completion);
+ dev_dbg(&client->adapter->dev, "mpu_release\n");
+
+ return result;
+}
+
+/* read function called when from /dev/mpu is read. Read from the FIFO */
+static ssize_t mpu_read(struct file *file,
+ char __user *buf, size_t count, loff_t *offset)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
+ int err;
+
+ if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
+ wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
+
+ if (!mpu->event || !buf
+ || count < sizeof(mpu->mpu_pm_event))
+ return 0;
+
+ err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
+ if (err) {
+ dev_err(&client->adapter->dev,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ mpu->event = 0;
+ return len;
+}
+
+static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ int mask = 0;
+
+ poll_wait(file, &mpu->mpu_event_wait, poll);
+ if (mpu->event)
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_platform_data(
+ struct i2c_client *client,
+ struct ext_slave_platform_data __user *arg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct ext_slave_platform_data *pdata_slave;
+ struct ext_slave_platform_data local_pdata_slave;
+
+ if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave)))
+ return -EFAULT;
+
+ if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES)
+ return -EINVAL;
+
+ pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type];
+ /* All but private data and irq_data */
+ if (!pdata_slave)
+ return -ENODEV;
+ if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave)))
+ return -EFAULT;
+ return 0;
+}
+
+static int mpu_dev_ioctl_get_mpu_platform_data(
+ struct i2c_client *client,
+ struct mpu_platform_data __user *arg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
+
+ if (copy_to_user(arg, pdata, sizeof(*pdata)))
+ return -EFAULT;
+ return 0;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_descr(
+ struct i2c_client *client,
+ struct ext_slave_descr __user *arg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct ext_slave_descr *slave;
+ struct ext_slave_descr local_slave;
+
+ if (copy_from_user(&local_slave, arg, sizeof(local_slave)))
+ return -EFAULT;
+
+ if (local_slave.type >= EXT_SLAVE_NUM_TYPES)
+ return -EINVAL;
+
+ slave = mpu->mldl_cfg.slave[local_slave.type];
+ /* All but private data and irq_data */
+ if (!slave)
+ return -ENODEV;
+ if (copy_to_user(arg, slave, sizeof(*slave)))
+ return -EFAULT;
+ return 0;
+}
+
+
+/**
+ * slave_config() - Pass a requested slave configuration to the slave sensor
+ *
+ * @adapter the adaptor to use to communicate with the slave
+ * @mldl_cfg the mldl configuration structuer
+ * @slave pointer to the slave descriptor
+ * @usr_config The configuration to pass to the slave sensor
+ *
+ * returns 0 or non-zero error code
+ */
+static int inv_mpu_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = gyro_config(gyro_adapter, mldl_cfg, &config);
+ kfree(config.data);
+ return retval;
+}
+
+static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+ void *user_data;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ user_data = config.data;
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = gyro_get_config(gyro_adapter, mldl_cfg, &config);
+ if (!retval)
+ retval = copy_to_user((unsigned char __user *)user_data,
+ config.data, config.len);
+ kfree(config.data);
+ return retval;
+}
+
+static int slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+ if ((!slave) || (!slave->config))
+ return -ENODEV;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
+ &config, slave, pdata);
+ kfree(config.data);
+ return retval;
+}
+
+static int slave_get_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+ void *user_data;
+ if (!(slave) || !(slave->get_config))
+ return -ENODEV;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ user_data = config.data;
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
+ slave_adapter, &config, slave, pdata);
+ if (retval) {
+ kfree(config.data);
+ return retval;
+ }
+ retval = copy_to_user((unsigned char __user *)user_data,
+ config.data, config.len);
+ kfree(config.data);
+ return retval;
+}
+
+static int inv_slave_read(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ void __user *usr_data)
+{
+ int retval;
+ unsigned char *data;
+ data = kzalloc(slave->read_len, GFP_KERNEL);
+ if (!data)
+ return -EFAULT;
+
+ retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
+ slave, pdata, data);
+
+ if ((!retval) &&
+ (copy_to_user((unsigned char __user *)usr_data,
+ data, slave->read_len)))
+ retval = -EFAULT;
+
+ kfree(data);
+ return retval;
+}
+
+static int mpu_handle_mlsl(void *sl_handle,
+ unsigned char addr,
+ unsigned int cmd,
+ struct mpu_read_write __user *usr_msg)
+{
+ int retval = 0;
+ struct mpu_read_write msg;
+ unsigned char *user_data;
+ retval = copy_from_user(&msg, usr_msg, sizeof(msg));
+ if (retval)
+ return -EFAULT;
+
+ user_data = msg.data;
+ if (msg.length && msg.data) {
+ unsigned char *data;
+ data = kmalloc(msg.length, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)msg.data, msg.length);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ msg.data = data;
+ } else {
+ return -EPERM;
+ }
+
+ switch (cmd) {
+ case MPU_READ:
+ retval = inv_serial_read(sl_handle, addr,
+ msg.address, msg.length, msg.data);
+ break;
+ case MPU_WRITE:
+ retval = inv_serial_write(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+ case MPU_READ_MEM:
+ retval = inv_serial_read_mem(sl_handle, addr,
+ msg.address, msg.length, msg.data);
+ break;
+ case MPU_WRITE_MEM:
+ retval = inv_serial_write_mem(sl_handle, addr,
+ msg.address, msg.length,
+ msg.data);
+ break;
+ case MPU_READ_FIFO:
+ retval = inv_serial_read_fifo(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+ case MPU_WRITE_FIFO:
+ retval = inv_serial_write_fifo(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+
+ };
+ if (retval) {
+ dev_err(&((struct i2c_adapter *)sl_handle)->dev,
+ "%s: i2c %d error %d\n",
+ __func__, cmd, retval);
+ kfree(msg.data);
+ return retval;
+ }
+ retval = copy_to_user((unsigned char __user *)user_data,
+ msg.data, msg.length);
+ kfree(msg.data);
+ return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_dev_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int retval = 0;
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct ext_slave_descr **slave = mldl_cfg->slave;
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+ retval = mutex_lock_interruptible(&mpu->mutex);
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s: mutex_lock_interruptible returned %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ switch (cmd) {
+ case MPU_GET_EXT_SLAVE_PLATFORM_DATA:
+ retval = mpu_dev_ioctl_get_ext_slave_platform_data(
+ client,
+ (struct ext_slave_platform_data __user *)arg);
+ break;
+ case MPU_GET_MPU_PLATFORM_DATA:
+ retval = mpu_dev_ioctl_get_mpu_platform_data(
+ client,
+ (struct mpu_platform_data __user *)arg);
+ break;
+ case MPU_GET_EXT_SLAVE_DESCR:
+ retval = mpu_dev_ioctl_get_ext_slave_descr(
+ client,
+ (struct ext_slave_descr __user *)arg);
+ break;
+ case MPU_READ:
+ case MPU_WRITE:
+ case MPU_READ_MEM:
+ case MPU_WRITE_MEM:
+ case MPU_READ_FIFO:
+ case MPU_WRITE_FIFO:
+ retval = mpu_handle_mlsl(
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ mldl_cfg->mpu_chip_info->addr, cmd,
+ (struct mpu_read_write __user *)arg);
+ break;
+ case MPU_CONFIG_GYRO:
+ retval = inv_mpu_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_ACCEL:
+ retval = slave_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave[EXT_SLAVE_TYPE_ACCEL],
+ pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_COMPASS:
+ retval = slave_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave[EXT_SLAVE_TYPE_COMPASS],
+ pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_PRESSURE:
+ retval = slave_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ slave[EXT_SLAVE_TYPE_PRESSURE],
+ pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_GYRO:
+ retval = inv_mpu_get_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_ACCEL:
+ retval = slave_get_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave[EXT_SLAVE_TYPE_ACCEL],
+ pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_COMPASS:
+ retval = slave_get_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave[EXT_SLAVE_TYPE_COMPASS],
+ pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_PRESSURE:
+ retval = slave_get_config(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ slave[EXT_SLAVE_TYPE_PRESSURE],
+ pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_SUSPEND:
+ retval = inv_mpu_suspend(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ arg);
+ break;
+ case MPU_RESUME:
+ retval = inv_mpu_resume(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ arg);
+ break;
+ case MPU_PM_EVENT_HANDLED:
+ dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
+ complete(&mpu->completion);
+ break;
+ case MPU_READ_ACCEL:
+ retval = inv_slave_read(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave[EXT_SLAVE_TYPE_ACCEL],
+ pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+ (unsigned char __user *)arg);
+ break;
+ case MPU_READ_COMPASS:
+ retval = inv_slave_read(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave[EXT_SLAVE_TYPE_COMPASS],
+ pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+ (unsigned char __user *)arg);
+ break;
+ case MPU_READ_PRESSURE:
+ retval = inv_slave_read(
+ mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ slave[EXT_SLAVE_TYPE_PRESSURE],
+ pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+ (unsigned char __user *)arg);
+ break;
+ case MPU_GET_REQUESTED_SENSORS:
+ if (copy_to_user(
+ (__u32 __user *)arg,
+ &mldl_cfg->inv_mpu_cfg->requested_sensors,
+ sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors)))
+ retval = -EFAULT;
+ break;
+ case MPU_SET_REQUESTED_SENSORS:
+ mldl_cfg->inv_mpu_cfg->requested_sensors = arg;
+ break;
+ case MPU_GET_IGNORE_SYSTEM_SUSPEND:
+ if (copy_to_user(
+ (unsigned char __user *)arg,
+ &mldl_cfg->inv_mpu_cfg->ignore_system_suspend,
+ sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend)))
+ retval = -EFAULT;
+ break;
+ case MPU_SET_IGNORE_SYSTEM_SUSPEND:
+ mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg;
+ break;
+ case MPU_GET_MLDL_STATUS:
+ if (copy_to_user(
+ (unsigned char __user *)arg,
+ &mldl_cfg->inv_mpu_state->status,
+ sizeof(mldl_cfg->inv_mpu_state->status)))
+ retval = -EFAULT;
+ break;
+ case MPU_GET_I2C_SLAVES_ENABLED:
+ if (copy_to_user(
+ (unsigned char __user *)arg,
+ &mldl_cfg->inv_mpu_state->i2c_slaves_enabled,
+ sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)))
+ retval = -EFAULT;
+ break;
+ default:
+ dev_err(&client->adapter->dev,
+ "%s: Unknown cmd %x, arg %lu\n",
+ __func__, cmd, arg);
+ retval = -EINVAL;
+ };
+
+ mutex_unlock(&mpu->mutex);
+ dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n",
+ __func__, cmd, arg, retval);
+
+ if (retval > 0)
+ retval = -retval;
+
+ return retval;
+}
+
+void mpu_shutdown(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+ mutex_lock(&mpu->mutex);
+ (void)inv_mpu_suspend(mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ INV_ALL_SENSORS);
+ mutex_unlock(&mpu->mutex);
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+}
+
+int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+ mutex_lock(&mpu->mutex);
+ if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+ dev_dbg(&client->adapter->dev,
+ "%s: suspending on event %d\n", __func__, mesg.event);
+ (void)inv_mpu_suspend(mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ INV_ALL_SENSORS);
+ } else {
+ dev_dbg(&client->adapter->dev,
+ "%s: Already suspended %d\n", __func__, mesg.event);
+ }
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+int mpu_dev_resume(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+ mutex_lock(&mpu->mutex);
+ if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+ (void)inv_mpu_resume(mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+ slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+ mldl_cfg->inv_mpu_cfg->requested_sensors);
+ dev_dbg(&client->adapter->dev,
+ "%s for pid %d\n", __func__, mpu->pid);
+ }
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+/* define which file operations are supported */
+static const struct file_operations mpu_fops = {
+ .owner = THIS_MODULE,
+ .read = mpu_read,
+ .poll = mpu_poll,
+ .unlocked_ioctl = mpu_dev_ioctl,
+ .open = mpu_dev_open,
+ .release = mpu_release,
+};
+
+int inv_mpu_register_slave(struct module *slave_module,
+ struct i2c_client *slave_client,
+ struct ext_slave_platform_data *slave_pdata,
+ struct ext_slave_descr *(*get_slave_descr)(void))
+{
+ struct mpu_private_data *mpu = mpu_private_data;
+ struct mldl_cfg *mldl_cfg;
+ struct ext_slave_descr *slave_descr;
+ struct ext_slave_platform_data **pdata_slave;
+ char *irq_name = NULL;
+ int result = 0;
+
+ if (!slave_client || !slave_pdata || !get_slave_descr)
+ return -EINVAL;
+
+ if (!mpu) {
+ dev_err(&slave_client->adapter->dev,
+ "%s: Null mpu_private_data\n", __func__);
+ return -EINVAL;
+ }
+ mldl_cfg = &mpu->mldl_cfg;
+ pdata_slave = mldl_cfg->pdata_slave;
+ slave_descr = get_slave_descr();
+
+ if (!slave_descr) {
+ dev_err(&slave_client->adapter->dev,
+ "%s: Null ext_slave_descr\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mpu->mutex);
+ if (mpu->pid) {
+ mutex_unlock(&mpu->mutex);
+ return -EBUSY;
+ }
+
+ if (pdata_slave[slave_descr->type]) {
+ result = -EBUSY;
+ goto out_unlock_mutex;
+ }
+
+ slave_pdata->address = slave_client->addr;
+ slave_pdata->irq = slave_client->irq;
+ slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter);
+
+ dev_info(&slave_client->adapter->dev,
+ "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n",
+ __func__,
+ slave_descr->name,
+ slave_descr->type,
+ slave_pdata->address,
+ slave_pdata->irq,
+ slave_pdata->adapt_num);
+
+ switch (slave_descr->type) {
+ case EXT_SLAVE_TYPE_ACCEL:
+ irq_name = "accelirq";
+ break;
+ case EXT_SLAVE_TYPE_COMPASS:
+ irq_name = "compassirq";
+ break;
+ case EXT_SLAVE_TYPE_PRESSURE:
+ irq_name = "pressureirq";
+ break;
+ default:
+ irq_name = "none";
+ };
+ if (slave_descr->init) {
+ result = slave_descr->init(slave_client->adapter,
+ slave_descr,
+ slave_pdata);
+ if (result) {
+ dev_err(&slave_client->adapter->dev,
+ "%s init failed %d\n",
+ slave_descr->name, result);
+ goto out_unlock_mutex;
+ }
+ }
+
+ 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);
+
+ if (!result && irq_name && (slave_pdata->irq > 0)) {
+ int warn_result;
+ dev_info(&slave_client->adapter->dev,
+ "Installing %s irq using %d\n",
+ irq_name,
+ slave_pdata->irq);
+ warn_result = slaveirq_init(slave_client->adapter,
+ slave_pdata, irq_name);
+ if (result)
+ dev_WARN(&slave_client->adapter->dev,
+ "%s irq assigned error: %d\n",
+ slave_descr->name, warn_result);
+ } else {
+ dev_WARN(&slave_client->adapter->dev,
+ "%s irq not assigned: %d %d %d\n",
+ slave_descr->name,
+ result, (int)irq_name, slave_pdata->irq);
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(inv_mpu_register_slave);
+
+void inv_mpu_unregister_slave(struct i2c_client *slave_client,
+ struct ext_slave_platform_data *slave_pdata,
+ struct ext_slave_descr *(*get_slave_descr)(void))
+{
+ struct mpu_private_data *mpu = mpu_private_data;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct ext_slave_descr *slave_descr;
+ int result;
+
+ dev_info(&slave_client->adapter->dev, "%s\n", __func__);
+
+ if (!slave_client || !slave_pdata || !get_slave_descr)
+ return;
+
+ if (slave_pdata->irq)
+ slaveirq_exit(slave_pdata);
+
+ slave_descr = get_slave_descr();
+ if (!slave_descr)
+ return;
+
+ mutex_lock(&mpu->mutex);
+
+ if (slave_descr->exit) {
+ result = slave_descr->exit(slave_client->adapter,
+ slave_descr,
+ slave_pdata);
+ if (result)
+ dev_err(&slave_client->adapter->dev,
+ "Accel exit failed %d\n", result);
+ }
+ mldl_cfg->slave[slave_descr->type] = NULL;
+ mldl_cfg->pdata_slave[slave_descr->type] = NULL;
+ mpu->slave_modules[slave_descr->type] = NULL;
+
+ mutex_unlock(&mpu->mutex);
+
+}
+EXPORT_SYMBOL(inv_mpu_unregister_slave);
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static const struct i2c_device_id mpu_id[] = {
+ {"mpu3050", 0},
+ {"mpu6050", 0},
+ {"mpu6050_no_accel", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mpu_id);
+
+int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+ struct mpu_platform_data *pdata;
+ struct mpu_private_data *mpu;
+ 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++);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ res = -ENODEV;
+ goto out_check_functionality_failed;
+ }
+
+ mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
+ if (!mpu) {
+ res = -ENOMEM;
+ goto out_alloc_data_failed;
+ }
+ mldl_cfg = &mpu->mldl_cfg;
+ mldl_cfg->mpu_ram = &mpu->mpu_ram;
+ mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg;
+ mldl_cfg->mpu_offsets = &mpu->mpu_offsets;
+ mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info;
+ mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg;
+ mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state;
+
+ mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE;
+ mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL);
+ if (!mldl_cfg->mpu_ram->ram) {
+ res = -ENOMEM;
+ goto out_alloc_ram_failed;
+ }
+ mpu_private_data = mpu;
+ i2c_set_clientdata(client, mpu);
+ mpu->client = client;
+
+ init_waitqueue_head(&mpu->mpu_event_wait);
+ mutex_init(&mpu->mutex);
+ init_completion(&mpu->completion);
+
+ mpu->response_timeout = 60; /* Seconds */
+ mpu->timeout.function = mpu_pm_timeout;
+ mpu->timeout.data = (u_long) mpu;
+ init_timer(&mpu->timeout);
+
+ mpu->nb.notifier_call = mpu_pm_notifier_callback;
+ mpu->nb.priority = 0;
+ res = register_pm_notifier(&mpu->nb);
+ if (res) {
+ dev_err(&client->adapter->dev,
+ "Unable to register pm_notifier %d\n", res);
+ goto out_register_pm_notifier_failed;
+ }
+
+ pdata = (struct mpu_platform_data *)client->dev.platform_data;
+ if (!pdata) {
+ dev_WARN(&client->adapter->dev,
+ "Missing platform data for mpu\n");
+ }
+ mldl_cfg->pdata = pdata;
+
+ mldl_cfg->mpu_chip_info->addr = client->addr;
+ res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+
+ if (res) {
+ dev_err(&client->adapter->dev,
+ "Unable to open %s %d\n", MPU_NAME, res);
+ res = -ENODEV;
+ goto out_whoami_failed;
+ }
+
+ mpu->dev.minor = MISC_DYNAMIC_MINOR;
+ mpu->dev.name = "mpu";
+ mpu->dev.fops = &mpu_fops;
+ res = misc_register(&mpu->dev);
+ if (res < 0) {
+ dev_err(&client->adapter->dev,
+ "ERROR: misc_register returned %d\n", res);
+ goto out_misc_register_failed;
+ }
+
+ if (client->irq) {
+ dev_info(&client->adapter->dev,
+ "Installing irq using %d\n", client->irq);
+ 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:
+ inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+out_whoami_failed:
+ unregister_pm_notifier(&mpu->nb);
+out_register_pm_notifier_failed:
+ kfree(mldl_cfg->mpu_ram->ram);
+ mpu_private_data = NULL;
+out_alloc_ram_failed:
+ kfree(mpu);
+out_alloc_data_failed:
+out_check_functionality_failed:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
+ return res;
+
+}
+
+static int mpu_remove(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+ int ii;
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+ if (!pdata_slave[ii])
+ slave_adapter[ii] = NULL;
+ else
+ slave_adapter[ii] =
+ i2c_get_adapter(pdata_slave[ii]->adapt_num);
+ }
+
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_close(mldl_cfg,
+ slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+ slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+ 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();
+
+ misc_deregister(&mpu->dev);
+
+ unregister_pm_notifier(&mpu->nb);
+
+ kfree(mpu->mldl_cfg.mpu_ram->ram);
+ kfree(mpu);
+
+ return 0;
+}
+
+static struct i2c_driver mpu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mpu_probe,
+ .remove = mpu_remove,
+ .id_table = mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MPU_NAME,
+ },
+ .address_list = normal_i2c,
+ .shutdown = mpu_shutdown, /* optional */
+ .suspend = mpu_dev_suspend, /* optional */
+ .resume = mpu_dev_resume, /* optional */
+
+};
+
+static int __init mpu_init(void)
+{
+ int res = i2c_add_driver(&mpu_driver);
+ pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mpu_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&mpu_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h
new file mode 100644
index 000000000000..686f6e5d86a5
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050b1.h
@@ -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_ */
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c
index dfd87cdabc6e..d8b721e43468 100644
--- a/drivers/misc/inv_mpu/mpuirq.c
+++ b/drivers/misc/inv_mpu/mpuirq.c
@@ -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");
diff --git a/drivers/misc/inv_mpu/mpuirq.h b/drivers/misc/inv_mpu/mpuirq.h
index 33480711f79d..073867d830c5 100644
--- a/drivers/misc/inv_mpu/mpuirq.h
+++ b/drivers/misc/inv_mpu/mpuirq.h
@@ -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
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c
index 95e690ee60cb..22cfa4e458e8 100644
--- a/drivers/misc/inv_mpu/slaveirq.c
+++ b/drivers/misc/inv_mpu/slaveirq.c
@@ -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);