input: misc: Invensense MPU 5.0.1 driver
Robert Collins [Tue, 11 Sep 2012 23:59:16 +0000 (16:59 -0700)]
Change-Id: I6391dc455d615e63d6b15f0f537805c88c259a15
Signed-off-by: Xiaohui Tao <xtao@nvidia.com>
Signed-off-by: Robert Collins <rcollins@nvidia.com>
Reviewed-on: http://git-master/r/132403
(cherry picked from commit de2d18bbe47fca8d56c3e0717b9463a05a110c88)
Reviewed-on: http://git-master/r/138745
Reviewed-by: Automatic_Commit_Validation_User
Tested-by: Gaurav Batra <gbatra@nvidia.com>
Reviewed-by: Thomas Cherry <tcherry@nvidia.com>

19 files changed:
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/compass/Kconfig [new file with mode: 0644]
drivers/input/misc/compass/Makefile [new file with mode: 0644]
drivers/input/misc/compass/ak8975_input.c [new file with mode: 0644]
drivers/input/misc/mpu/Kconfig [new file with mode: 0644]
drivers/input/misc/mpu/Makefile [new file with mode: 0644]
drivers/input/misc/mpu/dmpDefaultMPU6050.c [new file with mode: 0644]
drivers/input/misc/mpu/dmpKey.h [new file with mode: 0644]
drivers/input/misc/mpu/dmpmap.h [new file with mode: 0644]
drivers/input/misc/mpu/inv_gyro.c [new file with mode: 0644]
drivers/input/misc/mpu/inv_gyro.h [new file with mode: 0644]
drivers/input/misc/mpu/inv_gyro_misc.c [new file with mode: 0644]
drivers/input/misc/mpu/inv_mpu3050.c [new file with mode: 0644]
drivers/input/misc/mpu/inv_slave_bma250.c [new file with mode: 0644]
drivers/input/misc/mpu/inv_slave_kxtf9.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/Makefile
include/linux/mpu.h

index 873a2c0..2fc637b 100644 (file)
@@ -628,4 +628,7 @@ config INPUT_CAPELLA_CM3217
        help
          Say Y here to enable the CM3217 Ambient Light Sensor.
 
+source "drivers/input/misc/compass/Kconfig"
+source "drivers/input/misc/mpu/Kconfig"
+
 endif
index a97637b..e3ed2fe 100644 (file)
@@ -59,3 +59,5 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS)      += wistron_btns.o
 obj-$(CONFIG_INPUT_WM831X_ON)          += wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)        += xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)            += yealink.o
+obj-y                                  += compass/
+obj-y                                  += mpu/
diff --git a/drivers/input/misc/compass/Kconfig b/drivers/input/misc/compass/Kconfig
new file mode 100644 (file)
index 0000000..bd844fc
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Kconfig for stand-alone compass input device drivers
+#
+
+config INV_AK8975
+    tristate "Invensense AKM AK8975 compass input device driver"
+    depends on I2C && SYSFS && INPUT && INPUT_EVDEV
+    default n
+    help
+      This driver supports the AKM AK8975 compass device.
+      This driver can be built as a module. The module will be called
+      inv-ak8975.
diff --git a/drivers/input/misc/compass/Makefile b/drivers/input/misc/compass/Makefile
new file mode 100644 (file)
index 0000000..0274a12
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for stand-alone compass input device drivers
+#
+
+obj-$(CONFIG_INV_AK8975) += inv-ak8975.o
+inv-ak8975-objs += ak8975_input.o
+
diff --git a/drivers/input/misc/compass/ak8975_input.c b/drivers/input/misc/compass/ak8975_input.c
new file mode 100644 (file)
index 0000000..29754bc
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief        Hardware drivers.
+ *
+ *  @{
+ *       @file ak8975_input.c
+ *       @brief   A sysfs device driver for Invensense devices
+ *       @details This driver currently works for the AK8975
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+
+#include <linux/mpu.h>
+
+#define AK8975_DEBUG_IF        0
+#define AK8975_DEBUG_DATA      0
+
+#define AK8975_I2C_NAME "ak8975"
+
+#define SENSOR_DATA_SIZE       8
+#define YPR_DATA_SIZE          12
+#define RWBUF_SIZE             16
+
+#define ACC_DATA_FLAG          0
+#define MAG_DATA_FLAG          1
+#define ORI_DATA_FLAG          2
+#define AKM_NUM_SENSORS                3
+
+#define ACC_DATA_READY         (1 << (ACC_DATA_FLAG))
+#define MAG_DATA_READY         (1 << (MAG_DATA_FLAG))
+#define ORI_DATA_READY         (1 << (ORI_DATA_FLAG))
+
+/*! \name AK8975 constant definition
+ \anchor AK8975_Def
+ Constant definitions of the AK8975.*/
+#define AK8975_MEASUREMENT_TIME_US     10000
+
+/*! \name AK8975 operation mode
+ \anchor AK8975_Mode
+ Defines an operation mode of the AK8975.*/
+/*! @{*/
+#define AK8975_CNTL_MODE_SNG_MEASURE   0x01
+#define        AK8975_CNTL_MODE_SELF_TEST      0x08
+#define        AK8975_CNTL_MODE_FUSE_ACCESS    0x0F
+#define        AK8975_CNTL_MODE_POWER_DOWN     0x00
+/*! @}*/
+
+/*! \name AK8975 register address
+\anchor AK8975_REG
+Defines a register address of the AK8975.*/
+/*! @{*/
+#define AK8975_REG_WIA         0x00
+#define AK8975_REG_INFO                0x01
+#define AK8975_REG_ST1         0x02
+#define AK8975_REG_HXL         0x03
+#define AK8975_REG_HXH         0x04
+#define AK8975_REG_HYL         0x05
+#define AK8975_REG_HYH         0x06
+#define AK8975_REG_HZL         0x07
+#define AK8975_REG_HZH         0x08
+#define AK8975_REG_ST2         0x09
+#define AK8975_REG_CNTL                0x0A
+#define AK8975_REG_RSV         0x0B
+#define AK8975_REG_ASTC                0x0C
+#define AK8975_REG_TS1         0x0D
+#define AK8975_REG_TS2         0x0E
+#define AK8975_REG_I2CDIS      0x0F
+/*! @}*/
+
+/*! \name AK8975 fuse-rom address
+\anchor AK8975_FUSE
+Defines a read-only address of the fuse ROM of the AK8975.*/
+/*! @{*/
+#define AK8975_FUSE_ASAX       0x10
+#define AK8975_FUSE_ASAY       0x11
+#define AK8975_FUSE_ASAZ       0x12
+/*! @}*/
+
+#define AK8975_MAX_DELAY       (100)
+#define AK8975_MIN_DELAY       (10)
+
+/**
+ *  struct inv_gyro_state_s - Driver state variables.
+ *  @dev:              Represents read-only node for accessing buffered data.
+ *  @idev:             Handle to input device.
+ *  @sl_handle:                Handle to I2C port.
+ */
+struct inv_compass_state {
+       struct i2c_client *i2c;
+       atomic_t enable;
+       atomic_t delay;
+       struct mpu_platform_data plat_data;
+       short i2c_addr;
+       void *sl_handle;
+       struct device *inv_dev;
+       struct input_dev *idev;
+       struct delayed_work work;
+
+       struct mutex value_mutex;
+       struct mutex enable_mutex;
+       short value[3];
+       char asa[3];    /* axis sensitivity adjustment */
+};
+
+/* -------------------------------------------------------------------------- */
+/**
+ *  inv_serial_read() - Read one or more bytes from the device registers.
+ *  @st:       Device driver instance.
+ *  @reg:      First device register to be read from.
+ *  @length:   Number of bytes to read.
+ *  @data:     Data read from device.
+ *  NOTE: The slave register will not increment when reading from the FIFO.
+ */
+static int inv_serial_read(struct inv_compass_state *st,
+       unsigned char reg, unsigned short length, unsigned char *data)
+{
+       struct i2c_msg msgs[2];
+       int res;
+
+       if (!data || !st->sl_handle)
+               return -EINVAL;
+
+       msgs[0].addr = st->i2c_addr;
+       msgs[0].flags = 0;      /* write */
+       msgs[0].buf = &reg;
+       msgs[0].len = 1;
+
+       msgs[1].addr = st->i2c_addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].buf = data;
+       msgs[1].len = length;
+
+       pr_debug("%s RD%02X%02X%02X\n",
+                st->idev->name, st->i2c_addr, reg, length);
+       res = i2c_transfer(st->sl_handle, msgs, 2);
+       if (res < 2) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       } else
+               return 0;
+}
+
+/**
+ *  inv_serial_single_write() - Write a byte to a device register.
+ *  @st:       Device driver instance.
+ *  @reg:      Device register to be written to.
+ *  @data:     Byte to write to device.
+ */
+static int inv_serial_single_write(struct inv_compass_state *st,
+       unsigned char reg, unsigned char data)
+{
+       unsigned char tmp[2];
+       struct i2c_msg msg;
+       int res;
+
+       if (!st->sl_handle)
+               return -EINVAL;
+
+       tmp[0] = reg;
+       tmp[1] = data;
+
+       msg.addr = st->i2c_addr;
+       msg.flags = 0;  /* write */
+       msg.buf = tmp;
+       msg.len = 2;
+
+       pr_debug("%s WS%02X%02X%02X\n",
+                st->idev->name, st->i2c_addr, reg, data);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res == 0)
+                       res = -EIO;
+               return res;
+       } else
+               return 0;
+}
+
+static int ak8975_init(struct inv_compass_state *st)
+{
+       int result = 0;
+       unsigned char serial_data[3];
+
+       result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                        AK8975_CNTL_MODE_POWER_DOWN);
+       if (result) {
+               pr_err("%s, line=%d\n", __func__, __LINE__);
+               return result;
+       }
+       /* Wait at least 100us */
+       udelay(100);
+
+       result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                        AK8975_CNTL_MODE_FUSE_ACCESS);
+       if (result) {
+               pr_err("%s, line=%d\n", __func__, __LINE__);
+               return result;
+       }
+
+       /* Wait at least 200us */
+       udelay(200);
+
+       result = inv_serial_read(st, AK8975_FUSE_ASAX, 3, serial_data);
+       if (result) {
+               pr_err("%s, line=%d\n", __func__, __LINE__);
+               return result;
+       }
+
+       st->asa[0] = serial_data[0];
+       st->asa[1] = serial_data[1];
+       st->asa[2] = serial_data[2];
+
+       result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                        AK8975_CNTL_MODE_POWER_DOWN);
+       if (result) {
+               pr_err("%s, line=%d\n", __func__, __LINE__);
+               return result;
+       }
+       udelay(100);
+
+       return result;
+}
+
+static int ak8975_read(struct inv_compass_state *st, short rawfixed[3])
+{
+       unsigned char regs[8];
+       unsigned char *stat = &regs[0];
+       unsigned char *stat2 = &regs[7];
+       int result = 0;
+       int status = 0;
+
+       result = inv_serial_read(st, AK8975_REG_ST1, 8, regs);
+       if (result) {
+               pr_err("%s, line=%d\n", __func__, __LINE__);
+       return result;
+       }
+
+       rawfixed[0] = (short)((regs[2]<<8) | regs[1]);
+       rawfixed[1] = (short)((regs[4]<<8) | regs[3]);
+       rawfixed[2] = (short)((regs[6]<<8) | regs[5]);
+
+       /*
+        * ST : data ready -
+        * Measurement has been completed and data is ready to be read.
+        */
+       if (*stat & 0x01)
+               status = 0;
+
+       /*
+        * ST2 : data error -
+        * occurs when data read is started outside of a readable period;
+        * data read would not be correct.
+        * Valid in continuous measurement mode only.
+        * In single measurement mode this error should not occour but we
+        * stil account for it and return an error, since the data would be
+        * corrupted.
+        * DERR bit is self-clearing when ST2 register is read.
+        */
+       if (*stat2 & 0x04)
+               status = 0x04;
+       /*
+        * ST2 : overflow -
+        * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
+        * This is likely to happen in presence of an external magnetic
+        * disturbance; it indicates, the sensor data is incorrect and should
+        * be ignored.
+        * An error is returned.
+        * HOFL bit clears when a new measurement starts.
+        */
+       if (*stat2 & 0x08)
+               status = 0x08;
+       /*
+        * ST : overrun -
+        * the previous sample was not fetched and lost.
+        * Valid in continuous measurement mode only.
+        * In single measurement mode this error should not occour and we
+        * don't consider this condition an error.
+        * DOR bit is self-clearing when ST2 or any meas. data register is
+        * read.
+        */
+       if (*stat & 0x02) {
+               /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
+               status = 0;
+       }
+
+       /*
+        * trigger next measurement if:
+        *      - stat is non zero;
+        *      - if stat is zero and stat2 is non zero.
+        * Won't trigger if data is not ready and there was no error.
+        */
+       if (*stat != 0x00 || *stat2 != 0x00) {
+               result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                        AK8975_CNTL_MODE_SNG_MEASURE);
+               if (result) {
+                       pr_err("%s, line=%d\n", __func__, __LINE__);
+                       return result;
+               }
+       }
+
+       if (status)
+               pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status);
+
+       return status;
+}
+
+static void ak8975_work_func(struct work_struct *work)
+{
+       struct inv_compass_state *st =
+               container_of((struct delayed_work *)work,
+                       struct inv_compass_state, work);
+       unsigned long delay = msecs_to_jiffies(atomic_read(&st->delay));
+       short c[3];
+       c[0] = c[1] = c[2] = 0;
+       if (0 == ak8975_read(st, c)) {
+               c[0] = ((c[0] * (st->asa[0] + 128)) >> 8);
+               c[1] = ((c[1] * (st->asa[1] + 128)) >> 8);
+               c[2] = ((c[2] * (st->asa[2] + 128)) >> 8);
+               input_report_rel(st->idev, REL_X, c[0]);
+               input_report_rel(st->idev, REL_Y, c[1]);
+               input_report_rel(st->idev, REL_Z, c[2]);
+               input_sync(st->idev);
+       }
+
+       mutex_lock(&st->value_mutex);
+       st->value[0] = c[0];
+       st->value[1] = c[1];
+       st->value[2] = c[2];
+       mutex_unlock(&st->value_mutex);
+       schedule_delayed_work(&st->work, delay);
+}
+
+static ssize_t ak8975_value_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       short c[3];
+
+       mutex_lock(&st->value_mutex);
+       c[0] = st->value[0];
+       c[1] = st->value[1];
+       c[2] = st->value[2];
+       mutex_unlock(&st->value_mutex);
+       return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]);
+}
+
+static ssize_t ak8975_scale_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%ld\n", 9830L * 32768L);
+}
+static ssize_t ak8975_reset_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       int result;
+       result = ak8975_init(st);
+       return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t ak8975_enable_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       return sprintf(buf, "%d\n", atomic_read(&st->enable));
+}
+
+static ssize_t ak8975_rate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       /* transform delay in ms to rate */
+       return sprintf(buf, "%d\n", 1000 / atomic_read(&st->delay));
+}
+
+static ssize_t akm8975_matrix_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       signed char *m;
+       m = st->plat_data.orientation;
+       return sprintf(buf,
+               "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+               m[0],  m[1],  m[2],  m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+static ssize_t ak8975_rate_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       unsigned long data;
+       int error;
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+
+       error = kstrtoul(buf, 10, &data);
+       if (error)
+               return error;
+       /* transform rate to delay in ms */
+       data = 1000 / data;
+       if (data > AK8975_MAX_DELAY)
+               data = AK8975_MAX_DELAY;
+       if (data < AK8975_MIN_DELAY)
+               data = AK8975_MIN_DELAY;
+       atomic_set(&st->delay, (unsigned int) data);
+       return count;
+}
+
+static void ak8975_set_enable(struct device *dev, int enable)
+{
+       struct inv_compass_state *st = dev_get_drvdata(dev);
+       int result = 0;
+       int pre_enable = atomic_read(&st->enable);
+
+       mutex_lock(&st->enable_mutex);
+       if (enable) {
+               if (pre_enable == 0) {
+                       result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                               AK8975_CNTL_MODE_SNG_MEASURE);
+                       if (result)
+                               pr_err("%s, line=%d\n", __func__, __LINE__);
+                       schedule_delayed_work(&st->work,
+                               msecs_to_jiffies(atomic_read(&st->delay)));
+                       atomic_set(&st->enable, 1);
+               }
+
+       } else {
+               if (pre_enable == 1) {
+                       cancel_delayed_work_sync(&st->work);
+                       atomic_set(&st->enable, 0);
+                       result = inv_serial_single_write(st, AK8975_REG_CNTL,
+                                               AK8975_CNTL_MODE_POWER_DOWN);
+                       if (result)
+                               pr_err("%s, line=%d\n", __func__, __LINE__);
+                       mdelay(1);      /* wait at least 100us */
+               }
+       }
+       mutex_unlock(&st->enable_mutex);
+}
+
+static ssize_t ak8975_enable_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       unsigned long data;
+       int error;
+
+       error = kstrtoul(buf, 10, &data);
+       if (error)
+               return error;
+       if (data == 0 || data == 1)
+               ak8975_set_enable(dev, data);
+
+       return count;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
+               ak8975_enable_show, ak8975_enable_store);
+static DEVICE_ATTR(value, S_IRUGO, ak8975_value_show, NULL);
+static DEVICE_ATTR(scale, S_IRUGO, ak8975_scale_show, NULL);
+static DEVICE_ATTR(reset, S_IRUGO, ak8975_reset_show, NULL);
+static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, ak8975_rate_show,
+               ak8975_rate_store);
+static DEVICE_ATTR(compass_matrix, S_IRUGO, akm8975_matrix_show, NULL);
+
+static struct attribute *ak8975_attributes[] = {
+       &dev_attr_enable.attr,
+       &dev_attr_value.attr,
+       &dev_attr_scale.attr,
+       &dev_attr_reset.attr,
+       &dev_attr_rate.attr,
+       &dev_attr_compass_matrix.attr,
+       NULL
+};
+
+static struct attribute_group ak8975_attribute_group = {
+       .name = "ak8975",
+       .attrs = ak8975_attributes
+};
+
+
+/**
+ *  inv_setup_input() - internal setup input device.
+ *  @st:       Device driver instance.
+ *  @**idev_in  pointer to input device
+ *  @*client   i2c client
+ *  @*name       name of the input device.
+ */
+static int inv_setup_input(struct inv_compass_state *st,
+       struct input_dev **idev_in, struct i2c_client *client,
+       unsigned char *name) {
+       int result;
+       struct input_dev *idev;
+       idev = input_allocate_device();
+       if (!idev) {
+               result = -ENOMEM;
+               return result;
+       }
+       /* Setup input device. */
+       idev->name = name;
+
+       idev->id.bustype = BUS_I2C;
+       idev->id.product = 'S';
+       idev->id.vendor  = ('I'<<8) | 'S';
+       idev->id.version        = 1;
+       idev->dev.parent = &client->dev;
+       /* Open and close method. */
+       idev->open = NULL;
+       idev->close = NULL;
+
+       __set_bit(EV_REL, idev->evbit);
+       input_set_capability(idev, EV_REL, REL_X);
+       input_set_capability(idev, EV_REL, REL_Y);
+       input_set_capability(idev, EV_REL, REL_Z);
+
+       input_set_capability(idev, EV_REL, REL_MISC);
+       input_set_capability(idev, EV_REL, REL_WHEEL);
+
+       input_set_drvdata(idev, st);
+       result = input_register_device(idev);
+       if (result)
+               input_free_device(idev);
+
+       *idev_in = idev;
+       return result;
+}
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int ak8975_mod_probe(struct i2c_client *client,
+                          const struct i2c_device_id *devid)
+{
+       struct mpu_platform_data *pdata;
+       struct inv_compass_state *st;
+       struct input_dev *idev;
+       int result = 0;
+
+       dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               result = -ENODEV;
+               goto out_no_free;
+       }
+
+       pdata = (struct mpu_platform_data *)dev_get_platdata(&client->dev);
+       if (!pdata) {
+               dev_err(&client->adapter->dev,
+                       "Missing platform data for slave %s\n", devid->name);
+               result = -EFAULT;
+               goto out_no_free;
+       }
+
+       st = kzalloc(sizeof(*st), GFP_KERNEL);
+       if (!st) {
+               result = -ENOMEM;
+               goto out_no_free;
+       }
+
+       i2c_set_clientdata(client, st);
+       st->i2c = client;
+       mutex_init(&st->value_mutex);
+       mutex_init(&st->enable_mutex);
+       atomic_set(&st->delay, 100);
+       st->sl_handle = client->adapter;
+       st->plat_data = *pdata;
+       st->i2c_addr = client->addr;
+       INIT_DELAYED_WORK(&st->work, ak8975_work_func);
+       result = inv_setup_input(st, &idev, client, "INV_AK8975");
+       if (result)
+               goto out_free_memory;
+       st->idev = idev;
+       result = sysfs_create_group(&st->idev->dev.kobj,
+                                                &ak8975_attribute_group);
+       if (result < 0)
+               goto error_sysfs;
+
+       result = ak8975_init(st);
+       if (result < 0)
+               goto error_sysfs;
+
+       return result;
+error_sysfs:
+       input_unregister_device(st->idev);
+out_free_memory:
+       kfree(st);
+out_no_free:
+       dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+       return result;
+
+}
+
+static int ak8975_mod_remove(struct i2c_client *client)
+{
+       struct inv_compass_state *st =
+               i2c_get_clientdata(client);
+
+       dev_dbg(&client->adapter->dev, "%s\n", __func__);
+       ak8975_set_enable(&st->idev->dev, 0);
+       sysfs_remove_group(&st->idev->dev.kobj, &ak8975_attribute_group);
+       input_unregister_device(st->idev);
+       kfree(st);
+       return 0;
+}
+
+static const struct i2c_device_id ak8975_mod_id[] = {
+       {"ak8975", COMPASS_ID_AK8975},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak8975_mod_id);
+
+static struct i2c_driver ak8975_mod_driver = {
+       .class = I2C_CLASS_HWMON,
+       .probe = ak8975_mod_probe,
+       .remove = ak8975_mod_remove,
+       .id_table = ak8975_mod_id,
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "ak8975_mod",
+                  },
+       .address_list = normal_i2c,
+};
+
+static int __init ak8975_mod_init(void)
+{
+       int res = i2c_add_driver(&ak8975_mod_driver);
+       pr_info("%s: Probe name %s\n", __func__, "ak8975_mod");
+       if (res)
+               pr_err("%s failed\n", __func__);
+       return res;
+}
+
+static void __exit ak8975_mod_exit(void)
+{
+       pr_info("%s\n", __func__);
+       i2c_del_driver(&ak8975_mod_driver);
+}
+
+module_init(ak8975_mod_init);
+module_exit(ak8975_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver for AK8975 sensor with input subsystem");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ak8975_mod");
+
+/**
+ *  @}
+ */
diff --git a/drivers/input/misc/mpu/Kconfig b/drivers/input/misc/mpu/Kconfig
new file mode 100644 (file)
index 0000000..9c3774f
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# inv-mpu drivers for Invensense MPU devices and combos
+#
+
+config INV_MPU
+    tristate "Invensense MPU devices"
+    depends on I2C && SYSFS && INPUT
+    default n
+    help
+      This driver supports the Invensense MPU devices.
+      This driver can be built as a module. The module will be called
+      inv-mpu.
diff --git a/drivers/input/misc/mpu/Makefile b/drivers/input/misc/mpu/Makefile
new file mode 100644 (file)
index 0000000..08bec34
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Makefile for Invensense inv-mpu device.
+#
+
+obj-$(CONFIG_INV_MPU) += inv-mpu.o
+
+inv-mpu-objs := inv_gyro.o
+inv-mpu-objs += inv_gyro_misc.o
+inv-mpu-objs += inv_mpu3050.o
+inv-mpu-objs += dmpDefaultMPU6050.o
+
+# the Kionix KXTF9 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+inv-mpu-objs += inv_slave_kxtf9.o
+
diff --git a/drivers/input/misc/mpu/dmpDefaultMPU6050.c b/drivers/input/misc/mpu/dmpDefaultMPU6050.c
new file mode 100644 (file)
index 0000000..c831b4f
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+/* WARNING: autogenerated code, do not modify */
+/**
+ *  @defgroup DMPDEFAULT
+ *  @brief
+ *
+ *  @{
+ *      @file   dmpDefaultMPU6050.c
+ *      @brief
+ *
+ */
+
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_27              (2097)
+#define CFG_21              (2085)
+#define CFG_20              (1491)
+#define CFG_23              (2100)
+#define CFG_TAP4            (2091)
+#define CFG_TAP5            (2092)
+#define CFG_TAP6            (2093)
+#define CFG_GYRO_SOURCE     (2042)
+#define CFG_TAP0            (2085)
+#define CFG_TAP1            (2087)
+#define CFG_TAP2            (2088)
+#define TEMPLABEL           (1591)
+#define CFG_ORIENT_IRQ_1    (1884)
+#define CFG_DR_INT          (1037)
+#define FCFG_3              (1107)
+#define CFG_AUTH            (1029)
+#define CGNOTICE_INTR       (1964)
+#define CFG_ACCEL_FILTER    (1138)
+#define FCFG_2              (1063)
+#define FCFG_1              (1059)
+#define FCFG_7              (1073)
+#define CFG_12              (2080)
+#define CFG_TAP7            (2094)
+#define CFG_14              (2054)
+#define CFG_15              (2059)
+#define CFG_16              (2101)
+#define CFG_6               (2108)
+#define CFG_1               (2048)
+#define CFG_TAP3            (2089)
+#define CFG_EXTERNAL        (2070)
+#define CFG_8               (2037)
+#define CFG_9               (2043)
+#define CFG_FLICK_IN        (1917)
+#define CFG_FIFO_ON_EVENT   (2017)
+
+#define D_0_22                  (22+512)
+#define D_0_24                  (24+512)
+
+#define D_0_36                  (36)
+#define D_0_52                  (52)
+#define D_0_96                  (96)
+#define D_0_104                 (104)
+#define D_0_108                 (108)
+#define D_0_163                 (163)
+#define D_0_188                 (188)
+#define D_0_192                 (192)
+#define D_0_224                 (224)
+#define D_0_228                 (228)
+#define D_0_232                 (232)
+#define D_0_236                 (236)
+
+#define D_1_2                   (256 + 2)
+#define D_1_4                   (256 + 4)
+#define D_1_8                   (256 + 8)
+#define D_1_10                  (256 + 10)
+#define D_1_24                  (256 + 24)
+#define D_1_28                  (256 + 28)
+#define D_1_36                  (256 + 36)
+#define D_1_40                  (256 + 40)
+#define D_1_44                  (256 + 44)
+#define D_1_72                  (256 + 72)
+#define D_1_74                  (256 + 74)
+#define D_1_79                  (256 + 79)
+#define D_1_88                  (256 + 88)
+#define D_1_90                  (256 + 90)
+#define D_1_92                  (256 + 92)
+#define D_1_96                  (256 + 96)
+#define D_1_98                  (256 + 98)
+#define D_1_106                 (256 + 106)
+#define D_1_108                 (256 + 108)
+#define D_1_112                 (256 + 112)
+#define D_1_128                 (256 + 144)
+#define D_1_152                 (256 + 12)
+#define D_1_160                 (256 + 160)
+#define D_1_176                 (256 + 176)
+#define D_1_178                 (256 + 178)
+#define D_1_218                 (256 + 218)
+#define D_1_232                 (256 + 232)
+#define D_1_236                 (256 + 236)
+#define D_1_240                 (256 + 240)
+#define D_1_244                 (256 + 244)
+#define D_1_250                 (256 + 250)
+#define D_1_252                 (256 + 252)
+#define D_2_12                  (512 + 12)
+#define D_2_96                  (512 + 96)
+#define D_2_108                 (512 + 108)
+#define D_2_208                 (512 + 208)
+#define D_2_224                 (512 + 224)
+#define D_2_236                 (512 + 236)
+#define D_2_244                 (512 + 244)
+#define D_2_248                 (512 + 248)
+#define D_2_252                 (512 + 252)
+
+#define CPASS_BIAS_X            (35 * 16 + 4)
+#define CPASS_BIAS_Y            (35 * 16 + 8)
+#define CPASS_BIAS_Z            (35 * 16 + 12)
+#define CPASS_MTX_00            (36 * 16)
+#define CPASS_MTX_01            (36 * 16 + 4)
+#define CPASS_MTX_02            (36 * 16 + 8)
+#define CPASS_MTX_10            (36 * 16 + 12)
+#define CPASS_MTX_11            (37 * 16)
+#define CPASS_MTX_12            (37 * 16 + 4)
+#define CPASS_MTX_20            (37 * 16 + 8)
+#define CPASS_MTX_21            (37 * 16 + 12)
+#define CPASS_MTX_22            (43 * 16 + 12)
+#define D_ACT0                  (40 * 16)
+#define D_ACSX                  (40 * 16 + 4)
+#define D_ACSY                  (40 * 16 + 8)
+#define D_ACSZ                  (40 * 16 + 12)
+
+#define FLICK_MSG               (45 * 16 + 4)
+#define FLICK_COUNTER           (45 * 16 + 8)
+#define FLICK_LOWER             (45 * 16 + 12)
+#define FLICK_UPPER             (46 * 16 + 12)
+
+#define D_AUTH_OUT               (32)
+#define D_AUTH_IN                (36)
+#define D_AUTH_A                 (40)
+#define D_AUTH_B                 (44)
+
+#define D_PEDSTD_BP_B          (768 + 0x1C)
+#define D_PEDSTD_HP_A          (768 + 0x78)
+#define D_PEDSTD_HP_B          (768 + 0x7C)
+#define D_PEDSTD_BP_A4         (768 + 0x40)
+#define D_PEDSTD_BP_A3         (768 + 0x44)
+#define D_PEDSTD_BP_A2         (768 + 0x48)
+#define D_PEDSTD_BP_A1         (768 + 0x4C)
+#define D_PEDSTD_INT_THRSH     (768 + 0x68)
+#define D_PEDSTD_CLIP          (768 + 0x6C)
+#define D_PEDSTD_SB            (768 + 0x28)
+#define D_PEDSTD_SB_TIME       (768 + 0x2C)
+#define D_PEDSTD_PEAKTHRSH     (768 + 0x98)
+#define D_PEDSTD_TIML          (768 + 0x2A)
+#define D_PEDSTD_TIMH          (768 + 0x2E)
+#define D_PEDSTD_PEAK          (768 + 0X94)
+#define D_PEDSTD_STEPCTR       (768 + 0x60)
+#define D_PEDSTD_TIMECTR       (768 + 0x22)
+#define D_PEDSTD_DECI          (768 + 0xA0)
+
+static const struct tKeyLabel dmpTConfig[] = {
+       {KEY_CFG_27,            CFG_27},
+       {KEY_CFG_21,            CFG_21},
+       {KEY_CFG_20,            CFG_20},
+       {KEY_CFG_23,            CFG_23},
+       {KEY_CFG_TAP4,          CFG_TAP4},
+       {KEY_CFG_TAP5,          CFG_TAP5},
+       {KEY_CFG_TAP6,          CFG_TAP6},
+       {KEY_CFG_GYRO_SOURCE,   CFG_GYRO_SOURCE},
+       {KEY_CFG_TAP0,          CFG_TAP0},
+       {KEY_CFG_TAP1,          CFG_TAP1},
+       {KEY_CFG_TAP2,          CFG_TAP2},
+       {KEY_TEMPLABEL,         TEMPLABEL},
+       {KEY_CFG_ORIENT_IRQ_1,  CFG_ORIENT_IRQ_1},
+       {KEY_CFG_DR_INT,        CFG_DR_INT},
+       {KEY_FCFG_3,            FCFG_3},
+       {KEY_CGNOTICE_INTR,     CGNOTICE_INTR},
+       {KEY_CFG_ACCEL_FILTER,  CFG_ACCEL_FILTER},
+       {KEY_FCFG_2,            FCFG_2},
+       {KEY_FCFG_1,            FCFG_1},
+       {KEY_FCFG_7,            FCFG_7},
+       {KEY_CFG_12,            CFG_12},
+       {KEY_CFG_TAP7,          CFG_TAP7},
+       {KEY_CFG_14,            CFG_14},
+       {KEY_CFG_15,            CFG_15},
+       {KEY_CFG_16,            CFG_16},
+       {KEY_CFG_6,             CFG_6},
+       {KEY_CFG_1,             CFG_1},
+       {KEY_CFG_TAP3,          CFG_TAP3},
+       {KEY_CFG_EXTERNAL,      CFG_EXTERNAL},
+       {KEY_CFG_8,             CFG_8},
+       {KEY_CFG_9,             CFG_9},
+       {KEY_CFG_FLICK_IN,      CFG_FLICK_IN},
+       {KEY_CFG_FIFO_ON_EVENT,       CFG_FIFO_ON_EVENT},
+       {KEY_TEMPLABEL,             TEMPLABEL},
+       {KEY_CFG_20, CFG_20},
+       {KEY_CFG_AUTH,          CFG_AUTH},
+       {KEY_D_0_22,                D_0_22},
+       {KEY_D_0_96,                D_0_96},
+       {KEY_D_0_104,               D_0_104},
+       {KEY_D_0_108,               D_0_108},
+       {KEY_D_1_36,               D_1_36},
+       {KEY_D_1_40,               D_1_40},
+       {KEY_D_1_44,               D_1_44},
+       {KEY_D_1_72,               D_1_72},
+       {KEY_D_1_74,               D_1_74},
+       {KEY_D_1_79,               D_1_79},
+       {KEY_D_1_88,               D_1_88},
+       {KEY_D_1_90,               D_1_90},
+       {KEY_D_1_92,               D_1_92},
+       {KEY_D_1_160,               D_1_160},
+       {KEY_D_1_176,               D_1_176},
+       {KEY_D_1_218,               D_1_218},
+       {KEY_D_1_232,               D_1_232},
+       {KEY_D_1_250,               D_1_250},
+       {KEY_DMP_TAPW_MIN,          DMP_TAPW_MIN},
+       {KEY_DMP_TAP_THR_X,         DMP_TAP_THX},
+       {KEY_DMP_TAP_THR_Y,         DMP_TAP_THY},
+       {KEY_DMP_TAP_THR_Z,         DMP_TAP_THZ},
+       {KEY_DMP_SH_TH_Y,           DMP_SH_TH_Y},
+       {KEY_DMP_SH_TH_X,           DMP_SH_TH_X},
+       {KEY_DMP_SH_TH_Z,           DMP_SH_TH_Z},
+       {KEY_DMP_ORIENT,            DMP_ORIENT},
+       {KEY_D_AUTH_OUT,            D_AUTH_OUT},
+       {KEY_D_AUTH_IN,             D_AUTH_IN},
+       {KEY_D_AUTH_A,              D_AUTH_A},
+       {KEY_D_AUTH_B,              D_AUTH_B},
+       {KEY_CPASS_BIAS_X,          CPASS_BIAS_X},
+       {KEY_CPASS_BIAS_Y,          CPASS_BIAS_Y},
+       {KEY_CPASS_BIAS_Z,          CPASS_BIAS_Z},
+       {KEY_CPASS_MTX_00,          CPASS_MTX_00},
+       {KEY_CPASS_MTX_01,          CPASS_MTX_01},
+       {KEY_CPASS_MTX_02,          CPASS_MTX_02},
+       {KEY_CPASS_MTX_10,          CPASS_MTX_10},
+       {KEY_CPASS_MTX_11,          CPASS_MTX_11},
+       {KEY_CPASS_MTX_12,          CPASS_MTX_12},
+       {KEY_CPASS_MTX_20,          CPASS_MTX_20},
+       {KEY_CPASS_MTX_21,          CPASS_MTX_21},
+       {KEY_CPASS_MTX_22,          CPASS_MTX_22},
+       {KEY_D_ACT0,                D_ACT0},
+       {KEY_D_ACSX,                D_ACSX},
+       {KEY_D_ACSY,                D_ACSY},
+       {KEY_D_ACSZ,                D_ACSZ},
+       {KEY_FLICK_MSG,             FLICK_MSG},
+       {KEY_FLICK_COUNTER,         FLICK_COUNTER},
+       {KEY_FLICK_LOWER,           FLICK_LOWER},
+       {KEY_FLICK_UPPER,           FLICK_UPPER},
+       {KEY_D_PEDSTD_BP_B, D_PEDSTD_BP_B},
+       {KEY_D_PEDSTD_HP_A, D_PEDSTD_HP_A},
+       {KEY_D_PEDSTD_HP_B, D_PEDSTD_HP_B},
+       {KEY_D_PEDSTD_BP_A4, D_PEDSTD_BP_A4},
+       {KEY_D_PEDSTD_BP_A3, D_PEDSTD_BP_A3},
+       {KEY_D_PEDSTD_BP_A2, D_PEDSTD_BP_A2},
+       {KEY_D_PEDSTD_BP_A1, D_PEDSTD_BP_A1},
+       {KEY_D_PEDSTD_INT_THRSH, D_PEDSTD_INT_THRSH},
+       {KEY_D_PEDSTD_CLIP, D_PEDSTD_CLIP},
+       {KEY_D_PEDSTD_SB, D_PEDSTD_SB},
+       {KEY_D_PEDSTD_SB_TIME, D_PEDSTD_SB_TIME},
+       {KEY_D_PEDSTD_PEAKTHRSH, D_PEDSTD_PEAKTHRSH},
+       {KEY_D_PEDSTD_TIML,      D_PEDSTD_TIML},
+       {KEY_D_PEDSTD_TIMH,      D_PEDSTD_TIMH},
+       {KEY_D_PEDSTD_PEAK,      D_PEDSTD_PEAK},
+       {KEY_D_PEDSTD_STEPCTR,   D_PEDSTD_STEPCTR},
+       {KEY_D_PEDSTD_TIMECTR,  D_PEDSTD_TIMECTR},
+       {KEY_D_PEDSTD_DECI,  D_PEDSTD_DECI}
+
+};
+
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key)
+{
+       static int isSorted;
+       if (!isSorted) {
+               int kk;
+               for (kk = 0; kk < NUM_KEYS; ++kk) {
+                       keys[kk].addr = 0xffff;
+                       keys[kk].key = kk;
+               }
+               for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+                       keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr;
+
+               isSorted = 1;
+       }
+       if (key >= NUM_KEYS)
+               return 0xffff;
+       return keys[key].addr;
+}
+
+/**
+ *  @}
+ */
diff --git a/drivers/input/misc/mpu/dmpKey.h b/drivers/input/misc/mpu/dmpKey.h
new file mode 100644 (file)
index 0000000..5762f85
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    dmpKey.h
+ *      @brief   dmp key file.
+ *      @details This file is part of inv_gyro driver code
+ */#ifndef DMPKEY_H__
+#define DMPKEY_H__
+
+#define KEY_CFG_25                  (0)
+#define KEY_CFG_24                  (KEY_CFG_25 + 1)
+#define KEY_CFG_26                  (KEY_CFG_24 + 1)
+#define KEY_CFG_27                  (KEY_CFG_26 + 1)
+#define KEY_CFG_21                  (KEY_CFG_27 + 1)
+#define KEY_CFG_20                  (KEY_CFG_21 + 1)
+#define KEY_CFG_TAP4                (KEY_CFG_20 + 1)
+#define KEY_CFG_TAP5                (KEY_CFG_TAP4 + 1)
+#define KEY_CFG_TAP6                (KEY_CFG_TAP5 + 1)
+#define KEY_CFG_TAP7                (KEY_CFG_TAP6 + 1)
+#define KEY_CFG_TAP0                (KEY_CFG_TAP7 + 1)
+#define KEY_CFG_TAP1                (KEY_CFG_TAP0 + 1)
+#define KEY_CFG_TAP2                (KEY_CFG_TAP1 + 1)
+#define KEY_CFG_TAP3                (KEY_CFG_TAP2 + 1)
+#define KEY_CFG_TAP_QUANTIZE        (KEY_CFG_TAP3 + 1)
+#define KEY_CFG_TAP_JERK            (KEY_CFG_TAP_QUANTIZE + 1)
+#define KEY_CFG_DR_INT              (KEY_CFG_TAP_JERK + 1)
+#define KEY_CFG_AUTH                (KEY_CFG_DR_INT + 1)
+#define KEY_CFG_TAP_SAVE_ACCB       (KEY_CFG_AUTH + 1)
+#define KEY_CFG_TAP_CLEAR_STICKY    (KEY_CFG_TAP_SAVE_ACCB + 1)
+#define KEY_CFG_FIFO_ON_EVENT       (KEY_CFG_TAP_CLEAR_STICKY + 1)
+#define KEY_FCFG_ACCEL_INPUT        (KEY_CFG_FIFO_ON_EVENT + 1)
+#define KEY_FCFG_ACCEL_INIT         (KEY_FCFG_ACCEL_INPUT + 1)
+#define KEY_CFG_23                  (KEY_FCFG_ACCEL_INIT + 1)
+#define KEY_FCFG_1                  (KEY_CFG_23 + 1)
+#define KEY_FCFG_3                  (KEY_FCFG_1 + 1)
+#define KEY_FCFG_2                  (KEY_FCFG_3 + 1)
+#define KEY_CFG_3D                  (KEY_FCFG_2 + 1)
+#define KEY_CFG_3B                  (KEY_CFG_3D + 1)
+#define KEY_CFG_3C                  (KEY_CFG_3B + 1)
+#define KEY_FCFG_5                  (KEY_CFG_3C + 1)
+#define KEY_FCFG_4                  (KEY_FCFG_5 + 1)
+#define KEY_FCFG_7                  (KEY_FCFG_4 + 1)
+#define KEY_FCFG_FSCALE             (KEY_FCFG_7 + 1)
+#define KEY_FCFG_AZ                 (KEY_FCFG_FSCALE + 1)
+#define KEY_FCFG_6                  (KEY_FCFG_AZ + 1)
+#define KEY_FCFG_LSB4               (KEY_FCFG_6 + 1)
+#define KEY_CFG_12                  (KEY_FCFG_LSB4 + 1)
+#define KEY_CFG_14                  (KEY_CFG_12 + 1)
+#define KEY_CFG_15                  (KEY_CFG_14 + 1)
+#define KEY_CFG_16                  (KEY_CFG_15 + 1)
+#define KEY_CFG_18                  (KEY_CFG_16 + 1)
+#define KEY_CFG_6                   (KEY_CFG_18 + 1)
+#define KEY_CFG_7                   (KEY_CFG_6 + 1)
+#define KEY_CFG_4                   (KEY_CFG_7 + 1)
+#define KEY_CFG_5                   (KEY_CFG_4 + 1)
+#define KEY_CFG_2                   (KEY_CFG_5 + 1)
+#define KEY_CFG_3                   (KEY_CFG_2 + 1)
+#define KEY_CFG_1                   (KEY_CFG_3 + 1)
+#define KEY_CFG_EXTERNAL            (KEY_CFG_1 + 1)
+#define KEY_CFG_8                   (KEY_CFG_EXTERNAL + 1)
+#define KEY_CFG_9                   (KEY_CFG_8 + 1)
+#define KEY_CFG_ORIENT_3            (KEY_CFG_9 + 1)
+#define KEY_CFG_ORIENT_2            (KEY_CFG_ORIENT_3 + 1)
+#define KEY_CFG_ORIENT_1            (KEY_CFG_ORIENT_2 + 1)
+#define KEY_CFG_GYRO_SOURCE         (KEY_CFG_ORIENT_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_1        (KEY_CFG_GYRO_SOURCE + 1)
+#define KEY_CFG_ORIENT_IRQ_2        (KEY_CFG_ORIENT_IRQ_1 + 1)
+#define KEY_CFG_ORIENT_IRQ_3        (KEY_CFG_ORIENT_IRQ_2 + 1)
+#define KEY_FCFG_MAG_VAL            (KEY_CFG_ORIENT_IRQ_3 + 1)
+#define KEY_FCFG_MAG_MOV            (KEY_FCFG_MAG_VAL + 1)
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER        (KEY_FCFG_MAG_MOV + 1)
+#define KEY_CFG_MOTION_BIAS         (KEY_CFG_ACCEL_FILTER + 1)
+#define KEY_TEMPLABEL               (KEY_CFG_MOTION_BIAS + 1)
+
+#define KEY_D_0_22                  (KEY_TEMPLABEL + 1)
+#define KEY_D_0_24                  (KEY_D_0_22 + 1)
+#define KEY_D_0_36                  (KEY_D_0_24 + 1)
+#define KEY_D_0_52                  (KEY_D_0_36 + 1)
+#define KEY_D_0_96                  (KEY_D_0_52 + 1)
+#define KEY_D_0_104                 (KEY_D_0_96 + 1)
+#define KEY_D_0_108                 (KEY_D_0_104 + 1)
+#define KEY_D_0_163                 (KEY_D_0_108 + 1)
+#define KEY_D_0_188                 (KEY_D_0_163 + 1)
+#define KEY_D_0_192                 (KEY_D_0_188 + 1)
+#define KEY_D_0_224                 (KEY_D_0_192 + 1)
+#define KEY_D_0_228                 (KEY_D_0_224 + 1)
+#define KEY_D_0_232                 (KEY_D_0_228 + 1)
+#define KEY_D_0_236                 (KEY_D_0_232 + 1)
+
+#define KEY_DMP_PREVPTAT            (KEY_D_0_236 + 1)
+#define KEY_D_1_2                   (KEY_DMP_PREVPTAT + 1)
+#define KEY_D_1_4                   (KEY_D_1_2 + 1)
+#define KEY_D_1_8                   (KEY_D_1_4 + 1)
+#define KEY_D_1_10                  (KEY_D_1_8 + 1)
+#define KEY_D_1_24                  (KEY_D_1_10 + 1)
+#define KEY_D_1_28                  (KEY_D_1_24 + 1)
+#define KEY_D_1_36                  (KEY_D_1_28 + 1)
+#define KEY_D_1_40                  (KEY_D_1_36 + 1)
+#define KEY_D_1_44                  (KEY_D_1_40 + 1)
+#define KEY_D_1_72                  (KEY_D_1_44 + 1)
+#define KEY_D_1_74                  (KEY_D_1_72 + 1)
+#define KEY_D_1_79                  (KEY_D_1_74 + 1)
+#define KEY_D_1_88                  (KEY_D_1_79 + 1)
+#define KEY_D_1_90                  (KEY_D_1_88 + 1)
+#define KEY_D_1_92                  (KEY_D_1_90 + 1)
+#define KEY_D_1_96                  (KEY_D_1_92 + 1)
+#define KEY_D_1_98                  (KEY_D_1_96 + 1)
+#define KEY_D_1_100                 (KEY_D_1_98 + 1)
+#define KEY_D_1_106                 (KEY_D_1_100 + 1)
+#define KEY_D_1_108                 (KEY_D_1_106 + 1)
+#define KEY_D_1_112                 (KEY_D_1_108 + 1)
+#define KEY_D_1_128                 (KEY_D_1_112 + 1)
+#define KEY_D_1_152                 (KEY_D_1_128 + 1)
+#define KEY_D_1_160                 (KEY_D_1_152 + 1)
+#define KEY_D_1_168                 (KEY_D_1_160 + 1)
+#define KEY_D_1_175                 (KEY_D_1_168 + 1)
+#define KEY_D_1_176                 (KEY_D_1_175 + 1)
+#define KEY_D_1_178                 (KEY_D_1_176 + 1)
+#define KEY_D_1_179                 (KEY_D_1_178 + 1)
+#define KEY_D_1_218                 (KEY_D_1_179 + 1)
+#define KEY_D_1_232                 (KEY_D_1_218 + 1)
+#define KEY_D_1_236                 (KEY_D_1_232 + 1)
+#define KEY_D_1_240                 (KEY_D_1_236 + 1)
+#define KEY_D_1_244                 (KEY_D_1_240 + 1)
+#define KEY_D_1_250                 (KEY_D_1_244 + 1)
+#define KEY_D_1_252                 (KEY_D_1_250 + 1)
+#define KEY_D_2_12                  (KEY_D_1_252 + 1)
+#define KEY_D_2_96                  (KEY_D_2_12 + 1)
+#define KEY_D_2_108                 (KEY_D_2_96 + 1)
+#define KEY_D_2_208                 (KEY_D_2_108 + 1)
+#define KEY_FLICK_MSG               (KEY_D_2_208 + 1)
+#define KEY_FLICK_COUNTER           (KEY_FLICK_MSG + 1)
+#define KEY_FLICK_LOWER             (KEY_FLICK_COUNTER + 1)
+#define KEY_CFG_FLICK_IN            (KEY_FLICK_LOWER + 1)
+#define KEY_FLICK_UPPER             (KEY_CFG_FLICK_IN + 1)
+#define KEY_CGNOTICE_INTR           (KEY_FLICK_UPPER + 1)
+#define KEY_D_2_224                 (KEY_CGNOTICE_INTR + 1)
+#define KEY_D_2_244                 (KEY_D_2_224 + 1)
+#define KEY_D_2_248                 (KEY_D_2_244 + 1)
+#define KEY_D_2_252                 (KEY_D_2_248 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_BIAS_X            (KEY_D_2_252 + 1)
+#define KEY_CPASS_BIAS_Y            (KEY_CPASS_BIAS_X + 1)
+#define KEY_CPASS_BIAS_Z            (KEY_CPASS_BIAS_Y + 1)
+#define KEY_CPASS_MTX_00            (KEY_CPASS_BIAS_Z + 1)
+#define KEY_CPASS_MTX_01            (KEY_CPASS_MTX_00 + 1)
+#define KEY_CPASS_MTX_02            (KEY_CPASS_MTX_01 + 1)
+#define KEY_CPASS_MTX_10            (KEY_CPASS_MTX_02 + 1)
+#define KEY_CPASS_MTX_11            (KEY_CPASS_MTX_10 + 1)
+#define KEY_CPASS_MTX_12            (KEY_CPASS_MTX_11 + 1)
+#define KEY_CPASS_MTX_20            (KEY_CPASS_MTX_12 + 1)
+#define KEY_CPASS_MTX_21            (KEY_CPASS_MTX_20 + 1)
+#define KEY_CPASS_MTX_22            (KEY_CPASS_MTX_21 + 1)
+
+/* Gesture Keys */
+#define KEY_DMP_TAPW_MIN            (KEY_CPASS_MTX_22 + 1)
+
+#define KEY_DMP_TAP_THR_X           (KEY_DMP_TAPW_MIN + 1)
+#define KEY_DMP_TAP_THR_Y           (KEY_DMP_TAP_THR_X + 1)
+#define KEY_DMP_TAP_THR_Z           (KEY_DMP_TAP_THR_Y + 1)
+#define KEY_DMP_SH_TH_Y             (KEY_DMP_TAP_THR_Z + 1)
+#define KEY_DMP_SH_TH_X             (KEY_DMP_SH_TH_Y + 1)
+#define KEY_DMP_SH_TH_Z             (KEY_DMP_SH_TH_X + 1)
+#define KEY_DMP_ORIENT              (KEY_DMP_SH_TH_Z + 1)
+#define KEY_D_ACT0                  (KEY_DMP_ORIENT + 1)
+#define KEY_D_ACSX                  (KEY_D_ACT0 + 1)
+#define KEY_D_ACSY                  (KEY_D_ACSX + 1)
+#define KEY_D_ACSZ                  (KEY_D_ACSY + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT              (KEY_D_ACSZ + 1)
+#define KEY_D_AUTH_IN               (KEY_D_AUTH_OUT + 1)
+#define KEY_D_AUTH_A                (KEY_D_AUTH_IN + 1)
+#define KEY_D_AUTH_B                (KEY_D_AUTH_A + 1)
+
+/* Pedometer standalone only keys */
+#define KEY_D_PEDSTD_BP_B           (KEY_D_AUTH_B + 1)
+#define KEY_D_PEDSTD_HP_A           (KEY_D_PEDSTD_BP_B + 1)
+#define KEY_D_PEDSTD_HP_B           (KEY_D_PEDSTD_HP_A + 1)
+#define KEY_D_PEDSTD_BP_A4          (KEY_D_PEDSTD_HP_B + 1)
+#define KEY_D_PEDSTD_BP_A3          (KEY_D_PEDSTD_BP_A4 + 1)
+#define KEY_D_PEDSTD_BP_A2          (KEY_D_PEDSTD_BP_A3 + 1)
+#define KEY_D_PEDSTD_BP_A1          (KEY_D_PEDSTD_BP_A2 + 1)
+#define KEY_D_PEDSTD_INT_THRSH      (KEY_D_PEDSTD_BP_A1 + 1)
+#define KEY_D_PEDSTD_CLIP           (KEY_D_PEDSTD_INT_THRSH + 1)
+#define KEY_D_PEDSTD_SB             (KEY_D_PEDSTD_CLIP + 1)
+#define KEY_D_PEDSTD_SB_TIME        (KEY_D_PEDSTD_SB + 1)
+#define KEY_D_PEDSTD_PEAKTHRSH      (KEY_D_PEDSTD_SB_TIME + 1)
+#define KEY_D_PEDSTD_TIML           (KEY_D_PEDSTD_PEAKTHRSH + 1)
+#define KEY_D_PEDSTD_TIMH           (KEY_D_PEDSTD_TIML + 1)
+#define KEY_D_PEDSTD_PEAK           (KEY_D_PEDSTD_TIMH + 1)
+#define KEY_D_PEDSTD_TIMECTR        (KEY_D_PEDSTD_PEAK + 1)
+#define KEY_D_PEDSTD_STEPCTR        (KEY_D_PEDSTD_TIMECTR + 1)
+#define KEY_D_PEDSTD_WALKTIME       (KEY_D_PEDSTD_STEPCTR + 1)
+#define KEY_D_PEDSTD_DECI           (KEY_D_PEDSTD_WALKTIME + 1)
+
+/* EIS keys */
+#define KEY_P_EIS_FIFO_FOOTER       (KEY_D_PEDSTD_DECI + 1)
+#define KEY_P_EIS_FIFO_YSHIFT       (KEY_P_EIS_FIFO_FOOTER + 1)
+#define KEY_P_EIS_DATA_RATE         (KEY_P_EIS_FIFO_YSHIFT + 1)
+#define KEY_P_EIS_FIFO_XSHIFT       (KEY_P_EIS_DATA_RATE + 1)
+#define KEY_P_EIS_FIFO_SYNC         (KEY_P_EIS_FIFO_XSHIFT + 1)
+#define KEY_P_EIS_FIFO_ZSHIFT       (KEY_P_EIS_FIFO_SYNC + 1)
+#define KEY_P_EIS_FIFO_READY        (KEY_P_EIS_FIFO_ZSHIFT + 1)
+#define KEY_DMP_FOOTER              (KEY_P_EIS_FIFO_READY + 1)
+#define KEY_DMP_INTX_HC             (KEY_DMP_FOOTER + 1)
+#define KEY_DMP_INTX_PH             (KEY_DMP_INTX_HC + 1)
+#define KEY_DMP_INTX_SH             (KEY_DMP_INTX_PH + 1)
+#define KEY_DMP_AINV_SH             (KEY_DMP_INTX_SH + 1)
+#define KEY_DMP_A_INV_XH            (KEY_DMP_AINV_SH + 1)
+#define KEY_DMP_AINV_PH             (KEY_DMP_A_INV_XH + 1)
+#define KEY_DMP_CTHX_H              (KEY_DMP_AINV_PH + 1)
+#define KEY_DMP_CTHY_H              (KEY_DMP_CTHX_H + 1)
+#define KEY_DMP_CTHZ_H              (KEY_DMP_CTHY_H + 1)
+#define KEY_DMP_NCTHX_H             (KEY_DMP_CTHZ_H + 1)
+#define KEY_DMP_NCTHY_H             (KEY_DMP_NCTHX_H + 1)
+#define KEY_DMP_NCTHZ_H             (KEY_DMP_NCTHY_H + 1)
+#define KEY_DMP_CTSQ_XH             (KEY_DMP_NCTHZ_H + 1)
+#define KEY_DMP_CTSQ_YH             (KEY_DMP_CTSQ_XH + 1)
+#define KEY_DMP_CTSQ_ZH             (KEY_DMP_CTSQ_YH + 1)
+#define KEY_DMP_INTX_H              (KEY_DMP_CTSQ_ZH + 1)
+#define KEY_DMP_INTY_H              (KEY_DMP_INTX_H + 1)
+#define KEY_DMP_INTZ_H              (KEY_DMP_INTY_H + 1)
+#define KEY_DMP_HPX_H               (KEY_DMP_INTZ_H + 1)
+#define KEY_DMP_HPY_H               (KEY_DMP_HPX_H + 1)
+#define KEY_DMP_HPZ_H               (KEY_DMP_HPY_H + 1)
+
+/* Stream keys */
+#define KEY_STREAM_P_GYRO_Z         (KEY_DMP_HPZ_H + 1)
+#define KEY_STREAM_P_GYRO_Y         (KEY_STREAM_P_GYRO_Z + 1)
+#define KEY_STREAM_P_GYRO_X         (KEY_STREAM_P_GYRO_Y + 1)
+#define KEY_STREAM_P_TEMP           (KEY_STREAM_P_GYRO_X + 1)
+#define KEY_STREAM_P_AUX_Y          (KEY_STREAM_P_TEMP + 1)
+#define KEY_STREAM_P_AUX_X          (KEY_STREAM_P_AUX_Y + 1)
+#define KEY_STREAM_P_AUX_Z          (KEY_STREAM_P_AUX_X + 1)
+#define KEY_STREAM_P_ACCEL_Y        (KEY_STREAM_P_AUX_Z + 1)
+#define KEY_STREAM_P_ACCEL_X        (KEY_STREAM_P_ACCEL_Y + 1)
+#define KEY_STREAM_P_FOOTER         (KEY_STREAM_P_ACCEL_X + 1)
+#define KEY_STREAM_P_ACCEL_Z        (KEY_STREAM_P_FOOTER + 1)
+
+#define NUM_KEYS                    (KEY_STREAM_P_ACCEL_Z + 1)
+
+struct tKeyLabel {
+       unsigned short key;
+       unsigned short addr;
+};
+
+#define DINA0A 0x0a
+#define DINA22 0x22
+#define DINA42 0x42
+#define DINA5A 0x5a
+
+#define DINA06 0x06
+#define DINA0E 0x0e
+#define DINA16 0x16
+#define DINA1E 0x1e
+#define DINA26 0x26
+#define DINA2E 0x2e
+#define DINA36 0x36
+#define DINA3E 0x3e
+#define DINA46 0x46
+#define DINA4E 0x4e
+#define DINA56 0x56
+#define DINA5E 0x5e
+#define DINA66 0x66
+#define DINA6E 0x6e
+#define DINA76 0x76
+#define DINA7E 0x7e
+
+#define DINA00 0x00
+#define DINA08 0x08
+#define DINA10 0x10
+#define DINA18 0x18
+#define DINA20 0x20
+#define DINA28 0x28
+#define DINA30 0x30
+#define DINA38 0x38
+#define DINA40 0x40
+#define DINA48 0x48
+#define DINA50 0x50
+#define DINA58 0x58
+#define DINA60 0x60
+#define DINA68 0x68
+#define DINA70 0x70
+#define DINA78 0x78
+
+#define DINA04 0x04
+#define DINA0C 0x0c
+#define DINA14 0x14
+#define DINA1C 0x1C
+#define DINA24 0x24
+#define DINA2C 0x2c
+#define DINA34 0x34
+#define DINA3C 0x3c
+#define DINA44 0x44
+#define DINA4C 0x4c
+#define DINA54 0x54
+#define DINA5C 0x5c
+#define DINA64 0x64
+#define DINA6C 0x6c
+#define DINA74 0x74
+#define DINA7C 0x7c
+
+#define DINA01 0x01
+#define DINA09 0x09
+#define DINA11 0x11
+#define DINA19 0x19
+#define DINA21 0x21
+#define DINA29 0x29
+#define DINA31 0x31
+#define DINA39 0x39
+#define DINA41 0x41
+#define DINA49 0x49
+#define DINA51 0x51
+#define DINA59 0x59
+#define DINA61 0x61
+#define DINA69 0x69
+#define DINA71 0x71
+#define DINA79 0x79
+
+#define DINA25 0x25
+#define DINA2D 0x2d
+#define DINA35 0x35
+#define DINA3D 0x3d
+#define DINA4D 0x4d
+#define DINA55 0x55
+#define DINA5D 0x5D
+#define DINA6D 0x6d
+#define DINA75 0x75
+#define DINA7D 0x7d
+
+#define DINADC 0xdc
+#define DINAF2 0xf2
+#define DINAAB 0xab
+#define DINAAA 0xaa
+#define DINAF1 0xf1
+#define DINADF 0xdf
+#define DINADA 0xda
+#define DINAB1 0xb1
+#define DINAB9 0xb9
+#define DINAF3 0xf3
+#define DINA8B 0x8b
+#define DINAA3 0xa3
+#define DINA91 0x91
+#define DINAB6 0xb6
+#define DINAB4 0xb4
+
+
+#define DINC00 0x00
+#define DINC01 0x01
+#define DINC02 0x02
+#define DINC03 0x03
+#define DINC08 0x08
+#define DINC09 0x09
+#define DINC0A 0x0a
+#define DINC0B 0x0b
+#define DINC10 0x10
+#define DINC11 0x11
+#define DINC12 0x12
+#define DINC13 0x13
+#define DINC18 0x18
+#define DINC19 0x19
+#define DINC1A 0x1a
+#define DINC1B 0x1b
+
+#define DINC20 0x20
+#define DINC21 0x21
+#define DINC22 0x22
+#define DINC23 0x23
+#define DINC28 0x28
+#define DINC29 0x29
+#define DINC2A 0x2a
+#define DINC2B 0x2b
+#define DINC30 0x30
+#define DINC31 0x31
+#define DINC32 0x32
+#define DINC33 0x33
+#define DINC38 0x38
+#define DINC39 0x39
+#define DINC3A 0x3a
+#define DINC3B 0x3b
+
+#define DINC40 0x40
+#define DINC41 0x41
+#define DINC42 0x42
+#define DINC43 0x43
+#define DINC48 0x48
+#define DINC49 0x49
+#define DINC4A 0x4a
+#define DINC4B 0x4b
+#define DINC50 0x50
+#define DINC51 0x51
+#define DINC52 0x52
+#define DINC53 0x53
+#define DINC58 0x58
+#define DINC59 0x59
+#define DINC5A 0x5a
+#define DINC5B 0x5b
+
+#define DINC60 0x60
+#define DINC61 0x61
+#define DINC62 0x62
+#define DINC63 0x63
+#define DINC68 0x68
+#define DINC69 0x69
+#define DINC6A 0x6a
+#define DINC6B 0x6b
+#define DINC70 0x70
+#define DINC71 0x71
+#define DINC72 0x72
+#define DINC73 0x73
+#define DINC78 0x78
+#define DINC79 0x79
+#define DINC7A 0x7a
+#define DINC7B 0x7b
+
+#define DIND40 0x40
+
+
+#define DINA80 0x80
+#define DINA90 0x90
+#define DINAA0 0xa0
+#define DINAC9 0xc9
+#define DINACB 0xcb
+#define DINACD 0xcd
+#define DINACF 0xcf
+#define DINAC8 0xc8
+#define DINACA 0xca
+#define DINACC 0xcc
+#define DINACE 0xce
+#define DINAD8 0xd8
+#define DINADD 0xdd
+#define DINAF8 0xf0
+#define DINAFE 0xfe
+
+#define DINBF8 0xf8
+#define DINAC0 0xb0
+#define DINAC1 0xb1
+#define DINAC2 0xb4
+#define DINAC3 0xb5
+#define DINAC4 0xb8
+#define DINAC5 0xb9
+#define DINBC0 0xc0
+#define DINBC2 0xc2
+#define DINBC4 0xc4
+#define DINBC6 0xc6
+
+
+
+#endif
diff --git a/drivers/input/misc/mpu/dmpmap.h b/drivers/input/misc/mpu/dmpmap.h
new file mode 100644 (file)
index 0000000..7dc354a
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ $License:
+    Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
+ $
+ */
+#ifndef DMPMAP_H
+#define DMPMAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define DMP_PTAT    0
+#define DMP_XGYR    2
+#define DMP_YGYR    4
+#define DMP_ZGYR    6
+#define DMP_XACC    8
+#define DMP_YACC    10
+#define DMP_ZACC    12
+#define DMP_ADC1    14
+#define DMP_ADC2    16
+#define DMP_ADC3    18
+#define DMP_BIASUNC    20
+#define DMP_FIFORT    22
+#define DMP_INVGSFH    24
+#define DMP_INVGSFL    26
+#define DMP_1H    28
+#define DMP_1L    30
+#define DMP_BLPFSTCH    32
+#define DMP_BLPFSTCL    34
+#define DMP_BLPFSXH    36
+#define DMP_BLPFSXL    38
+#define DMP_BLPFSYH    40
+#define DMP_BLPFSYL    42
+#define DMP_BLPFSZH    44
+#define DMP_BLPFSZL    46
+#define DMP_BLPFMTC    48
+#define DMP_SMC    50
+#define DMP_BLPFMXH    52
+#define DMP_BLPFMXL    54
+#define DMP_BLPFMYH    56
+#define DMP_BLPFMYL    58
+#define DMP_BLPFMZH    60
+#define DMP_BLPFMZL    62
+#define DMP_BLPFC    64
+#define DMP_SMCTH    66
+#define DMP_0H2    68
+#define DMP_0L2    70
+#define DMP_BERR2H    72
+#define DMP_BERR2L    74
+#define DMP_BERR2NH    76
+#define DMP_SMCINC    78
+#define DMP_ANGVBXH    80
+#define DMP_ANGVBXL    82
+#define DMP_ANGVBYH    84
+#define DMP_ANGVBYL    86
+#define DMP_ANGVBZH    88
+#define DMP_ANGVBZL    90
+#define DMP_BERR1H    92
+#define DMP_BERR1L    94
+#define DMP_ATCH    96
+#define DMP_BIASUNCSF    98
+#define DMP_ACT2H    100
+#define DMP_ACT2L    102
+#define DMP_GSFH    104
+#define DMP_GSFL    106
+#define DMP_GH    108
+#define DMP_GL    110
+#define DMP_0_5H    112
+#define DMP_0_5L    114
+#define DMP_0_0H    116
+#define DMP_0_0L    118
+#define DMP_1_0H    120
+#define DMP_1_0L    122
+#define DMP_1_5H    124
+#define DMP_1_5L    126
+#define DMP_TMP1AH    128
+#define DMP_TMP1AL    130
+#define DMP_TMP2AH    132
+#define DMP_TMP2AL    134
+#define DMP_TMP3AH    136
+#define DMP_TMP3AL    138
+#define DMP_TMP4AH    140
+#define DMP_TMP4AL    142
+#define DMP_XACCW    144
+#define DMP_TMP5    146
+#define DMP_XACCB    148
+#define DMP_TMP8    150
+#define DMP_YACCB    152
+#define DMP_TMP9    154
+#define DMP_ZACCB    156
+#define DMP_TMP10    158
+#define DMP_DZH    160
+#define DMP_DZL    162
+#define DMP_XGCH    164
+#define DMP_XGCL    166
+#define DMP_YGCH    168
+#define DMP_YGCL    170
+#define DMP_ZGCH    172
+#define DMP_ZGCL    174
+#define DMP_YACCW    176
+#define DMP_TMP7    178
+#define DMP_AFB1H    180
+#define DMP_AFB1L    182
+#define DMP_AFB2H    184
+#define DMP_AFB2L    186
+#define DMP_MAGFBH    188
+#define DMP_MAGFBL    190
+#define DMP_QT1H    192
+#define DMP_QT1L    194
+#define DMP_QT2H    196
+#define DMP_QT2L    198
+#define DMP_QT3H    200
+#define DMP_QT3L    202
+#define DMP_QT4H    204
+#define DMP_QT4L    206
+#define DMP_CTRL1H    208
+#define DMP_CTRL1L    210
+#define DMP_CTRL2H    212
+#define DMP_CTRL2L    214
+#define DMP_CTRL3H    216
+#define DMP_CTRL3L    218
+#define DMP_CTRL4H    220
+#define DMP_CTRL4L    222
+#define DMP_CTRLS1    224
+#define DMP_CTRLSF1    226
+#define DMP_CTRLS2    228
+#define DMP_CTRLSF2    230
+#define DMP_CTRLS3    232
+#define DMP_CTRLSFNLL    234
+#define DMP_CTRLS4    236
+#define DMP_CTRLSFNL2    238
+#define DMP_CTRLSFNL    240
+#define DMP_TMP30    242
+#define DMP_CTRLSFJT    244
+#define DMP_TMP31    246
+#define DMP_TMP11    248
+#define DMP_CTRLSF2_2    250
+#define DMP_TMP12    252
+#define DMP_CTRLSF1_2    254
+#define DMP_PREVPTAT    256
+#define DMP_ACCZB    258
+#define DMP_ACCXB    264
+#define DMP_ACCYB    266
+#define DMP_1HB    272
+#define DMP_1LB    274
+#define DMP_0H    276
+#define DMP_0L    278
+#define DMP_ASR22H    280
+#define DMP_ASR22L    282
+#define DMP_ASR6H    284
+#define DMP_ASR6L    286
+#define DMP_TMP13    288
+#define DMP_TMP14    290
+#define DMP_FINTXH    292
+#define DMP_FINTXL    294
+#define DMP_FINTYH    296
+#define DMP_FINTYL    298
+#define DMP_FINTZH    300
+#define DMP_FINTZL    302
+#define DMP_TMP1BH    304
+#define DMP_TMP1BL    306
+#define DMP_TMP2BH    308
+#define DMP_TMP2BL    310
+#define DMP_TMP3BH    312
+#define DMP_TMP3BL    314
+#define DMP_TMP4BH    316
+#define DMP_TMP4BL    318
+#define DMP_STXG    320
+#define DMP_ZCTXG    322
+#define DMP_STYG    324
+#define DMP_ZCTYG    326
+#define DMP_STZG    328
+#define DMP_ZCTZG    330
+#define DMP_CTRLSFJT2    332
+#define DMP_CTRLSFJTCNT    334
+#define DMP_PVXG    336
+#define DMP_TMP15    338
+#define DMP_PVYG    340
+#define DMP_TMP16    342
+#define DMP_PVZG    344
+#define DMP_TMP17    346
+#define DMP_MNMFLAGH    352
+#define DMP_MNMFLAGL    354
+#define DMP_MNMTMH    356
+#define DMP_MNMTML    358
+#define DMP_MNMTMTHRH    360
+#define DMP_MNMTMTHRL    362
+#define DMP_MNMTHRH    364
+#define DMP_MNMTHRL    366
+#define DMP_ACCQD4H    368
+#define DMP_ACCQD4L    370
+#define DMP_ACCQD5H    372
+#define DMP_ACCQD5L    374
+#define DMP_ACCQD6H    376
+#define DMP_ACCQD6L    378
+#define DMP_ACCQD7H    380
+#define DMP_ACCQD7L    382
+#define DMP_ACCQD0H    384
+#define DMP_ACCQD0L    386
+#define DMP_ACCQD1H    388
+#define DMP_ACCQD1L    390
+#define DMP_ACCQD2H    392
+#define DMP_ACCQD2L    394
+#define DMP_ACCQD3H    396
+#define DMP_ACCQD3L    398
+#define DMP_XN2H    400
+#define DMP_XN2L    402
+#define DMP_XN1H    404
+#define DMP_XN1L    406
+#define DMP_YN2H    408
+#define DMP_YN2L    410
+#define DMP_YN1H    412
+#define DMP_YN1L    414
+#define DMP_YH    416
+#define DMP_YL    418
+#define DMP_B0H    420
+#define DMP_B0L    422
+#define DMP_A1H    424
+#define DMP_A1L    426
+#define DMP_A2H    428
+#define DMP_A2L    430
+#define DMP_SEM1    432
+#define DMP_FIFOCNT    434
+#define DMP_SH_TH_X    436
+#define DMP_PACKET    438
+#define DMP_SH_TH_Y    440
+#define DMP_FOOTER    442
+#define DMP_SH_TH_Z    444
+#define DMP_TEMP29    448
+#define DMP_TEMP30    450
+#define DMP_XACCB_PRE    452
+#define DMP_XACCB_PREL    454
+#define DMP_YACCB_PRE    456
+#define DMP_YACCB_PREL    458
+#define DMP_ZACCB_PRE    460
+#define DMP_ZACCB_PREL    462
+#define DMP_TMP22    464
+#define DMP_TAP_TIMER    466
+#define DMP_TAP_THX    468
+#define DMP_TAP_THY    472
+#define DMP_TAP_THZ    476
+#define DMP_TAPW_MIN    478
+#define DMP_TMP25    480
+#define DMP_TMP26    482
+#define DMP_TMP27    484
+#define DMP_TMP28    486
+#define DMP_ORIENT    488
+#define DMP_THRSH    490
+#define DMP_ENDIANH    492
+#define DMP_ENDIANL    494
+#define DMP_BLPFNMTCH    496
+#define DMP_BLPFNMTCL    498
+#define DMP_BLPFNMXH    500
+#define DMP_BLPFNMXL    502
+#define DMP_BLPFNMYH    504
+#define DMP_BLPFNMYL    506
+#define DMP_BLPFNMZH    508
+#define DMP_BLPFNMZL    510
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/drivers/input/misc/mpu/inv_gyro.c b/drivers/input/misc/mpu/inv_gyro.c
new file mode 100644 (file)
index 0000000..4de34ae
--- /dev/null
@@ -0,0 +1,2890 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_gyro.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This driver currently works for the ITG3500, MPU6050, MPU9150
+ *               MPU3050
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/byteorder/generic.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include "inv_gyro.h"
+
+static struct inv_reg_map_s chip_reg = {
+       .who_am_i               = 0x75,
+       .sample_rate_div        = 0x19,
+       .lpf                    = 0x1A,
+       .product_id             = 0x0C,
+       .bank_sel               = 0x6D,
+       .user_ctrl              = 0x6A,
+       .fifo_en                = 0x23,
+       .gyro_config            = 0x1B,
+       .accl_config            = 0x1C,
+       .fifo_count_h           = 0x72,
+       .fifo_r_w               = 0x74,
+       .raw_gyro               = 0x43,
+       .raw_accl               = 0x3B,
+       .temperature            = 0x41,
+       .int_enable             = 0x38,
+       .int_status             = 0x3A,
+       .pwr_mgmt_1             = 0x6B,
+       .pwr_mgmt_2             = 0x6C,
+       .mem_start_addr         = 0x6E,
+       .mem_r_w                = 0x6F,
+       .prgm_strt_addrh        = 0x70
+};
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+       {119, "ITG3500"},
+       { 63, "MPU3050"},
+       {118, "MPU6050"},
+       {118, "MPU9150"}
+};
+
+s64 get_time_ns(void)
+{
+       struct timespec ts;
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
+/**
+ *  inv_i2c_read_base() - Read one or more bytes from the device registers.
+ *  @st:       Device driver instance.
+ *  @reg:      First device register to be read from.
+ *  @length:   Number of bytes to read.
+ *  @data:     Data read from device.
+ *  NOTE: The slave register will not increment when reading from the FIFO.
+ */
+int inv_i2c_read_base(struct inv_gyro_state_s *st, unsigned short i2c_addr,
+                     unsigned char reg, unsigned short length,
+                     unsigned char *data)
+{
+       struct i2c_msg msgs[2];
+       int res;
+
+       if (!data)
+               return -EINVAL;
+
+       msgs[0].addr = i2c_addr;
+       msgs[0].flags = 0;      /* write */
+       msgs[0].buf = &reg;
+       msgs[0].len = 1;
+
+       msgs[1].addr = i2c_addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].buf = data;
+       msgs[1].len = length;
+
+       pr_debug("%s RD%02X%02X%02X\n", st->hw->name, i2c_addr, reg, length);
+       res = i2c_transfer(st->sl_handle, msgs, 2);
+       if (res < 2) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_i2c_single_write_base() - Write a byte to a device register.
+ *  @st:       Device driver instance.
+ *  @reg:      Device register to be written to.
+ *  @data:     Byte to write to device.
+ */
+int inv_i2c_single_write_base(struct inv_gyro_state_s *st,
+                             unsigned short i2c_addr, unsigned char reg,
+                             unsigned char data)
+{
+       unsigned char tmp[2];
+       struct i2c_msg msg;
+       int res;
+
+       tmp[0] = reg;
+       tmp[1] = data;
+
+       msg.addr = i2c_addr;
+       msg.flags = 0;  /* write */
+       msg.buf = tmp;
+       msg.len = 2;
+
+       pr_debug("%s WS%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res == 0)
+                       res = -EIO;
+               return res;
+       }
+
+       return 0;
+}
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:       Device driver instance.
+ */
+void inv_clear_kfifo(struct inv_gyro_state_s *st)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&st->time_stamp_lock, flags);
+       kfifo_reset(&st->trigger.timestamps);
+       spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+static int set_power_itg(struct inv_gyro_state_s *st, unsigned char power_on)
+{
+       struct inv_reg_map_s *reg;
+       unsigned char data;
+       int result;
+
+       reg = st->reg;
+       if (power_on)
+               data = 0;
+       else
+               data = BIT_SLEEP;
+       data |= (st->chip_config.lpa_mode << 5);
+       if (st->chip_config.gyro_enable) {
+               result = inv_i2c_single_write(st,
+                       reg->pwr_mgmt_1, data | INV_CLK_PLL);
+               if (result)
+                       return result;
+
+               st->chip_config.clk_src = INV_CLK_PLL;
+       } else {
+               result = inv_i2c_single_write(st,
+                       reg->pwr_mgmt_1, data | INV_CLK_INTERNAL);
+               if (result)
+                       return result;
+
+               st->chip_config.clk_src = INV_CLK_INTERNAL;
+       }
+
+       if (power_on) {
+               mdelay(POWER_UP_TIME);
+               data = 0;
+               if (0 == st->chip_config.accl_enable)
+                       data |= BIT_PWR_ACCL_STBY;
+               if (0 == st->chip_config.gyro_enable)
+                       data |= BIT_PWR_GYRO_STBY;
+               data |= (st->chip_config.lpa_freq << 6);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+               if (result)
+                       return result;
+
+               mdelay(POWER_UP_TIME);
+               st->chip_config.is_asleep = 0;
+       } else {
+               st->chip_config.is_asleep = 1;
+       }
+       return 0;
+}
+
+/**
+ *  inv_set_power_state() - Turn device on/off.
+ *  @st:       Device driver instance.
+ *  @power_on: 1 to turn on, 0 to suspend.
+ */
+int inv_set_power_state(struct inv_gyro_state_s *st, unsigned char power_on)
+{
+       if (INV_MPU3050 == st->chip_type)
+               return set_power_mpu3050(st, power_on);
+       else
+               return set_power_itg(st, power_on);
+}
+
+/**
+ *  reset_fifo_itg() - Reset FIFO related registers.
+ *  @st:       Device driver instance.
+ */
+static int reset_fifo_itg(struct inv_gyro_state_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char val;
+
+       reg = st->reg;
+       /* disable interrupt */
+       result = inv_i2c_single_write(st, reg->int_enable, 0);
+       if (result) {
+               pr_err("%s failed\n", __func__);
+               return result;
+       }
+
+       /* disable the sensor output to FIFO */
+       result = inv_i2c_single_write(st, reg->fifo_en, 0);
+       if (result)
+               goto reset_fifo_fail;
+
+       /* disable fifo reading */
+       result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+       if (result)
+               goto reset_fifo_fail;
+
+       if (st->chip_config.dmp_on) {
+               val = (BIT_FIFO_RST | BIT_DMP_RST);
+               if (st->chip_config.compass_enable)
+                       val |= BIT_I2C_MST_RST;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+
+               mdelay(POWER_UP_TIME);
+               st->last_isr_time = get_time_ns();
+               result = inv_i2c_single_write(st, reg->int_enable,
+                                             BIT_DMP_INT_EN);
+               if (result)
+                       return result;
+
+               val = (BIT_DMP_EN | BIT_FIFO_EN);
+               if (st->chip_config.compass_enable)
+                       val |= BIT_I2C_MST_EN;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+
+       } else {
+               /* reset FIFO and possibly reset I2C*/
+               val = BIT_FIFO_RST;
+               if (st->chip_config.compass_enable)
+                       val |= BIT_I2C_MST_RST;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+
+               mdelay(POWER_UP_TIME);
+               st->last_isr_time = get_time_ns();
+               /* enable interrupt */
+               if (st->chip_config.accl_fifo_enable ||
+                       st->chip_config.gyro_fifo_enable ||
+                       st->chip_config.compass_enable){
+                       result = inv_i2c_single_write(st, reg->int_enable,
+                                               BIT_DATA_RDY_EN);
+                       if (result)
+                               return result;
+               }
+
+               /* enable FIFO reading and I2C master interface*/
+               val = BIT_FIFO_EN;
+               if (st->chip_config.compass_enable)
+                       val |= BIT_I2C_MST_EN;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+
+               /* enable sensor output to FIFO */
+               val = 0;
+               if (st->chip_config.gyro_fifo_enable)
+                       val |= BITS_GYRO_OUT;
+               if (st->chip_config.accl_fifo_enable)
+                       val |= BIT_ACCEL_OUT;
+               result = inv_i2c_single_write(st, reg->fifo_en, val);
+               if (result)
+                       goto reset_fifo_fail;
+       }
+
+       return 0;
+
+reset_fifo_fail:
+       if (st->chip_config.dmp_on)
+               val = BIT_DMP_INT_EN;
+       else
+               val = BIT_DATA_RDY_EN;
+       inv_i2c_single_write(st, reg->int_enable, val);
+       pr_err("%s failed\n", __func__);
+       return result;
+}
+
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:       Device driver instance.
+ */
+int inv_reset_fifo(struct inv_gyro_state_s *st)
+{
+       if (INV_MPU3050 == st->chip_type)
+               return reset_fifo_mpu3050(st);
+       else
+               return reset_fifo_itg(st);
+}
+
+/**
+ *  set_inv_enable() - Reset FIFO related registers.
+ *  @st:       Device driver instance.
+ *  @fifo_enable: enable/disable
+ */
+int set_inv_enable(struct inv_gyro_state_s *st, unsigned long enable)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+
+       if (st->chip_config.is_asleep)
+               return -EINVAL;
+
+       reg = st->reg;
+       if (enable) {
+               result = inv_reset_fifo(st);
+               if (result)
+                       return result;
+
+               inv_clear_kfifo(st);
+               st->chip_config.enable = 1;
+
+       } else {
+               result = inv_i2c_single_write(st, reg->fifo_en, 0);
+               if (result)
+                       return result;
+
+               result = inv_i2c_single_write(st, reg->int_enable, 0);
+               if (result)
+                       return result;
+
+               /* disable fifo reading */
+               if (INV_MPU3050 != st->chip_type) {
+                       result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+                       if (result)
+                               return result;
+               }
+
+               st->chip_config.enable = 0;
+       }
+
+       return 0;
+}
+
+static void inv_input_close(struct input_dev *d)
+{
+       struct inv_gyro_state_s *st;
+
+       st = input_get_drvdata(d);
+       set_inv_enable(st, 0);
+       inv_set_power_state(st, 0);
+}
+
+/**
+ *  inv_setup_input() - internal setup input device.
+ *  @st:       Device driver instance.
+ *  @**idev_in  pointer to input device
+ *  @*client    i2c client
+ *  @*name      name of the input device.
+ */
+static int inv_setup_input(struct inv_gyro_state_s *st,
+                          struct input_dev **idev_in,
+                          struct i2c_client *client, unsigned char *name)
+{
+       int result;
+       struct input_dev *idev;
+
+       idev = input_allocate_device();
+       if (!idev) {
+               result = -ENOMEM;
+               return result;
+       }
+
+       /* Setup input device. */
+       idev->name = name;
+       idev->id.bustype = BUS_I2C;
+       idev->id.product = 'S';
+       idev->id.vendor     = ('I'<<8) | 'S';
+       idev->id.version    = 1;
+       idev->dev.parent = &client->dev;
+       /* Open and close method. */
+       if (strcmp(name, "INV_DMP") && strcmp(name, "INV_COMPASS"))
+               idev->close = inv_input_close;
+       input_set_capability(idev, EV_REL, REL_X);
+       input_set_capability(idev, EV_REL, REL_Y);
+       input_set_capability(idev, EV_REL, REL_Z);
+       input_set_capability(idev, EV_REL, REL_RX);
+       input_set_capability(idev, EV_REL, REL_RY);
+       input_set_capability(idev, EV_REL, REL_RZ);
+       input_set_capability(idev, EV_REL, REL_MISC);
+       input_set_capability(idev, EV_REL, REL_WHEEL);
+       input_set_drvdata(idev, st);
+       result = input_register_device(idev);
+       if (result)
+               input_free_device(idev);
+       *idev_in = idev;
+       return result;
+}
+
+/**
+ *  inv_init_config() - Initialize hardware, disable FIFO.
+ *  @st:       Device driver instance.
+ *  Initial configuration:
+ *  FSR: +/- 2000DPS
+ *  DLPF: 42Hz
+ *  FIFO rate: 50Hz
+ *  Clock source: Gyro PLL
+ */
+static int inv_init_config(struct inv_gyro_state_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       reg = st->reg;
+       result = set_inv_enable(st, 0);
+       if (result)
+               return result;
+
+       result = inv_i2c_single_write(st, reg->gyro_config,
+               INV_FSR_2000DPS << 3);
+       if (result)
+               return result;
+
+       st->chip_config.fsr = INV_FSR_2000DPS;
+       result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
+       if (result)
+               return result;
+
+       st->chip_config.lpf = INV_FILTER_42HZ;
+       result = inv_i2c_single_write(st, reg->sample_rate_div, 19);
+       if (result)
+               return result;
+
+       st->chip_config.fifo_rate = 50;
+       st->irq_dur_us            = 20*1000;
+       st->chip_config.enable = 0;
+       st->chip_config.dmp_on = 0;
+       st->compass_divider = 0;
+       st->compass_counter = 0;
+       st->chip_config.compass_enable = 0;
+       st->chip_config.firmware_loaded = 0;
+       st->chip_config.prog_start_addr = DMP_START_ADDR;
+       st->chip_config.gyro_enable = 1;
+       st->chip_config.gyro_fifo_enable = 1;
+       if (INV_ITG3500 != st->chip_type) {
+               st->chip_config.accl_enable = 1;
+               st->chip_config.accl_fifo_enable = 1;
+               st->chip_config.accl_fs = INV_FS_02G;
+               result = inv_i2c_single_write(st, reg->accl_config,
+                       (INV_FS_02G << 3));
+               if (result)
+                       return result;
+
+       } else {
+               st->chip_config.accl_enable = 0;
+               st->chip_config.accl_fifo_enable = 0;
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_raw_gyro_show() - Read gyro data directly from registers.
+ */
+static ssize_t inv_raw_gyro_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char data[6];
+
+       st = dev_get_drvdata(dev);
+       reg = st->reg;
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (0 == st->chip_config.gyro_enable)
+               return -EPERM;
+
+       result = inv_i2c_read(st, reg->raw_gyro, 6, data);
+       if (result) {
+               printk(KERN_ERR "Could not read raw registers.\n");
+               return result;
+       }
+
+       return sprintf(buf, "%d %d %d %lld\n",
+               (signed short)(be16_to_cpup((short *)&data[0])),
+               (signed short)(be16_to_cpup((short *)&data[2])),
+               (signed short)(be16_to_cpup((short *)&data[4])),
+               get_time_ns());
+}
+
+/**
+ *  inv_raw_accl_show() - Read accel data directly from registers.
+ */
+static ssize_t inv_raw_accl_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char data[6];
+
+       st = dev_get_drvdata(dev);
+       reg = st->reg;
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (0 == st->chip_config.accl_enable)
+               return -EPERM;
+
+       result = inv_i2c_read(st, reg->raw_accl, 6, data);
+       if (result) {
+               printk(KERN_ERR "Could not read raw registers.\n");
+               return result;
+       }
+
+       return sprintf(buf, "%d %d %d %lld\n",
+               ((signed short)(be16_to_cpup((short *)&data[0]))*
+                               st->chip_info.multi),
+               ((signed short)(be16_to_cpup((short *)&data[2]))*
+                               st->chip_info.multi),
+               ((signed short)(be16_to_cpup((short *)&data[4]))*
+                               st->chip_info.multi),
+               get_time_ns());
+}
+
+/**
+ *  inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char data[2];
+
+       st = dev_get_drvdata(dev);
+       reg = st->reg;
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = inv_i2c_read(st, reg->temperature, 2, data);
+       if (result) {
+               printk(KERN_ERR "Could not read temperature register.\n");
+               return result;
+       }
+
+       return sprintf(buf, "%d %lld\n",
+               (signed short)(be16_to_cpup((short *)&data[0])),
+               get_time_ns());
+}
+
+static int inv_set_lpf(struct inv_gyro_state_s *st, int rate)
+{
+       short hz[6] = {188, 98, 42, 20, 10, 5};
+       int   d[6] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+                       INV_FILTER_42HZ, INV_FILTER_20HZ,
+                       INV_FILTER_10HZ, INV_FILTER_5HZ};
+       int i, h, data, result;
+       struct inv_reg_map_s *reg;
+
+       reg = st->reg;
+       h = (rate >> 1);
+       i = 0;
+       while ((h < hz[i]) && (i < 6))
+               i++;
+       data = d[i];
+       if (INV_MPU3050 == st->chip_type) {
+               if (st->mpu_slave != NULL) {
+                       result = st->mpu_slave->set_lpf(st, rate);
+                       if (result)
+                               return result;
+               }
+
+               result = inv_i2c_single_write(st, reg->lpf,
+                       data | (st->chip_config.fsr << 3));
+
+       } else {
+               result = inv_i2c_single_write(st, reg->lpf, data);
+       }
+
+       if (result)
+               return result;
+
+       st->chip_config.lpf = data;
+       return 0;
+}
+
+/**
+ *  inv_fifo_rate_store() - Set fifo rate.
+ */
+static ssize_t inv_fifo_rate_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       unsigned long fifo_rate;
+       unsigned char data;
+       int result;
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+
+       st = dev_get_drvdata(dev);
+       reg = st->reg;
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (kstrtoul(buf, 10, &fifo_rate))
+               return -EINVAL;
+
+       if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+               return -EINVAL;
+
+       if (fifo_rate == st->chip_config.fifo_rate)
+               return count;
+
+       if (st->has_compass) {
+               data = 10*fifo_rate/ONE_K_HZ;
+               if (data > 0)
+                       data -= 1;
+               st->compass_divider = data;
+               st->compass_counter = 0;
+               /* I2C_MST_DLY is set according to sample rate,
+                  AKM cannot be read or set at sample rate higher than 100Hz*/
+               result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, data);
+               if (result)
+                       return result;
+       }
+
+       data = ONE_K_HZ / fifo_rate - 1;
+       result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+       if (result)
+               return result;
+
+       st->chip_config.fifo_rate = fifo_rate;
+       result = inv_set_lpf(st, fifo_rate);
+       if (result)
+               return result;
+
+       st->irq_dur_us = (data + 1) * ONE_K_HZ;
+       st->last_isr_time = get_time_ns();
+       return count;
+}
+
+/**
+ *  inv_power_state_store() - Turn device on/off.
+ */
+static ssize_t inv_power_state_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       int result;
+       unsigned long power_state;
+       struct inv_gyro_state_s *st;
+
+       st = dev_get_drvdata(dev);
+       if (kstrtoul(buf, 10, &power_state))
+               return -EINVAL;
+
+       if (!power_state == st->chip_config.is_asleep)
+               return count;
+
+       result = inv_set_power_state(st, power_state);
+       return count;
+}
+
+/**
+ *  inv_enable_store() - Enable/disable chip operation.
+ */
+static ssize_t inv_enable_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       unsigned long enable;
+       struct inv_gyro_state_s *st;
+       int result;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (kstrtoul(buf, 10, &enable))
+               return -EINVAL;
+
+       if (!enable == !st->chip_config.enable)
+               return count;
+
+       result = set_inv_enable(st, enable);
+       if (result)
+               return result;
+
+       return count;
+}
+
+/**
+ *  inv_accl_fifo_enable_store() - Enable/disable accl fifo output.
+ */
+static ssize_t inv_accl_fifo_enable_store(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       unsigned long data, en;
+       int result;
+       struct inv_gyro_state_s *st;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (st->chip_config.enable)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return -EINVAL;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (en == st->chip_config.accl_fifo_enable)
+               return count;
+
+       if (en && (0 == st->chip_config.accl_enable)) {
+               result = inv_set_power_state(st, 0);
+               if (result)
+                       return result;
+
+               st->chip_config.accl_enable = en;
+               result = inv_set_power_state(st, 1);
+               if (result)
+                       return result;
+       }
+
+       st->chip_config.accl_fifo_enable = en;
+       return count;
+}
+
+/**
+ *  inv_gyro_fifo_enable_store() - Enable/disable gyro fifo output.
+ */
+ssize_t inv_gyro_fifo_enable_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       unsigned long data, en;
+       int result;
+       struct inv_gyro_state_s *st;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (st->chip_config.enable)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return -EINVAL;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (en == st->chip_config.gyro_fifo_enable)
+               return count;
+
+       if (en && (0 == st->chip_config.gyro_enable)) {
+               result = inv_set_power_state(st, 0);
+               if (result)
+                       return result;
+
+               st->chip_config.gyro_enable = en;
+               result = inv_set_power_state(st, 1);
+               if (result)
+                       return result;
+       }
+
+       st->chip_config.gyro_fifo_enable = en;
+       return count;
+}
+
+/**
+ *  inv_gyro_enable_store() - Enable/disable gyro.
+ */
+ssize_t inv_gyro_enable_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       unsigned long data, en;
+       struct inv_gyro_state_s *st;
+       int result;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (st->chip_config.enable)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return -EINVAL;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (en == st->chip_config.gyro_enable)
+               return count;
+
+       if (0 == en)
+               st->chip_config.gyro_fifo_enable = 0;
+       result = inv_set_power_state(st, 0);
+       if (result)
+               return result;
+
+       st->chip_config.gyro_enable = en;
+       result = inv_set_power_state(st, 1);
+       if (result) {
+               st->chip_config.gyro_enable ^= 1;
+               return result;
+       }
+
+       return count;
+}
+
+/**
+ *  inv_accl_enable_store() - Enable/disable accl.
+ */
+static ssize_t inv_accl_enable_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       unsigned long data, en;
+       struct inv_gyro_state_s *st;
+       int result;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (st->chip_config.enable)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return -EINVAL;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (en == st->chip_config.accl_enable)
+               return count;
+
+       if (0 == en)
+               st->chip_config.accl_fifo_enable = 0;
+       result = inv_set_power_state(st, 0);
+       if (result)
+               return result;
+
+       st->chip_config.accl_enable = en;
+       result = inv_set_power_state(st, 1);
+       if (result) {
+               st->chip_config.accl_enable ^= 1;
+               return result;
+       }
+
+       return count;
+}
+
+/**
+ *  inv_gyro_fs_store() - Change the gyro full-scale range (and scale factor).
+ */
+static ssize_t inv_gyro_fs_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       unsigned long fsr;
+       int result;
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &fsr);
+       if (result)
+               return -EINVAL;
+
+       if (fsr > 3)
+               return -EINVAL;
+
+       if (fsr == st->chip_config.fsr)
+               return count;
+
+       reg = st->reg;
+       if (INV_MPU3050 == st->chip_type)
+               result = inv_i2c_single_write(st, reg->lpf,
+                       (fsr << 3) | st->chip_config.lpf);
+       else
+               result = inv_i2c_single_write(st, reg->gyro_config,
+                       fsr << 3);
+       if (result)
+               return result;
+
+       st->chip_config.fsr = fsr;
+       return count;
+}
+
+/**
+ *  inv_accl_fs_store() - Configure the accelerometer's scale range.
+ */
+ssize_t inv_accl_fs_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       unsigned long fs;
+       int result;
+       struct inv_gyro_state_s *st;
+       struct inv_reg_map_s *reg;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (kstrtoul(buf, 10, &fs))
+               return -EINVAL;
+
+       if (fs > 3)
+               return -EINVAL;
+
+       if (fs == st->chip_config.accl_fs)
+               return count;
+
+       reg = st->reg;
+       if ((INV_MPU3050 == st->chip_type) && (st->mpu_slave != NULL))
+               result = st->mpu_slave->set_fs(st, fs);
+       else
+               result = inv_i2c_single_write(st, reg->accl_config, (fs << 3));
+       if (result)
+               return result;
+
+       /* reset fifo because the data could be mixed with old bad data */
+       st->chip_config.accl_fs = fs;
+       return count;
+}
+
+/**
+ * inv_firmware_loaded_store() -  calling this function will change
+ *                        firmware load
+ */
+static ssize_t inv_firmware_loaded_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned long data, result;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       if (data != 0)
+               return -EINVAL;
+
+       st->chip_config.firmware_loaded = 0;
+       st->chip_config.dmp_on = 0;
+       return count;
+}
+
+/**
+ *  inv_lpa_mode_store() - store current low power settings
+ */
+static ssize_t inv_lpa_mode_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned long result, lpa_mode;
+       unsigned char d;
+       struct inv_reg_map_s *reg;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &lpa_mode);
+       if (result)
+               return result;
+
+       reg = st->reg;
+       result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+       if (result)
+               return result;
+
+       d &= ~BIT_CYCLE;
+       if (lpa_mode)
+               d |= BIT_CYCLE;
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+       if (result)
+               return result;
+
+       st->chip_config.lpa_mode = lpa_mode;
+       return count;
+}
+
+/**
+ *  inv_lpa_freq_store() - store current low power frequency setting.
+ */
+static ssize_t inv_lpa_freq_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned long result, lpa_freq;
+       unsigned char d;
+       struct inv_reg_map_s *reg;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &lpa_freq);
+       if (result)
+               return result;
+
+       if (lpa_freq > 3)
+               return -EINVAL;
+
+       reg = st->reg;
+       result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &d);
+       if (result)
+               return result;
+
+       d &= ~BIT_LPA_FREQ;
+       d |= (unsigned char)(lpa_freq << 6);
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_2, d);
+       if (result)
+               return result;
+
+       st->chip_config.lpa_freq = lpa_freq;
+       return count;
+}
+
+/**
+ * inv_compass_en_store() -  calling this function will store compass
+ *                         enable
+ */
+static ssize_t inv_compass_en_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st;
+       unsigned long data, result, en;
+
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (st->chip_config.enable)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (en == st->chip_config.compass_enable)
+               return count;
+
+       st->chip_config.compass_enable = en;
+       return count;
+}
+
+/**
+ *  inv_compass_scale_store() - show current compass scale settings
+ */
+static ssize_t inv_compass_scale_store(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st;
+       unsigned long data, result, en;
+       char d;
+
+       st = dev_get_drvdata(dev);
+       if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+               return count;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       if (data)
+               en = 1;
+       else
+               en = 0;
+       if (st->compass_scale == en)
+               return count;
+
+       st->compass_scale = en;
+       d = (1 | (st->compass_scale << 4));
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d);
+       if (result)
+               return result;
+
+       return count;
+}
+
+/**
+ * inv_flick_lower_store() -  calling this function will store current
+ *                        flick lower bound
+ */
+static ssize_t inv_flick_lower_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtol(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_FLICK_LOWER, 4, p);
+       if (result)
+               return result;
+
+       st->flick.lower = data;
+       return count;
+}
+
+/**
+ * inv_flick_upper_store() -  calling this function will store current
+ *                        flick upper bound
+ */
+static ssize_t inv_flick_upper_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_FLICK_UPPER, 4, p);
+       if (result)
+               return result;
+
+       st->flick.upper = data;
+       return count;
+}
+
+/**
+ * inv_flick_counter_store() -  calling this function will store current
+ *                        flick counter value
+ */
+static ssize_t inv_flick_counter_store(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_FLICK_COUNTER, 4, p);
+       if (result)
+               return result;
+
+       st->flick.counter = data;
+       return count;
+}
+
+/**
+ * inv_flick_int_on_store() -  calling this function will store current
+ *                        flick interrupt on value
+ */
+static ssize_t inv_flick_int_on_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned long result, data;
+       unsigned char d[4];
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       if (data)
+               /* Use interrupt to signal when gesture was observed */
+               d[0] = DIND40+4;
+       else
+               d[0] = DINAA0+8;
+       result = mem_w_key(KEY_CGNOTICE_INTR, 1, d);
+       if (result)
+               return result;
+
+       st->flick.int_on = data;
+       return count;
+}
+
+/**
+ * inv_flick_axis_store() -  calling this function will store current
+ *                        flick axis value
+ */
+static ssize_t inv_flick_axis_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned long result, data;
+       unsigned char d[4];
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       if (data == 0)
+               d[0] = DINBC2;
+       else if (data == 2)
+               d[2] = DINBC6;
+       else
+               d[0] = DINBC4;
+       result = mem_w_key(KEY_CFG_FLICK_IN, 1, d);
+       if (result)
+               return result;
+
+       st->flick.axis = data;
+       return count;
+}
+
+/**
+ * inv_flick_msg_on_store() -  calling this function will store current
+ *                        flick message on value
+ */
+static ssize_t inv_flick_msg_on_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       if (data)
+               data = DATA_MSG_ON;
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_FLICK_MSG, 4, p);
+       if (result)
+               return result;
+
+       st->flick.msg_on = data;
+       return count;
+}
+
+/**
+ * inv_pedometer_steps_store() -  calling this function will store current
+ *                        pedometer steps into MPU memory
+ */
+static ssize_t inv_pedometer_steps_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_D_PEDSTD_STEPCTR, 4, p);
+       if (result)
+               return result;
+
+       return count;
+}
+
+/**
+ * inv_pedometer_time_store() -  calling this function will store current
+ *                        pedometer time into MPU memory
+ */
+static ssize_t inv_pedometer_time_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w_key(KEY_D_PEDSTD_TIMECTR, 4, p);
+       if (result)
+               return result;
+
+       return count;
+}
+
+/**
+ * inv_key_store() -  calling this function will store authenticate key
+ */
+static ssize_t inv_key_store(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned int result, data, out;
+       unsigned char *p, d[4];
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = kstrtoul(buf, 10, (long unsigned int *)&data);
+       if (result)
+               return result;
+
+       out = cpu_to_be32p(&data);
+       p = (unsigned char *)&out;
+       result = mem_w(D_AUTH_IN, 4, p);
+       if (result)
+               return result;
+
+       result = mpu_memory_read(st->sl_handle, st->i2c_addr,
+               D_AUTH_IN, 4, d);
+       return count;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/**
+ *  inv_early_suspend_on_store() - set early_suspend_enable value
+ */
+static ssize_t inv_early_suspend_en_store(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct inv_gyro_state_s *st;
+       unsigned long data;
+       int result;
+
+       st = dev_get_drvdata(dev);
+       result = kstrtoul(buf, 10, &data);
+       if (result)
+               return result;
+
+       atomic_set(&(st->early_suspend_enable), !!data);
+       return count;
+}
+#endif
+
+/**
+ *  inv_gyro_fs_show() - Get the current gyro full-scale range.
+ */
+static ssize_t inv_gyro_fs_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", (1 << st->chip_config.fsr)*250);
+}
+
+/**
+ *  inv_accl_fs_show() - Get the current gyro full-scale range.
+ */
+ssize_t inv_accl_fs_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", 2 << st->chip_config.accl_fs);
+}
+
+/**
+ *  inv_clk_src_show() - Show the device's clock source.
+ */
+static ssize_t inv_clk_src_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       switch (st->chip_config.clk_src) {
+       case INV_CLK_INTERNAL:
+               return sprintf(buf, "INTERNAL\n");
+
+       case INV_CLK_PLL:
+               return sprintf(buf, "Gyro PLL\n");
+
+       default:
+               return sprintf(buf, "Oh no!\n");
+       }
+}
+
+/**
+ *  inv_fifo_rate_show() - Get the current sampling rate.
+ */
+static ssize_t inv_fifo_rate_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+}
+
+/**
+ *  inv_enable_show() - Check if the chip are enabled.
+ */
+static ssize_t inv_enable_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.enable);
+}
+
+/**
+ *  inv_gyro_fifo_enable_show() - Check if gyro FIFO are enabled.
+ */
+ssize_t inv_gyro_fifo_enable_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.gyro_fifo_enable);
+}
+
+/**
+ *  inv_accl_fifo_enable_show() - Check if accl FIFO are enabled.
+ */
+ssize_t inv_accl_fifo_enable_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.accl_fifo_enable);
+}
+
+/**
+ *  inv_gyro_enable_show() - Check if the FIFO and ring buffer are enabled.
+ */
+ssize_t inv_gyro_enable_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+}
+
+/**
+ *  inv_accl_enable_show() - Check if the FIFO and ring buffer are enabled.
+ */
+ssize_t inv_accl_enable_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.accl_enable);
+}
+
+/**
+ *  inv_power_state_show() - Check if the device is on or in sleep mode.
+ */
+static ssize_t inv_power_state_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       if (st->chip_config.is_asleep)
+               return sprintf(buf, "0\n");
+
+       else
+               return sprintf(buf, "1\n");
+}
+
+/**
+ *  inv_temp_scale_show() - Get the temperature scale factor in LSBs/degree C.
+ */
+static ssize_t inv_temp_scale_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       if (INV_MPU3050 == st->chip_type)
+               return sprintf(buf, "280\n");
+
+       else
+               return sprintf(buf, "340\n");
+}
+
+/**
+ *  inv_temp_offset_show() - Get the temperature offset in LSBs/degree C.
+ */
+static ssize_t inv_temp_offset_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       if (INV_MPU3050 == st->chip_type)
+               return sprintf(buf, "-13200\n");
+
+       else
+               return sprintf(buf, "-521\n");
+}
+
+/**
+ *  inv_lpa_mode_show() - show current low power settings
+ */
+static ssize_t inv_lpa_mode_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.lpa_mode);
+}
+
+/**
+ *  inv_lpa_freq_show() - show current low power frequency setting
+ */
+static ssize_t inv_lpa_freq_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       switch (st->chip_config.lpa_freq) {
+       case 0:
+               return sprintf(buf, "1.25\n");
+
+       case 1:
+               return sprintf(buf, "5\n");
+
+       case 2:
+               return sprintf(buf, "20\n");
+
+       case 3:
+               return sprintf(buf, "40\n");
+
+       default:
+               return sprintf(buf, "0\n");
+       }
+}
+
+/**
+ *  inv_compass_scale_show() - show current compass scale settings
+ */
+static ssize_t inv_compass_scale_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       long scale;
+
+       if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+               scale = DATA_AKM8975_SCALE;
+       else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+               scale = DATA_AKM8972_SCALE;
+       else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+               if (st->compass_scale)
+                       scale = DATA_AKM8963_SCALE1;
+               else
+                       scale = DATA_AKM8963_SCALE0;
+       else
+               return -EINVAL;
+
+       scale *= (1L << 15);
+       return sprintf(buf, "%ld\n", scale);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/**
+ *  inv_early_suspend_en_show() - show the current status of
+ *  early_suspend_enable
+ */
+static ssize_t inv_early_suspend_en_show(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", atomic_read(&st->early_suspend_enable));
+}
+#endif
+
+/**
+ *  inv_reg_dump_show() - Register dump for testing.
+ *  TODO: Only for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       int ii;
+       char data;
+       ssize_t bytes_printed = 0;
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       for (ii = 0; ii < st->hw->num_reg; ii++) {
+               /* don't read fifo r/w register */
+               if (ii == st->reg->fifo_r_w)
+                       data = 0;
+               else
+                       inv_i2c_read(st, ii, 1, &data);
+               bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n",
+                                        ii, data);
+       }
+       return bytes_printed;
+}
+
+/**
+ * inv_self_test_show() - self test result. 0 for fail; 1 for success.
+ *                        calling this function will trigger self test
+ *                        and return test result.
+ */
+static ssize_t inv_self_test_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       int result;
+       int bias[3];
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       if (INV_MPU3050 == st->chip_type) {
+               bias[0] = bias[1] = bias[2] = 0;
+               result = 0;
+       } else {
+               result = inv_hw_self_test(st, bias);
+       }
+       return sprintf(buf, "%d, %d, %d, %d\n",
+               bias[0], bias[1], bias[2], result);
+}
+
+/**
+ * inv_get_accl_bias_show() - show accl bias value
+ */
+static ssize_t inv_get_accl_bias_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       int result;
+       int bias[3];
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       result = inv_get_accl_bias(st, bias);
+       if (result)
+               return -EINVAL;
+
+       return sprintf(buf, "%d, %d, %d\n", bias[0], bias[1], bias[2]);
+}
+
+/**
+ * inv_gyro_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_gyro_matrix_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       signed char *m;
+
+       m = st->plat_data.orientation;
+       return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+                      m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+/**
+ * inv_accl_matrix_show() - show orientation matrix
+ */
+ssize_t inv_accl_matrix_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       signed char *m;
+
+       if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_ACCEL)
+               m = st->plat_data.secondary_orientation;
+       else
+               m = st->plat_data.orientation;
+       return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+                      m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+/**
+ * inv_compass_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_compass_matrix_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       signed char *m;
+
+       if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_COMPASS)
+               m = st->plat_data.secondary_orientation;
+       else
+               return -1;
+
+       return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+                      m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+/**
+ * inv_key_show() -  calling this function will show the key
+ *
+ */
+static ssize_t inv_key_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       unsigned char *key;
+       key = st->plat_data.key;
+
+       return sprintf(buf,
+                       "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+                       key[0], key[1], key[2], key[3], key[4], key[5], key[6],
+                       key[7], key[8], key[9], key[10], key[11], key[12],
+                       key[13], key[14], key[15]);
+}
+
+/**
+ * inv_firmware_loaded_show() -  calling this function will show current
+ *                        firmware load status
+ */
+static ssize_t inv_firmware_loaded_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+}
+
+/**
+ * inv_compass_en_show() -  calling this function will show compass
+ *                         enable status
+ */
+static ssize_t inv_compass_en_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->chip_config.compass_enable);
+}
+
+/**
+ * inv_flick_lower_show() -  calling this function will show current
+ *                        flick lower bound
+ */
+static ssize_t inv_flick_lower_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.lower);
+}
+
+/**
+ * inv_flick_upper_show() -  calling this function will show current
+ *                        flick upper bound
+ */
+static ssize_t inv_flick_upper_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.upper);
+}
+
+/**
+ * inv_flick_counter_show() -  calling this function will show current
+ *                        flick counter value
+ */
+static ssize_t inv_flick_counter_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.counter);
+}
+
+/**
+ * inv_flick_int_on_show() -  calling this function will show current
+ *                        flick interrupt on value
+ */
+static ssize_t inv_flick_int_on_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.int_on);
+}
+
+/**
+ * inv_flick_axis_show() -  calling this function will show current
+ *                        flick axis value
+ */
+static ssize_t inv_flick_axis_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.axis);
+}
+
+/**
+ * inv_flick_msg_on_show() -  calling this function will show current
+ *                        flick message on value
+ */
+static ssize_t inv_flick_msg_on_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", st->flick.msg_on);
+}
+
+/**
+ * inv_pedometer_steps_show() -  calling this function will store current
+ *                        pedometer steps into MPU memory
+ */
+static ssize_t inv_pedometer_steps_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       int result, data;
+       unsigned char d[4];
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = mpu_memory_read(st->sl_handle, st->i2c_addr,
+                                inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR),
+                                4, d);
+       if (result)
+               return result;
+
+       data = be32_to_cpup((int *)d);
+       return sprintf(buf, "%d\n", data);
+}
+
+/**
+ * inv_pedometer_time_show() -  calling this function will store current
+ *                        pedometer steps into MPU memory
+ */
+static ssize_t inv_pedometer_time_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct inv_gyro_state_s *st = dev_get_drvdata(dev);
+       int result, data;
+       unsigned char d[4];
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       result = mpu_memory_read(st->sl_handle, st->i2c_addr,
+                                inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR),
+                                4, d);
+       if (result)
+               return result;
+
+       data = be32_to_cpup((int *)d);
+       return sprintf(buf, "%d\n", data);
+}
+
+static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t,
+                                unsigned char *data)
+{
+       short x, y, z;
+       int ind;
+       struct inv_chip_config_s *conf;
+
+       conf = &st->chip_config;
+       ind = 0;
+       if (conf->accl_fifo_enable | conf->dmp_on) {
+               x = ((data[ind] << 8)|data[ind + 1])*st->chip_info.multi;
+               y = ((data[ind + 2] << 8)|data[ind + 3])*st->chip_info.multi;
+               z = ((data[ind + 4] << 8)|data[ind + 5])*st->chip_info.multi;
+               if (conf->accl_fifo_enable) {
+                       /*it is possible that accl disabled when dmp is on*/
+                       input_report_rel(st->idev, REL_RX,  x);
+                       input_report_rel(st->idev, REL_RY,  y);
+                       input_report_rel(st->idev, REL_RZ,  z);
+               }
+               ind += 6;
+       }
+       if (conf->gyro_fifo_enable | conf->dmp_on) {
+               x = (data[ind] << 8)     | data[ind + 1];
+               y = (data[ind + 2] << 8) | data[ind + 3];
+               z = (data[ind + 4] << 8) | data[ind + 5];
+               if (conf->gyro_fifo_enable) {
+                       /*it is possible that gyro disabled when dmp is on*/
+                       input_report_rel(st->idev, REL_X,  x);
+                       input_report_rel(st->idev, REL_Y,  y);
+                       input_report_rel(st->idev, REL_Z,  z);
+               }
+               ind += 6;
+       }
+       if (conf->dmp_on) {
+               /* report tap information */
+               if (data[ind + 1] & 1) {
+                       input_report_rel(st->idev_dmp, REL_RX, data[ind+3]);
+                       input_sync(st->idev_dmp);
+               }
+               /* report orientation information */
+               if (data[ind + 1] & 2) {
+                       input_report_rel(st->idev_dmp, REL_RY, data[ind+2]);
+                       input_sync(st->idev_dmp);
+               }
+       }
+       if (conf->accl_fifo_enable | conf->gyro_fifo_enable) {
+               input_report_rel(st->idev, REL_MISC, (unsigned int)(t >> 32));
+               input_report_rel(st->idev, REL_WHEEL,
+                       (unsigned int)(t & 0xffffffff));
+               input_sync(st->idev);
+       }
+}
+
+static int inv_report_compass(struct inv_gyro_state_s *st, s64 t)
+{
+       short x, y, z;
+       int result;
+       unsigned char data[8];
+
+       /*mpu_memory_read(st->sl_handle,
+               st->i2c_addr,
+               14,
+               10, data);*/
+       /*divider and counter is used to decrease the speed of read in
+               high frequency sample rate*/
+       if (st->compass_divider == st->compass_counter) {
+               /*read from external sensor data register */
+               result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, 8, data);
+               if (result)
+                       return result;
+
+               /* data[7] is status 2 register */
+               /*for AKM8975, bit 2 and 3 should be all be zero*/
+               /* for AMK8963, bit 3 should be zero*/
+               if ((DATA_AKM_DRDY == data[0]) &&
+                       (0 == (data[7] & DATA_AKM_STAT_MASK))) {
+                       unsigned char *sens;
+                       sens = st->chip_info.compass_sens;
+                       x = (short)((data[2] << 8) | data[1]);
+                       y = (short)((data[4] << 8) | data[3]);
+                       z = (short)((data[6] << 8) | data[5]);
+                       x = ((x * (sens[0] + 128)) >> 8);
+                       y = ((y * (sens[1] + 128)) >> 8);
+                       z = ((z * (sens[2] + 128)) >> 8);
+                       input_report_rel(st->idev_compass, REL_X, x);
+                       input_report_rel(st->idev_compass, REL_Y, y);
+                       input_report_rel(st->idev_compass, REL_Z, z);
+                       input_report_rel(st->idev_compass, REL_MISC,
+                                        (unsigned int)(t >> 32));
+                       input_report_rel(st->idev_compass, REL_WHEEL,
+                                        (unsigned int)(t & 0xffffffff));
+                       input_sync(st->idev_compass);
+               }
+               st->compass_counter = 0;
+
+       } else if (st->compass_divider != 0) {
+               st->compass_counter++;
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+static irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+       struct inv_gyro_state_s *st;
+       unsigned char bytes_per_datum;
+       const unsigned short fifo_thresh = 500;
+       int result;
+       unsigned char data[16];
+       unsigned short fifo_count;
+       unsigned int copied;
+       s64 timestamp;
+       struct inv_reg_map_s *reg;
+
+       st = (struct inv_gyro_state_s *)dev_id;
+       reg = st->reg;
+       if (st->chip_config.is_asleep)
+               goto end_session;
+
+       if (!(st->chip_config.enable))
+               goto end_session;
+
+       if (!(st->chip_config.accl_fifo_enable |
+               st->chip_config.gyro_fifo_enable |
+               st->chip_config.dmp_on |
+               st->chip_config.compass_enable))
+               goto end_session;
+
+       if (st->chip_config.dmp_on && st->flick.int_on) {
+               /*dmp interrupt status */
+               inv_i2c_read(st, REG_DMP_INT_STATUS, 2, data);
+               if (data[0] & 8) {
+                       input_report_rel(st->idev_dmp, REL_RZ, data[0]);
+                       input_sync(st->idev_dmp);
+               }
+       }
+       if (st->chip_config.lpa_mode) {
+               result = inv_i2c_read(st, reg->raw_accl, 6, data);
+               if (result)
+                       goto end_session;
+
+               inv_report_gyro_accl(st, get_time_ns(), data);
+               goto end_session;
+       }
+
+       if (st->chip_config.dmp_on)
+               bytes_per_datum = BYTES_FOR_DMP;
+       else
+               bytes_per_datum = (st->chip_config.accl_fifo_enable +
+                           st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR;
+       fifo_count = 0;
+       if (bytes_per_datum != 0) {
+               result = inv_i2c_read(st, reg->fifo_count_h, 2, data);
+               if (result)
+                       goto end_session;
+
+               fifo_count = (data[0] << 8) + data[1];
+               if (fifo_count < bytes_per_datum)
+                       goto end_session;
+
+               if (fifo_count%2)
+                       goto flush_fifo;
+
+               if (fifo_count > fifo_thresh)
+                       goto flush_fifo;
+
+               /* Timestamp mismatch. */
+               if (kfifo_len(&st->trigger.timestamps) <
+                       fifo_count / bytes_per_datum)
+                       goto flush_fifo;
+
+               if (kfifo_len(&st->trigger.timestamps) >
+                       fifo_count / bytes_per_datum + TIME_STAMP_TOR) {
+                       if (st->chip_config.dmp_on) {
+                               result = kfifo_to_user(&st->trigger.timestamps,
+                               &timestamp, sizeof(timestamp), &copied);
+                               if (result)
+                                       goto flush_fifo;
+
+                       } else {
+                               goto flush_fifo;
+                       }
+               }
+       }
+
+       if (bytes_per_datum == 0) {
+               result = kfifo_to_user(&st->trigger.timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+       }
+
+       while ((bytes_per_datum != 0) && (fifo_count >= bytes_per_datum)) {
+               result = inv_i2c_read(st, reg->fifo_r_w, bytes_per_datum,
+                       data);
+               if (result)
+                       goto flush_fifo;
+
+               result = kfifo_to_user(&st->trigger.timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+
+               inv_report_gyro_accl(st, timestamp, data);
+               fifo_count -= bytes_per_datum;
+       }
+
+       if (st->chip_config.compass_enable)
+               inv_report_compass(st, timestamp);
+
+end_session:
+       return IRQ_HANDLED;
+
+flush_fifo:
+       /* Flush HW and SW FIFOs. */
+       inv_reset_fifo(st);
+       inv_clear_kfifo(st);
+       return IRQ_HANDLED;
+}
+
+/**
+ *  inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+       struct inv_gyro_state_s *st;
+       long long timestamp;
+       int result, catch_up;
+       unsigned int time_since_last_irq;
+
+       st = (struct inv_gyro_state_s *)dev_id;
+       timestamp = get_time_ns();
+       time_since_last_irq = ((unsigned int)(timestamp - st->last_isr_time)) /
+                             ONE_K_HZ;
+       spin_lock(&st->time_stamp_lock);
+       catch_up = 0;
+       while ((time_since_last_irq > st->irq_dur_us*2) &&
+              (catch_up < MAX_CATCH_UP) && (0 == st->chip_config.lpa_mode)) {
+               st->last_isr_time += st->irq_dur_us * ONE_K_HZ;
+               result = kfifo_in(&st->trigger.timestamps,
+                                 &st->last_isr_time, 1);
+               time_since_last_irq = ((unsigned int)(timestamp -
+                                       st->last_isr_time)) / ONE_K_HZ;
+               catch_up++;
+       }
+       result = kfifo_in(&st->trigger.timestamps, &timestamp, 1);
+       st->last_isr_time = timestamp;
+       spin_unlock(&st->time_stamp_lock);
+       return IRQ_WAKE_THREAD;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/**
+ * inv_early_suspend - callback function for early_suspend
+ */
+static void inv_early_suspend(struct early_suspend *h)
+{
+       struct inv_gyro_state_s *st =
+                      container_of(h, struct inv_gyro_state_s, early_suspend);
+
+       if (atomic_read(&st->early_suspend_enable) == 1)
+               inv_set_power_state(st, 0);
+}
+
+/**
+ * inv_late_resume - callback function for late_resume
+ */
+static void inv_late_resume(struct early_suspend *h)
+{
+       struct inv_gyro_state_s *st =
+                      container_of(h, struct inv_gyro_state_s, early_suspend);
+
+       if (atomic_read(&st->early_suspend_enable) == 1)
+               inv_set_power_state(st, 1);
+}
+#endif
+
+static DEVICE_ATTR(raw_gyro, S_IRUGO, inv_raw_gyro_show, NULL);
+static DEVICE_ATTR(raw_accl, S_IRUGO, inv_raw_accl_show, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+static DEVICE_ATTR(fifo_rate, S_IRUGO | S_IWUSR, inv_fifo_rate_show,
+                  inv_fifo_rate_store);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, inv_enable_show,
+                  inv_enable_store);
+static DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR,
+                  inv_gyro_fifo_enable_show, inv_gyro_fifo_enable_store);
+static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_gyro_enable_show,
+                  inv_gyro_enable_store);
+static DEVICE_ATTR(accl_fifo_enable, S_IRUGO | S_IWUSR,
+                  inv_accl_fifo_enable_show, inv_accl_fifo_enable_store);
+static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_accl_enable_show,
+                  inv_accl_enable_store);
+static DEVICE_ATTR(accl_fs, S_IRUGO | S_IWUSR, inv_accl_fs_show,
+                  inv_accl_fs_store);
+static DEVICE_ATTR(gyro_fs, S_IRUGO | S_IWUSR, inv_gyro_fs_show,
+                  inv_gyro_fs_store);
+static DEVICE_ATTR(clock_source, S_IRUGO, inv_clk_src_show, NULL);
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_power_state_show,
+                  inv_power_state_store);
+static DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR,
+                  inv_firmware_loaded_show, inv_firmware_loaded_store);
+static DEVICE_ATTR(lpa_mode, S_IRUGO | S_IWUSR, inv_lpa_mode_show,
+                  inv_lpa_mode_store);
+static DEVICE_ATTR(lpa_freq, S_IRUGO | S_IWUSR, inv_lpa_freq_show,
+                  inv_lpa_freq_store);
+static DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_compass_en_show,
+                  inv_compass_en_store);
+static DEVICE_ATTR(compass_scale, S_IRUGO | S_IWUSR, inv_compass_scale_show,
+                  inv_compass_scale_store);
+static DEVICE_ATTR(temp_scale, S_IRUGO, inv_temp_scale_show, NULL);
+static DEVICE_ATTR(temp_offset, S_IRUGO, inv_temp_offset_show, NULL);
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(self_test, S_IRUGO, inv_self_test_show, NULL);
+static DEVICE_ATTR(key, S_IRUGO | S_IWUSR, inv_key_show, inv_key_store);
+static DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_gyro_matrix_show, NULL);
+static DEVICE_ATTR(accl_matrix, S_IRUGO, inv_accl_matrix_show, NULL);
+static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL);
+static DEVICE_ATTR(accl_bias, S_IRUGO, inv_get_accl_bias_show, NULL);
+static DEVICE_ATTR(flick_lower, S_IRUGO | S_IWUSR, inv_flick_lower_show,
+                  inv_flick_lower_store);
+static DEVICE_ATTR(flick_upper, S_IRUGO | S_IWUSR, inv_flick_upper_show,
+                  inv_flick_upper_store);
+static DEVICE_ATTR(flick_counter, S_IRUGO | S_IWUSR, inv_flick_counter_show,
+                  inv_flick_counter_store);
+static DEVICE_ATTR(flick_message_on, S_IRUGO | S_IWUSR, inv_flick_msg_on_show,
+                  inv_flick_msg_on_store);
+static DEVICE_ATTR(flick_int_on, S_IRUGO | S_IWUSR, inv_flick_int_on_show,
+                  inv_flick_int_on_store);
+static DEVICE_ATTR(flick_axis, S_IRUGO | S_IWUSR, inv_flick_axis_show,
+                  inv_flick_axis_store);
+static DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_pedometer_time_show,
+                  inv_pedometer_time_store);
+static DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR,
+                  inv_pedometer_steps_show, inv_pedometer_steps_store);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static DEVICE_ATTR(early_suspend_enable, S_IRUGO | S_IWUSR,
+       inv_early_suspend_en_show,
+       inv_early_suspend_en_store);
+#endif
+
+static struct device_attribute *inv_attributes[] = {
+       &dev_attr_raw_gyro,
+       &dev_attr_temperature,
+       &dev_attr_fifo_rate,
+       &dev_attr_enable,
+       &dev_attr_clock_source,
+       &dev_attr_power_state,
+       &dev_attr_gyro_fs,
+       &dev_attr_temp_scale,
+       &dev_attr_temp_offset,
+       &dev_attr_reg_dump,
+       &dev_attr_self_test,
+       &dev_attr_key,
+       &dev_attr_gyro_matrix,
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       &dev_attr_early_suspend_enable,
+#endif
+       NULL
+};
+
+static struct device_attribute *inv_mpu6050_attributes[] = {
+       &dev_attr_gyro_fifo_enable,
+       &dev_attr_gyro_enable,
+       &dev_attr_accl_fifo_enable,
+       &dev_attr_accl_enable,
+       &dev_attr_accl_fs,
+       &dev_attr_accl_bias,
+       &dev_attr_raw_accl,
+       &dev_attr_accl_matrix,
+       &dev_attr_firmware_loaded,
+       &dev_attr_lpa_mode,
+       &dev_attr_lpa_freq,
+       &dev_attr_flick_lower,
+       &dev_attr_flick_upper,
+       &dev_attr_flick_counter,
+       &dev_attr_flick_message_on,
+       &dev_attr_flick_int_on,
+       &dev_attr_flick_axis,
+       &dev_attr_pedometer_time,
+       &dev_attr_pedometer_steps,
+       NULL
+};
+
+static struct device_attribute *inv_compass_attributes[] = {
+       &dev_attr_compass_enable,
+       &dev_attr_compass_scale,
+       &dev_attr_compass_matrix,
+       NULL
+};
+
+static int inv_setup_compass(struct inv_gyro_state_s *st)
+{
+       int result;
+       unsigned char data[4];
+
+       result = inv_i2c_read(st, 1, 1, data);
+       if (result)
+               return result;
+
+       data[0] &= ~(0x80);
+       data[0] |= (st->plat_data.level_shifter << 7);
+       /*set up VDDIO register */
+       result = inv_i2c_single_write(st, 1, data[0]);
+       if (result)
+               return result;
+
+       /* set to bypass mode */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG, BIT_BYPASS_EN);
+       if (result)
+               return result;
+
+       /*read secondary i2c ID register */
+       result = inv_secondary_read(REG_AKM_ID, 2, data);
+       if (result)
+               return result;
+
+       if (data[0] != DATA_AKM_ID)
+               return -ENXIO;
+
+       /*set AKM to Fuse ROM access mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_FR);
+       if (result)
+               return result;
+
+       result = inv_secondary_read(REG_AKM_SENSITIVITY, 3,
+                                       st->chip_info.compass_sens);
+       if (result)
+               return result;
+
+       /*revert to power down mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN);
+       if (result)
+               return result;
+
+       printk(KERN_ERR"senx=%d, seny=%d,senz=%d\n",
+               st->chip_info.compass_sens[0],
+               st->chip_info.compass_sens[1],
+               st->chip_info.compass_sens[2]);
+       /*restore to non-bypass mode */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG, 0);
+       if (result)
+               return result;
+
+       /*setup master mode and master clock and ES bit*/
+       result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+       if (result)
+               return result;
+
+       /* slave 0 is used to read data from compass */
+       /*read mode */
+       result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, BIT_I2C_READ|
+               st->plat_data.secondary_i2c_addr);
+       if (result)
+               return result;
+
+       /* AKM status register address is 2 */
+       result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, REG_AKM_STATUS);
+       if (result)
+               return result;
+
+       /* slave 0 is enabled at the beginning, read 8 bytes from here */
+       result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, BIT_SLV_EN | 8);
+       if (result)
+               return result;
+
+       /*slave 1 is used for AKM mode change only*/
+       /*write mode, slave address 0x0E */
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+               st->plat_data.secondary_i2c_addr);
+       if (result)
+               return result;
+
+       /* AKM mode register address is 0x0A */
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, REG_AKM_MODE);
+       if (result)
+               return result;
+
+       /* slave 1 is enabled, byte length is 1 */
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, BIT_SLV_EN | 1);
+       if (result)
+               return result;
+
+       /* output data for slave 1 is fixed, single measure mode*/
+       st->compass_scale = 1;
+       if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) {
+               st->compass_st_upper[0] = DATA_AKM8975_ST_X_UP;
+               st->compass_st_upper[1] = DATA_AKM8975_ST_Y_UP;
+               st->compass_st_upper[2] = DATA_AKM8975_ST_Z_UP;
+               st->compass_st_lower[0] = DATA_AKM8975_ST_X_LW;
+               st->compass_st_lower[1] = DATA_AKM8975_ST_Y_LW;
+               st->compass_st_lower[2] = DATA_AKM8975_ST_Z_LW;
+               data[0] = 1;
+       } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+               st->compass_st_upper[0] = DATA_AKM8972_ST_X_UP;
+               st->compass_st_upper[1] = DATA_AKM8972_ST_Y_UP;
+               st->compass_st_upper[2] = DATA_AKM8972_ST_Z_UP;
+               st->compass_st_lower[0] = DATA_AKM8972_ST_X_LW;
+               st->compass_st_lower[1] = DATA_AKM8972_ST_Y_LW;
+               st->compass_st_lower[2] = DATA_AKM8972_ST_Z_LW;
+               data[0] = 1;
+       } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+               st->compass_st_upper[0] = DATA_AKM8963_ST_X_UP;
+               st->compass_st_upper[1] = DATA_AKM8963_ST_Y_UP;
+               st->compass_st_upper[2] = DATA_AKM8963_ST_Z_UP;
+               st->compass_st_lower[0] = DATA_AKM8963_ST_X_LW;
+               st->compass_st_lower[1] = DATA_AKM8963_ST_Y_LW;
+               st->compass_st_lower[2] = DATA_AKM8963_ST_Z_LW;
+               data[0] = (1 | (st->compass_scale << 4));
+       }
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, data[0]);
+       if (result)
+               return result;
+
+       /* slave 0 and 1 timer action is enabled every sample*/
+       result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL,
+                               BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN);
+       return result;
+}
+
+static int inv_check_chip_type(struct inv_gyro_state_s *st,
+                              const struct i2c_device_id *id)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+
+       reg = st->reg;
+       st->mpu_slave = NULL;
+       if (!strcmp(id->name, "itg3500"))
+               st->chip_type = INV_ITG3500;
+       else if (!strcmp(id->name, "mpu3050")) {
+               st->chip_type = INV_MPU3050;
+               inv_setup_reg_mpu3050(reg);
+       } else if (!strcmp(id->name, "mpu6050"))
+               st->chip_type = INV_MPU6050;
+       else if (!strcmp(id->name, "mpu9150"))
+               st->chip_type = INV_MPU9150;
+       if (INV_MPU9150 == st->chip_type) {
+               st->plat_data.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+               st->plat_data.sec_slave_id = COMPASS_ID_AK8975;
+               /*is MPU9150 i2c address hard coded? */
+               /*st->plat_data.secondary_i2c_addr = 0xE;*/
+               st->has_compass = 1;
+       }
+       if (SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) {
+               if (st->plat_data.sec_slave_id == ACCEL_ID_KXTF9)
+                       inv_register_kxtf9_slave(st);
+       }
+       if (SECONDARY_SLAVE_TYPE_COMPASS == st->plat_data.sec_slave_type)
+               st->has_compass = 1;
+       else
+               st->has_compass = 0;
+       st->chip_config.gyro_enable = 1;
+       /*reset register to power up default*/
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_RESET);
+       if (result)
+               return result;
+
+       mdelay(POWER_UP_TIME);
+       result = inv_set_power_state(st, 1);
+       if (result)
+               return result;
+
+       if (st->has_compass) {
+               result = inv_setup_compass(st);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+static int inv_create_input(struct inv_gyro_state_s *st,
+               struct i2c_client *client){
+       struct input_dev *idev;
+       int result;
+
+       idev = NULL;
+       result = inv_setup_input(st, &idev, client, st->hw->name);
+       if (result)
+               return result;
+
+       st->idev = idev;
+       if (INV_ITG3500 == st->chip_type)
+               return 0;
+
+       result = inv_setup_input(st, &idev, client, "INV_DMP");
+       if (result) {
+               input_unregister_device(st->idev);
+               return result;
+       }
+
+       st->idev_dmp = idev;
+       if (!st->has_compass)
+               return 0;
+
+       if (st->has_compass) {
+               result = inv_setup_input(st, &idev, client, "INV_COMPASS");
+               if (result) {
+                       input_unregister_device(st->idev);
+                       input_unregister_device(st->idev_dmp);
+                       return result;
+               }
+
+               st->idev_compass = idev;
+       }
+
+       return 0;
+}
+
+int create_device_attributes(struct device *dev,
+       struct device_attribute **attrs)
+{
+       int i;
+       int err = 0;
+
+       for (i = 0 ; NULL != attrs[i] ; ++i) {
+               err = sysfs_create_file(&dev->kobj, &attrs[i]->attr);
+               if (0 != err)
+                       break;
+       }
+       if (0 != err) {
+               for (; i >= 0 ; --i)
+                       sysfs_remove_file(&dev->kobj, &attrs[i]->attr);
+       }
+       return err;
+}
+
+void remove_device_attributes(struct device *dev,
+       struct device_attribute **attrs)
+{
+       int i;
+
+       for (i = 0 ; NULL != attrs[i] ; ++i)
+               device_remove_file(dev, attrs[i]);
+}
+
+static char const *const inv_class_name = "invensense";
+static char const *const inv_device_name = "mpu";
+static dev_t const inv_device_dev_t = MKDEV(MISC_MAJOR, MISC_DYNAMIC_MINOR);
+static struct bin_attribute dmp_firmware = {
+       .attr = {
+               .name = "dmp_firmware",
+               .mode = S_IRUGO | S_IWUSR
+       },
+       .size = 4096,
+       .read = inv_dmp_firmware_read,
+       .write = inv_dmp_firmware_write,
+};
+
+static int create_sysfs_interfaces(struct inv_gyro_state_s *st)
+{
+       int result;
+       result = 0;
+
+       st->inv_class = class_create(THIS_MODULE, inv_class_name);
+       if (IS_ERR(st->inv_class)) {
+               result = PTR_ERR(st->inv_class);
+               goto exit_nullify_class;
+       }
+
+       st->inv_dev = device_create(st->inv_class, &st->i2c->dev,
+                       inv_device_dev_t, st, inv_device_name);
+       if (IS_ERR(st->inv_dev)) {
+               result = PTR_ERR(st->inv_dev);
+               goto exit_destroy_class;
+       }
+
+       result = create_device_attributes(st->inv_dev, inv_attributes);
+       if (result < 0)
+               goto exit_destroy_device;
+
+       if (INV_ITG3500 == st->chip_type)
+               return 0;
+
+       result = sysfs_create_bin_file(&st->inv_dev->kobj, &dmp_firmware);
+       if (result < 0)
+               goto exit_remove_device_attributes;
+
+       if (INV_MPU3050 == st->chip_type) {
+               result = inv_mpu3050_create_sysfs(st);
+               if (result)
+                       goto exit_remove_bin_file;
+
+               return 0;
+       }
+
+       result = create_device_attributes(st->inv_dev, inv_mpu6050_attributes);
+       if (result < 0)
+               goto exit_remove_bin_file;
+
+       if (!st->has_compass)
+               return 0;
+
+       result = create_device_attributes(st->inv_dev, inv_compass_attributes);
+       if (result < 0)
+               goto exit_remove_6050_attributes;
+
+       return 0;
+
+exit_remove_6050_attributes:
+       remove_device_attributes(st->inv_dev, inv_mpu6050_attributes);
+exit_remove_bin_file:
+       sysfs_remove_bin_file(&st->inv_dev->kobj, &dmp_firmware);
+exit_remove_device_attributes:
+       remove_device_attributes(st->inv_dev, inv_attributes);
+exit_destroy_device:
+       device_destroy(st->inv_class, inv_device_dev_t);
+exit_destroy_class:
+       st->inv_dev = NULL;
+       class_destroy(st->inv_class);
+exit_nullify_class:
+       st->inv_class = NULL;
+       return result;
+}
+
+static void remove_sysfs_interfaces(struct inv_gyro_state_s *st)
+{
+       remove_device_attributes(st->inv_dev, inv_attributes);
+       if (INV_ITG3500 != st->chip_type)
+               sysfs_remove_bin_file(&st->inv_dev->kobj, &dmp_firmware);
+       if ((INV_ITG3500 != st->chip_type) && (INV_MPU3050 != st->chip_type))
+               remove_device_attributes(st->inv_dev, inv_mpu6050_attributes);
+       if (INV_MPU3050 == st->chip_type)
+               inv_mpu3050_remove_sysfs(st);
+       if (st->has_compass)
+               remove_device_attributes(st->inv_dev, inv_compass_attributes);
+       device_destroy(st->inv_class, inv_device_dev_t);
+       class_destroy(st->inv_class);
+       st->inv_dev = NULL;
+       st->inv_class = NULL;
+}
+
+static int inv_mod_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct inv_gyro_state_s *st;
+       int result;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               result = -ENODEV;
+               goto out_no_free;
+       }
+
+       st = kzalloc(sizeof(*st), GFP_KERNEL);
+       if (!st) {
+               result = -ENOMEM;
+               goto out_no_free;
+       }
+
+       /* Make state variables available to all _show and _store functions. */
+       i2c_set_clientdata(client, st);
+       st->i2c = client;
+       st->sl_handle = client->adapter;
+       st->reg = (struct inv_reg_map_s *)&chip_reg ;
+       st->hw  = (struct inv_hw_s *)hw_info;
+       st->i2c_addr = client->addr;
+       st->plat_data =
+               *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+       /* power is turned on inside check chip type*/
+       result = inv_check_chip_type(st, id);
+       if (result)
+               goto out_free;
+
+       st->hw  = (struct inv_hw_s *)(hw_info  + st->chip_type);
+       if (INV_MPU3050 == st->chip_type)
+               result = inv_init_config_mpu3050(st);
+       else
+               result = inv_init_config(st);
+       if (result) {
+               dev_err(&client->adapter->dev,
+                       "Could not initialize device.\n");
+               goto out_free;
+       }
+
+       if (INV_ITG3500 != st->chip_type && INV_MPU3050 != st->chip_type) {
+               result = inv_get_silicon_rev_mpu6050(st);
+               if (result) {
+                       dev_err(&client->adapter->dev,
+                               "%s get silicon error.\n", st->hw->name);
+                       goto out_free;
+               }
+       }
+
+       result = inv_set_power_state(st, 0);
+       if (result) {
+               dev_err(&client->adapter->dev,
+                       "%s could not be turned off.\n", st->hw->name);
+               goto out_free;
+       }
+
+       INIT_KFIFO(st->trigger.timestamps);
+       result = create_sysfs_interfaces(st);
+       if (result)
+               goto out_free_kfifo;
+
+       if (!client->irq) {
+               dev_err(&client->adapter->dev, "IRQ not assigned.\n");
+               result = -EPERM;
+               goto out_close_sysfs;
+       }
+
+       st->trigger.irq = client->irq;
+       if (INV_MPU3050 == st->chip_type)
+               result = request_threaded_irq(client->irq, inv_irq_handler,
+                                             inv_read_fifo_mpu3050,
+                                             IRQF_TRIGGER_RISING |
+                                             IRQF_SHARED, "inv_irq", st);
+       else
+               result = request_threaded_irq(client->irq, inv_irq_handler,
+                                             inv_read_fifo,
+                                             IRQF_TRIGGER_RISING |
+                                             IRQF_SHARED, "inv_irq", st);
+       if (result)
+               goto out_close_sysfs;
+
+       spin_lock_init(&st->time_stamp_lock);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       atomic_set(&st->early_suspend_enable, 1);
+       st->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+       st->early_suspend.suspend = inv_early_suspend;
+       st->early_suspend.resume = inv_late_resume;
+       register_early_suspend(&st->early_suspend);
+#endif
+
+       result = inv_create_input(st, client);
+       if (result) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+               unregister_early_suspend(&st->early_suspend);
+#endif
+               free_irq(client->irq, st);
+               goto out_close_sysfs;
+       }
+
+       pr_info("%s: Probe name %s\n", __func__, id->name);
+       dev_info(&client->adapter->dev, "%s is ready to go!\n", st->hw->name);
+       return 0;
+
+out_close_sysfs:
+       remove_sysfs_interfaces(st);
+out_free_kfifo:
+       kfifo_free(&st->trigger.timestamps);
+out_free:
+       result = inv_i2c_single_write(st, st->reg->pwr_mgmt_1, BIT_RESET);
+       kfree(st);
+out_no_free:
+       dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+       return -EIO;
+}
+
+static int inv_mod_remove(struct i2c_client *client)
+{
+       int result;
+       struct inv_gyro_state_s *st = i2c_get_clientdata(client);
+
+       result = inv_set_power_state(st, 0);
+       if (result)
+               dev_err(&client->adapter->dev, "%s could not be turned off.\n",
+                       st->hw->name);
+       remove_sysfs_interfaces(st);
+       result = inv_i2c_single_write(st, st->reg->pwr_mgmt_1, BIT_RESET);
+       kfifo_free(&st->trigger.timestamps);
+       free_irq(client->irq, st);
+       input_unregister_device(st->idev);
+       if (INV_ITG3500 != st->chip_type)
+               input_unregister_device(st->idev_dmp);
+       if (st->has_compass)
+               input_unregister_device(st->idev_compass);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       unregister_early_suspend(&st->early_suspend);
+#endif
+       kfree(st);
+       dev_info(&client->adapter->dev, "Gyro module removed.\n");
+       return 0;
+}
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static struct i2c_device_id inv_mod_id[] = {
+       {"itg3500", 0},
+       {"mpu3050", 0},
+       {"mpu6050", 0},
+       {"mpu9150", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mod_id);
+
+static struct i2c_driver inv_mod_driver = {
+       .class = I2C_CLASS_HWMON,
+       .probe          =       inv_mod_probe,
+       .remove         =       inv_mod_remove,
+       .id_table       =       inv_mod_id,
+       .driver = {
+               .owner  =       THIS_MODULE,
+               .name   =       "inv_dev",
+       },
+       .address_list = normal_i2c,
+};
+
+static int __init inv_mod_init(void)
+{
+       int result = i2c_add_driver(&inv_mod_driver);
+
+       if (result) {
+               pr_err("%s failed\n", __func__);
+               return result;
+       }
+
+       return 0;
+}
+
+static void __exit inv_mod_exit(void)
+{
+       i2c_del_driver(&inv_mod_driver);
+}
+
+module_init(inv_mod_init);
+module_exit(inv_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv_dev");
+
diff --git a/drivers/input/misc/mpu/inv_gyro.h b/drivers/input/misc/mpu/inv_gyro.h
new file mode 100644 (file)
index 0000000..acc7b18
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup DRIVERS
+ *  @brief      Hardware drivers.
+ *
+ *  @{
+ *      @file  inv_gyro.h
+ *      @brief Struct definitions for the Invensense gyro driver.
+ */
+
+#ifndef _INV_GYRO_H_
+#define _INV_GYRO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include "dmpKey.h"
+/**
+ *  struct inv_reg_map_s - Notable slave registers.
+ *  @who_am_i:         Upper 6 bits of the device's slave address.
+ *  @sample_rate_div:  Divider applied to gyro output rate.
+ *  @lpf:              Configures internal LPF.
+ *  @product_id:       Product revision.
+ *  @bank_sel:         Selects between memory banks.
+ *  @user_ctrl:                Enables/resets the FIFO.
+ *  @fifo_en:          Determines which data will appear in FIFO.
+ *  @gyro_config:      gyro config register.
+ *  @accl_config:      accel config register
+ *  @fifo_count_h:     Upper byte of FIFO count.
+ *  @fifo_r_w:         FIFO register.
+ *  @raw_gyro          Address of first gyro register.
+ *  @raw_accl          Address of first accel register.
+ *  @temperature       temperature register
+ *  @int_enable:       Interrupt enable register.
+ *  @int_status:       Interrupt flags.
+ *  @pwr_mgmt_1:       Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:       Controls power state of individual sensors.
+ *  @mem_start_addr:   Address of first memory read.
+ *  @mem_r_w:          Access to memory.
+ *  @prgm_strt_addrh   firmware program start address register
+ */
+struct inv_reg_map_s {
+       unsigned char who_am_i;
+       unsigned char sample_rate_div;
+       unsigned char lpf;
+       unsigned char product_id;
+       unsigned char bank_sel;
+       unsigned char user_ctrl;
+       unsigned char fifo_en;
+       unsigned char gyro_config;
+       unsigned char accl_config;
+       unsigned char fifo_count_h;
+       unsigned char fifo_r_w;
+       unsigned char raw_gyro;
+       unsigned char raw_accl;
+       unsigned char temperature;
+       unsigned char int_enable;
+       unsigned char int_status;
+       unsigned char pwr_mgmt_1;
+       unsigned char pwr_mgmt_2;
+       unsigned char mem_start_addr;
+       unsigned char mem_r_w;
+       unsigned char prgm_strt_addrh;
+};
+
+enum inv_devices {
+       INV_ITG3500 = 0,
+       INV_MPU3050 = 1,
+       INV_MPU6050 = 2,
+       INV_MPU9150 = 3,
+       INV_NUM_PARTS
+};
+
+/**
+ *  struct test_setup_t - set up parameters for self test.
+ *  @sample_rate: sensitity for gyro.
+ *  @sample_rate: sample rate, i.e, fifo rate.
+ *  @lpf:      low pass filter.
+ *  @fsr:      full scale range.
+ *  @accl_fs:  accel full scale range.
+ *  @accl_sens:        accel sensitivity
+ */
+struct test_setup_t {
+       int gyro_sens;
+       int sample_rate;
+       int lpf;
+       int fsr;
+       int accl_fs;
+       unsigned int accl_sens[3];
+};
+
+/**
+ *  struct inv_hw_s - Other important hardware information.
+ *  @num_reg:  Number of registers on device.
+ *  @name:      name of the chip
+ */
+struct inv_hw_s {
+       unsigned char num_reg;
+       unsigned char *name;
+};
+
+/**
+ *  struct inv_chip_config_s - Cached chip configuration data.
+ *  @fsr:              Full scale range.
+ *  @lpf:              Digital low pass filter frequency.
+ *  @clk_src:          Clock source.
+ *  @accl_fs:          accel full scale range.
+ *  @fifo_rate:                FIFO update rate.
+ *  @enable:           master enable to enable output
+ *  @accl_enable:      enable accel functionality
+ *  @accl_fifo_enable: enable accel data output
+ *  @gyro_enable:      enable gyro functionality
+ *  @gyro_fifo_enable: enable gyro data output
+ *  @compass_enable:   enable compass
+ *  @is_asleep:                1 if chip is powered down.
+ *  @dmp_on:           dmp is on/off
+ *  @firmware_loaded:  flag indicate firmware loaded or not.
+ *  @lpa_mod:          low power mode
+ *  @lpa_freq:          low power accel frequency.
+ *  @prog_start_addr:  firmware program start address
+ */
+struct inv_chip_config_s {
+       unsigned char fsr;
+       unsigned char lpf;
+       unsigned char clk_src;
+       unsigned char accl_fs;
+       unsigned short fifo_rate;
+       unsigned char enable;
+       unsigned char accl_enable;
+       unsigned char accl_fifo_enable;
+       unsigned char gyro_enable;
+       unsigned char gyro_fifo_enable;
+       unsigned char compass_enable;
+       unsigned char is_asleep;
+       unsigned char dmp_on;
+       unsigned char firmware_loaded;
+       unsigned char lpa_mode;
+       unsigned char lpa_freq;
+       unsigned int  prog_start_addr;
+};
+
+/**
+ *  struct inv_chip_info_s - Chip related information.
+ *  @product_id:       Product id.
+ *  @product_revision: Product revision.
+ *  @silicon_revision: Silicon revision.
+ *  @software_revision:        software revision.
+ *  @multi:            accel specific multiplier.
+ *  @compass_sens:     compass sensitivity.
+ *  @gyro_sens_trim:   Gyro sensitivity trim factor.
+ *  @accl_sens_trim:    accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+       unsigned char product_id;
+       unsigned char product_revision;
+       unsigned char silicon_revision;
+       unsigned char software_revision;
+       unsigned char multi;
+       unsigned char compass_sens[3];
+       unsigned long gyro_sens_trim;
+       unsigned long accl_sens_trim;
+};
+
+/**
+ *  struct inv_trigger_s - Variables passed between interrupt and kernel space.
+ *  @irq:              Interrupt number.
+ *  @timestamps:       Timestamp buffer.
+ */
+struct inv_trigger_s {
+#define TIMESTAMP_FIFO_SIZE 16
+       unsigned long irq;
+       DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
+};
+
+/**
+ *  struct inv_flick_s structure to store flick data.
+ *  @lower:    lower bound of flick.
+ *  @upper:     upper bound of flick.
+ *  @counter:  counterof flick.
+ *  @int_on:   interrupt on for flick.
+ *  @msg_on;    message to carry flick
+ *  @axis:      axis of flick
+ */
+struct inv_flick_s {
+       int lower;
+       int upper;
+       int counter;
+       char int_on;
+       char msg_on;
+       char axis;
+};
+
+/**
+ *  struct inv_tap_s structure to store tap data.
+ *  @tap_on:   tap on
+ *  @min_taps:  minimut taps counted.
+ *  @thresh:    tap threshold.
+ *  @time:     tap time.
+ */
+struct inv_tap_s {
+       char tap_on;
+       char min_tap;
+       short thresh;
+       short time;
+};
+
+struct inv_mpu_slave;
+/**
+ *  struct inv_gyro_state_s - Driver state variables.
+ *  @chip_config:      Cached attribute information.
+ *  @chip_info:                Chip information from read-only registers.
+ *  @flick:            flick data structure
+ *  @reg:              Map of important registers.
+ *  @hw:               Other hardware-specific information.
+ *  @idev:             Handle to input device.
+ *  @idev_dmp:         Handle to input device for DMP.
+ *  @idev_compass:     Handle to input device for compass.
+ *  @chip_type:                chip type.
+ *  @time_stamp_lock:  spin lock to time stamp.
+ *  @inv_class:                store class handle.
+ *  @inv_dev:          store device handle.
+ *  @i2c:              i2c client handle.
+ *  @plat_data:                platform data.
+ *  @mpu_slave:                mpu slave handle.
+ *  @fifo_counter:     MPU3050 specific work around.
+ *  @has_compass:      has compass or not.
+ *  @compass_scale:    compass scale.
+ *  @i2c_addr:         i2c address.
+ *  @compass_divider:  slow down compass rate.
+ *  @compass_counter:  slow down compass rate.
+ *  @sample_divider:    sample divider for dmp.
+ *  @fifo_divider:      fifo divider for dmp.
+ *  @sl_handle:                Handle to I2C port.
+ *  @irq_dur_us:       duration between each irq.
+ *  @last_isr_time:    last isr time.
+ *  @early_suspend:     struct for early suspend.
+ *  @early_suspend_enable: sysfs interface to store current early_suspend.
+ */
+struct inv_gyro_state_s {
+       struct inv_chip_config_s chip_config;
+       struct inv_chip_info_s chip_info;
+       struct inv_flick_s flick;
+       struct inv_tap_s   tap;
+       struct inv_reg_map_s *reg;
+       struct inv_hw_s *hw;
+       struct inv_trigger_s trigger;
+       struct input_dev *idev;
+       struct input_dev *idev_dmp;
+       struct input_dev *idev_compass;
+       enum   inv_devices chip_type;
+       spinlock_t time_stamp_lock;
+       struct class *inv_class;
+       struct device   *inv_dev;
+       struct i2c_client *i2c;
+       struct mpu_platform_data plat_data;
+       struct inv_mpu_slave *mpu_slave;
+       short compass_st_upper[3];
+       short compass_st_lower[3];
+       unsigned char fifo_counter;
+       unsigned char has_compass;
+       unsigned char compass_scale;
+       unsigned char i2c_addr;
+       unsigned char compass_divider;
+       unsigned char compass_counter;
+       unsigned char sample_divider;
+       unsigned char fifo_divider;
+       void *sl_handle;
+       unsigned int irq_dur_us;
+       long long last_isr_time;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       struct early_suspend early_suspend;
+       atomic_t early_suspend_enable;
+#endif
+};
+/* produces an unique identifier for each device based on the
+   combination of product version and product revision */
+struct prod_rev_map_t {
+       unsigned short mpl_product_key;
+       unsigned char silicon_rev;
+       unsigned short gyro_trim;
+       unsigned short accel_trim;
+};
+/**
+ *  struct inv_mpu_slave - MPU slave structure.
+ *  @suspend:          suspend operation.
+ *  @resume:           resume operation.
+ *  @setup:            setup chip. initialization.
+ *  @create_sysfs:     create chip specific sysfs entries.
+ *  @remove_sysfs:     remove chip specific sysfs entries.
+ *  @combine_data:     combine raw data into meaningful data.
+ *  @get_mode:         get current chip mode.
+ */
+struct inv_mpu_slave {
+       int (*suspend)(struct inv_gyro_state_s *);
+       int (*resume)(struct inv_gyro_state_s *);
+       int (*setup)(struct inv_gyro_state_s *);
+       int (*combine_data)(unsigned char *in, short *out);
+       int (*get_mode)(struct inv_gyro_state_s *);
+       int (*set_lpf)(struct inv_gyro_state_s *, int rate);
+       int (*set_fs)(struct inv_gyro_state_s *, int fs);
+};
+/* AKM definitions */
+#define REG_AKM_ID              (0x00)
+#define REG_AKM_STATUS          (0x02)
+#define REG_AKM_MEASURE_DATA    (0x03)
+#define REG_AKM_MODE            (0x0A)
+#define REG_AKM_ST_CTRL         (0x0C)
+#define REG_AKM_SENSITIVITY     (0x10)
+#define REG_AKM8963_CNTL1       (0x0A)
+
+#define DATA_AKM_ID             (0x48)
+#define DATA_AKM_MODE_PW_DN     (0x00)
+#define DATA_AKM_MODE_PW_SM     (0x01)
+#define DATA_AKM_MODE_PW_ST     (0x08)
+#define DATA_AKM_MODE_PW_FR     (0x0F)
+#define DATA_AKM_SELF_TEST      (0x40)
+#define DATA_AKM_DRDY           (0x01)
+#define DATA_AKM8963_BIT        (0x10)
+#define DATA_AKM_STAT_MASK      (0x0C)
+
+#define DATA_AKM8975_SCALE      (9830)
+#define DATA_AKM8972_SCALE      (19661)
+#define DATA_AKM8963_SCALE0     (19661)
+#define DATA_AKM8963_SCALE1     (4915)
+
+#define DATA_AKM8975_ST_X_UP    (100)
+#define DATA_AKM8975_ST_X_LW    (-100)
+#define DATA_AKM8975_ST_Y_UP    (100)
+#define DATA_AKM8975_ST_Y_LW    (-100)
+#define DATA_AKM8975_ST_Z_UP    (-300)
+#define DATA_AKM8975_ST_Z_LW    (-1000)
+
+/*need to verify the range for 8972 */
+#define DATA_AKM8972_ST_X_UP    (50)
+#define DATA_AKM8972_ST_X_LW    (-50)
+#define DATA_AKM8972_ST_Y_UP    (50)
+#define DATA_AKM8972_ST_Y_LW    (-50)
+#define DATA_AKM8972_ST_Z_UP    (-100)
+#define DATA_AKM8972_ST_Z_LW    (-500)
+
+#define DATA_AKM8963_ST_X_UP    (200)
+#define DATA_AKM8963_ST_X_LW    (-200)
+#define DATA_AKM8963_ST_Y_UP    (200)
+#define DATA_AKM8963_ST_Y_LW    (-200)
+#define DATA_AKM8963_ST_Z_UP    (-800)
+#define DATA_AKM8963_ST_Z_LW    (-3200)
+
+
+/* register definition*/
+#define REG_3050_AUX_VDDIO      (0x13)
+#define REG_3050_SLAVE_ADDR     (0x14)
+#define REG_3050_SLAVE_REG      (0x18)
+#define REG_3050_AUX_XOUT_H     (0x23)
+
+#define REG_3500_OTP            (0x00)
+
+#define REG_ST_GCT_X            (0x0D)
+#define REG_I2C_MST_CTRL        (0x24)
+#define REG_I2C_SLV0_ADDR       (0x25)
+#define REG_I2C_SLV0_REG        (0x26)
+#define REG_I2C_SLV0_CTRL       (0x27)
+#define REG_I2C_SLV1_ADDR       (0x28)
+#define REG_I2C_SLV1_REG        (0x29)
+#define REG_I2C_SLV1_CTRL       (0x2A)
+
+#define REG_I2C_SLV4_CTRL       (0x34)
+#define REG_INT_PIN_CFG         (0x37)
+#define REG_DMP_INT_STATUS      (0x39)
+#define REG_EXT_SENS_DATA_00    (0x49)
+#define REG_I2C_SLV1_DO         (0x64)
+#define REG_I2C_MST_DELAY_CTRL  (0x67)
+#define REG_BANK_SEL            (0x6D)
+#define REG_MEM_START           (0x6E)
+#define REG_MEM_RW              (0x6F)
+
+/* bit definitions */
+#define BIT_3050_VDDIO          (0x04)
+#define BIT_3050_AUX_IF_EN      (0x20)
+#define BIT_3050_AUX_IF_RST     (0x08)
+#define BIT_3050_FIFO_RST       (0x02)
+
+#define BIT_BYPASS_EN           (0x2)
+#define BIT_WAIT_FOR_ES         (0x40)
+#define BIT_I2C_READ            (0x80)
+#define BIT_SLV_EN              (0x80)
+
+#define BIT_DMP_EN              (0x80)
+#define BIT_FIFO_EN            (0x40)
+#define BIT_I2C_MST_EN          (0x20)
+#define BIT_DMP_RST             (0x08)
+#define BIT_FIFO_RST           (0x04)
+#define BIT_I2C_MST_RST         (0x02)
+
+#define BIT_SLV0_DLY_EN         (0x01)
+#define BIT_SLV1_DLY_EN         (0x02)
+
+#define BIT_FIFO_OVERFLOW      (0x10)
+#define BIT_DATA_RDY_EN                (0x01)
+#define BIT_DMP_INT_EN          (0x02)
+
+#define BIT_PWR_ACCL_STBY       (0x38)
+#define BIT_PWR_GYRO_STBY       (0x07)
+
+#define BIT_GYRO_XOUT          (0x40)
+#define BIT_GYRO_YOUT          (0x20)
+#define BIT_GYRO_ZOUT          (0x10)
+#define BIT_ACCEL_OUT          (0x08)
+#define BITS_GYRO_OUT          (0x70)
+#define BITS_SELF_TEST_EN       (0xE0)
+#define BITS_3050_ACCL_OUT     (0x0E)
+#define BITS_3050_POWER1        (0x30)
+#define BITS_3050_POWER2        (0x10)
+#define BITS_3050_GYRO_STANDBY  (0x38)
+#define BITS_FSR               (0x18)
+#define BITS_LPF               (0x07)
+#define BITS_CLK               (0x07)
+#define BIT_3500_FIFO_OVERFLOW (0x10)
+#define BIT_RESET               (0x80)
+#define BIT_SLEEP              (0x40)
+#define BIT_CYCLE               (0x20)
+#define BIT_LPA_FREQ            (0xC0)
+
+#define DMP_START_ADDR          (0x400)
+#define BYTES_FOR_DMP           (16)
+#define BYTES_PER_SENSOR        (6)
+#define POWER_UP_TIME           (40)
+#define MPU_MEM_BANK_SIZE        (256)
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2              1       /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1              2       /* MPU6050B1 Device */
+
+#define BIT_PRFTCH_EN                           0x40
+#define BIT_CFG_USER_BANK                       0x20
+#define BITS_MEM_SEL                            0x1f
+/* time stamp tolerance */
+#define TIME_STAMP_TOR           (5)
+#define MAX_CATCH_UP             (5)
+#define DEFAULT_ACCL_TRIM        (16384)
+#define MAX_FIFO_RATE            (1000)
+#define MIN_FIFO_RATE            (4)
+#define ONE_K_HZ                 (1000)
+
+/* authenticate key */
+#define D_AUTH_OUT               (32)
+#define D_AUTH_IN                (36)
+#define D_AUTH_A                 (40)
+#define D_AUTH_B                 (44)
+
+/* flick related defines */
+#define DATA_INT            (2097)
+#define DATA_MSG_ON         (262144)
+
+/*tap related defines */
+#define INV_TAP                               0x08
+#define INV_NUM_TAP_AXES (3)
+
+#define INV_TAP_AXIS_X_POS                    0x20
+#define INV_TAP_AXIS_X_NEG                    0x10
+#define INV_TAP_AXIS_Y_POS                    0x08
+#define INV_TAP_AXIS_Y_NEG                    0x04
+#define INV_TAP_AXIS_Z_POS                    0x02
+#define INV_TAP_AXIS_Z_NEG                    0x01
+#define INV_TAP_ALL_DIRECTIONS                0x3f
+
+#define INV_TAP_AXIS_X                        0x1
+#define INV_TAP_AXIS_Y                        0x2
+#define INV_TAP_AXIS_Z                        0x4
+
+#define INV_TAP_AXIS_ALL                      \
+               (INV_TAP_AXIS_X            |   \
+               INV_TAP_AXIS_Y             |   \
+               INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP    0x01
+#define INT_SRC_ORIENT 0x02
+
+/*orientation related */
+#define INV_X_UP                          0x01
+#define INV_X_DOWN                        0x02
+#define INV_Y_UP                          0x04
+#define INV_Y_DOWN                        0x08
+#define INV_Z_UP                          0x10
+#define INV_Z_DOWN                        0x20
+#define INV_ORIENTATION_ALL               0x3F
+
+#define INV_ORIENTATION_FLIP              0x40
+#define INV_X_AXIS_INDEX                 (0x00)
+#define INV_Y_AXIS_INDEX                 (0x01)
+#define INV_Z_AXIS_INDEX                 (0x02)
+
+#define INV_ELEMENT_1                    (0x0001)
+#define INV_ELEMENT_2                    (0x0002)
+#define INV_ELEMENT_3                    (0x0004)
+#define INV_ELEMENT_4                    (0x0008)
+#define INV_ELEMENT_5                    (0x0010)
+#define INV_ELEMENT_6                    (0x0020)
+#define INV_ELEMENT_7                    (0x0040)
+#define INV_ELEMENT_8                    (0x0080)
+#define INV_ALL                          (0xFFFF)
+#define INV_ELEMENT_MASK                 (0x00FF)
+#define INV_GYRO_ACC_MASK                (0x007E)
+
+enum inv_filter_e {
+       INV_FILTER_256HZ_NOLPF2 = 0,
+       INV_FILTER_188HZ,
+       INV_FILTER_98HZ,
+       INV_FILTER_42HZ,
+       INV_FILTER_20HZ,
+       INV_FILTER_10HZ,
+       INV_FILTER_5HZ,
+       INV_FILTER_2100HZ_NOLPF,
+       NUM_FILTER
+};
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+       MEM_RAM_BANK_0 = 0,
+       MEM_RAM_BANK_1,
+       MEM_RAM_BANK_2,
+       MEM_RAM_BANK_3,
+       MEM_RAM_BANK_4,
+       MEM_RAM_BANK_5,
+       MEM_RAM_BANK_6,
+       MEM_RAM_BANK_7,
+       MEM_RAM_BANK_8,
+       MEM_RAM_BANK_9,
+       MEM_RAM_BANK_10,
+       MEM_RAM_BANK_11,
+       MPU_MEM_NUM_RAM_BANKS,
+       MPU_MEM_OTP_BANK_0 = 16
+};
+
+enum inv_fsr_e {
+       INV_FSR_250DPS = 0,
+       INV_FSR_500DPS,
+       INV_FSR_1000DPS,
+       INV_FSR_2000DPS,
+       NUM_FSR
+};
+enum inv_accl_fs_e {
+       INV_FS_02G = 0,
+       INV_FS_04G,
+       INV_FS_08G,
+       INV_FS_16G,
+       NUM_ACCL_FSR
+};
+
+enum inv_clock_sel_e {
+       INV_CLK_INTERNAL = 0,
+       INV_CLK_PLL,
+       NUM_CLK
+};
+
+int inv_hw_self_test(struct inv_gyro_state_s *st, int *gyro_bias_regular);
+int inv_get_silicon_rev_mpu6050(struct inv_gyro_state_s *st);
+int inv_i2c_read_base(struct inv_gyro_state_s *st, unsigned short i2c_addr,
+       unsigned char reg, unsigned short length, unsigned char *data);
+int inv_i2c_single_write_base(struct inv_gyro_state_s *st,
+       unsigned short i2c_addr, unsigned char reg, unsigned char data);
+#define inv_i2c_read(st, reg, len, data) \
+       inv_i2c_read_base(st, st->i2c_addr, reg, len, data)
+#define inv_i2c_single_write(st, reg, data) \
+       inv_i2c_single_write_base(st, st->i2c_addr, reg, data)
+#define inv_secondary_read(reg, len, data) \
+       inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data)
+#define inv_secondary_write(reg, data) \
+       inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \
+               reg, data)
+int inv_set_power_state(struct inv_gyro_state_s *st, unsigned char power_on);
+int set_inv_enable(struct inv_gyro_state_s *st, unsigned long enable);
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+                           unsigned char mpu_addr,
+                           unsigned short mem_addr,
+                           unsigned int len, unsigned char const *data);
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+                          unsigned char mpu_addr,
+                          unsigned short mem_addr,
+                          unsigned int len, unsigned char *data);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_init_config_mpu3050(struct inv_gyro_state_s *st);
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id);
+int inv_reset_fifo(struct inv_gyro_state_s *st);
+void inv_clear_kfifo(struct inv_gyro_state_s *st);
+int inv_setup_mpu3050(struct inv_gyro_state_s *st);
+int inv_register_kxtf9_slave(struct inv_gyro_state_s *st);
+int create_device_attributes(struct device *dev,
+       struct device_attribute **attrs);
+void remove_device_attributes(struct device *dev,
+       struct device_attribute **attrs);
+int set_3050_bypass(struct inv_gyro_state_s *st, int enable);
+inline s64 get_time_ns(void);
+int inv_mpu3050_create_sysfs(struct inv_gyro_state_s *st);
+int inv_mpu3050_remove_sysfs(struct inv_gyro_state_s *st);
+int inv_get_accl_bias(struct inv_gyro_state_s *st, int *accl_bias_regular);
+int set_power_mpu3050(struct inv_gyro_state_s *st, unsigned char power_on);
+int reset_fifo_mpu3050(struct inv_gyro_state_s *st);
+int inv_enable_tap_dmp(struct inv_gyro_state_s *st, unsigned char on);
+int inv_enable_orientation_dmp(struct inv_gyro_state_s *st);
+unsigned short inv_dmp_get_address(unsigned short key);
+ssize_t inv_gyro_fifo_enable_show(struct device *dev,
+       struct device_attribute *attr, char *buf);
+ssize_t inv_gyro_fifo_enable_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count);
+ssize_t inv_gyro_enable_show(struct device *dev,
+       struct device_attribute *attr, char *buf);
+ssize_t inv_gyro_enable_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count);
+ssize_t inv_accl_enable_show(struct device *dev,
+       struct device_attribute *attr, char *buf);
+ssize_t inv_accl_fifo_enable_show(struct device *dev,
+       struct device_attribute *attr, char *buf);
+ssize_t inv_accl_fs_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count);
+ssize_t inv_accl_fs_show(struct device *dev,
+               struct device_attribute *attr, char *buf);
+ssize_t inv_accl_matrix_show(struct device *dev,
+       struct device_attribute *attr, char *buf);
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t pos, size_t size);
+
+ssize_t inv_dmp_firmware_read(struct file *filp, struct kobject *kobj,
+                             struct bin_attribute *bin_attr, char *buf,
+                             loff_t off, size_t count);
+
+#define mem_w(a, b, c) mpu_memory_write(st->sl_handle, \
+                       st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle, \
+                       st->i2c_addr, inv_dmp_get_address(key), b, c)
+
+#endif  /* #ifndef _INV_GYRO_H_ */
+
diff --git a/drivers/input/misc/mpu/inv_gyro_misc.c b/drivers/input/misc/mpu/inv_gyro_misc.c
new file mode 100644 (file)
index 0000000..80bc3ca
--- /dev/null
@@ -0,0 +1,1608 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_gyro_misc.c
+ *      @brief   A sysfs device driver for Invensense gyroscopes.
+ *      @details This file is part of inv_gyro driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+
+#include "inv_gyro.h"
+
+/*
+    Defines
+*/
+
+/*--- Test parameters defaults --- */
+
+#define DEF_OLDEST_SUPP_PROD_REV    (8)
+#define DEF_OLDEST_SUPP_SW_REV      (2)
+
+/* sample rate */
+#define DEF_SELFTEST_SAMPLE_RATE             (0)
+/* LPF parameter */
+#define DEF_SELFTEST_LPF_PARA                (1)
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FULL_SCALE         (0 << 3)
+#define DEF_SELFTEST_ACCL_FULL_SCALE         (2 << 3)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME          (50)
+#define DEF_GYRO_PACKET_THRESH      DEF_GYRO_WAIT_TIME
+#define DEF_GYRO_THRESH             (10)
+#define DEF_GYRO_PRECISION          (1000)
+#define X                           0
+#define Y                           1
+#define Z                           2
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5      105
+#define MPU_PRODUCT_KEY_B2_F1        431
+/* accelerometer Hw self test min and max bias shift (mg) */
+#define DEF_ACCEL_ST_SHIFT_MIN      (300)
+#define DEF_ACCEL_ST_SHIFT_MAX      (950)
+
+#define DEF_ACCEL_ST_SHIFT_DELTA    (140)
+#define DEF_GYRO_CT_SHIFT_DELTA     (140)
+/* gyroscope Coriolis self test min and max bias shift (dps) */
+#define DEF_GYRO_CT_SHIFT_MIN       (10)
+#define DEF_GYRO_CT_SHIFT_MAX       (105)
+
+static struct test_setup_t test_setup = {
+       .gyro_sens   = 32768 / 250,
+       .sample_rate = DEF_SELFTEST_SAMPLE_RATE,
+       .lpf         = DEF_SELFTEST_LPF_PARA,
+       .fsr         = DEF_SELFTEST_GYRO_FULL_SCALE,
+       .accl_fs     = DEF_SELFTEST_ACCL_FULL_SCALE
+};
+
+/* NOTE: product entries are in chronological order */
+static struct prod_rev_map_t prod_rev_map[] = {
+       /* prod_ver = 0 */
+       {MPL_PROD_KEY(0,   1), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   2), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   3), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   4), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   5), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   6), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/C2-1) */
+       /* prod_ver = 1, forced to 0 for MPU6050 A2 */
+       {MPL_PROD_KEY(0,   7), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   8), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,   9), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  10), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  11), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D2-1) */
+       {MPL_PROD_KEY(0,  12), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  13), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  14), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  15), MPU_SILICON_REV_A2, 131, 16384},
+       {MPL_PROD_KEY(0,  27), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D4)   */
+       /* prod_ver = 1 */
+       {MPL_PROD_KEY(1,  16), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-1) */
+       {MPL_PROD_KEY(1,  17), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-2) */
+       {MPL_PROD_KEY(1,  18), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-3) */
+       {MPL_PROD_KEY(1,  19), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-4) */
+       {MPL_PROD_KEY(1,  20), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-5) */
+       {MPL_PROD_KEY(1,  28), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D4)   */
+       {MPL_PROD_KEY(1,   1), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-1) */
+       {MPL_PROD_KEY(1,   2), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-2) */
+       {MPL_PROD_KEY(1,   3), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-3) */
+       {MPL_PROD_KEY(1,   4), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-4) */
+       {MPL_PROD_KEY(1,   5), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-5) */
+       {MPL_PROD_KEY(1,   6), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-6) */
+       /* prod_ver = 2 */
+       {MPL_PROD_KEY(2,   7), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-1) */
+       {MPL_PROD_KEY(2,   8), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-2) */
+       {MPL_PROD_KEY(2,   9), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-3) */
+       {MPL_PROD_KEY(2,  10), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-4) */
+       {MPL_PROD_KEY(2,  11), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-5) */
+       {MPL_PROD_KEY(2,  12), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-6) */
+       {MPL_PROD_KEY(2,  29), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/D4)   */
+       /* prod_ver = 3 */
+       {MPL_PROD_KEY(3,  30), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E2)   */
+       /* prod_ver = 4 */
+       {MPL_PROD_KEY(4,  31), MPU_SILICON_REV_B1, 131,  8192}, /* (B2/F1)   */
+       {MPL_PROD_KEY(4,   1), MPU_SILICON_REV_B1, 131,  8192}, /* (B3/F1)   */
+       {MPL_PROD_KEY(4,   3), MPU_SILICON_REV_B1, 131,  8192}, /* (B4/F1)   */
+       /* prod_ver = 5 */
+       {MPL_PROD_KEY(5,   3), MPU_SILICON_REV_B1, 131, 16384}, /* (B4/F1)   */
+       /* prod_ver = 6 */
+       {MPL_PROD_KEY(6,  19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2)   */
+       /* prod_ver = 7 */
+       {MPL_PROD_KEY(7,  19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2)   */
+       /* prod_ver = 8 */
+       {MPL_PROD_KEY(8,  19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2)   */
+       /* prod_ver = 9 */
+       {MPL_PROD_KEY(9,  19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2)   */
+       /* prod_ver = 10 */
+       {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}  /* (B5/E2)   */
+};
+
+/*
+   List of product software revisions
+
+   NOTE :
+   software revision 0 falls back to the old detection method
+   based off the product version and product revision per the
+   table above
+*/
+static struct prod_rev_map_t sw_rev_map[] = {
+       {0,                  0,   0,     0},
+       {1, MPU_SILICON_REV_B1, 131,  8192},    /* rev C */
+       {2, MPU_SILICON_REV_B1, 131, 16384}     /* rev D */
+};
+
+static int accl_st_tb[31] = {
+       340, 351, 363, 375, 388, 401, 414, 428,
+       443, 458, 473, 489, 506, 523, 541, 559,
+       578, 597, 617, 638, 660, 682, 705, 729,
+       753, 779, 805, 832, 860, 889, 919};
+
+static int gyro_6050_st_tb[31] = {
+       3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486,
+       4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429,
+       6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213,
+       9637, 10080, 10544, 11029, 11537, 12067, 12622};
+
+static int gyro_3500_st_tb[255] = {
+       2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+       2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+       3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+       3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+       3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+       3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+       4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+       4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+       4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+       5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+       5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+       6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+       6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+       7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+       7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+       8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+       9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+       10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+       10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+       11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+       12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+       13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+       15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+       16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+       17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+       19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+       20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+       22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+       24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+       26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+       28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+       30903, 31212, 31524, 31839, 32157, 32479, 32804};
+
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+                    unsigned char mpu_addr,
+                    unsigned short mem_addr,
+                    unsigned int len, unsigned char const *data)
+{
+       unsigned char bank[2];
+       unsigned char addr[2];
+       unsigned char buf[513];
+       struct i2c_msg msgs[3];
+       int res;
+
+       if (!data || !i2c_adap)
+               return -EINVAL;
+
+       if (len >= (sizeof(buf) - 1))
+               return -ENOMEM;
+
+       bank[0] = REG_BANK_SEL;
+       bank[1] = mem_addr >> 8;
+       addr[0] = REG_MEM_START;
+       addr[1] = mem_addr & 0xFF;
+       buf[0] = REG_MEM_RW;
+       memcpy(buf + 1, data, len);
+       /* write message */
+       msgs[0].addr = mpu_addr;
+       msgs[0].flags = 0;
+       msgs[0].buf = bank;
+       msgs[0].len = sizeof(bank);
+       msgs[1].addr = mpu_addr;
+       msgs[1].flags = 0;
+       msgs[1].buf = addr;
+       msgs[1].len = sizeof(addr);
+       msgs[2].addr = mpu_addr;
+       msgs[2].flags = 0;
+       msgs[2].buf = (unsigned char *)buf;
+       msgs[2].len = len + 1;
+       res = i2c_transfer(i2c_adap, msgs, 3);
+       if (res != 3) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       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 res;
+
+       if (!data || !i2c_adap)
+               return -EINVAL;
+
+       bank[0] = REG_BANK_SEL;
+       bank[1] = mem_addr >> 8;
+       addr[0] = REG_MEM_START;
+       addr[1] = mem_addr & 0xFF;
+       buf = REG_MEM_RW;
+       /* write message */
+       msgs[0].addr = mpu_addr;
+       msgs[0].flags = 0;
+       msgs[0].buf = bank;
+       msgs[0].len = sizeof(bank);
+       msgs[1].addr = mpu_addr;
+       msgs[1].flags = 0;
+       msgs[1].buf = addr;
+       msgs[1].len = sizeof(addr);
+       msgs[2].addr = mpu_addr;
+       msgs[2].flags = 0;
+       msgs[2].buf = &buf;
+       msgs[2].len = 1;
+       msgs[3].addr = mpu_addr;
+       msgs[3].flags = I2C_M_RD;
+       msgs[3].buf = data;
+       msgs[3].len = len;
+       res = i2c_transfer(i2c_adap, msgs, 4);
+       if (res != 4) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       return 0;
+}
+
+/**
+ *  @internal
+ *  @brief  Inverse lookup of the index of an MPL product key .
+ *  @param  key
+ *              the MPL product indentifier also referred to as 'key'.
+ *  @return the index position of the key in the array, -1 if not found.
+ */
+static short index_of_key(unsigned short key)
+{
+       int i;
+
+       for (i = 0; i < NUM_OF_PROD_REVS; i++)
+               if (prod_rev_map[i].mpl_product_key == key)
+                       return (short)i;
+
+       return -1;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_gyro_state_s *st)
+{
+       int result;
+       struct inv_reg_map_s *reg;
+       unsigned char prod_ver = 0x00, prod_rev = 0x00;
+       struct prod_rev_map_t *p_rev;
+       unsigned char bank =
+           (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+       unsigned short mem_addr = ((bank << 8) | 0x06);
+       unsigned short key;
+       unsigned char regs[5];
+       unsigned short sw_rev;
+       short index;
+       struct inv_chip_info_s *chip_info = &st->chip_info;
+
+       reg = st->reg;
+       result = inv_i2c_read(st, reg->product_id, 1, &prod_ver);
+       if (result)
+               return result;
+
+       prod_ver &= 0xf;
+       result = mpu_memory_read(st->sl_handle, st->i2c_addr, mem_addr,
+                       1, &prod_rev);
+       mdelay(100);
+       result = mpu_memory_read(st->sl_handle, st->i2c_addr, mem_addr,
+                       1, &prod_rev);
+       if (result)
+               return result;
+
+       prod_rev >>= 2;
+       /* clean the prefetch and cfg user bank bits */
+       result = inv_i2c_single_write(st, reg->bank_sel, 0);
+       if (result)
+               return result;
+
+       /* get the software-product version, read from XA_OFFS_L */
+       result = inv_i2c_read(st, 0x7, 5, regs);
+       if (result)
+               return result;
+
+       sw_rev = (regs[4] & 0x01) << 2 |        /* 0x0b, bit 0 */
+                (regs[2] & 0x01) << 1 |        /* 0x09, bit 0 */
+                (regs[0] & 0x01);              /* 0x07, bit 0 */
+       /* if 0, use the product key to determine the type of part */
+       if (sw_rev == 0) {
+               key = MPL_PROD_KEY(prod_ver, prod_rev);
+               if (key == 0)
+                       return -1;
+
+               index = index_of_key(key);
+               if (index == -1 || index >= NUM_OF_PROD_REVS)
+                       return -1;
+
+               /* check MPL is compiled for this device */
+               if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+                       return -1;
+
+               p_rev = &prod_rev_map[index];
+       /* if valid, use the software product key */
+       } else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+               p_rev = &sw_rev_map[sw_rev];
+       } else {
+               return -1;
+       }
+
+       chip_info->product_id = prod_ver;
+       chip_info->product_revision = prod_rev;
+       chip_info->silicon_revision = p_rev->silicon_rev;
+       chip_info->software_revision = sw_rev;
+       chip_info->gyro_sens_trim = p_rev->gyro_trim;
+       chip_info->accl_sens_trim = p_rev->accel_trim;
+       if (chip_info->accl_sens_trim == 0)
+               chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+       chip_info->multi = DEFAULT_ACCL_TRIM/chip_info->accl_sens_trim;
+       if (chip_info->multi != 1)
+               printk(KERN_ERR"multi is %d\n", chip_info->multi);
+       return result;
+}
+
+/**
+ *  @internal
+ *  @brief  read the accelerometer hardware self-test bias shift calculated
+ *          during final production test and stored in chip non-volatile memory.
+ *  @param  mlsl_handle
+ *              serial interface handle to allow serial communication with the
+ *              device, both gyro and accelerometer.
+ *  @param  ct_shift_prod
+ *              A pointer to an array of 3 float elements to hold the values
+ *              for production hardware self-test bias shifts returned to the
+ *              user.
+ *  @return INV_SUCCESS on success, or a non-zero error code otherwise.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_gyro_state_s *st,
+                                       int *st_prod)
+{
+       unsigned char regs[4];
+       unsigned char shift_code[3];
+       int result, i;
+       st_prod[0] = st_prod[1] = st_prod[2] = 0;
+
+       result = inv_i2c_read(st, 0x0d, 4, regs);
+       if (result)
+               return result;
+
+       if ((0 == regs[0])  && (0 == regs[1]) &&
+               (0 == regs[2]) && (0 == regs[3]))
+               return -1;
+
+       shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4);
+       shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2);
+       shift_code[Z] = ((regs[2] & 0xE0) >> 3) |  (regs[3] & 0x03);
+       for (i = 0; i < 3; i++) {
+               if (shift_code[i] != 0)
+                       st_prod[i] = test_setup.accl_sens[i]*
+                               accl_st_tb[shift_code[i] - 1];
+       }
+       return 0;
+}
+
+static int inv_check_accl_self_test(struct inv_gyro_state_s *st,
+       int *reg_avg, int *st_avg){
+       int gravity, reg_z_avg, g_z_sign, fs, j, ret_val;
+       int tmp1;
+       int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3];
+
+       if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+               st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+               return 0;
+
+       fs = 8000UL;    /* assume +/- 2 mg as typical */
+       g_z_sign = 1;
+       ret_val = 0;
+       test_setup.accl_sens[X] = (unsigned int)((1L << 15) * 1000 / fs);
+       test_setup.accl_sens[Y] = (unsigned int)((1L << 15) * 1000 / fs);
+       test_setup.accl_sens[Z] = (unsigned int)((1L << 15) * 1000 / fs);
+       if (MPL_PROD_KEY(st->chip_info.product_id,
+               st->chip_info.product_revision) ==
+               MPU_PRODUCT_KEY_B1_E1_5) {
+               /* half sensitivity Z accelerometer parts */
+               test_setup.accl_sens[Z] /= 2;
+       } else {
+               /* half sensitivity X, Y, Z accelerometer parts */
+               test_setup.accl_sens[X] /= st->chip_info.multi;
+               test_setup.accl_sens[Y] /= st->chip_info.multi;
+               test_setup.accl_sens[Z] /= st->chip_info.multi;
+       }
+       gravity = test_setup.accl_sens[Z];
+       reg_z_avg = reg_avg[Z] - g_z_sign * gravity * DEF_GYRO_PRECISION;
+       read_accel_hw_self_test_prod_shift(st, st_shift_prod);
+       for (j = 0; j < 3; j++) {
+               st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+               if (st_shift_prod[j]) {
+                       tmp1 = st_shift_prod[j]/1000;
+                       st_shift_ratio[j] = st_shift_cust[j]/tmp1
+                               - 1000;
+                       if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+                               ret_val |= 1 << j;
+                       if (st_shift_ratio[j] < -DEF_ACCEL_ST_SHIFT_DELTA)
+                               ret_val |= 1 << j;
+               } else {
+                       if (st_shift_cust[j] <
+                               DEF_ACCEL_ST_SHIFT_MIN*gravity)
+                               ret_val |= 1 << j;
+                       if (st_shift_cust[j] >
+                               DEF_ACCEL_ST_SHIFT_MAX*gravity)
+                               ret_val |= 1 << j;
+               }
+       }
+       return ret_val;
+}
+
+static int inv_check_3500_gyro_self_test(struct inv_gyro_state_s *st,
+       int *reg_avg, int *st_avg){
+       int result;
+       int gst[3], ret_val;
+       int gst_otp[3], i;
+       unsigned char st_code[3];
+
+       ret_val = 0;
+       for (i = 0; i < 3; i++)
+               gst[i] = st_avg[i] - reg_avg[i];
+       result = inv_i2c_read(st, REG_3500_OTP, 3, st_code);
+       if (result)
+               return result;
+
+       gst_otp[0] = gst_otp[1] = gst_otp[2] = 0;
+       for (i = 0; i < 3; i++) {
+               if (st_code[i] != 0)
+                       gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1];
+       }
+       for (i = 0; i < 3; i++) {
+               if (gst_otp[i] == 0) {
+                       if (abs(gst[i])*4 < 60*2*1000*131)
+                               ret_val |= (1<<i);
+               } else {
+                       if (abs(gst[i]/gst_otp[i] - 1000) > 140)
+                               ret_val |= (1<<i);
+               }
+       }
+       for (i = 0; i < 3; i++) {
+               if (abs(reg_avg[i])*4 > 20*2*1000*131)
+                       ret_val |= (1<<i);
+       }
+
+       return ret_val;
+}
+static int inv_check_6050_gyro_self_test(struct inv_gyro_state_s *st,
+                                        int *reg_avg, int *st_avg)
+{
+       int result;
+       int ret_val;
+       int ct_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+       unsigned char regs[3];
+
+       if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+               st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+               return 0;
+
+       ret_val = 0;
+       result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs);
+       regs[X] &= 0x1f;
+       regs[Y] &= 0x1f;
+       regs[Z] &= 0x1f;
+       for (i = 0; i < 3; i++) {
+               if (regs[i] != 0)
+                       ct_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+               else
+                       ct_shift_prod[i] = 0;
+       }
+       for (i = 0; i < 3; i++) {
+               st_shift_cust[i] = abs(reg_avg[i] - st_avg[i]);
+               if (ct_shift_prod[i]) {
+                       st_shift_ratio[i] = st_shift_cust[i]/
+                               ct_shift_prod[i] - 1000;
+                       if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+                               ret_val |= 1 << i;
+                       if (st_shift_ratio[i] < -DEF_GYRO_CT_SHIFT_DELTA)
+                               ret_val |= 1 << i;
+               } else {
+                       if (st_shift_cust[i] < 1000*DEF_GYRO_CT_SHIFT_MIN*
+                               test_setup.gyro_sens)
+                               ret_val |= 1 << i;
+                       if (st_shift_cust[i] > 1000*DEF_GYRO_CT_SHIFT_MAX*
+                               test_setup.gyro_sens)
+                               ret_val |= 1 << i;
+               }
+       }
+       for (i = 0; i < 3; i++) {
+               if (abs(reg_avg[i])*4 > 20*2*1000*131)
+                       ret_val |= (1<<i);
+       }
+       return ret_val;
+}
+
+/**
+ *  inv_do_test() - do the actual test of self testing
+ */
+static int inv_do_test(struct inv_gyro_state_s *st, int self_test_flag,
+                      int *gyro_result, int *accl_result)
+{
+       struct inv_reg_map_s *reg;
+       int result, i, packet_size;
+       unsigned char data[12], has_accl;
+       int fifo_count, packet_count, ind;
+
+       reg = st->reg;
+       has_accl = (st->chip_type != INV_ITG3500);
+       packet_size = 6 + 6 * has_accl;
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, INV_CLK_PLL);
+       if (result)
+               return result;
+
+       mdelay(POWER_UP_TIME);
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_2, 0);
+       if (result)
+               return result;
+
+       mdelay(POWER_UP_TIME);
+       result = inv_i2c_single_write(st, reg->int_enable, 0);
+       if (result)
+               return result;
+
+       /* disable the sensor output to FIFO */
+       result = inv_i2c_single_write(st, reg->fifo_en, 0);
+       if (result)
+               return result;
+
+       /* disable fifo reading */
+       result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+       if (result)
+               return result;
+
+       /* clear FIFO */
+       result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST);
+       if (result)
+               return result;
+
+       mdelay(15);
+       /* setup parameters */
+       result = inv_i2c_single_write(st, reg->lpf, test_setup.lpf);
+       if (result)
+               return result;
+
+       result = inv_i2c_single_write(st, reg->sample_rate_div,
+               test_setup.sample_rate);
+       if (result)
+               return result;
+
+       result = inv_i2c_single_write(st, reg->gyro_config,
+               self_test_flag | test_setup.fsr);
+       if (result)
+               return result;
+
+       if (has_accl) {
+               result = inv_i2c_single_write(st, reg->accl_config,
+                       self_test_flag | test_setup.accl_fs);
+               if (result)
+                       return result;
+       }
+
+       /*wait for the output to stable*/
+       if (self_test_flag)
+               mdelay(200);
+       /* enable FIFO reading */
+       result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+       if (result)
+               return result;
+
+       /* enable sensor output to FIFO */
+       result = inv_i2c_single_write(st, reg->fifo_en, BITS_GYRO_OUT
+               | (has_accl << 3));
+       if (result)
+               return result;
+
+       mdelay(DEF_GYRO_WAIT_TIME);
+       /* stop sending data to FIFO */
+       result = inv_i2c_single_write(st, reg->fifo_en, 0);
+       if (result)
+               return result;
+
+       result = inv_i2c_read(st, reg->fifo_count_h, 2, data);
+       if (result)
+               return result;
+
+       fifo_count = (data[0] << 8) + data[1];
+       packet_count = fifo_count / packet_size;
+       gyro_result[0] = gyro_result[1] = gyro_result[2] = 0;
+       accl_result[0] = accl_result[1] = accl_result[2] = 0;
+       if (abs(packet_count - DEF_GYRO_PACKET_THRESH)
+               <= DEF_GYRO_THRESH) {
+               for (i = 0; i < packet_count; i++) {
+                       /* getting FIFO data */
+                       result = inv_i2c_read(st, reg->fifo_r_w,
+                               packet_size, data);
+                       if (result)
+                               return result;
+
+                       ind = 0;
+                       if (has_accl) {
+                               accl_result[0] += (short)((data[ind] << 8)
+                                               | data[ind + 1]);
+                               accl_result[1] += (short)((data[ind + 2] << 8)
+                                               | data[ind + 3]);
+                               accl_result[2] += (short)((data[ind + 4] << 8)
+                                               | data[ind + 5]);
+                               ind += 6;
+                       }
+                       gyro_result[0] += (short)((data[ind] << 8) |
+                               data[ind + 1]);
+                       gyro_result[1] += (short)((data[ind + 2] << 8) |
+                               data[ind + 3]);
+                       gyro_result[2] += (short)((data[ind + 4] << 8) |
+                               data[ind + 5]);
+               }
+       } else {
+               return -EAGAIN;
+       }
+
+       gyro_result[0] = gyro_result[0] * DEF_GYRO_PRECISION / packet_count;
+       gyro_result[1] = gyro_result[1] * DEF_GYRO_PRECISION / packet_count;
+       gyro_result[2] = gyro_result[2] * DEF_GYRO_PRECISION / packet_count;
+       if (has_accl) {
+               accl_result[0] =
+                       accl_result[0] * DEF_GYRO_PRECISION / packet_count;
+               accl_result[1] =
+                       accl_result[1] * DEF_GYRO_PRECISION / packet_count;
+               accl_result[2] =
+                       accl_result[2] * DEF_GYRO_PRECISION / packet_count;
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_recover_setting() recover the old settings after everything is done
+ */
+static void inv_recover_setting(struct inv_gyro_state_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int data;
+
+       reg = st->reg;
+       set_inv_enable(st, st->chip_config.enable);
+       inv_i2c_single_write(st, reg->gyro_config, st->chip_config.fsr<<3);
+       inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+       data = ONE_K_HZ/st->chip_config.fifo_rate - 1;
+       inv_i2c_single_write(st, reg->sample_rate_div, data);
+       if (INV_ITG3500 != st->chip_type) {
+               inv_i2c_single_write(st, reg->accl_config,
+                       (st->chip_config.accl_fs << 3)|0);
+       }
+       if (st->chip_config.is_asleep)
+               inv_set_power_state(st, 0);
+       else
+               inv_set_power_state(st, 1);
+}
+
+static int inv_check_compass_self_test(struct inv_gyro_state_s *st)
+{
+       int result;
+       unsigned char data[6];
+       unsigned char counter, cntl;
+       short x, y, z;
+       unsigned char *sens;
+
+       sens = st->chip_info.compass_sens;
+       /*set to bypass mode */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG, BIT_BYPASS_EN);
+       if (result) {
+               inv_i2c_single_write(st, REG_INT_PIN_CFG, 0x0);
+               return result;
+       }
+
+       /*set to power down mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN);
+       if (result)
+               goto AKM_fail;
+
+       /*write 1 to ASTC register */
+       result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST);
+       if (result)
+               goto AKM_fail;
+
+       /*set self test mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_ST);
+       if (result)
+               goto AKM_fail;
+
+       counter = 10;
+       while (counter > 0) {
+               mdelay(10);
+               result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+               if (result)
+                       goto AKM_fail;
+
+               if (data[0] != DATA_AKM_DRDY)
+                       counter--;
+               else
+                       counter = 0;
+       }
+
+       if (data[0] != DATA_AKM_DRDY) {
+               result = -1;
+               goto AKM_fail;
+       }
+
+       result = inv_secondary_read(REG_AKM_MEASURE_DATA, 6, data);
+       if (result)
+               goto AKM_fail;
+
+       x = (short)((data[1]<<8) | data[0]);
+       y = (short)((data[3]<<8) | data[2]);
+       z = (short)((data[5]<<8) | data[4]);
+       x = ((x * (sens[0] + 128)) >> 8);
+       y = ((y * (sens[1] + 128)) >> 8);
+       z = ((z * (sens[2] + 128)) >> 8);
+       if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+               result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+               if (result)
+                       goto AKM_fail;
+
+               if (0 == (cntl & DATA_AKM8963_BIT)) {
+                       x <<= 2;
+                       y <<= 2;
+                       z <<= 2;
+               }
+       }
+
+       result = 1;
+       result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+       if (x > st->compass_st_upper[0] || x < st->compass_st_lower[0])
+               goto AKM_fail;
+
+       if (y > st->compass_st_upper[1] || y < st->compass_st_lower[1])
+               goto AKM_fail;
+
+       if (z > st->compass_st_upper[2] || z < st->compass_st_lower[2])
+               goto AKM_fail;
+
+       result = 0;
+AKM_fail:
+       /*write 0 to ASTC register */
+       result |= inv_secondary_write(REG_AKM_ST_CTRL, 0);
+       /*set to power down mode */
+       result |= inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN);
+       /*restore to non-bypass mode */
+       result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, 0x0);
+       return result;
+}
+
+/**
+ *  inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_gyro_state_s *st,
+                    int *gyro_bias_regular)
+{
+       int result;
+       int gyro_bias_st[3];
+       int accl_bias_st[3], accl_bias_regular[3];
+       int test_times;
+       char compass_result, accel_result, gyro_result;
+
+       compass_result = accel_result = gyro_result = 0;
+       test_times = 2;
+       while (test_times > 0) {
+               result = inv_do_test(st, 0,  gyro_bias_regular,
+                       accl_bias_regular);
+               if (result == -EAGAIN)
+                       test_times--;
+               else
+                       test_times = 0;
+       }
+       if (result)
+               goto test_fail;
+
+       test_times = 2;
+       while (test_times > 0) {
+               result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+                                       accl_bias_st);
+               if (result == -EAGAIN)
+                       test_times--;
+               else
+                       break;
+       }
+       if (result)
+               goto test_fail;
+
+       if (st->chip_type == INV_ITG3500) {
+               gyro_result = !inv_check_3500_gyro_self_test(st,
+                       gyro_bias_regular, gyro_bias_st);
+       } else {
+               if (st->has_compass)
+                       compass_result = !inv_check_compass_self_test(st);
+               accel_result = !inv_check_accl_self_test(st,
+                       accl_bias_regular, accl_bias_st);
+               gyro_result = !inv_check_6050_gyro_self_test(st,
+                       gyro_bias_regular, gyro_bias_st);
+       }
+test_fail:
+       inv_recover_setting(st);
+       return (compass_result<<2) | (accel_result<<1) | gyro_result;
+}
+
+/**
+ *  inv_get_accl_bias() - main function to do hardware self test
+ */
+int inv_get_accl_bias(struct inv_gyro_state_s *st, int *accl_bias_regular)
+{
+       int result;
+       int gyro_bias_regular[3];
+       int test_times;
+
+       test_times = 2;
+       while (test_times > 0) {
+               result = inv_do_test(st, 0,  gyro_bias_regular,
+                       accl_bias_regular);
+               if (result == -EAGAIN)
+                       test_times--;
+               else
+                       test_times = 0;
+       }
+       inv_recover_setting(st);
+       return result;
+}
+
+static int inv_load_firmware(struct inv_gyro_state_s *st,
+                            unsigned char *data, int size)
+{
+       int bank, write_size;
+       int result;
+       unsigned short memaddr;
+
+       /* Write and verify memory */
+       for (bank = 0; size > 0; bank++,
+               size -= write_size,
+               data += write_size) {
+               if (size > MPU_MEM_BANK_SIZE)
+                       write_size = MPU_MEM_BANK_SIZE;
+               else
+                       write_size = size;
+
+               memaddr = ((bank << 8) | 0x00);
+               result = mem_w(memaddr, write_size, data);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+static int inv_verify_firmware(struct inv_gyro_state_s *st,
+                              unsigned char *data, int size)
+{
+       int bank, write_size;
+       int result;
+       unsigned short memaddr;
+       unsigned char firmware[MPU_MEM_BANK_SIZE];
+
+       /* Write and verify memory */
+       for (bank = 0; size > 0; bank++,
+               size -= write_size,
+               data += write_size) {
+               if (size > MPU_MEM_BANK_SIZE)
+                       write_size = MPU_MEM_BANK_SIZE;
+               else
+                       write_size = size;
+               memaddr = ((bank << 8) | 0x00);
+               result = mpu_memory_read(st->sl_handle,
+                       st->i2c_addr, memaddr, write_size, firmware);
+               if (result)
+                       return result;
+
+               if (0 != memcmp(firmware, data, write_size))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int inv_set_fifo_div(struct inv_gyro_state_s *st,
+                           unsigned short fifoRate)
+{
+       unsigned char regs[2];
+       int result = 0;
+       /* For some reason DINAC4 is defined as 0xb8,
+          but DINBC4 is not defined*/
+       unsigned char regs_end[8] = {DINAFE, DINAF2, DINAAB,
+               0xC4, DINAAA, DINAF1, DINADF, DINADF};
+
+       regs[0] = (unsigned char)((fifoRate >> 8) & 0xff);
+       regs[1] = (unsigned char)(fifoRate & 0xff);
+       result = mem_w_key(KEY_D_0_22, 2, regs);
+       if (result)
+               return result;
+
+       /*Modify the FIFO handler to reset the tap/orient interrupt flags*/
+       /* each time the FIFO handler runs*/
+       result = mem_w_key(KEY_CFG_6, 8, regs_end);
+
+       return result;
+}
+
+int inv_set_fifo_rate(struct inv_gyro_state_s *st, unsigned long fifo_rate)
+{
+       unsigned char divider;
+       int result;
+
+       divider = (unsigned char)(1000/fifo_rate) - 1;
+       if (divider > 4) {
+               st->sample_divider = 4;
+               st->fifo_divider = (unsigned char)(200/fifo_rate) - 1;
+       } else {
+               st->sample_divider = divider;
+               st->fifo_divider = 0;
+       }
+       result = inv_set_fifo_div(st, st->fifo_divider);
+       return result;
+}
+
+static int inv_set_tap_interrupt_dmp(struct inv_gyro_state_s *st,
+                                    unsigned char on)
+{
+       int result;
+       unsigned char  regs[2] = {0};
+
+       if (on)
+               regs[0] = 0xf8;
+       else
+               regs[0] = DINAD8;
+       result = mem_w_key(KEY_CFG_20, 1, regs);
+       if (result)
+               return result;
+
+       return result;
+}
+
+static int inv_set_orientation_interrupt_dmp(struct inv_gyro_state_s *st,
+                                            unsigned char on)
+{
+       int result;
+       unsigned char  regs[2] = {0};
+
+       if (on) {
+               regs[0] = DINBF8;
+               regs[1] = DINBF8;
+       } else {
+               regs[0] = DINAD8;
+               regs[1] = DINAD8;
+       }
+       result = mem_w_key(KEY_CFG_ORIENT_IRQ_1, 2, regs);
+       if (result)
+               return result;
+
+       return result;
+}
+
+static int inv_set_tap_threshold_dmp(struct inv_gyro_state_s *st,
+                                    unsigned int axis,
+                                    unsigned short threshold)
+{
+       /* Sets the tap threshold in the dmp
+       Simultaneously sets secondary tap threshold to help correct the tap
+       direction for soft taps */
+       int result;
+       /* DMP Algorithm */
+       unsigned char data[2];
+       int sampleDivider;
+       int scaledThreshold;
+       unsigned int dmpThreshold;
+       unsigned char sample_div;
+#define accel_sens  (0x20000000/(0x00010000))
+
+       if ((axis & ~(INV_TAP_AXIS_ALL)) || (threshold > (1<<15)))
+               return -EINVAL;
+
+       sample_div = st->sample_divider;
+       sampleDivider = (1 + sample_div);
+       /* Scale factor corresponds linearly using
+       * 0  : 0
+       * 25 : 0.0250  g/ms
+       * 50 : 0.0500  g/ms
+       * 100: 1.0000  g/ms
+       * 200: 2.0000  g/ms
+       * 400: 4.0000  g/ms
+       * 800: 8.0000  g/ms
+       */
+       /*multiply by 1000 to avoid floating point 1000/1000*/
+       scaledThreshold = threshold;
+       /* Convert to per sample */
+       scaledThreshold *= sampleDivider;
+       /* Scale to DMP 16 bit value */
+       if (accel_sens != 0)
+               dmpThreshold = (unsigned int)(scaledThreshold*accel_sens);
+       else
+               return -EINVAL;
+
+       dmpThreshold = dmpThreshold/1000;
+       data[0] = dmpThreshold >> 8;
+       data[1] = dmpThreshold & 0xFF;
+       /* MPL algorithm */
+       if (axis & INV_TAP_AXIS_X) {
+               result = mem_w_key(KEY_DMP_TAP_THR_X, 2, data);
+               if (result)
+                       return result;
+
+               /*Also set additional threshold for correcting the direction
+               of taps that were very near the threshold. */
+               data[0] = (dmpThreshold*3/4) >> 8;
+               data[1] = (dmpThreshold*3/4) & 0xFF;
+               result = mem_w_key(KEY_D_1_36, 2, data);
+               if (result)
+                       return result;
+       }
+
+       if (axis & INV_TAP_AXIS_Y) {
+               result = mem_w_key(KEY_DMP_TAP_THR_Y, 2, data);
+               if (result)
+                       return result;
+
+               data[0] = (dmpThreshold*3/4) >> 8;
+               data[1] = (dmpThreshold*3/4) & 0xFF;
+               result = mem_w_key(KEY_D_1_40, 2, data);
+               if (result)
+                       return result;
+       }
+
+       if (axis & INV_TAP_AXIS_Z) {
+               result = mem_w_key(KEY_DMP_TAP_THR_Z, 2, data);
+               if (result)
+                       return result;
+
+               data[0] = (dmpThreshold*3/4) >> 8;
+               data[1] = (dmpThreshold*3/4) & 0xFF;
+               result = mem_w_key(KEY_D_1_44, 2, data);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+static int inv_set_tap_axes_dmp(struct inv_gyro_state_s *st,
+                               unsigned int axes)
+{
+       /* Sets a mask in the DMP that indicates what tap events
+       should result in an interrupt */
+       unsigned char regs[4];
+       unsigned char result;
+
+       /* check if any spurious bit other the ones expected are set */
+       if (axes & (~(INV_TAP_ALL_DIRECTIONS)))
+               return -EINVAL;
+
+       regs[0] = (unsigned char)axes;
+       result = mem_w_key(KEY_D_1_72, 1, regs);
+       return result;
+}
+
+static int inv_set_min_taps_dmp(struct inv_gyro_state_s *st,
+                               unsigned int min_taps) {
+       /*Indicates the minimum number of consecutive taps required
+               before the DMP will generate an interrupt */
+       unsigned char regs[1];
+       unsigned char result;
+
+       /* check if any spurious bit other the ones expected are set */
+       if ((min_taps > 4) || (min_taps < 1))
+               return -EINVAL;
+
+       regs[0] = (unsigned char)(min_taps-1);
+       result = mem_w_key(KEY_D_1_79, 1, regs);
+       return result;
+}
+
+static int  inv_set_tap_time_dmp(struct inv_gyro_state_s *st,
+                                unsigned int time)
+{
+       /* Determines how long after a tap the DMP requires before
+         another tap can be registered*/
+       int result;
+       /* DMP Algorithm */
+       unsigned short dmpTime;
+       unsigned char data[2];
+       unsigned char sampleDivider;
+
+       sampleDivider = st->sample_divider;
+       sampleDivider++;
+       /* 60 ms minimum time added */
+       dmpTime = ((time) / sampleDivider);
+       data[0] = dmpTime >> 8;
+       data[1] = dmpTime & 0xFF;
+       result = mem_w_key(KEY_DMP_TAPW_MIN, 2, data);
+       return result;
+}
+
+static int inv_set_multiple_tap_time_dmp(struct inv_gyro_state_s *st,
+                                        unsigned int time)
+{
+       /*Determines how close together consecutive taps must occur
+       to be considered double/triple taps*/
+       int result;
+       /* DMP Algorithm */
+       unsigned short dmpTime;
+       unsigned char data[2];
+       unsigned char sampleDivider;
+
+       sampleDivider = st->sample_divider;
+       sampleDivider++;
+       /* 60 ms minimum time added */
+       dmpTime = ((time) / sampleDivider);
+       data[0] = dmpTime >> 8;
+       data[1] = dmpTime & 0xFF;
+       result = mem_w_key(KEY_D_1_218, 2, data);
+       return result;
+}
+
+static long inv_q30_mult(long a, long b)
+{
+       long long temp;
+       long result;
+
+       temp = (long long)a * b;
+       result = (long)(temp >> 30);
+       return result;
+}
+
+#define gyro_sens 0x03e80000
+
+static int inv_set_gyro_sf_dmp(struct inv_gyro_state_s *st)
+{
+       /*The gyro threshold, in dps, above which taps will be rejected*/
+       int result, out;
+       /* DMP Algorithm */
+       unsigned char sampleDivider;
+       unsigned char *regs;
+       int gyro_sf;
+
+       sampleDivider = st->sample_divider;
+       gyro_sf = inv_q30_mult(gyro_sens,
+                       (int)((767603923/5) * (sampleDivider+1)));
+       out = cpu_to_be32p(&gyro_sf);
+       regs = (unsigned char *)&out;
+       result = mem_w_key(KEY_D_0_104, 4, regs);
+       return result;
+}
+
+static int inv_set_shake_reject_thresh_dmp(struct inv_gyro_state_s *st,
+                                          int thresh)
+{/*THIS FUNCTION FAILS MEM_W*/
+       /*The gyro threshold, in dps, above which taps will be rejected */
+       int result, out;
+       /* DMP Algorithm */
+       unsigned char sampleDivider;
+       int thresh_scaled;
+       unsigned char *regs;
+       long gyro_sf;
+
+       sampleDivider = st->sample_divider;
+       gyro_sf = inv_q30_mult(gyro_sens, (int)((767603923/5) *
+                       (sampleDivider+1)));
+       /* We're in units of DPS, convert it back to chip units*/
+       /*split the operation to aviod overflow of integer*/
+       thresh_scaled = gyro_sens/(1L<<16);
+       thresh_scaled = thresh_scaled/thresh;
+       thresh_scaled = gyro_sf / thresh_scaled;
+       out = cpu_to_be32p(&thresh_scaled);
+       regs = (unsigned char *)&out;
+       result = mem_w_key(KEY_D_1_92, 4, regs);
+       return result;
+}
+
+static int inv_set_shake_reject_time_dmp(struct inv_gyro_state_s *st,
+                                        unsigned int time)
+{
+       /* How long a gyro axis must remain above its threshold
+       before taps are rejected */
+       int result;
+       /* DMP Algorithm */
+       unsigned short dmpTime;
+       unsigned char data[2];
+       unsigned char sampleDivider;
+
+       sampleDivider = st->sample_divider;
+       sampleDivider++;
+       /* 60 ms minimum time added */
+       dmpTime = ((time) / sampleDivider);
+       data[0] = dmpTime >> 8;
+       data[1] = dmpTime & 0xFF;
+       result = mem_w_key(KEY_D_1_88, 2, data);
+       return result;
+}
+
+static int inv_set_shake_reject_timeout_dmp(struct inv_gyro_state_s *st,
+                                           unsigned int time)
+{
+       /*How long the gyros must remain below their threshold,
+       after taps have been rejected, before taps can be detected again*/
+       int result;
+       /* DMP Algorithm */
+       unsigned short dmpTime;
+       unsigned char data[2];
+       unsigned char sampleDivider;
+
+       sampleDivider = st->sample_divider;
+       sampleDivider++;
+       /* 60 ms minimum time added */
+       dmpTime = ((time) / sampleDivider);
+       data[0] = dmpTime >> 8;
+       data[1] = dmpTime & 0xFF;
+       result = mem_w_key(KEY_D_1_90, 2, data);
+       return result;
+}
+
+static int inv_set_interrupt_on_gesture_event(struct inv_gyro_state_s *st,
+                                             char on)
+{
+       unsigned char result;
+       unsigned char regs_on[12] = {DINADA, DINADA, DINAB1, DINAB9,
+                                       DINAF3, DINA8B, DINAA3, DINA91,
+                                       DINAB6, DINADA, DINAB4, DINADA};
+       unsigned char regs_off[12] = {0xd8, 0xd8, 0xb1, 0xb9, 0xf3, 0x8b,
+                                       0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9};
+       /*For some reason DINAC4 is defined as 0xb8,
+       but DINBC4 is not defined.*/
+       unsigned char regs_end[8] = {DINAFE, DINAF2, DINAAB, 0xc4,
+                                       DINAAA, DINAF1, DINADF, DINADF};
+
+       if (on) {
+               /*Sets the DMP to send an interrupt and put a FIFO packet
+               in the FIFO if and only if a tap/orientation event
+               just occurred*/
+               result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, 12, regs_on);
+               if (result)
+                       return result;
+
+       } else {
+               /*Sets the DMP to send an interrupt and put a FIFO packet
+               in the FIFO at the rate specified by the FIFO div.
+               see inv_set_fifo_div in hw_setup.c to set the FIFO div.*/
+               result = mem_w_key(KEY_CFG_FIFO_ON_EVENT, 12, regs_off);
+               if (result)
+                       return result;
+       }
+
+       result = mem_w_key(KEY_CFG_6, 8, regs_end);
+       return result;
+}
+
+/**
+ * inv_enable_tap_dmp() -  calling this function will enable/disable tap function.
+ */
+int inv_enable_tap_dmp(struct inv_gyro_state_s *st, unsigned char on)
+{
+       int result;
+
+       result = inv_set_tap_interrupt_dmp(st, on);
+       if (result)
+               return result;
+
+       if (on) {
+               result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_X, 100) ;
+               if (result)
+                       return result;
+
+               result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Y, 100) ;
+               if (result)
+                       return result;
+
+               result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Z, 100) ;
+               if (result)
+                       return result;
+       }
+
+       result = inv_set_tap_axes_dmp(st, INV_TAP_ALL_DIRECTIONS) ;
+       if (result)
+               return result;
+
+       result = inv_set_min_taps_dmp(st, 2);
+       if (result)
+               return result;
+
+       result = inv_set_tap_time_dmp(st, 100);
+       if (result)
+               return result;
+
+       result = inv_set_multiple_tap_time_dmp(st, 500) ;
+       if (result)
+               return result;
+
+       result = inv_set_gyro_sf_dmp(st);
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_thresh_dmp(st, 100) ;
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_time_dmp(st, 10) ;
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_timeout_dmp(st, 10);
+       if (result)
+               return result;
+
+       result = inv_set_interrupt_on_gesture_event(st, 0);
+       return result;
+}
+
+static int inv_set_orientation_dmp(struct inv_gyro_state_s *st,
+                                  int orientation)
+{
+       /*Set a mask in the DMP determining what orientations
+                       will trigger interrupts*/
+       unsigned char regs[4];
+       unsigned char result;
+
+       /* check if any spurious bit other the ones expected are set */
+       if (orientation & (~(INV_ORIENTATION_ALL | INV_ORIENTATION_FLIP)))
+               return -EINVAL;
+
+       regs[0] = (unsigned char)orientation;
+       result = mem_w_key(KEY_D_1_74, 1, regs);
+       return result;
+}
+
+static int inv_set_orientation_thresh_dmp(struct inv_gyro_state_s *st,
+                                         int angle)
+{
+       /*Set an angle threshold in the DMP determining
+               when orientations change*/
+       unsigned char *regs;
+       unsigned char result;
+       unsigned int out;
+       unsigned int threshold;
+
+       /*threshold = (long)((1<<29) * sin((angle * M_PI) / 180.));*/
+       threshold = 464943848;
+       out = cpu_to_be32p(&threshold);
+       regs = (unsigned char *)&out;
+       result = mem_w_key(KEY_D_1_232, 4, regs);
+       return result;
+}
+
+static int inv_set_orientation_time_dmp(struct inv_gyro_state_s *st,
+                                       unsigned int time)
+{
+       /*Determines the stability time required before a
+       new orientation can be adopted */
+       unsigned short dmpTime;
+       unsigned char data[2];
+       unsigned char sampleDivider;
+       unsigned char result;
+
+       /* First check if we are allowed to call this function here */
+       sampleDivider = st->sample_divider;
+       sampleDivider++;
+       /* 60 ms minimum time added */
+       dmpTime = ((time) / sampleDivider);
+       data[0] = dmpTime >> 8;
+       data[1] = dmpTime & 0xFF;
+       result = mem_w_key(KEY_D_1_250, 2, data);
+       return result;
+}
+
+/**
+ * inv_enable_orientation_dmp() -  calling this function will
+ *                  enable/disable orientation function.
+ */
+int inv_enable_orientation_dmp(struct inv_gyro_state_s *st)
+{
+       int result;
+
+       result = inv_set_orientation_interrupt_dmp(st, 1);
+       if (result)
+               return result;
+
+       result = inv_set_orientation_dmp(st, 0x40 | INV_ORIENTATION_ALL);
+       if (result)
+               return result;
+
+       result = inv_set_gyro_sf_dmp(st);
+       if (result)
+               return result;
+
+       result = inv_set_orientation_thresh_dmp(st, 60);
+       if (result)
+               return result;
+
+       result = inv_set_orientation_time_dmp(st, 500);
+       return result;
+}
+
+static int inv_send_sensor_data(struct inv_gyro_state_s *st,
+                               unsigned short elements)
+{
+       int result;
+       unsigned char regs[10] = { DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+                                 DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+                                 DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
+                                 DINAA0 + 3 };
+
+       if (elements & INV_ELEMENT_1)
+               regs[0] = DINACA;
+       if (elements & INV_ELEMENT_2)
+               regs[4] = DINBC4;
+       if (elements & INV_ELEMENT_3)
+               regs[5] = DINACC;
+       if (elements & INV_ELEMENT_4)
+               regs[6] = DINBC6;
+       if ((elements & INV_ELEMENT_5) || (elements & INV_ELEMENT_6)
+               || (elements & INV_ELEMENT_7)) {
+               regs[1] = DINBC0;
+               regs[2] = DINAC8;
+               regs[3] = DINBC2;
+       }
+       result = mem_w_key(KEY_CFG_15, 10, regs);
+       return result;
+}
+
+static int inv_send_interrupt_word(struct inv_gyro_state_s *st)
+{
+       unsigned char regs[1] = { DINA20 };
+       unsigned char result;
+
+       result = mem_w_key(KEY_CFG_27, 1, regs);
+       return result;
+}
+
+/**
+ * inv_dmp_firmware_write() -  calling this function will load the firmware.
+ *                        This is the write function of file "dmp_firmware".
+ */
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+       struct bin_attribute *attr,
+       char *buf, loff_t pos, size_t size)
+{
+       struct inv_gyro_state_s *st;
+       unsigned char *firmware;
+       int result;
+       struct inv_reg_map_s *reg;
+
+       st = dev_get_drvdata(container_of(kobj, struct device, kobj));
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       if (1 == st->chip_config.firmware_loaded)
+               return -EINVAL;
+
+       reg = st->reg;
+       firmware = kmalloc(size, GFP_KERNEL);
+       if (!firmware)
+               return -ENOMEM;
+
+       memcpy(firmware, buf, size);
+       result = inv_load_firmware(st, firmware, size);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_verify_firmware(st, firmware, size);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_i2c_single_write(st, reg->prgm_strt_addrh,
+       st->chip_config.prog_start_addr >> 8);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1,
+       st->chip_config.prog_start_addr & 0xff);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_verify_firmware(st, firmware, size);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_set_fifo_rate(st, 200);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_enable_tap_dmp(st, 1);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_enable_orientation_dmp(st);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_send_sensor_data(st, INV_GYRO_ACC_MASK);
+       if (result)
+               goto firmware_write_fail;
+
+       result = inv_send_interrupt_word(st);
+       if (result)
+               goto firmware_write_fail;
+
+       st->chip_config.firmware_loaded = 1;
+       st->chip_config.dmp_on = 1;
+       result = size;
+firmware_write_fail:
+       kfree(firmware);
+       return result;
+}
+
+ssize_t inv_dmp_firmware_read(struct file *filp, struct kobject *kobj,
+                             struct bin_attribute *bin_attr,
+                             char *buf, loff_t off, size_t count)
+{
+       struct inv_gyro_state_s *st;
+       int bank, write_size, size, data, result;
+       unsigned short memaddr;
+       size = count;
+
+       st = dev_get_drvdata(container_of(kobj, struct device, kobj));
+       data = 0;
+       for (bank = 0; size > 0; bank++, size -= write_size,
+                                data += write_size) {
+               if (size > MPU_MEM_BANK_SIZE)
+                       write_size = MPU_MEM_BANK_SIZE;
+               else
+                       write_size = size;
+               memaddr = ((bank << 8) | 0x00);
+               result = mpu_memory_read(st->sl_handle,
+                       st->i2c_addr, memaddr, write_size, &buf[data]);
+               if (result)
+                       return result;
+       }
+
+       return count;
+}
+
diff --git a/drivers/input/misc/mpu/inv_mpu3050.c b/drivers/input/misc/mpu/inv_mpu3050.c
new file mode 100644 (file)
index 0000000..f170091
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_mpu3050.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of inv_gyro driver code
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_gyro.h"
+int set_3050_bypass(struct inv_gyro_state_s *st, int enable)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char b;
+
+       reg = st->reg;
+       result = inv_i2c_read(st, reg->user_ctrl, 1, &b);
+       if (result)
+               return result;
+       if (((b & BIT_3050_AUX_IF_EN) == 0) && enable)
+               return 0;
+       if ((b & BIT_3050_AUX_IF_EN) && (enable == 0))
+               return 0;
+       b &= ~BIT_3050_AUX_IF_EN;
+       if (!enable) {
+               b |= BIT_3050_AUX_IF_EN;
+               result = inv_i2c_single_write(st, reg->user_ctrl, b);
+               return result;
+       } else {
+               /* Coming out of I2C is tricky due to several erratta.  Do not
+               * modify this algorithm
+               */
+               /*
+               * 1) wait for the right time and send the command to change
+               * the aux i2c slave address to an invalid address that will
+               * get nack'ed
+               *
+               * 0x00 is broadcast.  0x7F is unlikely to be used by any aux.
+               */
+               result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+                                               0x7F);
+               if (result)
+                       return result;
+               /*
+               * 2) wait enough time for a nack to occur, then go into
+               *    bypass mode:
+               */
+               mdelay(2);
+               result = inv_i2c_single_write(st, reg->user_ctrl, b);
+               if (result)
+                       return result;
+               /*
+               * 3) wait for up to one MPU cycle then restore the slave
+               *    address
+               */
+               mdelay(20);
+               result = inv_i2c_single_write(st, REG_3050_SLAVE_REG,
+                                    st->plat_data.secondary_read_reg);
+               if (result)
+                       return result;
+
+               result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+                       st->plat_data.secondary_i2c_addr);
+               if (result)
+                       return result;
+
+               result = inv_i2c_single_write(st, reg->user_ctrl,
+                                               (b | BIT_3050_AUX_IF_RST));
+               if (result)
+                       return result;
+
+               mdelay(2);
+       }
+       return 0;
+}
+
+/**
+ *  inv_raw_accl_show() - Read accel data directly from registers.
+ */
+static ssize_t inv_raw_accl_mpu3050_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       unsigned char data[6];
+       short out[3];
+       int result;
+       struct inv_gyro_state_s *st;
+
+       st = dev_get_drvdata(dev);
+       if (st->mpu_slave != NULL) {
+               if (0 == st->mpu_slave->get_mode(st))
+                       return -EINVAL;
+       }
+       result = inv_i2c_read(st, REG_3050_AUX_XOUT_H, 6, data);
+       out[0] = out[1] = out[2];
+       if ((0 == result) && (st->mpu_slave != NULL))
+               st->mpu_slave->combine_data(data, out);
+       return sprintf(buf, "%d %d %d %lld\n", out[0],
+               out[1], out[2], get_time_ns());
+}
+
+/**
+ *  inv_accl_fifo_enable_store() - Enable/disable accl fifo output.
+ */
+static ssize_t inv_accl_fifo_mpu3050_enable_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       unsigned long en;
+       struct inv_gyro_state_s *st;
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EINVAL;
+       if (kstrtoul(buf, 10, &en))
+               return -EINVAL;
+       if (en == !(!(st->chip_config.accl_fifo_enable)))
+               return count;
+       st->chip_config.accl_fifo_enable = en;
+       if (en && (0 == st->chip_config.accl_enable)) {
+               st->chip_config.accl_enable = en;
+               if (st->mpu_slave != NULL)
+                       st->mpu_slave->resume(st);
+       }
+       return count;
+}
+/**
+ *  inv_accl_mpu3050_enable_store() - Enable/disable accl.
+ */
+static ssize_t inv_accl_mpu3050_enable_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       unsigned long en;
+       struct inv_gyro_state_s *st;
+       st = dev_get_drvdata(dev);
+       if (st->chip_config.is_asleep)
+               return -EINVAL;
+       if (kstrtoul(buf, 10, &en))
+               return -EINVAL;
+       if (en == !(!(st->chip_config.accl_enable)))
+               return count;
+       st->chip_config.accl_enable = en;
+       if ((0 == en) && st->chip_config.accl_fifo_enable)
+               st->chip_config.accl_fifo_enable = 0;
+       if (st->mpu_slave != NULL) {
+               if (en)
+                       st->mpu_slave->resume(st);
+               else
+                       st->mpu_slave->suspend(st);
+       }
+       return count;
+}
+
+static DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR,
+       inv_gyro_fifo_enable_show,
+       inv_gyro_fifo_enable_store);
+static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_gyro_enable_show,
+       inv_gyro_enable_store);
+static DEVICE_ATTR(raw_accl, S_IRUGO, inv_raw_accl_mpu3050_show, NULL);
+static DEVICE_ATTR(accl_fifo_enable, S_IRUGO | S_IWUSR,
+       inv_accl_fifo_enable_show,
+       inv_accl_fifo_mpu3050_enable_store);
+static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_accl_enable_show,
+       inv_accl_mpu3050_enable_store);
+static DEVICE_ATTR(accl_matrix, S_IRUGO, inv_accl_matrix_show, NULL);
+static DEVICE_ATTR(accl_fs, S_IRUGO | S_IWUSR, inv_accl_fs_show,
+                       inv_accl_fs_store);
+
+static struct device_attribute *inv_mpu3050_attributes[] = {
+       &dev_attr_gyro_fifo_enable,
+       &dev_attr_gyro_enable,
+       &dev_attr_accl_fifo_enable,
+       &dev_attr_accl_enable,
+       &dev_attr_raw_accl,
+       &dev_attr_accl_matrix,
+       &dev_attr_accl_fs,
+       NULL
+};
+
+int inv_mpu3050_create_sysfs(struct inv_gyro_state_s *st)
+{
+       int result;
+       result = create_device_attributes(st->inv_dev, inv_mpu3050_attributes);
+       if (result)
+               return result;
+       return result;
+}
+int inv_mpu3050_remove_sysfs(struct inv_gyro_state_s *st)
+{
+       remove_device_attributes(st->inv_dev, inv_mpu3050_attributes);
+       return 0;
+}
+
+static void inv_report_data_3050(struct inv_gyro_state_s *st, s64 t,
+                       int counter, unsigned char *data)
+{
+       short x, y, z = 0;
+       int ind;
+       short out[3];
+       struct inv_chip_config_s *conf;
+       conf = &st->chip_config;
+       ind = 0;
+       if (counter)
+               ind += 2;
+       if (conf->gyro_fifo_enable | conf->dmp_on) {
+               x = (data[ind] << 8)     | data[ind + 1];
+               y = (data[ind + 2] << 8) | data[ind + 3];
+               z = (data[ind + 4] << 8) | data[ind + 5];
+               if (conf->gyro_fifo_enable) {
+                       /*it is possible that gyro disabled when dmp is on*/
+                       input_report_rel(st->idev, REL_X,  x);
+                       input_report_rel(st->idev, REL_Y,  y);
+                       input_report_rel(st->idev, REL_Z,  z);
+               }
+               ind += 6;
+       }
+
+       if (conf->accl_fifo_enable | conf->dmp_on) {
+               if (st->mpu_slave != NULL) {
+                       st->mpu_slave->combine_data(&data[ind], out);
+                       x = out[0];
+                       y = out[1];
+                       z = out[2];
+               }
+               if (conf->accl_fifo_enable) {
+                       /*it is possible that accl disabled when dmp is on*/
+                       input_report_rel(st->idev, REL_RX,  x);
+                       input_report_rel(st->idev, REL_RY,  y);
+                       input_report_rel(st->idev, REL_RZ,  z);
+               }
+               ind += 6;
+       }
+       if (conf->dmp_on) {
+               /* report tap information */
+               if (data[ind + 1] & 1) {
+                       input_report_rel(st->idev_dmp, REL_RX, data[ind+3]);
+                       input_sync(st->idev_dmp);
+               }
+               /* report orientation information */
+               if (data[ind + 1] & 2) {
+                       input_report_rel(st->idev_dmp, REL_RY, data[ind+2]);
+                       input_sync(st->idev_dmp);
+               }
+       }
+       /* always report time */
+       {
+               input_report_rel(st->idev, REL_MISC, (unsigned int)(t >> 32));
+               input_report_rel(st->idev, REL_WHEEL,
+                       (unsigned int)(t & 0xffffffff));
+               input_sync(st->idev);
+       }
+}
+/**
+ *  inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+       struct inv_gyro_state_s *st;
+       unsigned char bytes_per_datum;
+       const unsigned short fifo_thresh = 500;
+       int result;
+       unsigned char data[16];
+       short fifo_count, byte_read;
+       unsigned int copied;
+       s64 timestamp;
+       struct inv_reg_map_s *reg;
+
+       st = (struct inv_gyro_state_s *)dev_id;
+       reg = st->reg;
+
+       if (st->chip_config.is_asleep)
+               goto end_session;
+       if (!(st->chip_config.enable))
+               goto end_session;
+       if (!(st->chip_config.accl_fifo_enable |
+               st->chip_config.gyro_fifo_enable |
+               st->chip_config.dmp_on |
+               st->chip_config.compass_enable))
+               goto end_session;
+       if (st->chip_config.dmp_on)
+               bytes_per_datum = BYTES_FOR_DMP;
+       else
+               bytes_per_datum = (st->chip_config.accl_fifo_enable +
+                                       st->chip_config.gyro_fifo_enable)*
+                                       BYTES_PER_SENSOR;
+       if (st->fifo_counter == 0)
+               byte_read = bytes_per_datum;
+       else
+               byte_read = bytes_per_datum + 2;
+
+       fifo_count = 0;
+       if (byte_read != 0) {
+               result = inv_i2c_read(st, reg->fifo_count_h, 2, data);
+               if (result)
+                       goto end_session;
+               fifo_count = (data[0] << 8) + data[1];
+               if (fifo_count < byte_read)
+                       goto end_session;
+               if (fifo_count%2)
+                       goto flush_fifo;
+               if (fifo_count > fifo_thresh)
+                       goto flush_fifo;
+               /* Timestamp mismatch. */
+               if (kfifo_len(&st->trigger.timestamps) <
+                       fifo_count / byte_read)
+                       goto flush_fifo;
+               if (kfifo_len(&st->trigger.timestamps) >
+                       fifo_count / byte_read + TIME_STAMP_TOR) {
+                       if (st->chip_config.dmp_on) {
+                               result = kfifo_to_user(&st->trigger.timestamps,
+                               &timestamp, sizeof(timestamp), &copied);
+                               if (result)
+                                       goto flush_fifo;
+                       } else
+                               goto flush_fifo;
+               }
+       }
+
+       while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) {
+               result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data);
+               if (result)
+                       goto flush_fifo;
+
+               result = kfifo_to_user(&st->trigger.timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+               inv_report_data_3050(st, timestamp, st->fifo_counter, data);
+               fifo_count -= byte_read;
+               if (st->fifo_counter == 0) {
+                       st->fifo_counter = 1;
+                       byte_read = bytes_per_datum + 2;
+               }
+       }
+end_session:
+       return IRQ_HANDLED;
+flush_fifo:
+       /* Flush HW and SW FIFOs. */
+       inv_reset_fifo(st);
+       inv_clear_kfifo(st);
+       return IRQ_HANDLED;
+}
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+       reg->fifo_en         = 0x12;
+       reg->sample_rate_div = 0x15;
+       reg->lpf             = 0x16;
+       reg->fifo_count_h    = 0x3a;
+       reg->fifo_r_w        = 0x3c;
+       reg->user_ctrl       = 0x3d;
+       reg->pwr_mgmt_1      = 0x3e;
+       reg->raw_gyro        = 0x1d;
+       reg->raw_accl        = 0x23;
+       reg->temperature     = 0x1b;
+       reg->int_enable      = 0x17;
+       reg->int_status      = 0x1a;
+}
+
+/**
+ *  inv_init_config_mpu3050() - Initialize hardware, disable FIFO.
+ *  @st:       Device driver instance.
+ *  Initial configuration:
+ *  FSR: +/- 2000DPS
+ *  DLPF: 42Hz
+ *  FIFO rate: 50Hz
+ *  Clock source: Gyro PLL
+ */
+int inv_init_config_mpu3050(struct inv_gyro_state_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char data;
+
+       if (st->chip_config.is_asleep)
+               return -EPERM;
+
+       /*reading AUX VDDIO register */
+       result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data);
+       if (result)
+               return result;
+       data &= ~BIT_3050_VDDIO;
+       data |= (st->plat_data.level_shifter << 2);
+       result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+       if (result)
+               return result;
+
+       reg = st->reg;
+       result = set_inv_enable(st, 0);
+       if (result)
+               return result;
+       /*2000dps full scale range*/
+       result = inv_i2c_single_write(st, reg->lpf, (INV_FSR_2000DPS << 3)
+                                       | INV_FILTER_42HZ);
+       if (result)
+               return result;
+       st->chip_config.fsr = INV_FSR_2000DPS;
+       st->chip_config.lpf = INV_FILTER_42HZ;
+       result = inv_i2c_single_write(st, reg->sample_rate_div, 19);
+       if (result)
+               return result;
+       st->chip_config.fifo_rate = 50;
+       st->irq_dur_us            = 20*1000;
+       st->chip_config.enable = 0;
+       st->chip_config.dmp_on = 0;
+       st->compass_divider = 0;
+       st->compass_counter = 0;
+       st->chip_config.compass_enable = 0;
+       st->chip_config.firmware_loaded = 0;
+       st->chip_config.prog_start_addr = DMP_START_ADDR;
+       st->chip_config.gyro_enable = 1;
+       st->chip_config.gyro_fifo_enable = 1;
+       if (SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) {
+               if (st->mpu_slave != NULL) {
+                       result = st->mpu_slave->setup(st);
+                       if (result)
+                               return result;
+
+                       result = st->mpu_slave->set_fs(st, 0);
+                       if (result)
+                               return result;
+
+                       result = st->mpu_slave->set_lpf(st, 50);
+                       if (result)
+                               return result;
+               }
+
+               st->chip_config.accl_enable = 1;
+               st->chip_config.accl_fifo_enable = 1;
+       } else {
+               st->chip_config.accl_enable = 0;
+               st->chip_config.accl_fifo_enable = 0;
+       }
+       return 0;
+}
+/**
+ *  set_power_mpu3050() - set power of mpu3050.
+ *  @st:       Device driver instance.
+ *  @power_on:  on/off
+ */
+int set_power_mpu3050(struct inv_gyro_state_s *st,
+       unsigned char power_on)
+{
+       struct inv_reg_map_s *reg;
+       unsigned char data, p;
+       int result;
+       reg = st->reg;
+       if (power_on) {
+               data = 0;
+       } else {
+               if (st->mpu_slave) {
+                       result = st->mpu_slave->suspend(st);
+                       if (result)
+                               return result;
+               }
+
+               data = BIT_SLEEP;
+       }
+       if (st->chip_config.gyro_enable) {
+               p = (BITS_3050_POWER1 | INV_CLK_PLL);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+               if (result)
+                       return result;
+
+               p = (BITS_3050_POWER2 | INV_CLK_PLL);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+               if (result)
+                       return result;
+
+               p = INV_CLK_PLL;
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+               if (result)
+                       return result;
+
+               st->chip_config.clk_src = INV_CLK_PLL;
+       } else {
+               data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+               if (result)
+                       return result;
+               st->chip_config.clk_src = INV_CLK_INTERNAL;
+       }
+       if (power_on) {
+               mdelay(POWER_UP_TIME);
+               if (st->mpu_slave) {
+                       result = st->mpu_slave->resume(st);
+                       if (result)
+                               return result;
+               }
+               st->chip_config.is_asleep = 0;
+       } else
+               st->chip_config.is_asleep = 1;
+       return 0;
+}
+/**
+ *  reset_fifo_mpu3050() - Reset FIFO related registers
+ *  @st:       Device driver instance.
+ */
+int reset_fifo_mpu3050(struct inv_gyro_state_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       unsigned char val, user_ctrl;
+
+       reg = st->reg;
+       /* disable interrupt */
+       result = inv_i2c_single_write(st, reg->int_enable, 0);
+       if (result)
+               return result;
+       /* disable the sensor output to FIFO */
+       result = inv_i2c_single_write(st, reg->fifo_en, 0);
+       if (result)
+               goto reset_fifo_fail;
+       result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl);
+       if (result)
+               goto reset_fifo_fail;
+       /* disable fifo reading */
+       user_ctrl &= ~BIT_FIFO_EN;
+       st->fifo_counter = 0;
+       /* reset fifo */
+       val = (BIT_3050_FIFO_RST | user_ctrl);
+       result = inv_i2c_single_write(st, reg->user_ctrl, val);
+       if (result)
+               goto reset_fifo_fail;
+       mdelay(POWER_UP_TIME);
+       st->last_isr_time = get_time_ns();
+       if (st->chip_config.dmp_on) {
+               /* enable interrupt when DMP is done */
+               result = inv_i2c_single_write(st, reg->int_enable,
+                                       BIT_DMP_INT_EN);
+               if (result)
+                       return result;
+
+               result = inv_i2c_single_write(st, reg->user_ctrl,
+                       BIT_FIFO_EN|user_ctrl);
+               if (result)
+                       return result;
+       } else {
+               /* enable interrupt */
+               if (st->chip_config.accl_fifo_enable ||
+                       st->chip_config.gyro_fifo_enable){
+                       result = inv_i2c_single_write(st, reg->int_enable,
+                                                       BIT_DATA_RDY_EN);
+                       if (result)
+                               return result;
+               }
+               /* enable FIFO reading and I2C master interface*/
+               result = inv_i2c_single_write(st, reg->user_ctrl,
+                       BIT_FIFO_EN | user_ctrl);
+               if (result)
+                       return result;
+               /* enable sensor output to FIFO and FIFO footer*/
+               val = 1;
+               if (st->chip_config.accl_fifo_enable)
+                       val |= BITS_3050_ACCL_OUT;
+               if (st->chip_config.gyro_fifo_enable)
+                       val |= BITS_GYRO_OUT;
+               result = inv_i2c_single_write(st, reg->fifo_en, val);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+reset_fifo_fail:
+       if (st->chip_config.dmp_on)
+               val = BIT_DMP_INT_EN;
+       else
+               val = BIT_DATA_RDY_EN;
+       inv_i2c_single_write(st, reg->int_enable, val);
+       pr_err("%s failed\n", __func__);
+       return result;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/input/misc/mpu/inv_slave_bma250.c b/drivers/input/misc/mpu/inv_slave_bma250.c
new file mode 100644 (file)
index 0000000..5192096
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_slave_bma250.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of inv_gyro driver code
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_gyro.h"
+#define BMA250_CHIP_ID                 3
+#define BMA250_RANGE_SET               0
+#define BMA250_BW_SET                  4
+
+/* range and bandwidth */
+
+#define BMA250_RANGE_2G                 0
+#define BMA250_RANGE_4G                 1
+#define BMA250_RANGE_8G                 2
+#define BMA250_RANGE_16G                3
+
+
+#define BMA250_BW_7_81HZ        0x08
+#define BMA250_BW_15_63HZ       0x09
+#define BMA250_BW_31_25HZ       0x0A
+#define BMA250_BW_62_50HZ       0x0B
+#define BMA250_BW_125HZ         0x0C
+#define BMA250_BW_250HZ         0x0D
+#define BMA250_BW_500HZ         0x0E
+#define BMA250_BW_1000HZ        0x0F
+
+/*      register definitions */
+#define BMA250_CHIP_ID_REG                      0x00
+#define BMA250_VERSION_REG                      0x01
+#define BMA250_X_AXIS_LSB_REG                   0x02
+#define BMA250_X_AXIS_MSB_REG                   0x03
+#define BMA250_Y_AXIS_LSB_REG                   0x04
+#define BMA250_Y_AXIS_MSB_REG                   0x05
+#define BMA250_Z_AXIS_LSB_REG                   0x06
+#define BMA250_Z_AXIS_MSB_REG                   0x07
+#define BMA250_TEMP_RD_REG                      0x08
+#define BMA250_STATUS1_REG                      0x09
+#define BMA250_STATUS2_REG                      0x0A
+#define BMA250_STATUS_TAP_SLOPE_REG             0x0B
+#define BMA250_STATUS_ORIENT_HIGH_REG           0x0C
+#define BMA250_RANGE_SEL_REG                    0x0F
+#define BMA250_BW_SEL_REG                       0x10
+#define BMA250_MODE_CTRL_REG                    0x11
+#define BMA250_LOW_NOISE_CTRL_REG               0x12
+#define BMA250_DATA_CTRL_REG                    0x13
+#define BMA250_RESET_REG                        0x14
+#define BMA250_INT_ENABLE1_REG                  0x16
+#define BMA250_INT_ENABLE2_REG                  0x17
+#define BMA250_INT1_PAD_SEL_REG                 0x19
+#define BMA250_INT_DATA_SEL_REG                 0x1A
+#define BMA250_INT2_PAD_SEL_REG                 0x1B
+#define BMA250_INT_SRC_REG                      0x1E
+#define BMA250_INT_SET_REG                      0x20
+#define BMA250_INT_CTRL_REG                     0x21
+#define BMA250_LOW_DURN_REG                     0x22
+#define BMA250_LOW_THRES_REG                    0x23
+#define BMA250_LOW_HIGH_HYST_REG                0x24
+#define BMA250_HIGH_DURN_REG                    0x25
+#define BMA250_HIGH_THRES_REG                   0x26
+#define BMA250_SLOPE_DURN_REG                   0x27
+#define BMA250_SLOPE_THRES_REG                  0x28
+#define BMA250_TAP_PARAM_REG                    0x2A
+#define BMA250_TAP_THRES_REG                    0x2B
+#define BMA250_ORIENT_PARAM_REG                 0x2C
+#define BMA250_THETA_BLOCK_REG                  0x2D
+#define BMA250_THETA_FLAT_REG                   0x2E
+#define BMA250_FLAT_HOLD_TIME_REG               0x2F
+#define BMA250_STATUS_LOW_POWER_REG             0x31
+#define BMA250_SELF_TEST_REG                    0x32
+#define BMA250_EEPROM_CTRL_REG                  0x33
+#define BMA250_SERIAL_CTRL_REG                  0x34
+#define BMA250_CTRL_UNLOCK_REG                  0x35
+#define BMA250_OFFSET_CTRL_REG                  0x36
+#define BMA250_OFFSET_PARAMS_REG                0x37
+#define BMA250_OFFSET_FILT_X_REG                0x38
+#define BMA250_OFFSET_FILT_Y_REG                0x39
+#define BMA250_OFFSET_FILT_Z_REG                0x3A
+#define BMA250_OFFSET_UNFILT_X_REG              0x3B
+#define BMA250_OFFSET_UNFILT_Y_REG              0x3C
+#define BMA250_OFFSET_UNFILT_Z_REG              0x3D
+#define BMA250_SPARE_0_REG                      0x3E
+#define BMA250_SPARE_1_REG                      0x3F
+
+/* mode settings */
+
+#define BMA250_MODE_NORMAL      0
+#define BMA250_MODE_LOWPOWER    1
+#define BMA250_MODE_SUSPEND     2
+
+struct bma_property {
+       int range;
+       int bandwidth;
+       int mode;
+};
+
+static struct bma_property bma_static_property = {
+       .range = BMA250_RANGE_SET,
+       .bandwidth = BMA250_BW_SET,
+       .mode = BMA250_MODE_SUSPEND
+};
+
+static int bma250_set_bandwidth(struct inv_gyro_state_s *st, unsigned char BW)
+{
+       int res = 0;
+       unsigned char data;
+       int Bandwidth = 0;
+       if (BW >= 8)
+               return -1;
+       switch (BW) {
+       case 0:
+               Bandwidth = BMA250_BW_7_81HZ;
+               break;
+       case 1:
+               Bandwidth = BMA250_BW_15_63HZ;
+               break;
+       case 2:
+               Bandwidth = BMA250_BW_31_25HZ;
+               break;
+       case 3:
+               Bandwidth = BMA250_BW_62_50HZ;
+               break;
+       case 4:
+               Bandwidth = BMA250_BW_125HZ;
+               break;
+       case 5:
+               Bandwidth = BMA250_BW_250HZ;
+               break;
+       case 6:
+               Bandwidth = BMA250_BW_500HZ;
+               break;
+       case 7:
+               Bandwidth = BMA250_BW_1000HZ;
+               break;
+       default:
+               break;
+       }
+       res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+       if (res)
+               return res;
+       data &= 0xe0;
+       data |= Bandwidth;
+       res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+       return res;
+}
+
+static int bma250_set_range(struct inv_gyro_state_s *st, unsigned char Range)
+{
+       int res = 0;
+       unsigned char orig, data = 0;
+       if (Range >= 4)
+               return -1;
+       switch (Range) {
+       case 0:
+               data  = 3;
+               break;
+       case 1:
+               data  = 5;
+               break;
+       case 2:
+               data  = 8;
+               break;
+       case 3:
+               data  = 12;
+               break;
+       default:
+               break;
+       }
+       res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+       if (res)
+               return res;
+       orig &= 0xf0;
+       data |= orig;
+       res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+       bma_static_property.range = Range;
+       return res;
+}
+
+static int setup_slave_bma250(struct inv_gyro_state_s *st)
+{
+       int result;
+       unsigned char data[4];
+       result = set_3050_bypass(st, 1);
+       if (result)
+               return result;
+       /*read secondary i2c ID register */
+       result = inv_secondary_read(0, 2, data);
+       if (result)
+               return result;
+       if (BMA250_CHIP_ID != data[0])
+               return result;
+       result = set_3050_bypass(st, 0);
+       if (result)
+               return result;
+       /*AUX(accel), slave address is set inside set_3050_bypass*/
+       /* bma250 x axis LSB register address is 2 */
+       result = inv_i2c_single_write(st, 0x18, BMA250_X_AXIS_LSB_REG);
+       return result;
+}
+
+static int bma250_set_mode(struct inv_gyro_state_s *st, unsigned char Mode)
+{
+       int res = 0;
+       unsigned char data = 0;
+
+       if (Mode >= 3)
+               return -1;
+       res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+       if (res)
+               return res;
+       data &= 0x3F;
+       switch (Mode) {
+       case BMA250_MODE_NORMAL:
+               break;
+       case BMA250_MODE_LOWPOWER:
+               data |= 0x40;
+               break;
+       case BMA250_MODE_SUSPEND:
+               data |= 0x80;
+               break;
+       default:
+               break;
+       }
+       res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+       bma_static_property.mode = Mode;
+       return res;
+}
+static int suspend_slave_bma250(struct inv_gyro_state_s *st)
+{
+       int result;
+       if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+               return 0;
+       /*set to bypass mode */
+       result = set_3050_bypass(st, 1);
+       if (result)
+               return result;
+       bma250_set_mode(st, BMA250_MODE_SUSPEND);
+       /* no need to recover to non-bypass mode because we need it now */
+       return result;
+}
+static int resume_slave_bma250(struct inv_gyro_state_s *st)
+{
+       int result;
+       if (bma_static_property.mode == BMA250_MODE_NORMAL)
+               return 0;
+       /*set to bypass mode */
+       result = set_3050_bypass(st, 1);
+       if (result)
+               return result;
+       bma250_set_mode(st, BMA250_MODE_NORMAL);
+       /* recover bypass mode */
+       result = set_3050_bypass(st, 0);
+       return result;
+}
+static int combine_data_slave_bma250(unsigned char *in, short *out)
+{
+       out[0] = (in[0] | (in[1]<<8));
+       out[1] = (in[2] | (in[3]<<8));
+       out[2] = (in[4] | (in[5]<<8));
+       return 0;
+}
+static int get_mode_slave_bma250(struct inv_gyro_state_s *st)
+{
+       if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+               return 0;
+       else if (bma_static_property.mode == BMA250_MODE_NORMAL)
+               return 1;
+       return -1;
+};
+/**
+ *  set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_gyro_state_s *st, int rate)
+{
+       short hz[8] = {1000, 500, 250, 125, 62, 31, 15, 7};
+       int   d[8] = {7, 6, 5, 4, 3, 2, 1, 0};
+       int i, h, data, result;
+       h = (rate >> 1);
+       i = 0;
+       while ((h < hz[i]) && (i < 8))
+               i++;
+       data = d[i];
+
+       result = set_3050_bypass(st, 1);
+       if (result)
+               return result;
+       result = bma250_set_bandwidth(st, (unsigned char) data);
+       result |= set_3050_bypass(st, 0);
+
+       return result;
+}
+/**
+ *  set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_gyro_state_s *st, int fs)
+{
+       int result;
+       result = set_3050_bypass(st, 1);
+       if (result)
+               return result;
+       result = bma250_set_range(st, (unsigned char) fs);
+       result |= set_3050_bypass(st, 0);
+       if (result)
+               return -EINVAL;
+       return result;
+}
+
+static struct inv_mpu_slave slave_bma250 = {
+       .suspend = suspend_slave_bma250,
+       .resume  = resume_slave_bma250,
+       .setup   = setup_slave_bma250,
+       .combine_data = combine_data_slave_bma250,
+       .get_mode = get_mode_slave_bma250,
+       .set_lpf = set_lpf_bma250,
+       .set_fs  = set_fs_bma250
+};
+
+int inv_register_bma250_slave(struct inv_gyro_state_s *st)
+{
+       st->mpu_slave = &slave_bma250;
+       return 0;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/input/misc/mpu/inv_slave_kxtf9.c b/drivers/input/misc/mpu/inv_slave_kxtf9.c
new file mode 100644 (file)
index 0000000..dac9058
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+* Copyright (C) 2012 nVidia Corp.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
+
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    inv_slave_kxtf9.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of inv_gyro driver code
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_gyro.h"
+#define KXTF9_CHIP_ID                  1
+#define KXTF9_RANGE_SET                0
+#define KXTF9_BW_SET                   4
+
+/* range and bandwidth */
+#define KXTF9_RANGE_2G                 0
+#define KXTF9_RANGE_4G                 1
+#define KXTF9_RANGE_8G                 2
+#define KXTF9_RANGE_16G                3
+
+/*      register definitions */
+#define KXTF9_XOUT_HPF_L                (0x00)
+#define KXTF9_XOUT_HPF_H                (0x01)
+#define KXTF9_YOUT_HPF_L                (0x02)
+#define KXTF9_YOUT_HPF_H                (0x03)
+#define KXTF9_ZOUT_HPF_L                (0x04)
+#define KXTF9_ZOUT_HPF_H                (0x05)
+#define KXTF9_XOUT_L                    (0x06)
+#define KXTF9_XOUT_H                    (0x07)
+#define KXTF9_YOUT_L                    (0x08)
+#define KXTF9_YOUT_H                    (0x09)
+#define KXTF9_ZOUT_L                    (0x0A)
+#define KXTF9_ZOUT_H                    (0x0B)
+#define KXTF9_ST_RESP                   (0x0C)
+#define KXTF9_WHO_AM_I                  (0x0F)
+#define KXTF9_TILT_POS_CUR              (0x10)
+#define KXTF9_TILT_POS_PRE              (0x11)
+#define KXTF9_INT_SRC_REG1              (0x15)
+#define KXTF9_INT_SRC_REG2              (0x16)
+#define KXTF9_STATUS_REG                (0x18)
+#define KXTF9_INT_REL                   (0x1A)
+#define KXTF9_CTRL_REG1                 (0x1B)
+#define KXTF9_CTRL_REG2                 (0x1C)
+#define KXTF9_CTRL_REG3                 (0x1D)
+#define KXTF9_INT_CTRL_REG1             (0x1E)
+#define KXTF9_INT_CTRL_REG2             (0x1F)
+#define KXTF9_INT_CTRL_REG3             (0x20)
+#define KXTF9_DATA_CTRL_REG             (0x21)
+#define KXTF9_TILT_TIMER                (0x28)
+#define KXTF9_WUF_TIMER                 (0x29)
+#define KXTF9_TDT_TIMER                 (0x2B)
+#define KXTF9_TDT_H_THRESH              (0x2C)
+#define KXTF9_TDT_L_THRESH              (0x2D)
+#define KXTF9_TDT_TAP_TIMER             (0x2E)
+#define KXTF9_TDT_TOTAL_TIMER           (0x2F)
+#define KXTF9_TDT_LATENCY_TIMER         (0x30)
+#define KXTF9_TDT_WINDOW_TIMER          (0x31)
+#define KXTF9_WUF_THRESH                (0x5A)
+#define KXTF9_TILT_ANGLE                (0x5C)
+#define KXTF9_HYST_SET                  (0x5F)
+
+/* mode settings */
+#define KXTF9_MODE_SUSPEND     0
+#define KXTF9_MODE_NORMAL      1
+
+#define KXTF9_MAX_DUR (0xFF)
+#define KXTF9_MAX_THS (0xFF)
+#define KXTF9_THS_COUNTS_P_G (32)
+
+#define KXTF9_TABLE_END        0xFF
+#define KXTF9_TABLE_WAIT_MS    0xFE
+
+struct kxtf9_reg {
+       u8 reg;
+       u8 val;
+};
+
+struct kxtf9_compare {
+       int data;
+       u8 val;
+};
+
+static struct kxtf9_compare kxtf9_compare_fs[] = {
+       {2, 0x00},
+       {4, 0x08},
+       {8, 0x10},
+};
+
+static struct kxtf9_compare kxtf9_compare_rate[] = {
+       {25, 0x01},
+       {50, 0x02},
+       {100, 0x03},
+       {200, 0x04},
+       {400, 0x05},
+       {800, 0x06},
+};
+
+static struct kxtf9_reg kxtf9_table_init[] = {
+       {KXTF9_CTRL_REG1, 0x40},
+       {KXTF9_DATA_CTRL_REG, 0x36},
+       {KXTF9_CTRL_REG3, 0xCD},
+       {KXTF9_TABLE_WAIT_MS, 2},
+       {KXTF9_TABLE_END, 0},
+};
+
+static struct kxtf9_reg kxtf9_table_suspend[] = {
+       {KXTF9_CTRL_REG1, 0x40},
+       {KXTF9_INT_CTRL_REG1, 0x00},
+       {KXTF9_WUF_THRESH, 0x02},
+       {KXTF9_DATA_CTRL_REG, 0x00},
+       {KXTF9_WUF_TIMER, 0x00},
+       {KXTF9_CTRL_REG1, 0x40},
+       {KXTF9_TABLE_END, 0},
+};
+
+enum kxtf9_seq_en {
+       KXTF9_SEQ_EN_CTRL_REG1_START = 0,
+       KXTF9_SEQ_EN_INT_CTRL_REG1,
+       KXTF9_SEQ_EN_WUF_THRESH,
+       KXTF9_SEQ_EN_DATA_CTRL_REG,
+       KXTF9_SEQ_EN_WUF_TIMER,
+       KXTF9_SEQ_EN_CTRL_REG1,
+       KXTF9_SEQ_EN_TABLE_END,
+       KXTF9_SEQ_EN_TABLE_SIZE,
+};
+
+static struct kxtf9_reg kxtf9_table_resume[] = {
+       [KXTF9_SEQ_EN_CTRL_REG1_START] = {KXTF9_CTRL_REG1, 0x40},
+       [KXTF9_SEQ_EN_INT_CTRL_REG1] = {KXTF9_INT_CTRL_REG1, 0x00},
+       [KXTF9_SEQ_EN_WUF_THRESH] = {KXTF9_WUF_THRESH, 0x01},
+       [KXTF9_SEQ_EN_DATA_CTRL_REG] = {KXTF9_DATA_CTRL_REG, 0x04},
+       [KXTF9_SEQ_EN_WUF_TIMER] = {KXTF9_WUF_TIMER, 0xFF},
+       [KXTF9_SEQ_EN_CTRL_REG1] = {KXTF9_CTRL_REG1, 0xC0},
+       [KXTF9_SEQ_EN_TABLE_END] = {KXTF9_TABLE_END, 0},
+};
+
+struct kxtf9_data {
+       int mode;
+};
+
+static struct kxtf9_data kxtf9_info = {
+       .mode = KXTF9_MODE_SUSPEND,
+};
+
+static int kxtf9_wr_table(struct inv_gyro_state_s *st, struct kxtf9_reg table[])
+{
+       int err;
+       struct kxtf9_reg *next;
+
+       err = set_3050_bypass(st, 1); /*set to bypass mode */
+       if (err)
+               return err;
+
+       for (next = table; next->reg != KXTF9_TABLE_END; next++) {
+               if (next->reg == KXTF9_TABLE_WAIT_MS) {
+                       msleep(next->val);
+                       continue;
+               }
+
+               err = inv_secondary_write(next->reg, next->val);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int kxtf9_wr(struct inv_gyro_state_s *st, u8 reg, u8 val)
+{
+       int err;
+
+       err = set_3050_bypass(st, 1);
+       if (err)
+               return err;
+
+       err = inv_secondary_write(KXTF9_CTRL_REG1, 0x40);
+       if (err)
+               return err;
+
+       if (reg)
+               err = inv_secondary_write(reg, val);
+       err |= inv_secondary_write(KXTF9_CTRL_REG1,
+                              kxtf9_table_resume[KXTF9_SEQ_EN_CTRL_REG1].val);
+       err |= set_3050_bypass(st, 0);
+       return err;
+}
+
+static int kxtf9_find_least_match(struct kxtf9_compare *cmp, unsigned int n,
+                                 int data, u8 *val)
+{
+       unsigned int i;
+
+       for (i = 0; i < n; i++) {
+               if (data <= cmp[i].data)
+                       break;
+       }
+
+       if (i == n)
+               return -EINVAL;
+
+       *val = cmp[i].val;
+       return 0;
+}
+
+static int kxtf9_setup(struct inv_gyro_state_s *st)
+{
+       int err;
+
+       kxtf9_info.mode = KXTF9_MODE_SUSPEND;
+       err = kxtf9_wr_table(st, kxtf9_table_init);
+       return err;
+}
+
+static int kxtf9_suspend(struct inv_gyro_state_s *st)
+{
+       int err;
+       unsigned char data;
+
+       if (kxtf9_info.mode == KXTF9_MODE_SUSPEND)
+               return 0;
+
+       err = kxtf9_wr_table(st, kxtf9_table_suspend);
+       err |= inv_secondary_read(KXTF9_INT_REL, 1, &data);
+       if (err)
+               return err;
+
+       kxtf9_info.mode = KXTF9_MODE_SUSPEND;
+       return 0;
+}
+
+static int kxtf9_resume(struct inv_gyro_state_s *st)
+{
+       int err;
+
+       if (kxtf9_info.mode == KXTF9_MODE_NORMAL)
+               return 0;
+
+       err = kxtf9_wr_table(st, kxtf9_table_resume);
+       err |= set_3050_bypass(st, 0); /* recover bypass mode */
+       if (err)
+               return err;
+
+       kxtf9_info.mode = KXTF9_MODE_NORMAL;
+       return 0;
+}
+
+static int kxtf9_combine_data(unsigned char *in, short *out)
+{
+       out[0] = (in[0] | (in[1]<<8));
+       out[1] = (in[2] | (in[3]<<8));
+       out[2] = (in[4] | (in[5]<<8));
+       return 0;
+}
+
+static int kxtf9_get_mode(struct inv_gyro_state_s *st)
+{
+       return kxtf9_info.mode;
+};
+
+static int kxtf9_set_lpf(struct inv_gyro_state_s *st, int rate)
+{
+       int err;
+       u8 val = 0;
+
+       err = kxtf9_find_least_match(kxtf9_compare_rate,
+                                    ARRAY_SIZE(kxtf9_compare_rate),
+                                    rate, &val);
+       if (err)
+               return err;
+
+       kxtf9_table_resume[KXTF9_SEQ_EN_DATA_CTRL_REG].val &= 0xF8;
+       kxtf9_table_resume[KXTF9_SEQ_EN_DATA_CTRL_REG].val |= val;
+       if (kxtf9_info.mode < KXTF9_MODE_NORMAL)
+               return 0;
+
+       err = kxtf9_wr(st, KXTF9_DATA_CTRL_REG, val);
+       return err;
+}
+
+static int kxtf9_set_fs(struct inv_gyro_state_s *st, int fs)
+{
+       int err;
+       u8 val = 0;
+
+       err = kxtf9_find_least_match(kxtf9_compare_fs,
+                                    ARRAY_SIZE(kxtf9_compare_fs), fs, &val);
+       if (err)
+               return err;
+
+       kxtf9_table_resume[KXTF9_SEQ_EN_CTRL_REG1].val &= 0xE7;
+       kxtf9_table_resume[KXTF9_SEQ_EN_CTRL_REG1].val |= val;
+       if (kxtf9_info.mode < KXTF9_MODE_NORMAL)
+               return 0;
+
+       err = kxtf9_wr(st, 0, 0); /* NULL reg to update CTRL_REG1 */
+       return err;
+}
+
+static struct inv_mpu_slave slave_kxtf9 = {
+       .suspend = kxtf9_suspend,
+       .resume  = kxtf9_resume,
+       .setup   = kxtf9_setup,
+       .combine_data = kxtf9_combine_data,
+       .get_mode = kxtf9_get_mode,
+       .set_lpf = kxtf9_set_lpf,
+       .set_fs  = kxtf9_set_fs
+};
+
+int inv_register_kxtf9_slave(struct inv_gyro_state_s *st)
+{
+       st->mpu_slave = &slave_kxtf9;
+       return 0;
+}
+
index 8a018ea..ff3b946 100644 (file)
@@ -587,7 +587,6 @@ source "drivers/misc/ti-st/Kconfig"
 source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
-source "drivers/misc/inv_mpu/Kconfig"
 source "drivers/misc/tegra-baseband/Kconfig"
 source "drivers/misc/tegra-cec/Kconfig"
 endmenu
index d23289e..1d7cd35 100644 (file)
@@ -56,7 +56,6 @@ obj-$(CONFIG_WL127X_RFKILL)   += wl127x-rfkill.o
 obj-$(CONFIG_SENSORS_AK8975)   += akm8975.o
 obj-$(CONFIG_SENSORS_NCT1008)  += nct1008.o
 obj-$(CONFIG_BCM4329_RFKILL)   += bcm4329_rfkill.o
-obj-$(CONFIG_INV_SENSORS)      += inv_mpu/
 CFLAGS_tegra-cryptodev.o        = -Werror
 obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o
 obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/
index e399a27..aafb19a 100644 (file)
@@ -1,32 +1,38 @@
 /*
-       $License:
-       Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* 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.
+*
+*/
 
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program.  If not, see <http://www.gnu.org/licenses/>.
-       $
+/**
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    mpu.h
+ *      @brief   mpu definition
  */
 
 #ifndef __MPU_H_
 #define __MPU_H_
 
+#ifdef __KERNEL__
 #include <linux/types.h>
 #include <linux/ioctl.h>
-
-/* Number of axes on each sensor */
-#define GYRO_NUM_AXES               (3)
-#define ACCEL_NUM_AXES              (3)
-#define COMPASS_NUM_AXES            (3)
+#elif defined LINUX
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#else
+#include "mltypes.h"
+#endif
 
 struct mpu_read_write {
        /* Memory address or register address depending on ioctl */
@@ -36,7 +42,8 @@ struct mpu_read_write {
 };
 
 enum mpuirq_data_type {
-       MPUIRQ_DATA_TYPE_MPU_IRQ,
+       MPUIRQ_DATA_TYPE_MPU_DATA_READY_IRQ,
+       MPUIRQ_DATA_TYPE_MPU_FIFO_READY_IRQ,
        MPUIRQ_DATA_TYPE_SLAVE_IRQ,
        MPUIRQ_DATA_TYPE_PM_EVENT,
        MPUIRQ_DATA_TYPE_NUM_TYPES,
@@ -46,24 +53,35 @@ enum mpuirq_data_type {
 #define MPU_PM_EVENT_SUSPEND_PREPARE (3)
 #define MPU_PM_EVENT_POST_SUSPEND    (4)
 
+/**
+ * struct mpuirq_data - structure to report what and when
+ * @interruptcount     : The number of times this IRQ has occured since open
+ * @irqtime            : monotonic time of the IRQ in ns
+ * @data_type          : The type of this IRQ enum mpuirq_data_type
+ * @data               : Data associated with this IRQ
+ */
 struct mpuirq_data {
        __u32 interruptcount;
-       __u64 irqtime;
+       __s64 irqtime_ns;
        __u32 data_type;
        __s32 data;
 };
 
 enum ext_slave_config_key {
+       /* TODO: Remove these first six. */
        MPU_SLAVE_CONFIG_ODR_SUSPEND,
        MPU_SLAVE_CONFIG_ODR_RESUME,
        MPU_SLAVE_CONFIG_FSR_SUSPEND,
        MPU_SLAVE_CONFIG_FSR_RESUME,
+       MPU_SLAVE_CONFIG_IRQ_SUSPEND,
+       MPU_SLAVE_CONFIG_IRQ_RESUME,
+       MPU_SLAVE_CONFIG_ODR,
+       MPU_SLAVE_CONFIG_FSR,
        MPU_SLAVE_CONFIG_MOT_THS,
        MPU_SLAVE_CONFIG_NMOT_THS,
        MPU_SLAVE_CONFIG_MOT_DUR,
        MPU_SLAVE_CONFIG_NMOT_DUR,
-       MPU_SLAVE_CONFIG_IRQ_SUSPEND,
-       MPU_SLAVE_CONFIG_IRQ_RESUME,
+       MPU_SLAVE_CONFIG_IRQ,
        MPU_SLAVE_WRITE_REGISTERS,
        MPU_SLAVE_READ_REGISTERS,
        MPU_SLAVE_CONFIG_INTERNAL_REFERENCE,
@@ -72,8 +90,6 @@ enum ext_slave_config_key {
        MPU_SLAVE_WINDOW,
        MPU_SLAVE_READWINPARAMS,
        MPU_SLAVE_SEARCHOFFSET,
-       /* AKM specific config keys */
-       MPU_SLAVE_READ_SCALE,
        /* MPU3050 and MPU6050 Keys */
        MPU_SLAVE_INT_CONFIG,
        MPU_SLAVE_EXT_SYNC,
@@ -141,16 +157,21 @@ enum ext_slave_type {
        EXT_SLAVE_NUM_TYPES
 };
 enum secondary_slave_type {
-        SECONDARY_SLAVE_TYPE_NONE,
-        SECONDARY_SLAVE_TYPE_ACCEL,
-        SECONDARY_SLAVE_TYPE_COMPASS,
-        SECONDARY_SLAVE_TYPE_PRESSURE,
+       SECONDARY_SLAVE_TYPE_NONE,
+       SECONDARY_SLAVE_TYPE_ACCEL,
+       SECONDARY_SLAVE_TYPE_COMPASS,
+       SECONDARY_SLAVE_TYPE_PRESSURE,
 
-        SECONDARY_SLAVE_TYPE_TYPES
+       SECONDARY_SLAVE_TYPE_TYPES
 };
 
 enum ext_slave_id {
        ID_INVALID = 0,
+       GYRO_ID_MPU3050,
+       GYRO_ID_MPU6050A2,
+       GYRO_ID_MPU6050B1,
+       GYRO_ID_MPU6050B1_NO_ACCEL,
+       GYRO_ID_ITG3500,
 
        ACCEL_ID_LIS331,
        ACCEL_ID_LSM303DLX,
@@ -165,8 +186,8 @@ enum ext_slave_id {
        ACCEL_ID_MMA845X,
        ACCEL_ID_MPU6050,
 
-       COMPASS_ID_AK8975,
        COMPASS_ID_AK8963,
+       COMPASS_ID_AK8975,
        COMPASS_ID_AK8972,
        COMPASS_ID_AMI30X,
        COMPASS_ID_AMI306,
@@ -182,6 +203,8 @@ enum ext_slave_id {
        PRESSURE_ID_BMA085,
 };
 
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
+
 enum ext_slave_endian {
        EXT_SLAVE_BIG_ENDIAN,
        EXT_SLAVE_LITTLE_ENDIAN,
@@ -307,6 +330,10 @@ struct ext_slave_descr {
  * @int_config:                Bits [7:3] of the int config register.
  * @level_shifter:     0: VLogic, 1: VDD
  * @orientation:       Orientation matrix of the gyroscope
+ * @sec_slave_type:     secondary slave device type, can be compass, accel, etc
+ * @sec_slave_id:       id of the secondary slave device
+ * @secondary_i2c_address: secondary device's i2c address
+ * @secondary_orientation: secondary device's orientation matrix
  *
  * Contains platform specific information on how to configure the MPU3050 to
  * work on this platform.  The orientation matricies are 3x3 rotation matricies
@@ -317,65 +344,13 @@ struct ext_slave_descr {
 struct mpu_platform_data {
        __u8 int_config;
        __u8 level_shifter;
-       __s8 orientation[GYRO_NUM_AXES * GYRO_NUM_AXES];
+       __s8 orientation[9];
        enum secondary_slave_type sec_slave_type;
-        enum ext_slave_id sec_slave_id;
-        __u16 secondary_i2c_addr;
-        __u8 secondary_read_reg;
-        __s8 secondary_orientation[9];
-        __u8 key[16];
+       enum ext_slave_id sec_slave_id;
+       __u16 secondary_i2c_addr;
+       __u8 secondary_read_reg;
+       __s8 secondary_orientation[9];
+       __u8 key[16];
 };
 
-#define MPU_IOCTL (0x81) /* Magic number for MPU Iocts */
-/* IOCTL commands for /dev/mpu */
-
-/*--------------------------------------------------------------------------
- * Deprecated, debugging only
- */
-#define MPU_SET_MPU_PLATFORM_DATA      \
-       _IOWR(MPU_IOCTL, 0x01, struct mpu_platform_data)
-#define MPU_SET_EXT_SLAVE_PLATFORM_DATA        \
-       _IOWR(MPU_IOCTL, 0x01, struct ext_slave_platform_data)
-/*--------------------------------------------------------------------------*/
-#define MPU_GET_EXT_SLAVE_PLATFORM_DATA        \
-       _IOWR(MPU_IOCTL, 0x02, struct ext_slave_platform_data)
-#define MPU_GET_MPU_PLATFORM_DATA      \
-       _IOWR(MPU_IOCTL, 0x02, struct mpu_platform_data)
-#define MPU_GET_EXT_SLAVE_DESCR        \
-       _IOWR(MPU_IOCTL, 0x02, struct ext_slave_descr)
-
-#define MPU_READ               _IOWR(MPU_IOCTL, 0x10, struct mpu_read_write)
-#define MPU_WRITE              _IOW(MPU_IOCTL,  0x10, struct mpu_read_write)
-#define MPU_READ_MEM           _IOWR(MPU_IOCTL, 0x11, struct mpu_read_write)
-#define MPU_WRITE_MEM          _IOW(MPU_IOCTL,  0x11, struct mpu_read_write)
-#define MPU_READ_FIFO          _IOWR(MPU_IOCTL, 0x12, struct mpu_read_write)
-#define MPU_WRITE_FIFO         _IOW(MPU_IOCTL,  0x12, struct mpu_read_write)
-
-#define MPU_READ_COMPASS       _IOR(MPU_IOCTL, 0x12, __u8)
-#define MPU_READ_ACCEL         _IOR(MPU_IOCTL, 0x13, __u8)
-#define MPU_READ_PRESSURE      _IOR(MPU_IOCTL, 0x14, __u8)
-
-#define MPU_CONFIG_GYRO                _IOW(MPU_IOCTL, 0x20, struct ext_slave_config)
-#define MPU_CONFIG_ACCEL       _IOW(MPU_IOCTL, 0x21, struct ext_slave_config)
-#define MPU_CONFIG_COMPASS     _IOW(MPU_IOCTL, 0x22, struct ext_slave_config)
-#define MPU_CONFIG_PRESSURE    _IOW(MPU_IOCTL, 0x23, struct ext_slave_config)
-
-#define MPU_GET_CONFIG_GYRO    _IOWR(MPU_IOCTL, 0x20, struct ext_slave_config)
-#define MPU_GET_CONFIG_ACCEL   _IOWR(MPU_IOCTL, 0x21, struct ext_slave_config)
-#define MPU_GET_CONFIG_COMPASS _IOWR(MPU_IOCTL, 0x22, struct ext_slave_config)
-#define MPU_GET_CONFIG_PRESSURE        _IOWR(MPU_IOCTL, 0x23, struct ext_slave_config)
-
-#define MPU_SUSPEND            _IOW(MPU_IOCTL, 0x30, __u32)
-#define MPU_RESUME             _IOW(MPU_IOCTL, 0x31, __u32)
-/* Userspace PM Event response */
-#define MPU_PM_EVENT_HANDLED   _IO(MPU_IOCTL, 0x32)
-
-#define MPU_GET_REQUESTED_SENSORS      _IOR(MPU_IOCTL, 0x40, __u8)
-#define MPU_SET_REQUESTED_SENSORS      _IOW(MPU_IOCTL, 0x40, __u8)
-#define MPU_GET_IGNORE_SYSTEM_SUSPEND  _IOR(MPU_IOCTL, 0x41, __u8)
-#define MPU_SET_IGNORE_SYSTEM_SUSPEND  _IOW(MPU_IOCTL, 0x41, __u8)
-#define MPU_GET_MLDL_STATUS            _IOR(MPU_IOCTL, 0x42, __u8)
-#define MPU_GET_I2C_SLAVES_ENABLED     _IOR(MPU_IOCTL, 0x43, __u8)
-
-
-#endif                         /* __MPU_H_ */
+#endif /* __MPU_H_ */