summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorRobert Collins <rcollins@nvidia.com>2010-11-23 13:54:32 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:46:33 -0800
commitdf3ec81e193b55c04362ab2fbbf4611a4f41947c (patch)
tree7101dae0c47e60860c5f1367fa75f9a8ec93e530 /drivers/misc
parent2816bee2242d6fa34586dc8043d239c2d1fd8bd6 (diff)
[ARM/tegra] Integrate Accelerometer source code files.
Inegrate MPL libraries and the following sensors: Accelerometers: kxtf9 Compi: ak8975 Original-Change-Id: I450b5b7ff018249a19bb23b78e722e9a355b7bd8 Reviewed-on: http://git-master/r/11803 Tested-by: Robert R Collins <rcollins@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Rebase-Id: R76e2da39a90190d176552fdb11aab2009964023f
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile5
-rwxr-xr-xdrivers/misc/mpu3050/Kconfig54
-rwxr-xr-xdrivers/misc/mpu3050/Makefile37
-rwxr-xr-xdrivers/misc/mpu3050/README138
-rwxr-xr-xdrivers/misc/mpu3050/accel/kxtf9.c144
-rwxr-xr-xdrivers/misc/mpu3050/compass/ak8975.c146
-rwxr-xr-xdrivers/misc/mpu3050/log.h287
-rwxr-xr-xdrivers/misc/mpu3050/mldl_cfg.c872
-rwxr-xr-xdrivers/misc/mpu3050/mldl_cfg.h98
-rwxr-xr-xdrivers/misc/mpu3050/mlos-kernel.c92
-rwxr-xr-xdrivers/misc/mpu3050/mlos.h78
-rwxr-xr-xdrivers/misc/mpu3050/mlsl-kernel.c171
-rwxr-xr-xdrivers/misc/mpu3050/mlsl.h81
-rwxr-xr-xdrivers/misc/mpu3050/mltypes.h215
-rwxr-xr-xdrivers/misc/mpu3050/mpu-dev.c835
-rwxr-xr-xdrivers/misc/mpu3050/mpu-i2c.c195
-rwxr-xr-xdrivers/misc/mpu3050/mpu-i2c.h60
-rwxr-xr-xdrivers/misc/mpu3050/mpuirq.c315
-rwxr-xr-xdrivers/misc/mpu3050/mpuirq.h50
20 files changed, 3872 insertions, 2 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 540b8d7f997f..f0459ceb7360 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -534,5 +534,6 @@ source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
+source "drivers/misc/mpu3050/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7f8788e533a0..7a13ef884b94 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -13,8 +13,8 @@ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_BMP085) += bmp085.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
-obj-$(CONFIG_TIFM_CORE) += tifm_core.o
-obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
+obj-$(CONFIG_TIFM_CORE) += tifm_core.o
+obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
@@ -49,5 +49,6 @@ obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o
obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o
+obj-$(CONFIG_SENSORS_MPU3050) += mpu3050/
obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o
obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o
diff --git a/drivers/misc/mpu3050/Kconfig b/drivers/misc/mpu3050/Kconfig
new file mode 100755
index 000000000000..99ea1c6aa8e2
--- /dev/null
+++ b/drivers/misc/mpu3050/Kconfig
@@ -0,0 +1,54 @@
+
+menu "Motion Sensors Support"
+
+config SENSORS_MPU3050
+ tristate "MPU3050 Gyroscope Driver"
+ depends on I2C
+ help
+ If you say yes here you get support for the MPU3050 Gyroscope driver
+ This driver can also be built as a module. If so, the module
+ will be called mpu3050.
+
+config SENSORS_MPU3050_DEBUG
+ bool "MPU3050 debug"
+ depends on SENSORS_MPU3050
+ help
+ If you say yes here you get extra debug messages from the MPU3050
+ and other slave sensors.
+
+choice
+ prompt "Accelerometer Type"
+ depends on SENSORS_MPU3050
+ default SENSORS_KXTF9_MPU
+
+config SENSORS_ACCELEROMETER_NONE
+ bool "NONE"
+ help
+ This disables accelerometer support for the MPU3050
+
+config SENSORS_KXTF9_MPU
+ bool "Kionix KXTF9"
+ help
+ This enables support for the Kionix KXFT9 accelerometer
+
+endchoice
+
+choice
+ prompt "Compass Type"
+ depends on SENSORS_MPU3050
+ default SENSORS_AK8975_MPU
+
+config SENSORS_COMPASS_NONE
+ bool "NONE"
+ help
+ This disables compass support for the MPU3050
+
+config SENSORS_AK8975_MPU
+ bool "AKM ak8975"
+ help
+ This enables support for the AKM ak8975 compass
+
+endchoice
+
+endmenu
+
diff --git a/drivers/misc/mpu3050/Makefile b/drivers/misc/mpu3050/Makefile
new file mode 100755
index 000000000000..99955ad487fc
--- /dev/null
+++ b/drivers/misc/mpu3050/Makefile
@@ -0,0 +1,37 @@
+
+# Kernel makefile for motions sensors
+#
+#
+
+# MPU
+obj-$(CONFIG_SENSORS_MPU3050) += mpu3050.o
+mpu3050-objs += mpuirq.o \
+ mpu-dev.o \
+ mpu-i2c.o \
+ mlsl-kernel.o \
+ mlos-kernel.o \
+ $(MLLITE_DIR)mldl_cfg.o
+
+#
+# Accel options
+#
+ifdef CONFIG_SENSORS_KXTF9_MPU
+mpu3050-objs += $(MLLITE_DIR)accel/kxtf9.o
+endif
+
+#
+# Compass options
+#
+ifdef CONFIG_SENSORS_AK8975_MPU
+mpu3050-objs += $(MLLITE_DIR)compass/ak8975.o
+endif
+
+EXTRA_CFLAGS += -I$(M)/$(MLLITE_DIR) \
+ -I$(M)/../../include \
+ -Idrivers/misc/mpu3050 \
+ -Iinclude/linux
+
+ifdef CONFIG_SENSORS_MPU3050_DEBUG
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/misc/mpu3050/README b/drivers/misc/mpu3050/README
new file mode 100755
index 000000000000..8ff0c3009eca
--- /dev/null
+++ b/drivers/misc/mpu3050/README
@@ -0,0 +1,138 @@
+Kernel driver mpu3050
+=====================
+
+Supported chips:
+ * Invensense IMU3050
+ Prefix: 'mpu3050'
+ Datasheet:
+ PS-MPU-3000A-00.2.4b.pdf
+
+Author: Invensense <http://invensense.com>
+
+Description
+-----------
+The mpu3050 is a motion processor that controls the mpu3050 gyroscope, a slave
+accelerometer and compass. This document describes how to install the driver
+into a linux kernel and a small note about how to set up the file permissions
+in an android file system.
+
+Sysfs entries
+-------------
+/dev/mpu
+/dev/mpuirq
+
+General Remarks
+---------------
+
+Valid addresses for the MPU3050 is 0x68.
+Accelerometer must be on the secondary I2C bus.
+
+Programming the chip using /dev/mpu
+----------------------------------
+Programming of the MPU3050 is done by first opening the /dev/mpu file and
+then performing a series of IOCTLS on the handle returned. The IOCTL codes can
+be found in mpu3050.h. Typically this is done by the mllite library in user
+space.
+
+Adding to a Kernel
+==================
+
+The mpu3050 driver is designed to be inserted in the drivers/misc part of the
+kernel. Copy the mpu3050 directory and the mpu3050 include file to:
+
+ <kernel root dir>/drivers/misc/mpu3050
+ <kernel root dir>/include/linux/mpu3050.h
+
+respectively.
+
+After this is done the drivers/misc/Kconfig must be edited to add the line:
+
+ source "drivers/misc/mpu3050/Kconfig"
+
+Similarly drivers/misc/Makefile must be edited to add the line:
+
+ obj-y += mpu3050/
+
+Configuration can then be done as normal.
+
+Board and Platform Data
+-----------------------
+
+In order for the driver to work, board and platform data specific to the device
+needs to be added to the board file. A mpu3050_platform_data structure must
+be created and populatd and set in the i2c_board_info_structure. For details of
+each structure member see mpu3050.h. All values below are modified for the ventana
+platform. You should add these lines into the
+kernel/arch/arm/mach-tegra/board-generic.c file.
+
+#include <linux/mpu3050.h>
+
+static struct mpu3050_platform_data mpu3050_data = {
+ .int_config = 0x10,
+ .orientation = { 0, -1, 0,
+ -1, 0, 0,
+ 0, 0, -1 }, //Orientation matrix for MPU on ventana
+ .level_shifter = 0,
+ .accel = {
+ .get_slave_descr = kxtf9_get_slave_descr,
+ .adapt_num = 0,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+ .address = 0x0F,
+ .orientation = { 0, -1, 0,
+ -1, 0, 0,
+ 0, 0, -1 }, //Orientation matrix for Kionix on ventana
+ },
+
+
+ .compass = {
+ .get_slave_descr = ak8975_get_slave_descr,
+ .adapt_num = 3, //bus number 3 on ventana
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .address = 0x0C,
+ .orientation = { 1, 0, 0,
+ 0, -1, 0,
+ 0, 0, -1 }, //Orientation matrix for AKM on ventana
+ },
+
+};
+
+static struct i2c_board_info __initdata mpu3050_i2c0_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("mpu3050", 0x68),
+ /*.irq = 299,*/
+ .platform_data = &mpu3050_data,
+ },
+};
+
+Note: If you are unsure where to add this code, look for other instances of "i2c_board_info"
+and copy paste this code after one of them.
+
+After this is done, we must register the board upon initialization. This is done in the
+i2c_device_setup function in the board-generic.c file. Look for this function and add these
+lines of code:
+
+if (ARRAY_SIZE(mpu3050_i2c0_boardinfo))
+ i2c_register_board_info(0, mpu3050_i2c0_boardinfo,
+ ARRAY_SIZE(mpu3050_i2c0_boardinfo));
+
+Before you can build the kernel, you will need to remove the existing NVidia AK8975 driver for the compass.
+In the board-generic.c file, rename the instance of CONFIG_SENSORS_AK8975 to NVIDIA_CONFIG_SENSORS_AK8975.
+In kernel/drivers/hwmon/Makefile, rename that instance of CONFIG_SENSORS_AK8975 to NVIDIA_CONFIG_SENSORS_AK8975 as well.
+This will prevent NVidia's driver for the compass from building.
+
+Now you can build the new kernel. First, we must configure the kernel. Take the tegra_ventana_android_defconfig
+file from this package and replace the existing one in the kernel/arch/arm/configs directory. In the root kernel
+directory, you can then run:
+
+export CCOMPILER=/<path to android environment>/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-
+make ARCH=arm CROSS_COMPILE=$CCOMPILER mrproper
+make ARCH=arm CROSS_COMPILE=$CCOMPILER tegra_ventana_android_defconfig
+make ARCH=arm CROSS_COMPILE=$CCOMPILER
+
+This should build your zImage, which you will find in kernel/arch/arm/boot/zImage. You can use the mkbootimg utility to then
+make a boot.img:
+
+ ./mkbootimg --kernel <path to android system>/kernel/arch/arm/boot/zImage --ramdisk <path to android system>/out/target/product/ventana/ramdisk.img --output boot.img
+
+You can then flash this boot.img using the nvflash utility.
+
diff --git a/drivers/misc/mpu3050/accel/kxtf9.c b/drivers/misc/mpu3050/accel/kxtf9.c
new file mode 100755
index 000000000000..ecf9e7dfd3d7
--- /dev/null
+++ b/drivers/misc/mpu3050/accel/kxtf9.c
@@ -0,0 +1,144 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: kxtf9.c 3867 2010-10-09 01:06:18Z prao $
+ *
+ *******************************************************************************/
+
+/**
+ * @defgroup ACCELDL (Motion Library - Accelerometer Driver Layer)
+ * @brief Provides the interface to setup and handle an accelerometers
+ * connected to the secondary I2C interface of the gyroscope.
+ *
+ * @{
+ * @file kxtf9.c
+ * @brief Accelerometer setup and handling methods.
+**/
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#ifdef __KERNEL__
+#include <linux/module.h>
+#endif
+
+#include "mpu3050.h"
+#include "mlsl.h"
+#include "mlos.h"
+
+#include <log.h>
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* --------------------- */
+/* - Variables. - */
+/* --------------------- */
+
+/*****************************************
+ Accelerometer Initialization Functions
+*****************************************/
+
+static int kxtf9_suspend(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ /* RAM reset */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x1d, 0xcd);
+ return result;
+}
+
+/* full scale setting - register and mask */
+#define ACCEL_KIONIX_CTRL_REG (0x1b)
+#define ACCEL_KIONIX_CTRL_MASK (0x18)
+
+static int kxtf9_resume(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = ML_SUCCESS;
+ unsigned char reg;
+
+ /* RAM reset */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x1d, 0xcd);
+ MLOSSleep(10);
+ /* Wake up */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x1b, 0x42);
+ /* INT_CTRL_REG1: */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x1e, 0x14);
+ /* WUF_THRESH: */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x5a, 0x00);
+ /* DATA_CTRL_REG */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x21, 0x04);
+ /* WUF_TIMER */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x29, 0x02);
+
+ /* Full Scale */
+ reg = 0xc2;
+ reg &= ~ACCEL_KIONIX_CTRL_MASK;
+ reg |= 0x00; /* TODO FIXME michelle */
+ if (slave->range.mantissa == 2) {
+ reg |= 0x00;
+ } else if (slave->range.mantissa == 4) {
+ reg |= 0x08;
+ } else if (slave->range.mantissa == 8) {
+ reg |= 0x10;
+ }
+ /* Normal operation */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, 0x1b, reg);
+ MLOSSleep(50);
+
+ return ML_SUCCESS;
+}
+
+static int kxtf9_read(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+static struct ext_slave_descr kxtf9_descr = {
+ /*.suspend = */ kxtf9_suspend,
+ /*.resume = */ kxtf9_resume,
+ /*.read = */ kxtf9_read,
+ /*.name = */ "kxtf9",
+ /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER,
+ /*.id = */ ACCEL_ID_KXTF9,
+ /*.reg = */ 0x06,
+ /*.len = */ 6,
+ /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
+ /*.range = */ {2, 0},
+};
+
+struct ext_slave_descr *kxtf9_get_slave_descr(void)
+{
+ return &kxtf9_descr;
+}
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(kxtf9_get_slave_descr);
+#endif
+
+/**
+ * @}
+**/
diff --git a/drivers/misc/mpu3050/compass/ak8975.c b/drivers/misc/mpu3050/compass/ak8975.c
new file mode 100755
index 000000000000..fb4c033556b3
--- /dev/null
+++ b/drivers/misc/mpu3050/compass/ak8975.c
@@ -0,0 +1,146 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: ak8975.c 3879 2010-10-12 03:12:37Z mcaramello $
+ *
+ *******************************************************************************/
+
+/**
+ * @defgroup COMPASSDL (Motion Library - Accelerometer Driver Layer)
+ * @brief Provides the interface to setup and handle an accelerometers
+ * connected to the secondary I2C interface of the gyroscope.
+ *
+ * @{
+ * @file AK8975.c
+ * @brief Magnetometer setup and handling methods for AKM 8975 compass.
+**/
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#ifdef __KERNEL__
+#include <linux/module.h>
+#endif
+
+#include "mpu3050.h"
+#include "mlsl.h"
+#include "mlos.h"
+
+#include <log.h>
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+#define AK8975_REG_ST1 (0x02)
+#define AK8975_REG_HXL (0x03)
+#define AK8975_REG_ST2 (0x09)
+
+#define AK8975_REG_CNTL (0x0A)
+
+#define AK8975_CNTL_MODE_POWER_DOWN (0x00)
+#define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
+
+int ak8975_suspend(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = ML_SUCCESS;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_POWER_DOWN);
+ return result;
+}
+
+int ak8975_resume(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = ML_SUCCESS;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
+ return result;
+}
+
+int ak8975_read(mlsl_handle_t mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata, unsigned char *data)
+{
+ unsigned char stat;
+ unsigned char stat2;
+ int result = ML_SUCCESS;
+
+ result = MLSLSerialRead(mlsl_handle, pdata->address,
+ AK8975_REG_ST1, 1, &stat);
+ ERROR_CHECK(result);
+ if (stat & 0x01) {
+ result = MLSLSerialRead(mlsl_handle, pdata->address,
+ AK8975_REG_HXL, 6,
+ (unsigned char *)data);
+ ERROR_CHECK(result);
+ result = MLSLSerialRead(mlsl_handle, pdata->address,
+ AK8975_REG_ST2, 1, &stat2);
+ ERROR_CHECK(result);
+ if (stat2 & 0x04) { /*data error */
+ return ML_ERROR_COMPASS_DATA_NOT_READY;
+ }
+ if (stat2 & 0x08) {
+ return ML_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
+ return ML_SUCCESS;
+ } else if (stat & 0x02) {
+ result = MLSLSerialRead(mlsl_handle, pdata->address,
+ AK8975_REG_ST2, 1, &stat2);
+ ERROR_CHECK(result);
+ return ML_ERROR_COMPASS_DATA_OVERFLOW;
+ } else {
+ return ML_ERROR_COMPASS_DATA_NOT_READY;
+ }
+
+}
+
+struct ext_slave_descr ak8975_descr = {
+ /*.suspend = */ ak8975_suspend,
+ /*.resume = */ ak8975_resume,
+ /*.read = */ ak8975_read,
+ /*.name = */ "ak8975",
+ /*.type = */ EXT_SLAVE_TYPE_COMPASS,
+ /*.id = */ COMPASS_ID_AKM,
+ /*.reg = */ 0x06,
+ /*.len = */ 6,
+ /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
+ /*.range = */ {9830, 4000}
+};
+
+struct ext_slave_descr *ak8975_get_slave_descr(void)
+{
+ return &ak8975_descr;
+}
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(ak8975_get_slave_descr);
+#endif
+
+/**
+ * @}
+**/
diff --git a/drivers/misc/mpu3050/log.h b/drivers/misc/mpu3050/log.h
new file mode 100755
index 000000000000..6cb7eae4afcf
--- /dev/null
+++ b/drivers/misc/mpu3050/log.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 InvenSense Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use MPL_LOG in a signal handler.
+*/
+#ifndef _LIBS_CUTILS_MPL_LOG_H
+#define _LIBS_CUTILS_MPL_LOG_H
+
+#include <stdarg.h>
+
+#ifdef ANDROID
+#include <utils/Log.h> /* For the LOG macro */
+#endif
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Normally we strip MPL_LOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define MPL_LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#define MPL_LOGV /* comment this out to enable VERBOSE level logging */
+#ifndef MPL_LOG_NDEBUG
+#ifdef NDEBUG
+#define MPL_LOG_NDEBUG 1
+#else
+#define MPL_LOG_NDEBUG 0
+#endif
+#endif
+
+#ifdef __KERNEL__
+#define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE
+#define MPL_LOG_DEFAULT KERN_DEFAULT
+#define MPL_LOG_VERBOSE KERN_CONT
+#define MPL_LOG_DEBUG KERN_NOTICE
+#define MPL_LOG_INFO KERN_INFO
+#define MPL_LOG_WARN KERN_WARNING
+#define MPL_LOG_ERROR KERN_ERR
+#define MPL_LOG_SILENT MPL_LOG_VERBOSE
+
+#else
+ /* Based off the log priorities in android
+ /system/core/include/android/log.h */
+#define MPL_LOG_UNKNOWN (0)
+#define MPL_LOG_DEFAULT (1)
+#define MPL_LOG_VERBOSE (2)
+#define MPL_LOG_DEBUG (3)
+#define MPL_LOG_INFO (4)
+#define MPL_LOG_WARN (5)
+#define MPL_LOG_ERROR (6)
+#define MPL_LOG_SILENT (8)
+#endif
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef MPL_LOG_TAG
+#ifdef __KERNEL__
+#define MPL_LOG_TAG
+#else
+#define MPL_LOG_TAG NULL
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose log message using the current MPL_LOG_TAG.
+ */
+#define MPL_LOGV
+#ifndef MPL_LOGV
+#if MPL_LOG_NDEBUG
+#define MPL_LOGV(...) ((void)0)
+#else
+#define MPL_LOGV(...) ((void)MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#ifndef CONDITION
+#define CONDITION(cond) ((cond) != 0)
+#endif
+
+#ifndef MPL_LOGV_IF
+#if MPL_LOG_NDEBUG
+#define MPL_LOGV_IF(cond, ...) ((void)0)
+#else
+#define MPL_LOGV_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGD
+#define MPL_LOGD(...) ((void)MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef MPL_LOGD_IF
+#define MPL_LOGD_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGI
+#define MPL_LOGI(...) ((void)MPL_LOG(LOG_INFO, MPL_LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef MPL_LOGI_IF
+#define MPL_LOGI_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)MPL_LOG(LOG_INFO, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGW
+#define MPL_LOGW(...) ((void)MPL_LOG(LOG_WARN, MPL_LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef MPL_LOGW_IF
+#define MPL_LOGW_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)MPL_LOG(LOG_WARN, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGE
+#define MPL_LOGE(...) ((void)MPL_LOG(LOG_ERROR, MPL_LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef MPL_LOGE_IF
+#define MPL_LOGE_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)MPL_LOG(LOG_ERROR, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#define MPL_LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)android_printAssert(#cond, MPL_LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+
+#define MPL_LOG_ALWAYS_FATAL(...) \
+ (((void)android_printAssert(NULL, MPL_LOG_TAG, __VA_ARGS__)))
+
+/*
+ * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if MPL_LOG_NDEBUG
+
+#define MPL_LOG_FATAL_IF(cond, ...) ((void)0)
+#define MPL_LOG_FATAL(...) ((void)0)
+
+#else
+
+#define MPL_LOG_FATAL_IF(cond, ...) MPL_LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
+#define MPL_LOG_FATAL(...) MPL_LOG_ALWAYS_FATAL(__VA_ARGS__)
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current MPL_LOG_TAG.
+ */
+#define MPL_LOG_ASSERT(cond, ...) MPL_LOG_FATAL_IF(!(cond), __VA_ARGS__)
+/*#define MPL_LOG_ASSERT(cond) MPL_LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef MPL_LOG
+#define MPL_LOG(priority, tag, ...) \
+ MPL_LOG_PRI(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef MPL_LOG_PRI
+#ifdef ANDROID
+#define MPL_LOG_PRI(priority, tag, ...) \
+ LOG(priority, tag, __VA_ARGS__)
+#elif defined __KERNEL__
+#define MPL_LOG_PRI(priority, tag, ...) \
+ printk(MPL_##priority tag __VA_ARGS__)
+#else
+#define MPL_LOG_PRI(priority, tag, ...) \
+ _MLPrintLog(MPL_##priority, tag, __VA_ARGS__)
+#endif
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef MPL_LOG_PRI_VA
+#ifdef ANDROID
+#define MPL_LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#elif defined __KERNEL__
+#define MPL_LOG_PRI_VA(priority, tag, fmt, args) \
+ vprintk(MPL_##priority tag fmt, args)
+#else
+#define MPL_LOG_PRI_VA(priority, tag, fmt, args) \
+ _MLPrintVaLog(priority, NULL, tag, fmt, args)
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+#ifndef ANDROID
+ int _MLPrintLog(int priority, const char *tag, const char *fmt, ...);
+ int _MLPrintVaLog(int priority, const char *tag, const char *fmt,
+ va_list args);
+/* Final implementation of actual writing to a character device */
+ int _MLWriteLog(const char *buf, int buflen);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LIBS_CUTILS_MPL_LOG_H */
diff --git a/drivers/misc/mpu3050/mldl_cfg.c b/drivers/misc/mpu3050/mldl_cfg.c
new file mode 100755
index 000000000000..d96b0a72a549
--- /dev/null
+++ b/drivers/misc/mpu3050/mldl_cfg.c
@@ -0,0 +1,872 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mldl_cfg.c 3881 2010-10-12 18:58:45Z prao $
+ *
+ ******************************************************************************/
+
+/**
+ * @addtogroup MLDL
+ *
+ * @{
+ * @file mldl_cfg.c
+ * @brief The Motion Library Driver Layer.
+ */
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#include <stddef.h>
+
+#include "mldl_cfg.h"
+#include "mpu3050.h"
+
+#include "mlsl.h"
+#include "mlos.h"
+
+#include "log.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "mldl_cfg:"
+
+/* --------------------- */
+/* - Variables. - */
+/* --------------------- */
+
+/* ---------------------- */
+/* - Static Functions. - */
+/* ---------------------- */
+
+/**
+ * @internal
+ * @brief MLDLCfgDMP configures the Digital Motion Processor internal to
+ * the MPU. The DMP can be enabled or disabled and the start address
+ * can be set.
+ *
+ * @param enableRun Enables the DMP processing if set to TRUE.
+ * @param enableFIFO Enables DMP output to the FIFO if set to TRUE.
+ *
+ * @return Zero if the command is successful, an error code otherwise.
+ */
+static int MLDLCtrlDmp(struct mldl_cfg *pdata, mlsl_handle_t mlsl_handle,
+ bool enableRun, bool enableFIFO)
+{
+ unsigned char b;
+
+ MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, 1, &b);
+ if (enableRun) {
+ b |= BIT_DMP_EN;
+ } else {
+ b &= ~BIT_DMP_EN;
+ }
+
+ if (enableFIFO) {
+ b |= BIT_FIFO_EN;
+ }
+
+ b |= BIT_DMP_RST;
+
+ MLSLSerialWriteSingle(mlsl_handle, pdata->addr, MPUREG_USER_CTRL, b);
+
+ return ML_SUCCESS;
+}
+
+/**
+ * @brief Starts the DMP running
+ *
+ * @return ML_SUCCESS or non-zero error code
+ */
+static int MLDLDmpStart(struct mldl_cfg *pdata, mlsl_handle_t mlsl_handle)
+{
+ unsigned char fifoBuf[2];
+ unsigned char tries = 0;
+ unsigned char userCtrlReg;
+ int result;
+ unsigned short len = !0;
+
+ result = MLSLSerialRead(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, 1, &userCtrlReg);
+
+ while (len != 0 && tries < 6) {
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL,
+ ((userCtrlReg & (~BIT_FIFO_EN))
+ | BIT_FIFO_RST));
+ MLSLSerialRead(mlsl_handle, pdata->addr,
+ MPUREG_FIFO_COUNTH, 2, fifoBuf);
+ len = (((unsigned short)fifoBuf[0] << 8)
+ | (unsigned short)fifoBuf[1]);
+ tries++;
+ }
+
+ MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, userCtrlReg);
+
+ return MLDLCtrlDmp(pdata, mlsl_handle,
+ pdata->dmp_enable, pdata->fifo_enable);
+}
+
+/**
+ * @brief enables/disables the I2C pass through to the accelerometer device.
+ * @param enable Non-zero to enable pass through.
+ * @return ML_SUCCESS if the command is successful, an error code otherwise.
+ */
+static int MLDLSetI2CBypass(struct mldl_cfg *mldl_cfg,
+ mlsl_handle_t mlsl_handle, unsigned char enable)
+{
+ unsigned char b;
+ int result;
+
+#ifdef ML_USE_DMP_SIM
+ if (!MLGetGyroPresent()) /* done this way so that pc demo */
+ return ML_SUCCESS; /* w/arm board works with universal api */
+#endif
+
+ /*---- get current 'USER_CTRL' into b ----*/
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, 1, &b);
+ ERROR_CHECK(result);
+
+ /* No change */
+ if ((b & BIT_AUX_IF_EN) != (enable * BIT_AUX_IF_EN))
+ return ML_SUCCESS;
+
+ b &= ~BIT_AUX_IF_EN;
+
+ if (!enable) {
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL,
+ (b | BIT_AUX_IF_EN));
+ ERROR_CHECK(result);
+ } else {
+ /* Coming out of I2C is tricky due to severla erratta. Do not modify
+ * this algorithm */
+ /*
+ * 1) wait for the right time and send the command to change the ime
+ * i2c slave address to an invalid address that will get naked
+ *
+ * 0x00 is broadcast. 0x7F is ulikely to be used by any accels.
+ */
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR, 0x7F);
+ ERROR_CHECK(result);
+ /*
+ * 2) wait enough time for a nack to occur, then go into bypass mode:
+ */
+ MLOSSleep(2);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, (b));
+ ERROR_CHECK(result);
+ /*
+ * 3) wait for up to one MPU cycle then restore the slave address
+ */
+ MLOSSleep(5);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR,
+ mldl_cfg->pdata->accel.address);
+ ERROR_CHECK(result);
+
+ /*
+ * 4) reset the ime interface
+ */
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL,
+ (b | BIT_AUX_IF_RST));
+ ERROR_CHECK(result);
+ MLOSSleep(2);
+ }
+
+ return result;
+}
+
+struct tsProdRevMap {
+ /*unsigned char prodRev; */
+ unsigned char siliconRev;
+ unsigned short sensTrim;
+};
+
+#define NUM_OF_PROD_REVS (DIM(prodRevsMap))
+
+#define OLDEST_PROD_REV_SUPPORTED 11
+static struct tsProdRevMap prodRevsMap[] = {
+ {0, 0},
+ {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */
+ {MPU_SILICON_REV_A4, 131}, /* 2 | */
+ {MPU_SILICON_REV_A4, 131}, /* 3 V */
+ {MPU_SILICON_REV_A4, 131}, /* 4 */
+ {MPU_SILICON_REV_A4, 131}, /* 5 */
+ {MPU_SILICON_REV_A4, 131}, /* 6 */
+ {MPU_SILICON_REV_A4, 131}, /* 7 */
+ {MPU_SILICON_REV_A4, 131}, /* 8 */
+ {MPU_SILICON_REV_A4, 131}, /* 9 */
+ {MPU_SILICON_REV_A4, 131}, /* 10 */
+ {MPU_SILICON_REV_B1, 131}, /* 11 B1 */
+ {MPU_SILICON_REV_B1, 131}, /* 12 | */
+ {MPU_SILICON_REV_B1, 131}, /* 13 V */
+ {MPU_SILICON_REV_B1, 131}, /* 14 B4 */
+ {MPU_SILICON_REV_B4, 131}, /* 15 | {MPU_SILICON_REV_B4, 131}, // 16 V */
+ {MPU_SILICON_REV_B4, 131}, /* 17 */
+ {MPU_SILICON_REV_B4, 131}, /* 18 */
+ {MPU_SILICON_REV_B4, 115}, /* 19 */
+ {MPU_SILICON_REV_B4, 115}, /* 20 */
+ {MPU_SILICON_REV_B6, 131}, /* 21 B6 */
+ {MPU_SILICON_REV_B4, 115}, /* 22 B4 */
+};
+
+/**
+ * @internal
+ * @brief Get the silicon revision ID from OTP.
+ * The silicon revision number is in read from OTP bank 0,
+ * ADDR6[7:2]. The corresponding ID is retrieved by lookup
+ * in a map.
+ * @return The silicon revision ID (0 on error).
+ */
+static int MLDLGetSiliconRev(struct mldl_cfg *pdata, mlsl_handle_t mlsl_handle)
+{
+ int result;
+ unsigned char index = 0x00;
+ unsigned char bank =
+ (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+ unsigned short memAddr = ((bank << 8) | 0x06);
+
+ result = MLSLSerialReadMem(mlsl_handle, pdata->addr,
+ memAddr, 1, &index);
+ if (result)
+ return result;
+ index >>= 2;
+
+ if (index < OLDEST_PROD_REV_SUPPORTED || NUM_OF_PROD_REVS < index) {
+ pdata->silicon_revision = 0;
+ return ML_ERROR_INVALID_MODULE;
+ } else {
+ pdata->silicon_revision = prodRevsMap[index].siliconRev;
+ pdata->trim = prodRevsMap[index].sensTrim;
+ }
+ return result;
+}
+
+/**
+ * @brief Enable/Disable the use MPU's VDDIO level shifters.
+ * When enabled the voltage interface with AUX or other external
+ * accelerometer is using Vlogic instead of VDD (supply).
+ *
+ * @note Must be called after MLSerialOpen().
+ * @note Typically be called before MLDmpOpen().
+ * If called after MLDmpOpen(), must be followed by a call to
+ * MLDLApplyLevelShifterBit() to write the setting on the hw.
+ *
+ * @param[in] enable
+ * 1 to enable, 0 to disable
+ *
+ * @return ML_SUCCESS if successfull, a non-zero error code otherwise.
+**/
+static int MLDLSetLevelShifterBit(struct mldl_cfg *pdata,
+ mlsl_handle_t mlsl_handle,
+ unsigned char enable)
+{
+ int result;
+ unsigned char reg;
+ unsigned char mask;
+ unsigned char regval;
+
+ if (0 == pdata->silicon_revision) {
+ return ML_ERROR_INVALID_PARAMETER;
+ }
+
+ /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR --
+ NOTE: this is incompatible with ST accelerometers where the VDDIO
+ bit MUST be set to enable ST's internal logic to autoincrement
+ the register address on burst reads --*/
+ if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) {
+ reg = MPUREG_ACCEL_BURST_ADDR;
+ mask = 0x80;
+ } else {
+ /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 =>
+ the mask is always 0x04 --*/
+ reg = MPUREG_FIFO_EN2;
+ mask = 0x04;
+ }
+
+ result = MLSLSerialRead(mlsl_handle, pdata->addr, reg, 1, &regval);
+ if (result)
+ return result;
+
+ if (enable)
+ regval |= mask;
+ else
+ regval &= ~mask;
+
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, reg, regval);
+
+ return result;
+}
+
+/**
+ * @internal
+ * @brief This function controls the power management on the MPU device.
+ * The entire chip can be put to low power sleep mode, or individual
+ * gyros can be turned on/off.
+ *
+ * Putting the device into sleep mode depending upon the changing needs
+ * of the associated applications is a recommended method for reducing
+ * power consuption. It is a safe opearation in that sleep/wake up of
+ * gyros while running will not result in any interruption of data.
+ *
+ * Although it is entirely allowed to put the device into full sleep
+ * while running the DMP, it is not recomended because it will disrupt
+ * the ongoing calculations carried on inside the DMP and consequently
+ * the sensor fusion algorithm. Furthermore, while in sleep mode
+ * read & write operation from the app processor on both registers and
+ * memory are disabled and can only regained by restoring the MPU in
+ * normal power mode.
+ * Disabling any of the gyro axis will reduce the associated power
+ * consuption from the PLL but will not stop the DMP from running
+ * state.
+ *
+ * @param reset
+ * Non-zero to reset the device. Note that this setting
+ * is volatile and the corresponding register bit will
+ * clear itself right after.
+ * @param sleep
+ * Non-zero to put device into full sleep.
+ * @param disable_gx
+ * Non-zero to disable gyro X.
+ * @param disable_gy
+ * Non-zero to disable gyro Y.
+ * @param disable_gz
+ * Non-zero to disable gyro Z.
+ *
+ * @return ML_SUCCESS if successfull; a non-zero error code otherwise.
+ */
+static int MLDLPowerMgmtMPU(struct mldl_cfg *pdata,
+ mlsl_handle_t mlsl_handle,
+ unsigned char reset,
+ unsigned char sleep,
+ unsigned char disable_gx,
+ unsigned char disable_gy, unsigned char disable_gz)
+{
+ unsigned char b;
+ int result;
+
+ result =
+ MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, &b);
+ ERROR_CHECK(result);
+
+ /* If we are awake, we need to put it in bypass before resetting */
+ if ((!(b & BIT_SLEEP)) && reset) {
+ result = MLDLSetI2CBypass(pdata, mlsl_handle, 1);
+ }
+
+ /* Reset if requested */
+ if (reset) {
+ result =
+ MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b | BIT_H_RESET);
+ MLOSSleep(5);
+ }
+
+ /* Some chips are awake after reset and some are asleep, check the status */
+ result =
+ MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, &b);
+ ERROR_CHECK(result);
+
+ /* Update the suspended state just in case we return early */
+ if (b & BIT_SLEEP) {
+ pdata->is_suspended = TRUE;
+ } else {
+ pdata->is_suspended = FALSE;
+ }
+
+ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG))
+ == ((sleep * BIT_SLEEP) |
+ (disable_gz * BIT_STBY_XG) |
+ (disable_gy * BIT_STBY_YG) | (disable_gz * BIT_STBY_ZG))) {
+ return ML_SUCCESS;
+ }
+
+ /*
+ * This specific transition between states needs to be reinterpreted:
+ * (1,1,1,1) -> (0,1,1,1) has to become
+ * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1)
+ * where
+ * (1,1,1,1) stands for (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1)
+ */
+ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) /* (1,1,1,1) */
+ && ((!sleep) && disable_gx && disable_gy && disable_gz)) { /* (0,1,1,1) */
+
+ result = MLDLPowerMgmtMPU(pdata, mlsl_handle, 0, 1, 0, 0, 0);
+ if (result)
+ return result;
+ b |= BIT_SLEEP;
+ b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
+ }
+
+ if ((b & BIT_SLEEP) != (sleep * BIT_SLEEP)) {
+ if (sleep) {
+ result = MLDLSetI2CBypass(pdata, mlsl_handle, 1);
+ ERROR_CHECK(result);
+ b |= BIT_SLEEP;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b);
+ ERROR_CHECK(result);
+ pdata->is_suspended = TRUE;
+ } else {
+ b &= ~BIT_SLEEP;
+
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b);
+ ERROR_CHECK(result);
+ pdata->is_suspended = FALSE;
+ MLOSSleep(5);
+ }
+ }
+ /*---
+ WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE
+ 1) put one axis at a time in stand-by
+ ---*/
+ if ((b & BIT_STBY_XG) != (disable_gx * BIT_STBY_XG)) {
+ b ^= BIT_STBY_XG;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b);
+ ERROR_CHECK(result);
+ }
+ if ((b & BIT_STBY_YG) != (disable_gy * BIT_STBY_YG)) {
+ b ^= BIT_STBY_YG;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b);
+ ERROR_CHECK(result);
+ }
+ if ((b & BIT_STBY_ZG) != (disable_gz * BIT_STBY_ZG)) {
+ b ^= BIT_STBY_ZG;
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
+ MPUREG_PWR_MGM, b);
+ ERROR_CHECK(result);
+ }
+
+ return ML_SUCCESS;
+}
+
+void mpu3050_print_cfg(struct mldl_cfg *mldl_cfg)
+{
+ struct mpu3050_platform_data *pdata = mldl_cfg->pdata;
+ struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel;
+ struct ext_slave_platform_data *compass = &mldl_cfg->pdata->compass;
+
+ MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr);
+ MPL_LOGD("mldl_cfg.int_config = %02x\n", mldl_cfg->int_config);
+ MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync);
+ MPL_LOGD("mldl_cfg.full_scale = %02x\n", mldl_cfg->full_scale);
+ MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf);
+ MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src);
+ MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider);
+ MPL_LOGD("mldl_cfg.dmp_enable = %02x\n", mldl_cfg->dmp_enable);
+ MPL_LOGD("mldl_cfg.fifo_enable = %02x\n", mldl_cfg->fifo_enable);
+ MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1);
+ MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2);
+ MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n", mldl_cfg->offset_tc[0]);
+ MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n", mldl_cfg->offset_tc[1]);
+ MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n", mldl_cfg->offset_tc[2]);
+ MPL_LOGD("mldl_cfg.silicon_revision = %02x\n",
+ mldl_cfg->silicon_revision);
+ MPL_LOGD("mldl_cfg.product_id = %02x\n", mldl_cfg->product_id);
+ MPL_LOGD("mldl_cfg.trim = %02x\n", mldl_cfg->trim);
+
+ if (mldl_cfg->accel) {
+ MPL_LOGD("slave_accel->suspend = %02x\n",
+ (int)mldl_cfg->accel->suspend);
+ MPL_LOGD("slave_accel->resume = %02x\n",
+ (int)mldl_cfg->accel->resume);
+ MPL_LOGD("slave_accel->read = %02x\n",
+ (int)mldl_cfg->accel->read);
+ MPL_LOGD("slave_accel->type = %02x\n",
+ mldl_cfg->accel->type);
+ MPL_LOGD("slave_accel->reg = %02x\n",
+ mldl_cfg->accel->reg);
+ MPL_LOGD("slave_accel->len = %02x\n",
+ mldl_cfg->accel->len);
+ MPL_LOGD("slave_accel->endian = %02x\n",
+ mldl_cfg->accel->endian);
+ MPL_LOGD("slave_accel->range.mantissa= %02lx\n",
+ mldl_cfg->accel->range.mantissa);
+ MPL_LOGD("slave_accel->range.fraction= %02lx\n",
+ mldl_cfg->accel->range.fraction);
+ } else {
+ MPL_LOGD("slave_accel = NULL\n");
+ }
+
+ if (mldl_cfg->compass) {
+ MPL_LOGD("slave_compass->suspend = %02x\n",
+ (int)mldl_cfg->compass->suspend);
+ MPL_LOGD("slave_compass->resume = %02x\n",
+ (int)mldl_cfg->compass->resume);
+ MPL_LOGD("slave_compass->read = %02x\n",
+ (int)mldl_cfg->compass->read);
+ MPL_LOGD("slave_compass->type = %02x\n",
+ mldl_cfg->compass->type);
+ MPL_LOGD("slave_compass->reg = %02x\n",
+ mldl_cfg->compass->reg);
+ MPL_LOGD("slave_compass->len = %02x\n",
+ mldl_cfg->compass->len);
+ MPL_LOGD("slave_compass->endian = %02x\n",
+ mldl_cfg->compass->endian);
+ MPL_LOGD("slave_compass->range.mantissa= %02lx\n",
+ mldl_cfg->compass->range.mantissa);
+ MPL_LOGD("slave_compass->range.fraction= %02lx\n",
+ mldl_cfg->compass->range.fraction);
+
+ } else {
+ MPL_LOGD("slave_compass = NULL\n");
+ }
+ MPL_LOGD("accel->get_slave_descr = %x\n",
+ (unsigned int)accel->get_slave_descr);
+ MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num);
+ MPL_LOGD("accel->bus = %02x\n", accel->bus);
+ MPL_LOGD("accel->address = %02x\n", accel->address);
+ MPL_LOGD("accel->orientation = \n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ accel->orientation[0], accel->orientation[1],
+ accel->orientation[2], accel->orientation[3],
+ accel->orientation[4], accel->orientation[5],
+ accel->orientation[6], accel->orientation[7],
+ accel->orientation[8]);
+ MPL_LOGD("compass->get_slave_descr = %x\n",
+ (unsigned int)compass->get_slave_descr);
+ MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num);
+ MPL_LOGD("compass->bus = %02x\n", compass->bus);
+ MPL_LOGD("compass->address = %02x\n", compass->address);
+ MPL_LOGD("compass->orientation = \n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ compass->orientation[0], compass->orientation[1],
+ compass->orientation[2], compass->orientation[3],
+ compass->orientation[4], compass->orientation[5],
+ compass->orientation[6], compass->orientation[7],
+ compass->orientation[8]);
+
+ MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config);
+ MPL_LOGD("pdata->level_shifter = %02x\n", pdata->level_shifter);
+ MPL_LOGD("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]);
+
+ MPL_LOGD("Struct sizes: mldl_cfg: %d, "
+ "ext_slave_descr:%d, mpu3050_platform_data:%d: RamOffset: %d\n",
+ sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr),
+ sizeof(struct mpu3050_platform_data),
+ offsetof(struct mldl_cfg, ram));
+}
+
+/*******************************************************************************
+ *******************************************************************************
+ * Exported functions
+ *******************************************************************************
+ ******************************************************************************/
+
+/**
+ * Initializes the pdata structure to defaults.
+ *
+ * Opens the device to read silicon revision, product id and whoami.
+ *
+ * @param mldl_cfg
+ * The internal device configuration data structure.
+ * @param mlsl_handle
+ * The serial communication handle.
+ *
+ * @return ML_SUCCESS if silicon revision, product id and woami are supported
+ * by this software.
+ */
+int mpu3050_open(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle)
+{
+ int result;
+ /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
+ mldl_cfg->int_config = BIT_INT_ANYRD_2CLEAR | BIT_DMP_INT_EN;
+ mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
+ mldl_cfg->lpf = MPU_FILTER_42HZ;
+ mldl_cfg->full_scale = MPU_FS_2000DPS;
+ mldl_cfg->divider = 4;
+ mldl_cfg->dmp_enable = 1;
+ mldl_cfg->fifo_enable = 1;
+ mldl_cfg->ext_sync = 0;
+ mldl_cfg->dmp_cfg1 = 0;
+ mldl_cfg->dmp_cfg2 = 0;
+ if (mldl_cfg->addr == 0) {
+#ifdef __KERNEL__
+ return ML_ERROR_INVALID_PARAMETER;
+#else
+ mldl_cfg->addr = 0x68;
+#endif
+ }
+
+ /*
+ * Reset,
+ * Take the DMP out of sleep, and
+ * read the product_id, sillicon rev and whoami
+ */
+ result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 1, 0, 0, 0, 0);
+ ERROR_CHECK(result);
+
+ result = MLDLGetSiliconRev(mldl_cfg, mlsl_handle);
+ ERROR_CHECK(result);
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PRODUCT_ID, 1, &mldl_cfg->product_id);
+ ERROR_CHECK(result);
+
+ /* Get the factory temperature compensation offsets */
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_XG_OFFS_TC, 1, &mldl_cfg->offset_tc[0]);
+ ERROR_CHECK(result);
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_YG_OFFS_TC, 1, &mldl_cfg->offset_tc[1]);
+ ERROR_CHECK(result);
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_ZG_OFFS_TC, 1, &mldl_cfg->offset_tc[2]);
+ ERROR_CHECK(result);
+
+ /* Configure the MPU */
+ result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0);
+ ERROR_CHECK(result);
+ return result;
+}
+
+int mpu3050_resume(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ mlsl_handle_t accel_handle,
+ mlsl_handle_t compass_handle,
+ bool resume_accel, bool resume_compass)
+{
+ int result;
+ int ii;
+ int jj;
+ unsigned char reg;
+
+ /* mpu3050_print_cfg(mldl_cfg); */
+ /* Wake up the part */
+ result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 1, 0, 0, 0, 0);
+ ERROR_CHECK(result);
+
+ /* Configure the MPU */
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_INT_CFG,
+ (mldl_cfg->int_config | mldl_cfg->
+ pdata->int_config));
+ ERROR_CHECK(result);
+
+ result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, 1, &reg);
+ ERROR_CHECK(result);
+ reg &= ~BITS_CLKSEL;
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, mldl_cfg->clk_src | reg);
+ ERROR_CHECK(result);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_SMPLRT_DIV, mldl_cfg->divider);
+ ERROR_CHECK(result);
+
+ reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync,
+ mldl_cfg->full_scale, mldl_cfg->lpf);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_DLPF_FS_SYNC, reg);
+ ERROR_CHECK(result);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_DMP_CFG_1, mldl_cfg->dmp_cfg1);
+ ERROR_CHECK(result);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_DMP_CFG_2, mldl_cfg->dmp_cfg2);
+ ERROR_CHECK(result);
+
+ /* Write and verify memory */
+ for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) {
+ unsigned char read[128];
+ result = MLSLSerialWriteMem(mlsl_handle, mldl_cfg->addr,
+ ((ii << 8)),
+ MPU_MEM_BANK_SIZE / 2,
+ mldl_cfg->ram[ii]);
+ ERROR_CHECK(result);
+ result = MLSLSerialReadMem(mlsl_handle, mldl_cfg->addr,
+ ((ii << 8) | 0),
+ MPU_MEM_BANK_SIZE / 2, read);
+ ERROR_CHECK(result);
+
+ for (jj = 0; jj < MPU_MEM_BANK_SIZE / 2; jj++) {
+ /* skip the register memory locations */
+ if (ii == 0 && jj < 20)
+ continue;
+ if (mldl_cfg->ram[ii][jj] != read[jj]) {
+ result = ML_ERROR_SERIAL_WRITE;
+ break;
+ }
+ }
+ ERROR_CHECK(result);
+
+ result = MLSLSerialWriteMem(mlsl_handle, mldl_cfg->addr,
+ ((ii << 8) | MPU_MEM_BANK_SIZE / 2),
+ MPU_MEM_BANK_SIZE / 2,
+ &mldl_cfg->ram[ii][MPU_MEM_BANK_SIZE
+ / 2]);
+ ERROR_CHECK(result);
+ result = MLSLSerialReadMem(mlsl_handle, mldl_cfg->addr,
+ ((ii << 8) | MPU_MEM_BANK_SIZE / 2),
+ MPU_MEM_BANK_SIZE / 2, read);
+ ERROR_CHECK(result);
+ for (jj = 0; jj < MPU_MEM_BANK_SIZE / 2; jj++) {
+ if (mldl_cfg->ram[ii][MPU_MEM_BANK_SIZE / 2 + jj] !=
+ read[jj]) {
+ result = ML_ERROR_SERIAL_WRITE;
+ break;
+ }
+ }
+ ERROR_CHECK(result);
+ }
+
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_XG_OFFS_TC,
+ mldl_cfg->offset_tc[0]);
+ ERROR_CHECK(result);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_YG_OFFS_TC,
+ mldl_cfg->offset_tc[1]);
+ ERROR_CHECK(result);
+ result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
+ MPUREG_ZG_OFFS_TC,
+ mldl_cfg->offset_tc[2]);
+ ERROR_CHECK(result);
+
+ /* Configure slaves */
+ result = MLDLSetLevelShifterBit(mldl_cfg, mlsl_handle,
+ mldl_cfg->pdata->level_shifter);
+ ERROR_CHECK(result);
+
+ if (resume_accel) {
+ if ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume)) {
+ return ML_ERROR_INVALID_PARAMETER;
+ }
+ result = mldl_cfg->accel->resume(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ ERROR_CHECK(result);
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
+ /* Address */
+ result =
+ MLSLSerialWriteSingle(accel_handle, mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR,
+ mldl_cfg->pdata->
+ accel.address);
+ ERROR_CHECK(result);
+ /* Register */
+ result = MLSLSerialRead(accel_handle, mldl_cfg->addr,
+ MPUREG_ACCEL_BURST_ADDR, 1,
+ &reg);
+ ERROR_CHECK(result);
+ reg = ((reg & 0x80) | mldl_cfg->accel->reg);
+ /* Set VDDIO bit for ST accel */
+ if ((ACCEL_ID_LIS331 == mldl_cfg->accel->id)
+ || (ACCEL_ID_LSM303 == mldl_cfg->accel->id)) {
+ reg |= 0x80;
+ }
+ result =
+ MLSLSerialWriteSingle(accel_handle, mldl_cfg->addr,
+ MPUREG_ACCEL_BURST_ADDR, reg);
+ ERROR_CHECK(result);
+ /* Length */
+ result = MLSLSerialRead(accel_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, 1, &reg);
+ ERROR_CHECK(result);
+ reg = (reg & ~BIT_AUX_RD_LENG);
+ result =
+ MLSLSerialWriteSingle(accel_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, reg);
+ ERROR_CHECK(result);
+ result = MLDLSetI2CBypass(mldl_cfg, accel_handle, 0);
+ ERROR_CHECK(result);
+ }
+ }
+
+ if (resume_compass) {
+ if ((mldl_cfg->compass) && (mldl_cfg->compass->resume)) {
+ result = mldl_cfg->compass->resume(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->
+ pdata->compass);
+ ERROR_CHECK(result);
+ }
+ }
+
+ /* Now start */
+ result = MLDLDmpStart(mldl_cfg, mlsl_handle);
+ ERROR_CHECK(result);
+ return result;
+}
+
+int mpu3050_suspend(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ mlsl_handle_t accel_handle,
+ mlsl_handle_t compass_handle, bool accel, bool compass)
+{
+ int result;
+ /* This puts the bus into bypass mode */
+ result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0);
+ if (ML_SUCCESS == result &&
+ accel && mldl_cfg->accel && mldl_cfg->accel->suspend) {
+ result = mldl_cfg->accel->suspend(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ }
+
+ if (ML_SUCCESS == result && compass &&
+ mldl_cfg->compass && mldl_cfg->compass->suspend) {
+ result = mldl_cfg->compass->suspend(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ }
+ return result;
+}
+
+int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ unsigned char *data)
+{
+ if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->read)
+ return mldl_cfg->accel->read(mlsl_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel, data);
+ else
+ return ML_ERROR_NOT_OPENED;
+}
+
+int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ unsigned char *data)
+{
+ if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->read)
+ return mldl_cfg->compass->read(mlsl_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass, data);
+ else
+ return ML_ERROR_NOT_OPENED;
+}
+
+/***************************/
+ /**@}*//* end of defgroup */
+/***************************/
diff --git a/drivers/misc/mpu3050/mldl_cfg.h b/drivers/misc/mpu3050/mldl_cfg.h
new file mode 100755
index 000000000000..03f752b75966
--- /dev/null
+++ b/drivers/misc/mpu3050/mldl_cfg.h
@@ -0,0 +1,98 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mldl_cfg.h 3876 2010-10-12 02:42:22Z prao $
+ *
+ *******************************************************************************/
+
+/**
+ * @addtogroup MLDL
+ *
+ * @{
+ * @file mldl_cfg.h
+ * @brief The Motion Library Driver Layer Configuration header file.
+ */
+
+#ifndef __MLDL_CFG_H__
+#define __MLDL_CFG_H__
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#include "mlsl.h"
+#include "mpu3050.h"
+
+/* --------------------- */
+/* - Variables. - */
+/* --------------------- */
+
+/* Platform data for the MPU */
+struct mldl_cfg {
+ /* MPU related configuration */
+ unsigned char addr;
+ unsigned char int_config;
+ unsigned char ext_sync;
+ unsigned char full_scale;
+ unsigned char lpf;
+ unsigned char clk_src;
+ unsigned char divider;
+ unsigned char dmp_enable;
+ unsigned char fifo_enable;
+ unsigned char dmp_cfg1;
+ unsigned char dmp_cfg2;
+ unsigned char offset_tc[MPU_NUM_AXES];
+ unsigned char __packing;
+ unsigned char ram[MPU_MEM_NUM_RAM_BANKS][MPU_MEM_BANK_SIZE];
+
+ /* MPU Related stored status and info */
+ unsigned char silicon_revision;
+ unsigned char product_id;
+ unsigned short trim;
+
+ /* Driver/Kernel related state information */
+ int is_suspended;
+
+ /* Slave related information */
+ struct ext_slave_descr *accel;
+ struct ext_slave_descr *compass;
+
+ struct mpu3050_platform_data *pdata;
+};
+
+int mpu3050_open(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle);
+int mpu3050_resume(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ mlsl_handle_t accel_handle,
+ mlsl_handle_t compass_handle,
+ bool resume_accel, bool resume_compass);
+int mpu3050_suspend(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ mlsl_handle_t accel_handle,
+ mlsl_handle_t compass_handle,
+ bool suspend_accel, bool suspend_compass);
+int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ unsigned char *data);
+int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, mlsl_handle_t mlsl_handle,
+ unsigned char *data);
+
+#endif /* __MLDL_CFG_H__ */
+
+/***************************/
+ /**@}*//* end of defgroup */
+/***************************/
diff --git a/drivers/misc/mpu3050/mlos-kernel.c b/drivers/misc/mpu3050/mlos-kernel.c
new file mode 100755
index 000000000000..9492ec11a0bf
--- /dev/null
+++ b/drivers/misc/mpu3050/mlos-kernel.c
@@ -0,0 +1,92 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/***************************************************************************** *
+ * $Id: mlos-kernel.c 3863 2010-10-08 22:05:31Z nroyer $
+ ******************************************************************************/
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ * @file mlos-kernel.c
+ * @brief
+ *
+ *
+ */
+
+#include "mlos.h"
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+void *MLOSMalloc(unsigned int numBytes)
+{
+ return kmalloc(numBytes, GFP_KERNEL);
+}
+
+tMLError MLOSFree(void *ptr)
+{
+ kfree(ptr);
+ return ML_SUCCESS;
+}
+
+tMLError MLOSCreateMutex(HANDLE *mutex)
+{
+ /* @todo implement if needed */
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+tMLError MLOSLockMutex(HANDLE mutex)
+{
+ /* @todo implement if needed */
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+tMLError MLOSUnlockMutex(HANDLE mutex)
+{
+ /* @todo implement if needed */
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+tMLError MLOSDestroyMutex(HANDLE handle)
+{
+ /* @todo implement if needed */
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+FILE *MLOSFOpen(char *filename)
+{
+ /* @todo implement if needed */
+ return NULL;
+}
+
+void MLOSFClose(FILE *fp)
+{
+ /* @todo implement if needed */
+}
+
+void MLOSSleep(int mSecs)
+{
+ msleep(mSecs);
+}
+
+unsigned long MLOSGetTickCount(void)
+{
+ /* @todo implement if needed */
+ return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
diff --git a/drivers/misc/mpu3050/mlos.h b/drivers/misc/mpu3050/mlos.h
new file mode 100755
index 000000000000..4a54ce236e95
--- /dev/null
+++ b/drivers/misc/mpu3050/mlos.h
@@ -0,0 +1,78 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mlos.h 3863 2010-10-08 22:05:31Z nroyer $
+ *
+ *******************************************************************************/
+
+#ifndef _MLOS_H
+#define _MLOS_H
+
+#ifndef __KERNEL__
+#include <stdio.h>
+#endif
+
+#include "mltypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* ------------ */
+ /* - Defines. - */
+ /* ------------ */
+
+ /* - MLOSCreateFile defines. - */
+
+#define MLOS_GENERIC_READ ((unsigned int)0x80000000)
+#define MLOS_GENERIC_WRITE ((unsigned int)0x40000000)
+#define MLOS_FILE_SHARE_READ ((unsigned int)0x00000001)
+#define MLOS_FILE_SHARE_WRITE ((unsigned int)0x00000002)
+#define MLOS_OPEN_EXISTING ((unsigned int)0x00000003)
+
+ /* ---------- */
+ /* - Enums. - */
+ /* ---------- */
+
+ /* --------------- */
+ /* - Structures. - */
+ /* --------------- */
+
+ /* --------------------- */
+ /* - Function p-types. - */
+ /* --------------------- */
+
+ void *MLOSMalloc(unsigned int numBytes);
+ tMLError MLOSFree(void *ptr);
+ tMLError MLOSCreateMutex(HANDLE *mutex);
+ tMLError MLOSLockMutex(HANDLE mutex);
+ tMLError MLOSUnlockMutex(HANDLE mutex);
+ FILE *MLOSFOpen(char *filename);
+ void MLOSFClose(FILE *fp);
+
+ tMLError MLOSDestroyMutex(HANDLE handle);
+
+ void MLOSSleep(int mSecs);
+ unsigned long MLOSGetTickCount(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _MLOS_H */
diff --git a/drivers/misc/mpu3050/mlsl-kernel.c b/drivers/misc/mpu3050/mlsl-kernel.c
new file mode 100755
index 000000000000..4b9494f1d919
--- /dev/null
+++ b/drivers/misc/mpu3050/mlsl-kernel.c
@@ -0,0 +1,171 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mlsl-kernel.c 3863 2010-10-08 22:05:31Z nroyer $
+ *
+ ******************************************************************************/
+
+#include "mlsl.h"
+#include "mpu-i2c.h"
+
+/* ------------ */
+/* - Defines. - */
+/* ------------ */
+
+/* ---------------------- */
+/* - Types definitions. - */
+/* ---------------------- */
+
+/* --------------------- */
+/* - Function p-types. - */
+/* --------------------- */
+
+/**
+ * @brief used to open the I2C or SPI serial port.
+ * This port is used to send and receive data to the MPU device.
+ * @param portNum
+ * The COM port number associated with the device in use.
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+tMLError MLSLSerialOpen(char const *port, mlsl_handle_t * sl_handle)
+{
+ return ML_SUCCESS;
+}
+
+/**
+ * @brief used to reset any buffering the driver may be doing
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+tMLError MLSLSerialReset(mlsl_handle_t sl_handle)
+{
+ return ML_SUCCESS;
+}
+
+/**
+ * @brief used to close the I2C or SPI serial port.
+ * This port is used to send and receive data to the MPU device.
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+tMLError MLSLSerialClose(mlsl_handle_t sl_handle)
+{
+ return ML_SUCCESS;
+}
+
+/**
+ * @brief used to read a single byte of data.
+ * This should be sent by I2C or SPI.
+ *
+ * @param slaveAddr I2C slave address of device.
+ * @param registerAddr Register address to read.
+ * @param data Single byte of data to read.
+ *
+ * @return ML_SUCCESS if the command is successful, an error code otherwise.
+ */
+tMLError MLSLSerialWriteSingle(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned char registerAddr, unsigned char data)
+{
+ return sensor_i2c_write_register((struct i2c_adapter *)sl_handle,
+ slaveAddr, registerAddr, data);
+
+}
+
+/**
+ * @brief used to read multiple bytes of data.
+ * This should be sent by I2C or SPI.
+ *
+ * @param slaveAddr I2C slave address of device.
+ * @param registerAddr Register address to read.
+ * @param length Length of burst data.
+ * @param data Pointer to block of data.
+ *
+ * @return Zero if the command is successful; an error code otherwise
+ */
+tMLError MLSLSerialRead(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned char registerAddr,
+ unsigned short length, unsigned char *data)
+{
+ return sensor_i2c_read((struct i2c_adapter *)sl_handle,
+ slaveAddr, registerAddr, length, data);
+}
+
+/**
+ * @brief used to write multiple bytes of data.
+ * This should be sent by I2C or SPI.
+ *
+ * @param slaveAddr I2C slave address of device.
+ * @param length Length of burst data.
+ * @param data Pointer to block of data. First byte is the
+ * register address to write.
+ *
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+tMLError MLSLSerialWrite(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short length, unsigned char const *data)
+{
+ return sensor_i2c_write((struct i2c_adapter *)sl_handle,
+ slaveAddr, length, data);
+}
+
+/**
+ * @brief used to read multiple bytes of data.
+ * This should be sent by I2C or SPI.
+ *
+ * @param slaveAddr I2C slave address of device.
+ * @param registerAddr Register address to read.
+ * @param length Length of burst data.
+ * @param data Pointer to block of data.
+ *
+ * @return Zero if the command is successful; an error code otherwise
+ */
+tMLError MLSLSerialReadMem(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short memAddr,
+ unsigned short length, unsigned char *data)
+{
+ return mpu_memory_read((struct i2c_adapter *)sl_handle,
+ slaveAddr, memAddr, length, data);
+}
+
+/**
+ * @brief used to write multiple bytes of data.
+ * This should be sent by I2C or SPI.
+ *
+ * @param slaveAddr I2C slave address of device.
+ * @param length Length of burst data.
+ * @param data Pointer to block of data. First byte is the
+ * register address to write.
+ *
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+tMLError MLSLSerialWriteMem(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short mem_addr,
+ unsigned short length, unsigned char const *data)
+{
+ return mpu_memory_write((struct i2c_adapter *)sl_handle,
+ slaveAddr, mem_addr, length, data);
+}
+
+/***********************/
+ /** @} *//* defgroup */
+/*********************/
diff --git a/drivers/misc/mpu3050/mlsl.h b/drivers/misc/mpu3050/mlsl.h
new file mode 100755
index 000000000000..d41683eddb77
--- /dev/null
+++ b/drivers/misc/mpu3050/mlsl.h
@@ -0,0 +1,81 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mlsl.h 3863 2010-10-08 22:05:31Z nroyer $
+ *
+ ******************************************************************************/
+
+#ifndef __MSSL_H__
+#define __MSSL_H__
+
+#include "mltypes.h"
+#include "mpu3050.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------ */
+/* - Defines. - */
+/* ------------ */
+
+/* ---------------------- */
+/* - Types definitions. - */
+/* ---------------------- */
+
+ typedef void *tMLSLHandle; /* For MPL coding standards */
+
+/* --------------------- */
+/* - Function p-types. - */
+/* --------------------- */
+
+ tMLError MLSLSerialOpen(char const *port, mlsl_handle_t * sl_handle);
+ tMLError MLSLSerialReset(mlsl_handle_t sl_handle);
+ tMLError MLSLSerialClose(mlsl_handle_t sl_handle);
+ tMLError MLSLSerialWriteSingle(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned char registerAddr,
+ unsigned char data);
+ tMLError MLSLSerialRead(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned char registerAddr,
+ unsigned short length, unsigned char *data);
+ tMLError MLSLSerialWrite(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short length,
+ unsigned char const *data);
+ tMLError MLSLSerialReadMem(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short memAddr,
+ unsigned short length, unsigned char *data);
+ tMLError MLSLSerialWriteMem(mlsl_handle_t sl_handle,
+ unsigned char slaveAddr,
+ unsigned short memAddr,
+ unsigned short length,
+ unsigned char const *data);
+ tMLError MLSLWriteCal(unsigned char *cal, unsigned int len);
+ tMLError MLSLGetCalLength(unsigned int *len);
+ tMLError MLSLReadCal(unsigned char *cal, unsigned int len);
+
+#ifdef __cplusplus
+}
+#endif
+ /***********************//** @} *//* defgroup *//*********************/
+#endif // MLSL_H
diff --git a/drivers/misc/mpu3050/mltypes.h b/drivers/misc/mpu3050/mltypes.h
new file mode 100755
index 000000000000..4ffc890ca7b8
--- /dev/null
+++ b/drivers/misc/mpu3050/mltypes.h
@@ -0,0 +1,215 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/*******************************************************************************
+ *
+ * $Id: mltypes.h 3866 2010-10-09 00:51:32Z nroyer $
+ *
+ *******************************************************************************/
+
+/**
+ * @defgroup MLERROR
+ * @brief Definition of the error codes used within the MPL and returned
+ * to the user.
+ * Every function tries to return a meaningful error code basing
+ * on the occuring error condition. The error code is numeric.
+ *
+ * The available error codes and their associated values are:
+ * - (0) ML_SUCCESS
+ * - (1) ML_ERROR
+ * - (2) ML_ERROR_INVALID_PARAMETER
+ * - (3) ML_ERROR_FEATURE_NOT_ENABLED
+ * - (4) ML_ERROR_FEATURE_NOT_IMPLEMENTED
+ * - (6) ML_ERROR_DMP_NOT_STARTED
+ * - (7) ML_ERROR_DMP_STARTED
+ * - (8) ML_ERROR_NOT_OPENED
+ * - (9) ML_ERROR_OPENED
+ * - (10) ML_ERROR_INVALID_MODULE
+ * - (11) ML_ERROR_MEMORY_EXAUSTED
+ * - (12) ML_ERROR_DIVIDE_BY_ZERO
+ * - (13) ML_ERROR_ASSERTION_FAILURE
+ * - (14) ML_ERROR_FILE_OPEN
+ * - (15) ML_ERROR_FILE_READ
+ * - (16) ML_ERROR_FILE_WRITE
+ * - (20) ML_ERROR_SERIAL_CLOSED
+ * - (21) ML_ERROR_SERIAL_OPEN_ERROR
+ * - (22) ML_ERROR_SERIAL_READ
+ * - (23) ML_ERROR_SERIAL_WRITE
+ * - (24) ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
+ * - (25) ML_ERROR_SM_TRANSITION
+ * - (26) ML_ERROR_SM_IMPROPER_STATE
+ * - (30) ML_ERROR_FIFO_OVERFLOW
+ * - (31) ML_ERROR_FIFO_FOOTER
+ * - (32) ML_ERROR_FIFO_READ_COUNT
+ * - (33) ML_ERROR_FIFO_READ_DATA
+ * - (40) ML_ERROR_MEMORY_SET
+ * - (50) ML_ERROR_LOG_MEMORY_ERROR
+ * - (51) ML_ERROR_LOG_OUTPUT_ERROR
+ * - (60) ML_ERROR_OS_BAD_PTR
+ * - (61) ML_ERROR_OS_BAD_HANDLE
+ * - (62) ML_ERROR_OS_CREATE_FAILED
+ * - (63) ML_ERROR_OS_LOCK_FAILED
+ * - (70) ML_ERROR_COMPASS_DATA_OVERFLOW
+ * - (71) ML_ERROR_COMPASS_DATA_UNDERFLOW
+ * - (72) ML_ERROR_COMPASS_DATA_NOT_READY
+ *
+**/
+
+#ifndef MLTYPES_H
+#define MLTYPES_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include "stdint_invensense.h"
+#endif
+#include "log.h"
+
+/*---------------------------
+ ML Types
+---------------------------*/
+
+typedef long long MLS64;
+typedef int MLS32;
+typedef unsigned int MLU32;
+typedef short MLS16;
+typedef unsigned short MLU16;
+typedef char MLS8;
+typedef unsigned char MLU8;
+typedef unsigned char MLBOOL;
+typedef double MLDBL;
+typedef float MLFLT;
+
+typedef unsigned char tReg;
+
+/**
+ * @struct tMLError The MPL Error Code return type.
+ *
+ * @code
+ * typedef unsigned char tMLError;
+ * @endcode
+ */
+typedef unsigned char tMLError;
+
+#if defined(LINUX) || defined (__KERNEL__)
+typedef unsigned int HANDLE;
+#endif
+
+#ifdef __KERNEL__
+typedef HANDLE FILE;
+#endif
+
+#ifndef __cplusplus
+#ifndef __KERNEL__
+typedef int_fast8_t bool;
+#endif
+#endif
+
+/*---------------------------
+ ML Defines
+---------------------------*/
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* Dimension of an array */
+#ifndef DIM
+#define DIM(array) (sizeof(array)/sizeof((array)[0]))
+#endif
+
+/* - ML Errors. - */
+#define ERROR_NAME(x) (#x)
+#define ERROR_CHECK(x) \
+{ \
+ if (ML_SUCCESS != x) { \
+ MPL_LOGE("%s|%s|%d returning %d\n", \
+ __FILE__, __func__, __LINE__, x); \
+ return x; \
+ } \
+}
+
+#define ERROR_CHECK_FIRST(first, x) {if (ML_SUCCESS == first) first = x; }
+
+#define ML_SUCCESS (0)
+/* Generic Error code. Propritary Error Codes only */
+#define ML_ERROR (1)
+
+/* Compatibility and other generic error codes */
+#define ML_ERROR_INVALID_PARAMETER (2)
+#define ML_ERROR_FEATURE_NOT_ENABLED (3)
+#define ML_ERROR_FEATURE_NOT_IMPLEMENTED (4)
+#define ML_ERROR_DMP_NOT_STARTED (6)
+#define ML_ERROR_DMP_STARTED (7)
+#define ML_ERROR_NOT_OPENED (8)
+#define ML_ERROR_OPENED (9)
+#define ML_ERROR_INVALID_MODULE (10)
+#define ML_ERROR_MEMORY_EXAUSTED (11)
+#define ML_ERROR_DIVIDE_BY_ZERO (12)
+#define ML_ERROR_ASSERTION_FAILURE (13)
+#define ML_ERROR_FILE_OPEN (14)
+#define ML_ERROR_FILE_READ (15)
+#define ML_ERROR_FILE_WRITE (16)
+
+/* Serial Communication */
+#define ML_ERROR_SERIAL_CLOSED (20)
+#define ML_ERROR_SERIAL_OPEN_ERROR (21)
+#define ML_ERROR_SERIAL_READ (22)
+#define ML_ERROR_SERIAL_WRITE (23)
+#define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (24)
+
+/* SM = State Machine */
+#define ML_ERROR_SM_TRANSITION (25)
+#define ML_ERROR_SM_IMPROPER_STATE (26)
+
+/* Fifo */
+#define ML_ERROR_FIFO_OVERFLOW (30)
+#define ML_ERROR_FIFO_FOOTER (31)
+#define ML_ERROR_FIFO_READ_COUNT (32)
+#define ML_ERROR_FIFO_READ_DATA (33)
+
+/* Memory & Registers, Set & Get */
+#define ML_ERROR_MEMORY_SET (40)
+
+#define ML_ERROR_LOG_MEMORY_ERROR (50)
+#define ML_ERROR_LOG_OUTPUT_ERROR (51)
+
+/*OS interface errors */
+#define ML_ERROR_OS_BAD_PTR (60)
+#define ML_ERROR_OS_BAD_HANDLE (61)
+#define ML_ERROR_OS_CREATE_FAILED (62)
+#define ML_ERROR_OS_LOCK_FAILED (63)
+
+/* Compass errors */
+#define ML_ERROR_COMPASS_DATA_OVERFLOW (70)
+#define ML_ERROR_COMPASS_DATA_UNDERFLOW (71)
+#define ML_ERROR_COMPASS_DATA_NOT_READY (72)
+
+/*---------------------------
+ p-Types
+---------------------------*/
+
+#endif /* MLTYPES_H */
diff --git a/drivers/misc/mpu3050/mpu-dev.c b/drivers/misc/mpu3050/mpu-dev.c
new file mode 100755
index 000000000000..dde346bc0e9b
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-dev.c
@@ -0,0 +1,835 @@
+/*
+ mpu-dev.c - mpu3050 char device interface
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ Copyright (C) 2010 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/>.
+*/
+/* Code inside mpudev_ioctl_rdrw is copied from i2c-dev.c
+ */
+#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>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "mpuirq.h"
+#include "mlsl.h"
+#include "mpu-i2c.h"
+#include "mldl_cfg.h"
+#include "mpu3050.h"
+
+#define MPU3050_EARLY_SUSPEND_IN_DRIVER 0
+#define MPU_NAME "mpu"
+#define MPU_SLAVE_ADDR (0x68)
+
+#define MPU_GET_INTERRUPT_CNT (2)
+#define MPU_GET_IRQ_TIME (3)
+#define MPU_GET_LED_VALUE (4)
+#define MPU_SET_TIMEOUT (5)
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+ struct mldl_cfg mldl_cfg;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static int pid;
+
+static struct i2c_client *this_client;
+
+static int mpu_open(struct inode *inode, struct file *file)
+{
+ printk("mpu_open\n");
+ printk("current->pid %d\n", current->pid);
+ pid = current->pid;
+ file->private_data = this_client;
+ /* we could do some checking on the flags supplied by "open"
+ // i.e. O_NONBLOCK
+ // -> set some flag to disable interruptible_sleep_on in mpu_read */
+ 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 i2c_client *client = (struct i2c_client *)file->private_data;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int result = 0;
+
+ pid = 0;
+
+ if (!mldl_cfg->is_suspended) {
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ accel_adapter =
+ i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter =
+ i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ result =
+ mpu3050_suspend(mldl_cfg, client->adapter, accel_adapter,
+ compass_adapter, TRUE, TRUE);
+ }
+
+ printk("mpu_release\n");
+ return result;
+}
+
+static noinline int mpudev_ioctl_rdrw(struct i2c_client *client,
+ unsigned long arg)
+{
+ struct i2c_rdwr_ioctl_data rdwr_arg;
+ struct i2c_msg *rdwr_pa;
+ u8 __user **data_ptrs;
+ int i, res;
+
+ if (copy_from_user(&rdwr_arg,
+ (struct i2c_rdwr_ioctl_data __user *)arg,
+ sizeof(rdwr_arg)))
+ return -EFAULT;
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
+ return -EINVAL;
+
+ rdwr_pa = (struct i2c_msg *)
+ kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
+ if (!rdwr_pa)
+ return -ENOMEM;
+
+ if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
+ rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
+ kfree(rdwr_pa);
+ return -EFAULT;
+ }
+
+ data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
+ if (data_ptrs == NULL) {
+ kfree(rdwr_pa);
+ return -ENOMEM;
+ }
+
+ res = 0;
+ for (i = 0; i < rdwr_arg.nmsgs; i++) {
+ /* Limit the size of the message to a sane amount;
+ * and don't let length change either. */
+ if ((rdwr_pa[i].len > 8192) ||
+ (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
+ res = -EINVAL;
+ break;
+ }
+ data_ptrs[i] = (u8 __user *) rdwr_pa[i].buf;
+ rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
+ if (rdwr_pa[i].buf == NULL) {
+ res = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
+ rdwr_pa[i].len)) {
+ ++i; /* Needs to be kfreed too */
+ res = -EFAULT;
+ break;
+ }
+ }
+ if (res < 0) {
+ int j;
+ for (j = 0; j < i; ++j)
+ kfree(rdwr_pa[j].buf);
+ kfree(data_ptrs);
+ kfree(rdwr_pa);
+ return res;
+ }
+
+ res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
+ while (i-- > 0) {
+ if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
+ if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
+ rdwr_pa[i].len))
+ res = -EFAULT;
+ }
+ kfree(rdwr_pa[i].buf);
+ }
+ kfree(data_ptrs);
+ kfree(rdwr_pa);
+ return res;
+}
+
+/* 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)
+{
+ char *tmp;
+ int ret;
+
+ struct i2c_client *client = (struct i2c_client *)file->private_data;
+
+ if (count > 8192)
+ count = 8192;
+
+ tmp = kmalloc(count, GFP_KERNEL);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
+ iminor(file->f_path.dentry->d_inode), count);
+
+/* @todo fix this to do a i2c trasnfer from the FIFO */
+ ret = i2c_master_recv(client, tmp, count);
+ if (ret >= 0)
+ ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
+ kfree(tmp);
+ return ret;
+}
+
+static int mpu_ioctl_get_mpu_pdata(struct i2c_client *client, unsigned long arg)
+{
+ int result;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int accel_adapt_num = mldl_cfg->pdata->accel.adapt_num;
+ int compass_adapt_num = mldl_cfg->pdata->compass.adapt_num;
+ int accel_bus = mldl_cfg->pdata->accel.bus;
+ int compass_bus = mldl_cfg->pdata->compass.bus;
+
+ result = copy_from_user(mldl_cfg->pdata,
+ (unsigned char *)arg,
+ sizeof(struct mpu3050_platform_data));
+ /* Don't allow userspace to change the adapter number or bus */
+ mldl_cfg->pdata->accel.adapt_num = accel_adapt_num;
+ mldl_cfg->pdata->compass.adapt_num = compass_adapt_num;
+ mldl_cfg->pdata->accel.bus = accel_bus;
+ mldl_cfg->pdata->compass.bus = compass_bus;
+
+ return result;
+}
+
+static int
+mpu_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+
+ printk("%s\n", __func__);
+ /*
+ * User space is not allowed to modify accel compass or pdata structs,
+ * as well as silicon_revision product_id or trim
+ */
+ if (copy_from_user(mldl_cfg,
+ (struct mldl_cfg *)arg,
+ offsetof(struct mldl_cfg, silicon_revision)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+mpu_ioctl_get_mpu_config(struct i2c_client *client, unsigned long arg)
+{
+ /* Have to be careful as there are 3 pointers in the mldl_cfg
+ * structure */
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mldl_cfg *local_mldl_cfg;
+ int retval = 0;
+
+ local_mldl_cfg = kmalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
+ if (NULL == local_mldl_cfg)
+ return -ENOMEM;
+
+ retval =
+ copy_from_user(local_mldl_cfg, (void *)arg,
+ sizeof(struct mldl_cfg));
+ if (retval)
+ goto out;
+
+ /* Fill in the accel, compass and pdata pointers */
+ if (mldl_cfg->accel) {
+ retval = copy_to_user(local_mldl_cfg->accel,
+ mldl_cfg->accel,
+ sizeof(*mldl_cfg->accel));
+ if (retval)
+ goto out;
+ }
+
+ if (mldl_cfg->compass) {
+ retval = copy_to_user(local_mldl_cfg->compass,
+ mldl_cfg->compass,
+ sizeof(*mldl_cfg->compass));
+ if (retval)
+ goto out;
+ }
+
+ if (mldl_cfg->pdata) {
+ retval = copy_to_user(local_mldl_cfg->pdata,
+ mldl_cfg->pdata,
+ sizeof(*mldl_cfg->pdata));
+ if (retval)
+ goto out;
+ }
+
+ /* Do not modify the accel, compass and pdata pointers */
+ retval = copy_to_user((struct mldl_cfg *)arg,
+ mldl_cfg, offsetof(struct mldl_cfg, accel));
+
+ out:
+ kfree(local_mldl_cfg);
+ return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_client *client = (struct i2c_client *)file->private_data;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int retval = 0;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ switch (cmd) {
+ case I2C_RDWR:
+ mpudev_ioctl_rdrw(client, arg);
+ break;
+ case I2C_SLAVE:
+ if ((arg & 0x7E) != (client->addr & 0x7E)) {
+ printk("%s: Invalid I2C_SLAVE arg %lu \n",
+ __func__, arg);
+ }
+ break;
+ case MPU_SET_MPU_CONFIG:
+ retval = mpu_ioctl_set_mpu_config(client, arg);
+ break;
+ case MPU_SET_INT_CONFIG:
+ mldl_cfg->int_config = (unsigned char)arg;
+ break;
+ case MPU_SET_EXT_SYNC:
+ mldl_cfg->ext_sync = (enum mpu_ext_sync)arg;
+ break;
+ case MPU_SET_FULL_SCALE:
+ mldl_cfg->full_scale = (enum mpu_fullscale)arg;
+ break;
+ case MPU_SET_LPF:
+ mldl_cfg->lpf = (enum mpu_filter)arg;
+ break;
+ case MPU_SET_CLK_SRC:
+ mldl_cfg->clk_src = (enum mpu_clock_sel)arg;
+ break;
+ case MPU_SET_DIVIDER:
+ mldl_cfg->divider = (unsigned char)arg;
+ break;
+ case MPU_SET_LEVEL_SHIFTER:
+ mldl_cfg->pdata->level_shifter = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_ENABLE:
+ mldl_cfg->dmp_enable = (unsigned char)arg;
+ break;
+ case MPU_SET_FIFO_ENABLE:
+ mldl_cfg->fifo_enable = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_CFG1:
+ mldl_cfg->dmp_cfg1 = (unsigned char)arg;
+ break;
+ case MPU_SET_DMP_CFG2:
+ mldl_cfg->dmp_cfg2 = (unsigned char)arg;
+ break;
+ case MPU_SET_OFFSET_TC:
+ retval = copy_from_user(mldl_cfg->offset_tc,
+ (unsigned char *)arg,
+ sizeof(mldl_cfg->offset_tc));
+ break;
+ case MPU_SET_RAM:
+ retval = copy_from_user(mldl_cfg->ram,
+ (unsigned char *)arg,
+ sizeof(mldl_cfg->ram));
+ break;
+ case MPU_SET_PLATFORM_DATA:
+ retval = mpu_ioctl_get_mpu_pdata(client, arg);
+ break;
+ case MPU_GET_MPU_CONFIG:
+ retval = mpu_ioctl_get_mpu_config(client, arg);
+ break;
+ case MPU_GET_INT_CONFIG:
+ mldl_cfg->int_config = (unsigned char)arg;
+ break;
+ case MPU_GET_EXT_SYNC:
+ mldl_cfg->ext_sync = (enum mpu_ext_sync)arg;
+ break;
+ case MPU_GET_FULL_SCALE:
+ mldl_cfg->full_scale = (enum mpu_fullscale)arg;
+ break;
+ case MPU_GET_LPF:
+ mldl_cfg->lpf = (enum mpu_filter)arg;
+ break;
+ case MPU_GET_CLK_SRC:
+ mldl_cfg->clk_src = (enum mpu_clock_sel)arg;
+ break;
+ case MPU_GET_DIVIDER:
+ mldl_cfg->divider = (unsigned char)arg;
+ break;
+ case MPU_GET_LEVEL_SHIFTER:
+ mldl_cfg->pdata->level_shifter = (unsigned char)arg;
+ break;
+ case MPU_GET_DMP_ENABLE:
+ mldl_cfg->dmp_enable = (unsigned char)arg;
+ break;
+ case MPU_GET_FIFO_ENABLE:
+ mldl_cfg->fifo_enable = (unsigned char)arg;
+ break;
+ case MPU_GET_DMP_CFG1:
+ mldl_cfg->dmp_cfg1 = (unsigned char)arg;
+ break;
+ case MPU_GET_DMP_CFG2:
+ mldl_cfg->dmp_cfg2 = (unsigned char)arg;
+ break;
+ case MPU_GET_OFFSET_TC:
+ retval = copy_to_user((unsigned char *)arg,
+ mldl_cfg->offset_tc,
+ sizeof(mldl_cfg->offset_tc));
+ break;
+ case MPU_GET_RAM:
+ retval = copy_to_user((unsigned char *)arg,
+ mldl_cfg->ram, sizeof(mldl_cfg->ram));
+ break;
+ case MPU_READ_MEMORY:
+ case MPU_WRITE_MEMORY:
+ case MPU_SUSPEND:
+ {
+ struct mpu_suspend_resume suspend;
+ retval =
+ copy_from_user(&suspend,
+ (struct mpu_suspend_resume *)
+ arg, sizeof(suspend));
+ if (retval)
+ break;
+ if (suspend.gyro) {
+ retval =
+ mpu3050_suspend(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ suspend.accel,
+ suspend.compass);
+ } else {
+ /* Cannot suspend the compass or accel while
+ * the MPU is running */
+ retval = ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+ }
+ break;
+ case MPU_RESUME:
+ {
+ struct mpu_suspend_resume resume;
+ retval =
+ copy_from_user(&resume,
+ (struct mpu_suspend_resume *)
+ arg, sizeof(resume));
+ if (retval)
+ break;
+ if (resume.gyro) {
+ retval =
+ mpu3050_resume(mldl_cfg,
+ client->adapter,
+ accel_adapter,
+ compass_adapter,
+ resume.accel,
+ resume.compass);
+ } else if (mldl_cfg->is_suspended) {
+ if (resume.accel) {
+ retval =
+ mldl_cfg->accel->
+ resume(accel_adapter,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ if (retval)
+ break;
+ }
+
+ if (resume.compass)
+ retval =
+ mldl_cfg->compass->
+ resume(compass_adapter,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ } else {
+ /* Cannot resume the compass or accel while
+ * the MPU is running */
+ retval = ML_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+ }
+ break;
+ case MPU_READ_ACCEL:
+ {
+ unsigned char data[6];
+ retval =
+ mpu3050_read_accel(mldl_cfg, client->adapter, data);
+ if (ML_SUCCESS == retval)
+ retval =
+ copy_to_user((unsigned char *)arg,
+ data, sizeof(data));
+ }
+ break;
+ case MPU_READ_COMPASS:
+ {
+ unsigned char data[6];
+ struct i2c_adapter *compass_adapt =
+ i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ retval =
+ mpu3050_read_compass(mldl_cfg, compass_adapt, data);
+ if (ML_SUCCESS == retval)
+ retval =
+ copy_to_user((unsigned char *)arg,
+ data, sizeof(data));
+ }
+ break;
+ default:
+ printk("%s: Unknown cmd %d, arg %lu \n", __func__, cmd, arg);
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void mpu3050_early_suspend(struct early_suspend *h)
+{
+ struct mpu_private_data *mpu = container_of(h,
+ struct
+ mpu_private_data,
+ early_suspend);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ printk("%s: %d, %d\n", __func__, h->level, mpu->mldl_cfg.is_suspended);
+ if (MPU3050_EARLY_SUSPEND_IN_DRIVER)
+ (void)mpu3050_suspend(mldl_cfg,
+ accel_adapter, compass_adapter,
+ this_client->adapter, TRUE, TRUE);
+}
+
+void mpu3050_early_resume(struct early_suspend *h)
+{
+ struct mpu_private_data *mpu = container_of(h,
+ struct
+ mpu_private_data,
+ early_suspend);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ if (MPU3050_EARLY_SUSPEND_IN_DRIVER) {
+ if (pid) {
+ (void)mpu3050_resume(mldl_cfg,
+ accel_adapter, compass_adapter,
+ this_client->adapter, TRUE, TRUE);
+ printk("%s for pid %d\n", __func__, pid);
+ }
+ }
+ printk("%s: %d\n", __func__, h->level);
+}
+#endif
+
+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 *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ (void)mpu3050_suspend(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter, TRUE, TRUE);
+ printk("%s\n", __func__);
+}
+
+int mpu_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 *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ if (!mpu->mldl_cfg.is_suspended) {
+ printk("%s: suspending on event %d\n", __func__, mesg.event);
+ (void)mpu3050_suspend(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter,
+ TRUE, TRUE);
+ } else {
+ printk("%s: Already suspended %d\n", __func__, mesg.event);
+ }
+ return 0;
+}
+
+int mpu_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 *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+
+ if (pid) {
+ (void)mpu3050_resume(mldl_cfg, this_client->adapter,
+ accel_adapter, compass_adapter,
+ TRUE, TRUE);
+ printk("%s for pid %d\n", __func__, pid);
+ }
+ return 0;
+}
+
+/* define which file operations are supported */
+struct file_operations mpu_fops = {
+ .owner = THIS_MODULE,
+ .read = mpu_read,
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = mpu_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = mpu_ioctl,
+#endif
+ .open = mpu_open,
+ .release = mpu_release,
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
+I2C_CLIENT_INSMOD;
+#endif
+
+static struct miscdevice i2c_mpu_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MPU_NAME,
+ .fops = &mpu_fops,
+};
+
+int mpu3050_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+ struct mpu3050_platform_data *pdata;
+ struct mpu_private_data *mpu;
+ int res = 0;
+ printk("%s\n", __func__);
+
+ 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;
+ }
+
+ i2c_set_clientdata(client, mpu);
+ this_client = client;
+
+ pdata = (struct mpu3050_platform_data *)client->dev.platform_data;
+ if (!pdata) {
+ printk("Warning no platform data for mpu3050\n");
+ } else {
+ mpu->mldl_cfg.pdata = pdata;
+
+#ifdef CONFIG_SENSORS_MPU3050_MODULE
+ pdata->accel.get_slave_descr = get_accel_slave_descr;
+ pdata->compass.get_slave_descr = get_compass_slave_descr;
+#endif
+
+ if (pdata->accel.get_slave_descr) {
+ mpu->mldl_cfg.accel = pdata->accel.get_slave_descr();
+ printk("MPU3050: +%s\n", mpu->mldl_cfg.accel->name);
+ } else {
+ printk("MPU3050: No Accel Present\n");
+ }
+
+ if (pdata->compass.get_slave_descr) {
+ mpu->mldl_cfg.compass =
+ pdata->compass.get_slave_descr();
+ printk("MPU3050: +%s\n", mpu->mldl_cfg.compass->name);
+ } else {
+ printk("MPU3050: No Compass Present\n");
+ }
+ }
+
+ mpu->mldl_cfg.addr = client->addr;
+ res = mpu3050_open(&mpu->mldl_cfg, (tMLSLHandle) client->adapter);
+
+ if (res) {
+ printk("Unable to open MPU3050 %d\n", res);
+ res = -ENODEV;
+ goto out_whoami_failed;
+ }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ mpu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ mpu->early_suspend.suspend = mpu3050_early_suspend;
+ mpu->early_suspend.resume = mpu3050_early_resume;
+ register_early_suspend(&mpu->early_suspend);
+#endif
+
+ res = misc_register(&i2c_mpu_device);
+ if (res < 0) {
+ printk("ERROR: misc_register returned %d\n", res);
+ goto out_misc_register_failed;
+ }
+
+ if (this_client->irq > 0) {
+ printk("Installing irq using %d\n", this_client->irq);
+ res = mpuirq_init(this_client);
+ if (res) {
+ goto out_mpuirq_failed;
+ }
+ } else {
+ printk("WARNING: mpu3050 irq not assigned\n");
+ }
+
+ return res;
+
+ out_mpuirq_failed:
+ misc_deregister(&i2c_mpu_device);
+ out_misc_register_failed:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mpu->early_suspend);
+#endif
+ out_whoami_failed:
+ kfree(mpu);
+ out_alloc_data_failed:
+ out_check_functionality_failed:
+ printk(KERN_ERR "%s failed %d\n", __func__, res);
+ return res;
+
+}
+
+static int mpu3050_remove(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ printk("%s\n", __func__);
+
+ if (client->irq) {
+ mpuirq_exit();
+ }
+
+ misc_deregister(&i2c_mpu_device);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mpu->early_suspend);
+#endif
+ kfree(mpu);
+
+ return 0;
+}
+
+static const struct i2c_device_id mpu3050_id[] = {
+ {"mpu3050", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mpu3050_id);
+
+static struct i2c_driver mpu3050_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mpu3050_probe,
+ .remove = mpu3050_remove,
+ .id_table = mpu3050_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mpu3050",
+ },
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
+ .address_data = &addr_data,
+#else
+ .address_list = normal_i2c,
+#endif
+
+ .shutdown = mpu_shutdown, /* optional */
+ .suspend = mpu_suspend, /* optional */
+ .resume = mpu_resume, /* optional */
+
+};
+
+static int __init mpu_init(void)
+{
+ int res = i2c_add_driver(&mpu3050_driver);
+ printk("%s\n", __func__);
+ if (res) {
+ printk("%s failed\n", __func__);
+ }
+ return res;
+}
+
+static void __exit mpu_exit(void)
+{
+ printk("%s\n", __func__);
+ i2c_del_driver(&mpu3050_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU3050");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mpu3050");
diff --git a/drivers/misc/mpu3050/mpu-i2c.c b/drivers/misc/mpu3050/mpu-i2c.c
new file mode 100755
index 000000000000..ca21b42a9e95
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-i2c.c
@@ -0,0 +1,195 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/***************************************************************************** *
+ * $Id: mpu-i2c.c 3863 2010-10-08 22:05:31Z nroyer $
+ ******************************************************************************/
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ * @file mpu-i2c.c
+ * @brief
+ *
+ *
+ */
+
+#include <linux/i2c.h>
+
+int sensor_i2c_write(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned int len, unsigned char *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (NULL == data || NULL == i2c_adap)
+ 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) {
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+int sensor_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 sensor_i2c_write(i2c_adap, address, 2, data);
+}
+
+int sensor_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 (NULL == data || NULL == i2c_adap)
+ 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) {
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+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 ret;
+
+ if (NULL == data || NULL == i2c_adap)
+ return -EINVAL;
+
+ bank[0] = 0x37; /*MPUREG_BANK_SEL; */
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = 0x38; /*MPUREG_MEM_START_ADDR; */
+ addr[1] = mem_addr & 0xFF;
+
+ buf = 0x39; /* 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;
+
+ ret = i2c_transfer(i2c_adap, msgs, 4);
+ if (ret != 4)
+ return ret;
+ else
+ return 0;
+}
+
+int mpu_memory_write(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[513];
+
+ struct i2c_msg msgs[3];
+ int ret;
+
+ if (NULL == data || NULL == i2c_adap)
+ return -EINVAL;
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = 0x37; /*MPUREG_BANK_SEL; */
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = 0x38; /*MPUREG_MEM_START_ADDR; */
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = 0x39; /* 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;
+
+ ret = i2c_transfer(i2c_adap, msgs, 3);
+ if (ret != 3)
+ return ret;
+ else
+ return 0;
+}
diff --git a/drivers/misc/mpu3050/mpu-i2c.h b/drivers/misc/mpu3050/mpu-i2c.h
new file mode 100755
index 000000000000..ac3f7e93b695
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-i2c.h
@@ -0,0 +1,60 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+/***************************************************************************** *
+ * $Id: mpu-i2c.h 3863 2010-10-08 22:05:31Z nroyer $
+ ******************************************************************************/
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ * @file mpu-i2c.c
+ * @brief
+ *
+ *
+ */
+
+#ifndef __MPU_I2C_H__
+#define __MPU_I2C_H__
+
+#include <linux/i2c.h>
+
+int sensor_i2c_write(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned int len, unsigned char const *data);
+
+int sensor_i2c_write_register(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned char reg, unsigned char value);
+
+int sensor_i2c_read(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned char reg, unsigned int len, unsigned char *data);
+
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char *data);
+
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char const *data);
+
+#endif /* __MPU_I2C_H__ */
diff --git a/drivers/misc/mpu3050/mpuirq.c b/drivers/misc/mpu3050/mpuirq.c
new file mode 100755
index 000000000000..bf3c10f176fe
--- /dev/null
+++ b/drivers/misc/mpu3050/mpuirq.c
@@ -0,0 +1,315 @@
+/*
+ $License:
+ Copyright (C) 2010 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/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/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/workqueue.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 <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "mpu3050.h"
+#include "mpuirq.h"
+#include "mldl_cfg.h"
+#include "mpu-i2c.h"
+
+#define MPUIRQ_NAME "mpuirq"
+
+/* function which gets accel data and sends it to MPU */
+
+DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait);
+
+struct mpuirq_dev_data {
+ struct work_struct work;
+ struct i2c_client *mpu_client;
+ struct miscdevice *dev;
+ int irq;
+ int pid;
+ int accel_divider;
+ int data_ready;
+ int timeout;
+};
+
+static struct mpuirq_dev_data mpuirq_dev_data;
+static struct mpuirq_data mpuirq_data;
+static char *interface = MPUIRQ_NAME;
+
+static void mpu_accel_data_work_fcn(struct work_struct *work);
+
+static int mpuirq_open(struct inode *inode, struct file *file)
+{
+ dev_dbg(mpuirq_dev_data.dev->this_device,
+ "%s current->pid %d\n", __func__, current->pid);
+ mpuirq_dev_data.pid = current->pid;
+ file->private_data = &mpuirq_dev_data;
+ /* we could do some checking on the flags supplied by "open"
+ // i.e. O_NONBLOCK
+ // -> set some flag to disable interruptible_sleep_on in mpuirq_read */
+ return 0;
+}
+
+/* close function - called when the "file" /dev/mpuirq is closed in userspace */
+static int mpuirq_release(struct inode *inode, struct file *file)
+{
+ dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n");
+ return 0;
+}
+
+/* read function called when from /dev/mpuirq is read */
+static ssize_t mpuirq_read(struct file *file,
+ char *buf, size_t count, loff_t * ppos)
+{
+ int len, err;
+ struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
+
+ if (!mpuirq_dev_data.data_ready) {
+ wait_event_interruptible_timeout(mpuirq_wait,
+ mpuirq_dev_data.data_ready,
+ mpuirq_dev_data.timeout);
+ }
+
+ if (mpuirq_dev_data.data_ready && NULL != buf
+ && count >= sizeof(mpuirq_data)) {
+ err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data));
+ } else {
+ return 0;
+ }
+ if (err != 0) {
+ dev_err(p_mpuirq_dev_data->dev->this_device,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ mpuirq_dev_data.data_ready = 0;
+ len = sizeof(mpuirq_data);
+ return len;
+}
+
+unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll)
+{
+ int mask = 0;
+ struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
+
+ poll_wait(file, &mpuirq_wait, poll);
+ if (mpuirq_dev_data.data_ready)
+ mask |= POLLIN | POLLRDNORM;
+ dev_dbg(p_mpuirq_dev_data->dev->this_device,
+ "%s: returning %d\n", __func__, mask);
+ return mask;
+}
+
+/* ioctl - I/O control */
+static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int data;
+
+ switch (cmd) {
+ case MPUIRQ_SET_TIMEOUT:
+ mpuirq_dev_data.timeout = arg;
+ break;
+
+ case MPUIRQ_GET_INTERRUPT_CNT:
+ data = mpuirq_data.interruptcount - 1;
+ if (mpuirq_data.interruptcount > 1) {
+ mpuirq_data.interruptcount = 1;
+ }
+ if (copy_to_user((int *)arg, &data, sizeof(int)))
+ return -EFAULT;
+ break;
+ case MPUIRQ_GET_IRQ_TIME:
+ if (copy_to_user((int *)arg, &mpuirq_data.irqtime,
+ sizeof(mpuirq_data.irqtime)))
+ return -EFAULT;
+ mpuirq_data.irqtime = 0;
+ break;
+ case MPUIRQ_SET_FREQUENCY_DIVIDER:
+ mpuirq_dev_data.accel_divider = arg;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+static void mpu_accel_data_work_fcn(struct work_struct *work)
+{
+ struct mpuirq_dev_data *mpuirq_dev_data =
+ (struct mpuirq_dev_data *)work;
+ struct mldl_cfg *mldl_cfg =
+ (struct mldl_cfg *)i2c_get_clientdata(mpuirq_dev_data->mpu_client);
+ struct i2c_adapter *accel_adapter;
+ unsigned char wbuff[16];
+ unsigned char rbuff[16];
+ int ii;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ mldl_cfg->accel->read((mlsl_handle_t) accel_adapter,
+ mldl_cfg->accel, &mldl_cfg->pdata->accel, rbuff);
+
+ /* @todo add other data formats here as well */
+ if (EXT_SLAVE_BIG_ENDIAN == mldl_cfg->accel->endian) {
+ for (ii = 0; ii < 3; ii++) {
+ wbuff[2 * ii + 1] = rbuff[2 * ii + 1];
+ wbuff[2 * ii + 2] = rbuff[2 * ii + 0];
+ }
+ } else {
+ memcpy(wbuff + 1, rbuff, mldl_cfg->accel->len);
+ }
+
+ wbuff[7] = 0;
+ wbuff[8] = 1; /*set semaphore */
+
+ mpu_memory_write(mpuirq_dev_data->mpu_client->adapter,
+ mldl_cfg->addr, 0x0108, 8, wbuff);
+}
+
+static irqreturn_t mpuirq_handler(int irq, void *dev_id)
+{
+ static int mycount;
+ struct timeval irqtime;
+ mycount++;
+
+ mpuirq_data.interruptcount++;
+
+ /* wake up (unblock) for reading data from userspace
+ // and ignore first interrupt generated in module init */
+ if (mpuirq_data.interruptcount > 1) {
+ mpuirq_dev_data.data_ready = 1;
+
+ do_gettimeofday(&irqtime);
+ mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32);
+ mpuirq_data.irqtime += irqtime.tv_usec;
+
+ if ((mpuirq_dev_data.accel_divider >= 0) &&
+ (0 == (mycount % (mpuirq_dev_data.accel_divider + 1)))) {
+ schedule_work((struct work_struct *)(&mpuirq_dev_data));
+ }
+
+ wake_up_interruptible(&mpuirq_wait);
+ }
+
+ return IRQ_HANDLED;
+
+}
+
+/* define which file operations are supported */
+struct file_operations mpuirq_fops = {
+ .owner = THIS_MODULE,
+ .read = mpuirq_read,
+ .poll = mpuirq_poll,
+
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = mpuirq_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = mpuirq_ioctl,
+#endif
+ .open = mpuirq_open,
+ .release = mpuirq_release,
+};
+
+static struct miscdevice mpuirq_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MPUIRQ_NAME,
+ .fops = &mpuirq_fops,
+};
+
+int mpuirq_init(struct i2c_client *mpu_client)
+{
+
+ int res;
+ struct mldl_cfg *mldl_cfg =
+ (struct mldl_cfg *)i2c_get_clientdata(mpu_client);
+
+ /* work_struct initialization */
+ INIT_WORK((struct work_struct *)&mpuirq_dev_data,
+ mpu_accel_data_work_fcn);
+ mpuirq_dev_data.mpu_client = mpu_client;
+
+ dev_info(&mpu_client->adapter->dev,
+ "Module Param interface = %s\n", interface);
+
+ mpuirq_dev_data.irq = mpu_client->irq;
+ mpuirq_dev_data.pid = 0;
+ mpuirq_dev_data.accel_divider = -1;
+ mpuirq_dev_data.data_ready = 0;
+ mpuirq_dev_data.timeout = 0;
+ 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;
+
+ res =
+ request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags,
+ interface, &mpuirq_dev_data.irq);
+ if (res) {
+ dev_err(&mpu_client->adapter->dev,
+ "myirqtest: cannot register IRQ %d\n",
+ mpuirq_dev_data.irq);
+ } else {
+ res = misc_register(&mpuirq_device);
+ if (res < 0) {
+ dev_err(&mpu_client->adapter->dev,
+ "misc_register returned %d\n", res);
+ free_irq(mpuirq_dev_data.irq,
+ &mpuirq_dev_data.irq);
+ }
+ }
+
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+void mpuirq_exit(void)
+{
+ /* Free the IRQ first before flushing the work */
+ if (mpuirq_dev_data.irq > 0) {
+ free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq);
+ }
+ flush_scheduled_work();
+
+ dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME);
+ misc_deregister(&mpuirq_device);
+
+ return;
+}
+
+module_param(interface, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(interface, "The Interface name");
diff --git a/drivers/misc/mpu3050/mpuirq.h b/drivers/misc/mpu3050/mpuirq.h
new file mode 100755
index 000000000000..69b7eaad5717
--- /dev/null
+++ b/drivers/misc/mpu3050/mpuirq.h
@@ -0,0 +1,50 @@
+/*
+ $License:
+ Copyright (C) 2010 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/>.
+ $
+ */
+
+#ifndef __MPUIRQ__
+#define __MPUIRQ__
+
+#ifdef __KERNEL__
+#include <linux/i2c-dev.h>
+#endif
+
+#define MPUIRQ_ENABLE_DEBUG (1)
+#define MPUIRQ_GET_INTERRUPT_CNT (2)
+#define MPUIRQ_GET_IRQ_TIME (3)
+#define MPUIRQ_GET_LED_VALUE (4)
+#define MPUIRQ_SET_TIMEOUT (5)
+#define MPUIRQ_SET_ACCEL_INFO (6)
+#define MPUIRQ_SET_FREQUENCY_DIVIDER (7)
+
+struct mpuirq_data {
+ int interruptcount;
+ unsigned long long irqtime;
+ int data_type;
+ int data_size;
+ void *data;
+};
+
+#ifdef __KERNEL__
+
+void mpuirq_exit(void);
+int mpuirq_init(struct i2c_client *mpu_client);
+
+#endif
+
+#endif