staging: iio: new invensence mpu6050/6500 driver
authorZhangbin Tong <zebulun.tong@rock-chips.com>
Wed, 18 May 2016 02:18:04 +0000 (10:18 +0800)
committerGerrit Code Review <gerrit@rock-chips.com>
Sun, 12 Jun 2016 09:32:50 +0000 (17:32 +0800)
Change-Id: I0f804afa1f5ad0815fe0b8f3663423db1b1c0e43
Signed-off-by: Zhangbin Tong <zebulun.tong@rock-chips.com>
Signed-off-by: Zorro Liu <lyx@rock-chips.com>
19 files changed:
drivers/staging/iio/Kconfig
drivers/staging/iio/Makefile
drivers/staging/iio/imu/inv_mpu/Kconfig [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/Makefile [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/README [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/dmpKey.h [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/dmpmap.h [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_counters.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_counters.h [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c [new file with mode: 0644]
drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c [new file with mode: 0644]
include/linux/mpu.h
include/uapi/linux/iio/types.h

index 9d7f0004d2d7b25bcd7e67b345aaa4f3ad407d32..c77a484d7b210b62a917a19447a384fa3d78217d 100644 (file)
@@ -11,6 +11,7 @@ source "drivers/staging/iio/cdc/Kconfig"
 source "drivers/staging/iio/frequency/Kconfig"
 source "drivers/staging/iio/gyro/Kconfig"
 source "drivers/staging/iio/impedance-analyzer/Kconfig"
+source "drivers/staging/iio/imu/inv_mpu/Kconfig"
 source "drivers/staging/iio/light/Kconfig"
 source "drivers/staging/iio/magnetometer/Kconfig"
 source "drivers/staging/iio/meter/Kconfig"
index d87106135b270133a50067a07f31f9459a8a6776..9c91287a307e58329bb73c8d44492c16ba7fe2a4 100644 (file)
@@ -16,6 +16,7 @@ obj-y += cdc/
 obj-y += frequency/
 obj-y += gyro/
 obj-y += impedance-analyzer/
+obj-y += imu/inv_mpu/
 obj-y += light/
 obj-y += magnetometer/
 obj-y += meter/
diff --git a/drivers/staging/iio/imu/inv_mpu/Kconfig b/drivers/staging/iio/imu/inv_mpu/Kconfig
new file mode 100644 (file)
index 0000000..c84db81
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices and combos
+#
+
+config INV_MPU_IIO
+       tristate "Invensense MPU devices"
+       depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU
+       default n
+       help
+         This driver supports the Invensense MPU devices.
+         This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250.
+         This driver can be built as a module. The module will be called
+         inv-mpu-iio.
+
+config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250
+       bool  "Invensense MPU3050 slave accelerometer device for bma250"
+       depends on INV_MPU_IIO
+       default n
+       help
+         This is slave device enable MPU3050 accelerometer slave device.
+         Right now, it is only bma250. For other acceleromter device,
+         it can be added to this menu if the proper interface is filled.
+         There are some interface function to be defined.
+
+config INV_TESTING
+       bool "Invensense IIO testing hooks"
+       depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
+       default n
+       help
+         This flag enables display of additional testing information from the
+         Invensense IIO drivers
diff --git a/drivers/staging/iio/imu/inv_mpu/Makefile b/drivers/staging/iio/imu/inv_mpu/Makefile
new file mode 100644 (file)
index 0000000..393ecad
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs := inv_mpu_core.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_trigger.o
+inv-mpu-iio-objs += inv_mpu_misc.o
+inv-mpu-iio-objs += inv_mpu3050_iio.o
+inv-mpu-iio-objs += dmpDefaultMPU6050.o
+ifeq ($(CONFIG_INV_TESTING), y)
+inv-mpu-iio-objs += inv_counters.o
+endif
+ifeq ($(VERSION),4)
+ifeq ($(PATCHLEVEL),4)
+CFLAGS_inv_mpu_core.o          += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_i2c.o           += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_ring.o          += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_trigger.o       += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_common.o        += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_load_image.o    += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu_misc.o          += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_inv_mpu3050_iio.o       += -Idrivers/iio -DINV_KERNEL_3_10
+CFLAGS_dmpDefaultMPU6050.o     += -Idrivers/iio -DINV_KERNEL_3_10
+else
+CFLAGS_inv_mpu_core.o      += -Idrivers/staging/iio
+CFLAGS_inv_mpu_ring.o      += -Idrivers/staging/iio
+CFLAGS_inv_mpu_trigger.o   += -Idrivers/staging/iio
+CFLAGS_inv_mpu_misc.o      += -Idrivers/staging/iio
+CFLAGS_inv_mpu3050_iio.o   += -Idrivers/staging/iio
+CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio
+endif
+endif
+
+# the Bosch BMA250 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y)
+inv-mpu-iio-objs += inv_slave_bma250.o
+ifeq ($(VERSION),4)
+ifeq ($(PATCHLEVEL),4)
+CFLAGS_inv_slave_bma250.o   += -Idrivers/iio
+else
+CFLAGS_inv_slave_bma250.o   += -Idrivers/staging/iio
+endif
+endif
+endif
+
diff --git a/drivers/staging/iio/imu/inv_mpu/README b/drivers/staging/iio/imu/inv_mpu/README
new file mode 100644 (file)
index 0000000..668335e
--- /dev/null
@@ -0,0 +1,603 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+    > Interrupt Pin
+    > Platform Data
+- Board File Modifications for Secondary I2C Configuration
+    > MPU-6050 + AKM8963 on the secondary I2C interface
+    > MPU-6500 + AKM8963 on the secondary I2C interface
+    > MPU-9150
+    > MPU-9250
+    > MPU-3050 + BMA250 on the secondary I2C interface
+- Board File Modifications for Invensense Devices
+    > MPU-3050
+    > ITG-3500
+    > MPU-6050
+    > MPU-6500
+    > MPU-6XXX
+    > MPU-9150
+    > MPU-9250
+- IIO Subsystem
+    > Communicating with the Driver in Userspace
+    > ITG-3500
+    > MPU-6050 and MPU-6500
+    > MPU-9150
+    > MPU-9250
+    > MPU-3050 + BMA250 on the secondary I2C interface
+- Suspend and Resume
+- DMP Event
+- Motion Event
+- Streaming Data to an Userspace Application
+- Recommended Sysfs Entry Setup Sequence
+    > With DMP Firmware
+    > Without DMP Firmware
+- Test Applications
+    > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+    > Running Test Applications with MPU-3050/ITG-3500
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The Invensense driver currently supports the following sensors:
+- ITG-3500
+- MPU-6050
+- MPU-9150
+- MPU-6500
+- MPU-9250
+- MPU-3050
+- MPU-6XXX(either MPU6050 or MPU6500, driver to do auto detection)
+
+The slave address of each device is either 0x68 or 0x69, depending on the AD0
+pin value of the device. Please refer to the appropriate product specification
+document for further information regarding the AD0 pin. The driver supports both
+addresses.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_mpu_core.c
+- inv_mpu_misc.c
+- inv_mpu_trigger.c
+- inv_mpu3050_iio.c
+- inv_mpu_iio.h
+- inv_mpu_ring.c
+- inv_slave_bma250.c
+- dmpDefaultMPU6050.c
+- dmpkey.h
+- dmpmap.h
+- mpu.h
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add mpu.h to "kernel/include/linux".
+- Add all other files to drivers/staging/iio/imu/inv_mpu
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+    modify "drivers/staging/iio/imu/Kconfig" with:
+    >> source "drivers/staging/iio/imu/inv_mpu/Kconfig"
+
+    modify "drivers/staging/iio/imu/Makefile" with:
+    >> obj-y += inv_mpu/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Interrupt Pin
+-------------
+The hardcoded value of 140 corresponds to the GPIO input pin connected to the
+Invensense device's interrupt pin.
+This pin will most likely be different for your platform, and the value should
+be changed accordingly.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+MPU-6050 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+       .int_config  = 0x00,
+       .level_shifter = 0,
+       .orientation = {  -1,  0,  0,
+                          0,  1,  0,
+                          0,  0, -1 },
+       .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+       .sec_slave_id   = COMPASS_ID_AK8963,
+       .secondary_i2c_addr = 0x0E,
+        .secondary_orientation = { 0,  1, 0,
+                                   1,  0,  0,
+                                   0,  0, -1 },
+};
+
+MPU-6500 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+        .int_config  = 0x00,
+        .level_shifter = 0,
+        .orientation = {  -1,  0,  0,
+                           0,  1,  0,
+                           0,  0, -1 },
+        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+        .sec_slave_id   = COMPASS_ID_AK8963,
+        .secondary_i2c_addr = 0x0E,
+        .secondary_orientation = { 0,  1, 0,
+                                   1,  0,  0,
+                                   0,  0, -1 },
+};
+
+MPU-9150
+--------
+For MPU-9150, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+       .int_config  = 0x10,
+       .level_shifter = 0,
+       .orientation = {   1,  0,  0,
+                          0,  1,  0,
+                          0,  0,  1 },
+       .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+       .sec_slave_id   = COMPASS_ID_AK8975,
+       .secondary_i2c_addr = 0x0C,
+        .secondary_orientation = { 0,  1, 0,
+                                   1,  0,  0,
+                                   0,  0, -1 },
+};
+
+MPU-9250
+--------
+For MPU-9250, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+        .int_config  = 0x00,
+        .level_shifter = 0,
+        .orientation = {   1,  0,  0,
+                           0,  1,  0,
+                           0,  0, 1 },
+        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+        .sec_slave_id   = COMPASS_ID_AK8963,
+        .secondary_i2c_addr = 0x0C,
+        .secondary_orientation = { 0,  1, 0,
+                                   -1, 0,  0,
+                                   0,  0,  1 },
+};
+
+MPU-3050 + BMA250 on the secondary I2C interface
+------------------------------------------------
+For BMA250 on the secondary I2C bus, please provide the following information.
+
+static struct mpu_platform_data gyro_platform_data = {
+       .int_config  = 0x10,
+       .level_shifter = 0,
+       .orientation = {  -1,  0,  0,
+                          0,  1,  0,
+                          0,  0, -1 },
+       .sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL,
+       .sec_slave_id   = ACCEL_ID_BMA250,
+       .secondary_i2c_addr = 0x18,
+       .secondary_orientation = {   -1,  0,   0,
+                                                                0,  -1,   0,
+                                                                0,  0,   1 },
+};
+
+
+Board File Modifications for Invensense Devices
+===============================================
+For Invensense devices, please provide the i2c init data as shown in the
+examples below.
+
+In the _i2c_init function, the device is registered in the following manner:
+
+    // arch/arm/mach-omap2/board-omap4panda.c
+    // in static int __init omap4_panda_i2c_init(void)
+    omap_register_i2c_bus(4, 400,
+                          single_chip_board_info,
+                          ARRAY_SIZE(single_chip_board_info));
+
+MPU-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+       {
+               I2C_BOARD_INFO("mpu3050", 0x68),
+               .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+               .platform_data = &gyro_platform_data,
+       },
+};
+
+ITG-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+       {
+               I2C_BOARD_INFO("itg3500", 0x68),
+               .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+       },
+};
+
+MPU6050
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+       {
+               I2C_BOARD_INFO("mpu6050", 0x68),
+               .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+               .platform_data = &gyro_platform_data,
+       },
+};
+
+MPU6500
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+        {
+                I2C_BOARD_INFO("mpu6500", 0x68),
+                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+        },
+};
+
+MPU6XXX
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+        {
+                I2C_BOARD_INFO("mpu6xxx", 0x68),
+                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+        },
+};
+
+MPU9150
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+       {
+               I2C_BOARD_INFO("mpu9150", 0x68),
+               .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+               .platform_data = &gyro_platform_data,
+       },
+};
+
+MPU9250
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+        {
+                I2C_BOARD_INFO("mpu9250", 0x68),
+                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+                .platform_data = &gyro_platform_data,
+        },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+    - iio:device0
+    - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+ITG-3500
+--------
+temperature (Read-only)
+--Read temperature data directly from the temperature register.
+
+sampling_frequency (Read/write)
+--Configure the ADC sampling rate and FIFO output rate.
+
+sampling_frequency_available(read-only)
+--show commonly used frequency
+
+clock_source (Read-only)
+--Check which clock-source is used by the chip.
+
+power_state (Read/write)
+--turn on/off the power supply
+
+self_test (read-only)
+--read this entry trigger self test. The return value is D.
+D is the success/fail.
+For different chip, the result is different for success/fail.
+1 means success 0 means fail. The LSB of D is for gyro; the bit
+next to LSB of D is for accel. The bit 2 of D is for compass result.
+
+key (read-only)
+--show the key value of this driver. Used by MPL.
+
+gyro_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+MPU-6050 and MPU-6500
+---------------------
+MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above).
+In addition, it has the files below:
+
+gyro_enable (read/write)
+--enable/disable gyro functionality. Affects raw_gyro. Turning this off this
+  will shut down gyro and save power.
+
+accl_enable (read/write)
+--enable/disable accelerometer functionality. Affects raw_accl.
+Turning this off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+--Flag indicating the whether firmware is loaded or not in the DMP engine.
+0 means no firmware loaded. 1 means firmware is already loaded . This
+flag can only be written as 0. It internally updates to 1.
+
+dmp_on(read/write)
+--This entry controls whether to run DMP or not.
+Write 1 to enable DMP and write 0 to disable dmp.
+Please note that firmware_loaded must be 1 in order to enable DMP.
+
+dmp_int_on(read/write)
+--This entry controls whether dmp interrupt is on/off.
+Please note that firmware_loaded must be 1.
+Also, we'd like to remind you that it is sometimes advantageous to
+turn interrupts off while the DMP is running.
+
+dmp_output_rate
+--control dmp output rate when dmp is on.
+
+dmp_event_int_on(read/write)
+--This entry controls whether dmp event interrupt is on/off.
+Please note that turning this on will turn off the data interrupt.
+Interrupts will be generated only when events occur.
+This is useful for saving power when the system is waiting for a special event
+to wake up.
+
+dmp_firmware (write only binary file)
+--DMP firmware code is loaded into this entry.
+If loading is successful, the firmware_loaded flag will be updated to 1.
+In order to load new firmware, the firmware_loaded flag must be first set to 0.
+
+accel_matrix
+--orientation matrix for accelerometer.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+pedometer_time
+pedometer_steps,
+--Pedometer related entries
+
+event_tap
+event_display_orientation
+event_accel_motion
+event_smd
+--Event related entries.
+Please poll these entries to read their values. Direct reads will yield
+meaningless results.
+Further details are provided in the DMP Events section of this README.
+
+tap_on
+--Controls tap function of DMP
+
+tap_time
+tap_min_count
+tap_threshold
+--Tap related entries. Controls various parameters of tap function.
+
+display_orientation_on
+--Turn on/off display orientation function of DMP.
+
+smd_enable
+enable SMD(Significant Motion Detection) detection.
+
+smd_threshold
+This set the threshold of the motion when SMD start to be triggered. The
+value is in acclerometer counts.
+
+smd_delay_threshold
+This sets the threshold of time after which SMD can be triggered.
+The value is in seconds.
+
+smd_delay_threshold2
+This sets the threshold of time during which SMD can be triggered (after the
+smd_delay_threshold timer has expired).
+The value is in seconds.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+Low power accel motion interrupt related settings.
+if motion_lpa_on is set, this will disable all engines except accel. Accel will
+enter low power mode and the whole chip will be turned on/off at specific frequency.
+-----------------------------------------------------------------------------
+motion_lpa_duration
+--set motion duration. in ms. This means filtered out all the motino interrupts
+  during this period.
+
+motion_lpa_threshold
+--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg.
+
+motion_lpa_on
+--turn on/off motion function.
+
+motion_lpa_freq
+--motion lpa frequency. which determines power on/off frequency.
+------------------------------------------------------------------------------
+MPU-9150
+--------
+MPU-9150 has all of MPU-6050's entries. It also has two additional entries,
+described below.
+
+compass_enable (read/write)
+--Enables compass function.
+
+compass_matrix (read-only)
+--Compass orientation matrix
+
+MPU-3050 with BMA250 on secondary I2C interface
+-----------------------------------------------
+MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry.
+It also has two additional entries, shown below:
+
+accl_matrix
+
+accl_enable
+
+Suspend and Resume
+===================================================
+The suspend and resume functions are call backs registered to the system
+and executed when the system goes in suspend and resumes.
+It is enabled when CONFIG_PM is defined.
+The current behavior is simple:
+- suspend will turn off the chip
+- resume will turn on the chip
+
+However, it is possible for the driver to do more complex things;
+for example, leaving pedometers running when system is off. This can save whole
+system power while letting pedometer working. Other behaviors are possible
+too.
+
+DMP Event
+=========
+A DMP Event is an event that is output by the DMP unit within the Invensense
+device (MPU).
+Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP.
+
+There are four sysfs entries for DMP events:
+- event_tap
+- event_display_orientation
+- event_accel_motion
+- event_smd
+
+These events must be polled before reading.
+
+The proper method to poll sysfs is as follows:
+1. open file.
+2. dummy read.
+3. poll.
+4. once the poll passed, use fopen and fread to read the sysfs entry.
+5. interpret the data.
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+gyro/accel/compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write a 1 to power_state to turn on the chip. This is the default setting
+   after installing the driver.
+2. Write the desired output rate to fifo_rate.
+3. Write 1 to enable to turn on the event.
+4. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+5. Parse this string to obtain each gyro/accel/compass element.
+6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp.
+7. If compass is enabled, the output will contain compass data.
+
+
+Recommended Sysfs Entry Setup Senquence
+=======================================
+
+Without DMP Firmware
+--------------------
+1. Set "power_state" to 1,
+2. Set the scale and fifo rate values according to your needs.
+3. Set gyro_enable, accel_enable, and compass_enable according to your needs.
+   For example:
+    - If you only want gyro data, set accel_enable to 0 (and compass_enable to
+      0, if applicable).
+    - If you only want accel data, set gyro_enable to 0 (and compass_enable to
+      0, if applicable).
+    - If you only want compass data, set gyro_enable to 0 and accel_enable to 0.
+4. Set "enable" to 1.
+5. You will now get the output that you want.
+
+With DMP Firmware
+-----------------
+1. Set "power_state" to 1.
+2. Write "0" to firmware_loaded if it is not zero already.
+3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware
+   image.
+4. Make sure firmware_loaded is 1 after loading the DMP image.
+5. Make appropriate configurations as shown above in the "without DMP firmware"
+   case.
+6. Set dmp_on to 1.
+7. Set "enable" to 1.
+
+Please note that the enable function uses the enable entry under
+"/sys/bus/iio/devices/iio:device0/buffer"
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+---------------------------------------------------------
+To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices,
+please use the following commands:
+
+1. For tap/display orientation events:
+   mpu_iio -c 10 -l 3 -p
+
+2. In addition, to test the motion interrupt (and no_motion on MPU6050) use:
+   mpu_iio -c 10 -l 3 -p -m
+
+3. For printing data normally:
+   mpu_iio -c 10 -l 3 -r
+
+Running Test Applications with MPU-3050/ITG-3500
+------------------------------------------------
+To run test applications with MPU-3050 or ITG-3500 devices,
+please use the following command:
+
+1. For printing data normally:
+   mpu_iio -c 10 -l 3 -r
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
+
+Stress test application
+=================================
+A stress test application is located under software/simple_apps/stress_iio.
+This application simulates HAL's usage calls to the driver. It creates three
+threads. One for data read; one for event read; one for sysfs control.
+It can run without any parameters or run with some control parameters. Please
+see README in the same directories for details.
+
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c b/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c
new file mode 100644 (file)
index 0000000..bcffec9
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include "inv_mpu_iio.h"
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_LP_QUAT             (2500)
+#define END_ORIENT_TEMP         (2063)
+#define CFG_27                  (2530)
+#define CFG_23                  (2533)
+#define CFG_PED_ENABLE          (2620)
+#define CFG_FIFO_ON_EVENT       (2475)
+#define CFG_PED_INT             (2873)
+#define END_PREDICTION_UPDATE   (1958)
+#define X_GRT_Y_TMP             (1555)
+#define CFG_DR_INT              (1029)
+#define CFG_AUTH                (1035)
+#define UPDATE_PROP_ROT         (2032)
+#define END_COMPARE_Y_X_TMP2    (1652)
+#define SKIP_X_GRT_Y_TMP        (1556)
+#define SKIP_END_COMPARE        (1632)
+#define FCFG_3                  (1087)
+#define FCFG_2                  (1066)
+#define FCFG_1                  (1062)
+#define END_COMPARE_Y_X_TMP3    (1631)
+#define FCFG_7                  (1073)
+#define FCFG_6                  (1105)
+#define FLAT_STATE_END          (1910)
+#define SWING_END_4             (1813)
+#define SWING_END_2             (1762)
+#define SWING_END_3             (1784)
+#define SWING_END_1             (1747)
+#define CFG_8                   (2506)
+#define CFG_15                  (2515)
+#define CFG_16                  (2534)
+#define CFG_EXT_GYRO_BIAS       (1184)
+#define END_COMPARE_Y_X_TMP     (1604)
+#define DO_NOT_UPDATE_PROP_ROT  (2036)
+#define CFG_7                   (1403)
+#define FLAT_STATE_END_TEMP     (1880)
+#define END_COMPARE_Y_X         (1681)
+#define SMD_TP2                 (1366)
+#define SKIP_SWING_END_1        (1748)
+#define SKIP_SWING_END_3        (1785)
+#define SKIP_SWING_END_2        (1763)
+#define SMD_TP1                 (1343)
+#define TILTG75_START           (1869)
+#define CFG_6                   (2541)
+#define TILTL75_END             (1866)
+#define END_ORIENT              (2081)
+#define TILTL75_START           (1840)
+#define CFG_MOTION_BIAS         (1405)
+#define X_GRT_Y                 (1605)
+#define TEMPLABEL               (2105)
+#define CFG_DISPLAY_ORIENT_INT  (2050)
+
+#define CFG_GYRO_RAW_DATA       (2510)
+#define X_GRT_Y_TMP2            (1576)
+
+#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_EXT_GYRO_BIAS_X       (61 * 16)
+#define D_EXT_GYRO_BIAS_Y       (61 * 16 + 4)
+#define D_EXT_GYRO_BIAS_Z       (61 * 16 + 8)
+#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_SMD_ENABLE            (18 * 16)
+#define D_SMD_MOT_THLD          (20 * 16)
+#define D_SMD_DELAY_THLD        (21 * 16 + 4)
+#define D_SMD_DELAY2_THLD       (21 * 16 + 12)
+#define D_SMD_EXE_STATE         (22 * 16)
+#define D_SMD_DELAY_CNTR        (21 * 16)
+
+#define D_AUTH_OUT              (992)
+#define D_AUTH_IN               (996)
+#define D_AUTH_A                (1000)
+#define D_AUTH_B                (1004)
+
+#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        (964)
+#define D_PEDSTD_DECI           (768 + 0xA0)
+
+#define D_HOST_NO_MOT           (976)
+#define D_ACCEL_BIAS            (660)
+
+#define D_ORIENT_GAP            (76)
+
+#define D_TILT0_H               (48)
+#define D_TILT0_L               (50)
+#define D_TILT1_H               (52)
+#define D_TILT1_L               (54)
+#define D_TILT2_H               (56)
+#define D_TILT2_L               (58)
+#define D_TILT3_H               (60)
+#define D_TILT3_L               (62)
+
+/* Batch mode */
+#define D_BM_BATCH_CNTR         (27 * 16 + 4)
+#define D_BM_BATCH_THLD         (27 * 16 + 8)
+#define D_BM_ENABLE             (28 * 16 + 6)
+#define D_BM_NUMWORD_TOFILL     (28 * 16 + 4)
+
+static const struct tKeyLabel dmpTConfig[] = {
+       {KEY_CFG_27,                    CFG_27},
+       {KEY_CFG_23,                    CFG_23},
+       {KEY_CFG_PED_ENABLE,            CFG_PED_ENABLE},
+       {KEY_CFG_FIFO_ON_EVENT,         CFG_FIFO_ON_EVENT},
+       {KEY_CFG_DR_INT,                CFG_DR_INT},
+       {KEY_CFG_AUTH,                  CFG_AUTH},
+       {KEY_FCFG_1,                    FCFG_1},
+       {KEY_FCFG_3,                    FCFG_3},
+       {KEY_FCFG_2,                    FCFG_2},
+       {KEY_CFG_DISPLAY_ORIENT_INT,    CFG_DISPLAY_ORIENT_INT},
+       {KEY_FCFG_7,                    FCFG_7},
+       {KEY_FCFG_6,                    FCFG_6},
+       {KEY_CFG_8,                     CFG_8},
+       {KEY_CFG_15,                    CFG_15},
+       {KEY_CFG_16,                    CFG_16},
+       {KEY_CFG_EXT_GYRO_BIAS,         CFG_EXT_GYRO_BIAS},
+       {KEY_CFG_6,                     CFG_6},
+       {KEY_CFG_LP_QUAT,               CFG_LP_QUAT},
+       {KEY_CFG_7,                     CFG_7},
+       {KEY_CFG_MOTION_BIAS,           CFG_MOTION_BIAS},
+       {KEY_CFG_DISPLAY_ORIENT_INT,    CFG_DISPLAY_ORIENT_INT},
+       {KEY_CFG_GYRO_RAW_DATA,         CFG_GYRO_RAW_DATA},
+       {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_178,               D_1_178},
+       {KEY_D_1_218,               D_1_218},
+       {KEY_D_1_232,               D_1_232},
+       {KEY_D_1_250,               D_1_250},
+       {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},
+       {KEY_D_HOST_NO_MOT,  D_HOST_NO_MOT},
+       {KEY_D_ACCEL_BIAS,  D_ACCEL_BIAS},
+       {KEY_D_ORIENT_GAP,  D_ORIENT_GAP},
+       {KEY_D_TILT0_H, D_TILT0_H},
+       {KEY_D_TILT0_L, D_TILT0_L},
+       {KEY_D_TILT1_H, D_TILT1_H},
+       {KEY_D_TILT1_L, D_TILT1_L},
+       {KEY_D_TILT2_H, D_TILT2_H},
+       {KEY_D_TILT2_L, D_TILT2_L},
+       {KEY_D_TILT3_H, D_TILT3_H},
+       {KEY_D_TILT3_L, D_TILT3_L},
+       {KEY_CFG_EXT_GYRO_BIAS_X, D_EXT_GYRO_BIAS_X},
+       {KEY_CFG_EXT_GYRO_BIAS_Y, D_EXT_GYRO_BIAS_Y},
+       {KEY_CFG_EXT_GYRO_BIAS_Z, D_EXT_GYRO_BIAS_Z},
+       {KEY_CFG_PED_INT, CFG_PED_INT},
+       {KEY_SMD_ENABLE, D_SMD_ENABLE},
+       {KEY_SMD_ACCEL_THLD, D_SMD_MOT_THLD},
+       {KEY_SMD_DELAY_THLD, D_SMD_DELAY_THLD},
+       {KEY_SMD_DELAY2_THLD, D_SMD_DELAY2_THLD},
+       {KEY_SMD_ENABLE_TESTPT1, SMD_TP1},
+       {KEY_SMD_ENABLE_TESTPT2, SMD_TP2},
+       {KEY_SMD_EXE_STATE, D_SMD_EXE_STATE},
+       {KEY_SMD_DELAY_CNTR, D_SMD_DELAY_CNTR},
+       {KEY_BM_ENABLE, D_BM_ENABLE},
+       {KEY_BM_BATCH_CNTR, D_BM_BATCH_CNTR},
+       {KEY_BM_BATCH_THLD, D_BM_BATCH_THLD},
+       {KEY_BM_NUMWORD_TOFILL, D_BM_NUMWORD_TOFILL}
+};
+
+#define NUM_LOCAL_KEYS (ARRAY_SIZE(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) {
+               pr_err("ERROR!! key not exist=%d!\n", key);
+               return 0xffff;
+       }
+       if (0xffff == keys[key].addr) {
+               pr_err("ERROR!!key not local=%d!\n", key);
+               dump_stack();
+       }
+       return keys[key].addr;
+}
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpKey.h b/drivers/staging/iio/imu/inv_mpu/dmpKey.h
new file mode 100644 (file)
index 0000000..4c70ec2
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * 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 definition
+ *      @details This file is part of invensense mpu 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)
+#define KEY_CFG_LP_QUAT             (KEY_FCFG_MAG_MOV + 1)
+#define KEY_CFG_GYRO_RAW_DATA       (KEY_CFG_LP_QUAT + 1)
+#define KEY_CFG_EXT_GYRO_BIAS       (KEY_CFG_GYRO_RAW_DATA + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_X     (KEY_CFG_EXT_GYRO_BIAS + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Y     (KEY_CFG_EXT_GYRO_BIAS_X + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Z     (KEY_CFG_EXT_GYRO_BIAS_Y + 1)
+#define KEY_bad_compass                                (KEY_CFG_EXT_GYRO_BIAS_Z + 1)
+#define KEY_COMPASS_CHG_SENSITIVITY (KEY_bad_compass + 1)
+#define KEY_CCS_HEADING_THLD        (KEY_COMPASS_CHG_SENSITIVITY + 1)
+#define KEY_CCS_TIME_THLD           (KEY_CCS_HEADING_THLD + 1)
+#define KEY_CCS_DOTP_THLD           (KEY_CCS_TIME_THLD + 1)
+#define KEY_CCS_COMP_CNTR           (KEY_CCS_DOTP_THLD + 1)
+#define KEY_CFG_NM_DET              (KEY_CCS_COMP_CNTR + 1)
+#define KEY_SMD_ENABLE              (KEY_CFG_NM_DET + 1)
+#define KEY_SMD_ACCEL_THLD          (KEY_SMD_ENABLE + 1)
+#define KEY_SMD_DELAY_THLD          (KEY_SMD_ACCEL_THLD + 1)
+#define KEY_SMD_DELAY2_THLD         (KEY_SMD_DELAY_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT1      (KEY_SMD_DELAY2_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT2      (KEY_SMD_ENABLE_TESTPT1 + 1)
+#define KEY_SMD_EXE_STATE           (KEY_SMD_ENABLE_TESTPT2 + 1)
+#define KEY_SMD_DELAY_CNTR          (KEY_SMD_EXE_STATE + 1)
+
+#define KEY_BREAK (81)
+#if KEY_SMD_DELAY_CNTR != KEY_BREAK
+#error
+#endif
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER        (KEY_BREAK + 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)
+
+#define KEY_D_GYRO_BIAS_X               (KEY_D_2_252 + 1)
+#define KEY_D_GYRO_BIAS_Y               (KEY_D_GYRO_BIAS_X + 1)
+#define KEY_D_GYRO_BIAS_Z               (KEY_D_GYRO_BIAS_Y + 1)
+#define KEY_D_ACC_BIAS_X                (KEY_D_GYRO_BIAS_Z + 1)
+#define KEY_D_ACC_BIAS_Y                (KEY_D_ACC_BIAS_X + 1)
+#define KEY_D_ACC_BIAS_Z                (KEY_D_ACC_BIAS_Y + 1)
+#define KEY_D_GYRO_ENABLE               (KEY_D_ACC_BIAS_Z + 1)
+#define KEY_D_ACCEL_ENABLE              (KEY_D_GYRO_ENABLE + 1)
+#define KEY_D_QUAT_ENABLE               (KEY_D_ACCEL_ENABLE + 1)
+#define KEY_D_OUTPUT_ENABLE             (KEY_D_QUAT_ENABLE + 1)
+#define KEY_D_ACCEL_CNTR                (KEY_D_OUTPUT_ENABLE + 1)
+#define KEY_D_GYRO_CNTR                 (KEY_D_ACCEL_CNTR + 1)
+#define KEY_D_QUAT0_CNTR                (KEY_D_GYRO_CNTR + 1)
+#define KEY_D_QUAT1_CNTR                (KEY_D_QUAT0_CNTR + 1)
+#define KEY_D_QUAT2_CNTR                (KEY_D_QUAT1_CNTR + 1)
+#define KEY_D_CR_TIME_G                 (KEY_D_QUAT2_CNTR + 1)
+#define KEY_D_CR_TIME_A                 (KEY_D_CR_TIME_G + 1)
+#define KEY_D_CR_TIME_Q                 (KEY_D_CR_TIME_A + 1)
+#define KEY_D_CS_TAX                    (KEY_D_CR_TIME_Q + 1)
+#define KEY_D_CS_TAY                    (KEY_D_CS_TAX + 1)
+#define KEY_D_CS_TAZ                    (KEY_D_CS_TAY + 1)
+#define KEY_D_CS_TGX                    (KEY_D_CS_TAZ + 1)
+#define KEY_D_CS_TGY                    (KEY_D_CS_TGX + 1)
+#define KEY_D_CS_TGZ                    (KEY_D_CS_TGY + 1)
+#define KEY_D_CS_TQ0                    (KEY_D_CS_TGZ + 1)
+#define KEY_D_CS_TQ1                    (KEY_D_CS_TQ0 + 1)
+#define KEY_D_CS_TQ2                    (KEY_D_CS_TQ1 + 1)
+#define KEY_D_CS_TQ3                    (KEY_D_CS_TQ2 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_GAIN              (KEY_D_CS_TQ3 + 1)
+#define KEY_CPASS_BIAS_X            (KEY_CPASS_GAIN + 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)
+
+#define KEY_X_GRT_Y_TMP             (KEY_D_ACSZ + 1)
+#define KEY_SKIP_X_GRT_Y_TMP        (KEY_X_GRT_Y_TMP + 1)
+#define KEY_SKIP_END_COMPARE        (KEY_SKIP_X_GRT_Y_TMP + 1)
+#define KEY_END_COMPARE_Y_X_TMP2    (KEY_SKIP_END_COMPARE + 1)
+#define KEY_CFG_DISPLAY_ORIENT_INT  (KEY_END_COMPARE_Y_X_TMP2 + 1)
+#define KEY_NO_ORIENT_INTERRUPT     (KEY_CFG_DISPLAY_ORIENT_INT + 1)
+#define KEY_END_COMPARE_Y_X_TMP     (KEY_NO_ORIENT_INTERRUPT + 1)
+#define KEY_END_ORIENT_1            (KEY_END_COMPARE_Y_X_TMP + 1)
+#define KEY_END_COMPARE_Y_X         (KEY_END_ORIENT_1 + 1)
+#define KEY_END_ORIENT              (KEY_END_COMPARE_Y_X + 1)
+#define KEY_X_GRT_Y                 (KEY_END_ORIENT + 1)
+#define KEY_NOT_TIME_MINUS_1        (KEY_X_GRT_Y + 1)
+#define KEY_END_COMPARE_Y_X_TMP3    (KEY_NOT_TIME_MINUS_1 + 1)
+#define KEY_X_GRT_Y_TMP2            (KEY_END_COMPARE_Y_X_TMP3 + 1)
+
+/*Shake Keys */
+#define KEY_D_0_64                  (KEY_X_GRT_Y_TMP2 + 1)
+#define KEY_D_2_4                   (KEY_D_0_64 + 1)
+#define KEY_D_2_8                   (KEY_D_2_4 + 1)
+#define KEY_D_2_48                  (KEY_D_2_8 + 1)
+#define KEY_D_2_92                  (KEY_D_2_48 + 1)
+#define KEY_D_2_94                  (KEY_D_2_92 + 1)
+#define KEY_D_2_160                 (KEY_D_2_94 + 1)
+#define KEY_D_3_180                 (KEY_D_2_160 + 1)
+#define KEY_D_3_184                 (KEY_D_3_180 + 1)
+#define KEY_D_3_188                 (KEY_D_3_184 + 1)
+#define KEY_D_3_208                 (KEY_D_3_188 + 1)
+#define KEY_D_3_240                 (KEY_D_3_208 + 1)
+#define KEY_RETRACTION_1            (KEY_D_3_240 + 1)
+#define KEY_RETRACTION_2            (KEY_RETRACTION_1 + 1)
+#define KEY_RETRACTION_3            (KEY_RETRACTION_2 + 1)
+#define KEY_RETRACTION_4            (KEY_RETRACTION_3 + 1)
+#define KEY_CFG_SHAKE_INT           (KEY_RETRACTION_4 + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT              (KEY_CFG_SHAKE_INT + 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)
+#define KEY_CFG_PED_INT             (KEY_D_PEDSTD_DECI + 1)
+#define KEY_CFG_PED_ENABLE          (KEY_CFG_PED_INT + 1)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT           (KEY_CFG_PED_ENABLE + 1)
+
+/*Host Based Accel Bias*/
+#define KEY_D_ACCEL_BIAS            (KEY_D_HOST_NO_MOT + 1)
+
+/*Screen/Display Orientation Keys*/
+#define KEY_D_ORIENT_GAP            (KEY_D_ACCEL_BIAS + 1)
+#define KEY_D_TILT0_H               (KEY_D_ORIENT_GAP + 1)
+#define KEY_D_TILT0_L               (KEY_D_TILT0_H + 1)
+#define KEY_D_TILT1_H               (KEY_D_TILT0_L + 1)
+#define KEY_D_TILT1_L               (KEY_D_TILT1_H + 1)
+#define KEY_D_TILT2_H               (KEY_D_TILT1_L + 1)
+#define KEY_D_TILT2_L               (KEY_D_TILT2_H  + 1)
+#define KEY_D_TILT3_H               (KEY_D_TILT2_L + 1)
+#define KEY_D_TILT3_L               (KEY_D_TILT3_H + 1)
+
+/* Stream keys */
+#define KEY_STREAM_P_GYRO_Z         (KEY_D_TILT3_L + 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)
+
+/* Batch mode */
+#define KEY_BM_ENABLE               (KEY_STREAM_P_ACCEL_Z + 1)
+#define KEY_BM_BATCH_THLD           (KEY_BM_ENABLE + 1)
+#define KEY_BM_BATCH_CNTR           (KEY_BM_BATCH_THLD + 1)
+#define KEY_BM_NUMWORD_TOFILL       (KEY_BM_BATCH_CNTR + 1)
+
+/* Watermark */
+#define KEY_CFG_WATERMARK_H         (KEY_BM_NUMWORD_TOFILL + 1)
+#define KEY_CFG_WATERMARK_L         (KEY_CFG_WATERMARK_H + 1)
+
+/* FIFO output control */
+#define KEY_CFG_OUT_ACCL            (KEY_CFG_WATERMARK_L + 1)
+#define KEY_CFG_OUT_GYRO            (KEY_CFG_OUT_ACCL + 1)
+#define KEY_CFG_OUT_3QUAT           (KEY_CFG_OUT_GYRO + 1)
+#define KEY_CFG_OUT_6QUAT           (KEY_CFG_OUT_3QUAT + 1)
+#define KEY_CFG_OUT_PQUAT           (KEY_CFG_OUT_6QUAT + 1)
+#define KEY_CFG_FIFO_INT            (KEY_CFG_OUT_PQUAT + 1)
+/* Ped Step detection */
+#define KEY_CFG_PEDSTEP_DET         (KEY_CFG_FIFO_INT + 1)
+
+/* Screen Orientation data */
+#define KEY_SO_DATA                 (KEY_CFG_PEDSTEP_DET + 1)
+
+/* MPU for DMP Android K */
+#define KEY_P_HW_ID                 (KEY_SO_DATA + 1)
+
+#define NUM_KEYS                    (KEY_P_HW_ID + 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/staging/iio/imu/inv_mpu/dmpmap.h b/drivers/staging/iio/imu/inv_mpu/dmpmap.h
new file mode 100644 (file)
index 0000000..28f59af
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+* 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    dmpmap.h
+ *      @brief   dmp map definition
+ *      @details This file is part of invensense mpu driver code
+ *
+ */
+#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/staging/iio/imu/inv_mpu/inv_counters.c b/drivers/staging/iio/imu/inv_mpu/inv_counters.c
new file mode 100644 (file)
index 0000000..3b26ca9
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * @file inv_counters.c
+ * @brief Exports i2c read write counts through sysfs
+ *
+ * @version 0.1
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+       uint32_t i2c_tempreads;
+       uint32_t i2c_mpureads;
+       uint32_t i2c_mpuwrites;
+       uint32_t i2c_accelreads;
+       uint32_t i2c_accelwrites;
+       uint32_t i2c_compassreads;
+       uint32_t i2c_compasswrites;
+       uint32_t i2c_compassirq;
+       uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+static ssize_t i2c_counters_show(struct class *cls,
+                       struct class_attribute *attr, char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE,
+               "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+               jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+               mpu_irq ? kstat_irqs(mpu_irq) : 0,
+               Counters.i2c_tempreads,
+               Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+               accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+               Counters.i2c_accelreads, Counters.i2c_accelwrites,
+               compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+               Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+       switch (type) {
+       case IRQ_MPU:
+               mpu_irq = irq;
+               break;
+       case IRQ_ACCEL:
+               accel_irq = irq;
+               break;
+       case IRQ_COMPASS:
+               compass_irq = irq;
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
+
+void inv_iio_counters_tempread(int count)
+{
+       Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+       Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+       Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+       Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+       Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+       Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+       Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+       Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+       Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+       __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+       __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+       .name = "inv_counters",
+       .owner = THIS_MODULE,
+       .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+       memset(&Counters, 0, sizeof(Counters));
+
+       return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+       class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_counters.h b/drivers/staging/iio/imu/inv_mpu/inv_counters.h
new file mode 100644 (file)
index 0000000..d60dac9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * @file  inv_counters.h
+ * @brief Debug file to keep track of various counters for the InvenSense
+ *        sensor drivers.
+ *
+ * @version 0.1
+ */
+
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_INV_TESTING
+
+enum irqtype {
+       IRQ_MPU,
+       IRQ_ACCEL,
+       IRQ_COMPASS
+};
+
+#define INV_I2C_INC_MPUREAD(x)         inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x)                inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x)       inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x)      inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x)     inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x)    inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x)                inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq)      inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()       inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ()         inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c
new file mode 100644 (file)
index 0000000..370c77f
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+* 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_iio.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of invensense mpu 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_mpu_iio.h"
+#define MPU3050_NACK_MIN_TIME (2 * 1000)
+#define MPU3050_NACK_MAX_TIME (3 * 1000)
+
+#define MPU3050_ONE_MPU_TIME 20
+#define MPU3050_BOGUS_ADDR  0x7F
+int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+       return 0;
+}
+
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       u8 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,
+                                               MPU3050_BOGUS_ADDR);
+               if (result)
+                       return result;
+               /*
+               * 2) wait enough time for a nack to occur, then go into
+               *    bypass mode:
+               */
+               usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+               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
+               */
+               msleep(MPU3050_ONE_MPU_TIME);
+
+               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;
+               usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+       }
+       return 0;
+}
+
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+       reg->fifo_en         = REG_3050_FIFO_EN;
+       reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV;
+       reg->lpf             = REG_3050_LPF;
+       reg->fifo_count_h    = REG_3050_FIFO_COUNT_H;
+       reg->fifo_r_w        = REG_3050_FIFO_R_W;
+       reg->user_ctrl       = REG_3050_USER_CTRL;
+       reg->pwr_mgmt_1      = REG_3050_PWR_MGMT_1;
+       reg->raw_gyro        = REG_3050_RAW_GYRO;
+       reg->raw_accl        = REG_3050_AUX_XOUT_H;
+       reg->temperature     = REG_3050_TEMPERATURE;
+       reg->int_enable      = REG_3050_INT_ENABLE;
+       reg->int_status      = REG_3050_INT_STATUS;
+}
+
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+       struct inv_reg_map_s *reg;
+       u8 data, p;
+       int result;
+       reg = &st->reg;
+       if (en) {
+               data = INV_CLK_PLL;
+               p = (BITS_3050_POWER1 | data);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+               if (result)
+                       return result;
+               p = (BITS_3050_POWER2 | data);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+               if (result)
+                       return result;
+               p = data;
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+               msleep(SENSOR_UP_TIME);
+       } else {
+               p = BITS_3050_GYRO_STANDBY;
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+       }
+
+       return result;
+}
+
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+       int result;
+       if (NULL == st->mpu_slave)
+               return -EPERM;
+       if (en)
+               result = st->mpu_slave->resume(st);
+       else
+               result = st->mpu_slave->suspend(st);
+
+       return result;
+}
+
+/**
+ *  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 iio_dev *indio_dev)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       u8 data;
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+       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;
+       if (st->plat_data.level_shifter)
+               data |= BIT_3050_VDDIO;
+       result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+       if (result)
+               return result;
+
+       reg = &st->reg;
+       /*2000dps full scale range*/
+       result = inv_i2c_single_write(st, reg->lpf,
+                               (INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT)
+                               | 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,
+                                       ONE_K_HZ/INIT_FIFO_RATE - 1);
+       if (result)
+               return result;
+       st->chip_config.fifo_rate = INIT_FIFO_RATE;
+       st->chip_config.new_fifo_rate = INIT_FIFO_RATE;
+       st->irq_dur_ns            = INIT_DUR_TIME;
+       if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) &&
+               st->mpu_slave) {
+               result = st->mpu_slave->setup(st);
+               if (result)
+                       return result;
+               result = st->mpu_slave->set_fs(st, INV_FS_02G);
+               if (result)
+                       return result;
+               result = st->mpu_slave->set_lpf(st, INIT_FIFO_RATE);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+/**
+ *  set_power_mpu3050() - set power of mpu3050.
+ *  @st:       Device driver instance.
+ *  @power_on:  on/off
+ */
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on)
+{
+       struct inv_reg_map_s *reg;
+       u8 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;
+       } else {
+               data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+               if (result)
+                       return result;
+       }
+       if (power_on) {
+               msleep(POWER_UP_TIME);
+               if (st->mpu_slave) {
+                       result = st->mpu_slave->resume(st);
+                       if (result)
+                               return result;
+               }
+       }
+       st->chip_config.is_asleep = !power_on;
+
+       return 0;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c
new file mode 100644 (file)
index 0000000..f10fce4
--- /dev/null
@@ -0,0 +1,2270 @@
+/*
+* 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_mpu_core.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This driver currently works for the
+ *               MPU3050/MPU6050/MPU9150/MPU6500/MPU9250 devices.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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_mpu_iio.h"
+#ifdef INV_KERNEL_3_10
+#include <linux/iio/sysfs.h>
+#else
+#include "sysfs.h"
+#endif
+#include "inv_counters.h"
+
+s64 get_time_ns(void)
+{
+       struct timespec ts;
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
+static const short AKM8975_ST_Lower[3] = {-100, -100, -1000};
+static const short AKM8975_ST_Upper[3] = {100, 100, -300};
+
+static const short AKM8972_ST_Lower[3] = {-50, -50, -500};
+static const short AKM8972_ST_Upper[3] = {50, 50, -100};
+
+static const short AKM8963_ST_Lower[3] = {-200, -200, -3200};
+static const short AKM8963_ST_Upper[3] = {200, 200, -800};
+
+/* This is for compatibility for power state. Should remove once HAL
+   does not use power_state sysfs entry */
+static bool fake_asleep;
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+       {119, "ITG3500"},
+       { 63, "MPU3050"},
+       {117, "MPU6050"},
+       {118, "MPU9150"},
+       {119, "MPU6500"},
+       {118, "MPU9250"},
+       {128, "MPU6515"},
+};
+
+static void inv_setup_reg(struct inv_reg_map_s *reg)
+{
+       reg->sample_rate_div    = REG_SAMPLE_RATE_DIV;
+       reg->lpf                = REG_CONFIG;
+       reg->bank_sel           = REG_BANK_SEL;
+       reg->user_ctrl          = REG_USER_CTRL;
+       reg->fifo_en            = REG_FIFO_EN;
+       reg->gyro_config        = REG_GYRO_CONFIG;
+       reg->accl_config        = REG_ACCEL_CONFIG;
+       reg->fifo_count_h       = REG_FIFO_COUNT_H;
+       reg->fifo_r_w           = REG_FIFO_R_W;
+       reg->raw_gyro           = REG_RAW_GYRO;
+       reg->raw_accl           = REG_RAW_ACCEL;
+       reg->temperature        = REG_TEMPERATURE;
+       reg->int_enable         = REG_INT_ENABLE;
+       reg->int_status         = REG_INT_STATUS;
+       reg->pwr_mgmt_1         = REG_PWR_MGMT_1;
+       reg->pwr_mgmt_2         = REG_PWR_MGMT_2;
+       reg->mem_start_addr     = REG_MEM_START_ADDR;
+       reg->mem_r_w            = REG_MEM_RW;
+       reg->prgm_strt_addrh    = REG_PRGM_STRT_ADDRH;
+};
+
+/**
+ *  inv_i2c_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:This is not re-implementation of i2c_smbus_read because i2c
+ *       address could be specified in this case. We could have two different
+ *       i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, u16 i2c_addr,
+       u8 reg, u16 length, u8 *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[0].scl_rate = 200*1000; */
+
+       msgs[1].addr = i2c_addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].buf = data;
+       msgs[1].len = length;
+       /* msgs[1].scl_rate = 200*1000; */
+
+       res = i2c_transfer(st->sl_handle, msgs, 2);
+
+       if (res < 2) {
+               if (res >= 0)
+                       res = -EIO;
+       } else
+               res = 0;
+
+       INV_I2C_INC_MPUWRITE(3);
+       INV_I2C_INC_MPUREAD(length);
+       {
+               char *read = 0;
+               pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name,
+                        i2c_addr, reg, length,
+                        wr_pr_debug_begin(data, length, read),
+                        wr_pr_debug_end(read));
+       }
+       return res;
+}
+
+/**
+ *  inv_i2c_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.
+ *  NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ *       address could be specified in this case. We could have two different
+ *       i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+       u16 i2c_addr, u8 reg, u8 data)
+{
+       u8 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;
+       /* msg.scl_rate = 200*1000; */
+
+       pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+       INV_I2C_INC_MPUWRITE(3);
+
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res == 0)
+                       res = -EIO;
+               return res;
+       } else
+               return 0;
+}
+
+static int inv_switch_engine(struct inv_mpu_iio_s *st, bool en, u32 mask)
+{
+       struct inv_reg_map_s *reg;
+       u8 data, mgmt_1;
+       int result;
+       reg = &st->reg;
+       /* switch clock needs to be careful. Only when gyro is on, can
+          clock source be switched to gyro. Otherwise, it must be set to
+          internal clock */
+       if (BIT_PWR_GYRO_STBY == mask) {
+               result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1);
+               if (result)
+                       return result;
+
+               mgmt_1 &= ~BIT_CLK_MASK;
+       }
+
+       if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+               /* turning off gyro requires switch to internal clock first.
+                  Then turn off gyro engine */
+               mgmt_1 |= INV_CLK_INTERNAL;
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+                                               mgmt_1);
+               if (result)
+                       return result;
+       }
+
+       result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data);
+       if (result)
+               return result;
+       if (en)
+               data &= (~mask);
+       else
+               data |= mask;
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+       if (result)
+               return result;
+
+       if ((BIT_PWR_GYRO_STBY == mask) && en) {
+               /* only gyro on needs sensor up time */
+               msleep(SENSOR_UP_TIME);
+               /* after gyro is on & stable, switch internal clock to PLL */
+               mgmt_1 |= INV_CLK_PLL;
+               result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+                                               mgmt_1);
+               if (result)
+                       return result;
+       }
+       if ((BIT_PWR_ACCL_STBY == mask) && en)
+               msleep(REG_UP_TIME);
+
+       return 0;
+}
+
+/**
+ *  inv_lpa_freq() - store current low power frequency setting.
+ */
+static int inv_lpa_freq(struct inv_mpu_iio_s *st, int lpa_freq)
+{
+       unsigned long result;
+       u8 d;
+       const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7};
+
+       if (lpa_freq > MAX_LPA_FREQ_PARAM)
+               return -EINVAL;
+
+       if (INV_MPU6500 == st->chip_type) {
+               d = mpu6500_lpa_mapping[lpa_freq];
+               result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d);
+               if (result)
+                       return result;
+       }
+       st->chip_config.lpa_freq = lpa_freq;
+
+       return 0;
+}
+
+static int set_power_itg(struct inv_mpu_iio_s *st, bool power_on)
+{
+       struct inv_reg_map_s *reg;
+       u8 data;
+       int result;
+
+       if ((!power_on) == st->chip_config.is_asleep)
+               return 0;
+       reg = &st->reg;
+       if (power_on)
+               data = 0;
+       else
+               data = BIT_SLEEP;
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+       if (result)
+               return result;
+
+       if (power_on) {
+               if (INV_MPU6500 == st->chip_type)
+                       msleep(POWER_UP_TIME);
+               else
+                       msleep(REG_UP_TIME);
+       }
+
+       st->chip_config.is_asleep = !power_on;
+
+       return 0;
+}
+
+/**
+ *  inv_init_config() - Initialize hardware, disable FIFO.
+ *  @indio_dev:        Device driver instance.
+ *  Initial configuration:
+ *  FSR: +/- 2000DPS
+ *  DLPF: 42Hz
+ *  FIFO rate: 50Hz
+ */
+static int inv_init_config(struct iio_dev *indio_dev)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+
+       reg = &st->reg;
+
+#if 0
+       /* set int latch en */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG, 0x20);
+       if (result)
+               return result;
+#endif
+       result = inv_i2c_single_write(st, reg->gyro_config,
+               INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
+       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,
+                                       ONE_K_HZ / INIT_FIFO_RATE - 1);
+       if (result)
+               return result;
+       st->chip_config.fifo_rate = INIT_FIFO_RATE;
+       st->chip_config.new_fifo_rate = INIT_FIFO_RATE;
+       st->irq_dur_ns            = INIT_DUR_TIME;
+       st->chip_config.prog_start_addr = DMP_START_ADDR;
+       st->chip_config.dmp_output_rate = INIT_DMP_OUTPUT_RATE;
+       st->self_test.samples = INIT_ST_SAMPLES;
+       st->self_test.threshold = INIT_ST_THRESHOLD;
+       if (INV_ITG3500 != st->chip_type) {
+               st->chip_config.accl_fs = INV_FS_02G;
+               result = inv_i2c_single_write(st, reg->accl_config,
+                       (INV_FS_02G << ACCL_CONFIG_FSR_SHIFT));
+               if (result)
+                       return result;
+               st->tap.time = INIT_TAP_TIME;
+               st->tap.thresh = INIT_TAP_THRESHOLD;
+               st->tap.min_count = INIT_TAP_MIN_COUNT;
+               st->smd.threshold = MPU_INIT_SMD_THLD;
+               st->smd.delay     = MPU_INIT_SMD_DELAY_THLD;
+               st->smd.delay2    = MPU_INIT_SMD_DELAY2_THLD;
+
+               result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR,
+                                               INIT_MOT_DUR);
+               if (result)
+                       return result;
+               st->mot_int.mot_dur = INIT_MOT_DUR;
+
+               result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR,
+                                               INIT_MOT_THR);
+               if (result)
+                       return result;
+               st->mot_int.mot_thr = INIT_MOT_THR;
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_compass_scale_show() - show compass scale.
+ */
+static int inv_compass_scale_show(struct inv_mpu_iio_s *st, int *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;
+
+       return IIO_VAL_INT;
+}
+
+/**
+ *  inv_sensor_show() - Read gyro/accel data directly from registers.
+ */
+static int inv_sensor_show(struct inv_mpu_iio_s  *st, int reg, int axis,
+                                       int *val)
+{
+       int ind, result;
+       u8 d[2];
+
+       ind = (axis - IIO_MOD_X) * 2;
+       result = i2c_smbus_read_i2c_block_data(st->client,
+                                              reg + ind, 2, d);
+       if (result != 2)
+               return -EINVAL;
+       *val = (short)be16_to_cpup((__be16 *)(d));
+
+       return IIO_VAL_INT;
+}
+
+/**
+ *  mpu_read_raw() - read raw method.
+ */
+static int mpu_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *chan,
+                       int *val, int *val2, long mask)
+{
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       int result;
+
+       switch (mask) {
+       case 0:
+               /* if enabled, power is on already */
+               if (!st->chip_config.enable)
+                       return -EBUSY;
+               switch (chan->type) {
+               case IIO_ANGL_VEL:
+                       if (!st->chip_config.gyro_enable)
+                               return -EPERM;
+                       return inv_sensor_show(st, st->reg.raw_gyro,
+                                               chan->channel2, val);
+               case IIO_ACCEL:
+                       if (!st->chip_config.accl_enable)
+                               return -EPERM;
+                       return inv_sensor_show(st, st->reg.raw_accl,
+                                               chan->channel2, val);
+               case IIO_MAGN:
+                       if (!st->chip_config.compass_enable)
+                               return -EPERM;
+                       *val = st->raw_compass[chan->channel2 - IIO_MOD_X];
+                       return IIO_VAL_INT;
+               case IIO_QUATERNION:
+                       if (!(st->chip_config.dmp_on
+                               && st->chip_config.quaternion_on))
+                               return -EPERM;
+                       if (IIO_MOD_R == chan->channel2)
+                               *val = st->raw_quaternion[0];
+                       else
+                               *val = st->raw_quaternion[chan->channel2 -
+                                                         IIO_MOD_X + 1];
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->type) {
+               case IIO_ANGL_VEL:
+               {
+                       const s16 gyro_scale[] = {250, 500, 1000, 2000};
+
+                       *val = gyro_scale[st->chip_config.fsr];
+
+                       return IIO_VAL_INT;
+               }
+               case IIO_ACCEL:
+               {
+                       const s16 accel_scale[] = {2, 4, 8, 16};
+                       *val = accel_scale[st->chip_config.accl_fs] *
+                                       st->chip_info.multi;
+                       return IIO_VAL_INT;
+               }
+               case IIO_MAGN:
+                       return inv_compass_scale_show(st, val);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_CALIBBIAS:
+               if (st->chip_config.self_test_run_once == 0) {
+                       /* This can only be run when enable is zero */
+                       if (st->chip_config.enable)
+                               return -EBUSY;
+                       mutex_lock(&indio_dev->mlock);
+
+                       result = inv_power_up_self_test(st);
+                       if (result)
+                               goto error_info_calibbias;
+                       result = inv_do_test(st, 0,  st->gyro_bias,
+                               st->accel_bias);
+                       if (result)
+                               goto error_info_calibbias;
+                       st->chip_config.self_test_run_once = 1;
+error_info_calibbias:
+                       /* Reset Accel and Gyro full scale range
+                          back to default value */
+                       inv_recover_setting(st);
+                       mutex_unlock(&indio_dev->mlock);
+               }
+
+               switch (chan->type) {
+               case IIO_ANGL_VEL:
+                       *val = st->gyro_bias[chan->channel2 - IIO_MOD_X];
+                       return IIO_VAL_INT;
+               case IIO_ACCEL:
+                       *val = st->accel_bias[chan->channel2 - IIO_MOD_X] *
+                                       st->chip_info.multi;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_OFFSET:
+               switch (chan->type) {
+               case IIO_ACCEL:
+                       *val = st->input_accel_bias[chan->channel2 - IIO_MOD_X];
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+/**
+ *  inv_write_fsr() - Configure the gyro's scale range.
+ */
+static int inv_write_fsr(struct inv_mpu_iio_s *st, int fsr)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       reg = &st->reg;
+       if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM))
+               return -EINVAL;
+       if (fsr == st->chip_config.fsr)
+               return 0;
+
+       if (INV_MPU3050 == st->chip_type)
+               result = inv_i2c_single_write(st, reg->lpf,
+                       (fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf);
+       else
+               result = inv_i2c_single_write(st, reg->gyro_config,
+                       fsr << GYRO_CONFIG_FSR_SHIFT);
+
+       if (result)
+               return result;
+       st->chip_config.fsr = fsr;
+
+       return 0;
+}
+
+/**
+ *  inv_write_accel_fs() - Configure the accelerometer's scale range.
+ */
+static int inv_write_accel_fs(struct inv_mpu_iio_s *st, int fs)
+{
+       int result;
+       struct inv_reg_map_s *reg;
+
+       reg = &st->reg;
+       if (fs < 0 || fs > MAX_ACCL_FS_PARAM)
+               return -EINVAL;
+       if (fs == st->chip_config.accl_fs)
+               return 0;
+       if (INV_MPU3050 == st->chip_type)
+               result = st->mpu_slave->set_fs(st, fs);
+       else
+               result = inv_i2c_single_write(st, reg->accl_config,
+                               (fs << ACCL_CONFIG_FSR_SHIFT));
+       if (result)
+               return result;
+
+       st->chip_config.accl_fs = fs;
+
+       return 0;
+}
+
+/**
+ *  inv_write_compass_scale() - Configure the compass's scale range.
+ */
+static int inv_write_compass_scale(struct inv_mpu_iio_s  *st, int data)
+{
+       char d, en;
+       int result;
+       if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+               return 0;
+       en = !!data;
+       if (st->compass_scale == en)
+               return 0;
+       d = (DATA_AKM_MODE_SM | (st->compass_scale << AKM8963_SCALE_SHIFT));
+       result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d);
+       if (result)
+               return result;
+       st->compass_scale = en;
+
+       return 0;
+}
+
+/**
+ *  mpu_write_raw() - write raw method.
+ */
+static int mpu_write_raw(struct iio_dev *indio_dev,
+                              struct iio_chan_spec const *chan,
+                              int val,
+                              int val2,
+                              long mask) {
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       int result;
+
+       if (st->chip_config.enable)
+               return -EBUSY;
+       mutex_lock(&indio_dev->mlock);
+       result = st->set_power_state(st, true);
+       if (result) {
+               mutex_unlock(&indio_dev->mlock);
+               return result;
+       }
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->type) {
+               case IIO_ANGL_VEL:
+                       result = inv_write_fsr(st, val);
+                       break;
+               case IIO_ACCEL:
+                       result = inv_write_accel_fs(st, val);
+                       break;
+               case IIO_MAGN:
+                       result = inv_write_compass_scale(st, val);
+                       break;
+               default:
+                       result = -EINVAL;
+                       break;
+               }
+               break;
+       case IIO_CHAN_INFO_OFFSET:
+               switch (chan->type) {
+               case IIO_ACCEL:
+                       if (!st->chip_config.firmware_loaded) {
+                               result = -EPERM;
+                               goto error_write_raw;
+                       }
+                       result = inv_set_accel_bias_dmp(st);
+                       if (result)
+                               goto error_write_raw;
+                       st->input_accel_bias[chan->channel2 - IIO_MOD_X] = val;
+                       result = 0;
+                       break;
+               default:
+                       result = -EINVAL;
+                       break;
+               }
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+
+error_write_raw:
+       result |= st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+
+       return result;
+}
+
+/**
+ *  inv_fifo_rate_store() - Set fifo rate.
+ */
+static int inv_fifo_rate_store(struct inv_mpu_iio_s *st, int fifo_rate)
+{
+       if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+               return -EINVAL;
+
+       if (st->chip_config.has_compass) {
+               st->compass_divider = COMPASS_RATE_SCALE * fifo_rate /
+                                       ONE_K_HZ;
+               if (st->compass_divider > 0)
+                       st->compass_divider -= 1;
+               st->compass_counter = 0;
+       }
+       st->irq_dur_ns = (ONE_K_HZ / fifo_rate) * NSEC_PER_MSEC;
+       st->chip_config.new_fifo_rate = fifo_rate;
+
+       return 0;
+}
+
+/**
+ *  inv_reg_dump_show() - Register dump 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 iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       if (!st->chip_config.enable)
+               st->set_power_state(st, true);
+       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);
+       }
+       if (!st->chip_config.enable)
+               st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+
+       return bytes_printed;
+}
+
+int write_be32_key_to_mem(struct inv_mpu_iio_s *st,
+                                       u32 data, int key)
+{
+       cpu_to_be32s(&data);
+       return mem_w_key(key, sizeof(data), (u8 *)&data);
+}
+
+/**
+ * inv_quaternion_on() -  calling this function will store
+ *                                 current quaternion on
+ */
+static int inv_quaternion_on(struct inv_mpu_iio_s *st,
+                                struct iio_buffer *ring, bool en)
+{
+       st->chip_config.quaternion_on = en;
+       if (!en) {
+               clear_bit(INV_MPU_SCAN_QUAT_R, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_X, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_Y, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_Z, ring->scan_mask);
+       }
+
+       return 0;
+}
+
+/**
+ * inv_dmp_attr_store() -  calling this function will store current
+ *                        dmp parameter settings
+ */
+static ssize_t inv_dmp_attr_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+       int result, data;
+
+       mutex_lock(&indio_dev->mlock);
+       if (st->chip_config.enable) {
+               result = -EBUSY;
+               goto dmp_attr_store_fail;
+       }
+       if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) {
+               if (!st->chip_config.firmware_loaded) {
+                       result = -EINVAL;
+                       goto dmp_attr_store_fail;
+               }
+               result = st->set_power_state(st, true);
+               if (result)
+                       goto dmp_attr_store_fail;
+       }
+
+       result = kstrtoint(buf, 10, &data);
+       if (result)
+               goto dmp_attr_store_fail;
+       switch (this_attr->address) {
+       case ATTR_DMP_SMD_ENABLE:
+       {
+               u8 on[] = {0, 1};
+               u8 off[] = {0, 0};
+               u8 *d;
+               if (data)
+                       d = on;
+               else
+                       d = off;
+               result = mem_w_key(KEY_SMD_ENABLE, ARRAY_SIZE(on), d);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->chip_config.smd_enable = !!data;
+       }
+               break;
+       case ATTR_DMP_SMD_THLD:
+               if (data < 0 || data > SHRT_MAX)
+                       goto dmp_attr_store_fail;
+               result = write_be32_key_to_mem(st, data << 16,
+                                               KEY_SMD_ACCEL_THLD);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->smd.threshold = data;
+               break;
+       case ATTR_DMP_SMD_DELAY_THLD:
+               if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+                       goto dmp_attr_store_fail;
+               result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+                                               KEY_SMD_DELAY_THLD);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->smd.delay = data;
+               break;
+       case ATTR_DMP_SMD_DELAY_THLD2:
+               if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+                       goto dmp_attr_store_fail;
+               result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+                                               KEY_SMD_DELAY2_THLD);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->smd.delay2 = data;
+               break;
+       case ATTR_DMP_TAP_ON:
+               result = inv_enable_tap_dmp(st, !!data);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->chip_config.tap_on = !!data;
+               break;
+       case ATTR_DMP_TAP_THRESHOLD: {
+               const char ax[] = {INV_TAP_AXIS_X, INV_TAP_AXIS_Y,
+                                                       INV_TAP_AXIS_Z};
+               int i;
+               if (data < 0 || data > USHRT_MAX) {
+                       result = -EINVAL;
+                       goto dmp_attr_store_fail;
+               }
+               for (i = 0; i < ARRAY_SIZE(ax); i++) {
+                       result = inv_set_tap_threshold_dmp(st, ax[i], data);
+                       if (result)
+                               goto dmp_attr_store_fail;
+               }
+               st->tap.thresh = data;
+               break;
+       }
+       case ATTR_DMP_TAP_MIN_COUNT:
+               if (data < 0 || data > USHRT_MAX) {
+                       result = -EINVAL;
+                       goto dmp_attr_store_fail;
+               }
+               result = inv_set_min_taps_dmp(st, data);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->tap.min_count = data;
+               break;
+       case ATTR_DMP_TAP_TIME:
+               if (data < 0 || data > USHRT_MAX) {
+                       result = -EINVAL;
+                       goto dmp_attr_store_fail;
+               }
+               result = inv_set_tap_time_dmp(st, data);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->tap.time = data;
+               break;
+       case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+               result = inv_set_display_orient_interrupt_dmp(st, !!data);
+               if (result)
+                       goto dmp_attr_store_fail;
+               st->chip_config.display_orient_on = !!data;
+               break;
+       /* from here, power of chip is not turned on */
+       case ATTR_DMP_ON:
+               st->chip_config.dmp_on = !!data;
+               break;
+       case ATTR_DMP_INT_ON:
+               st->chip_config.dmp_int_on = !!data;
+               break;
+       case ATTR_DMP_EVENT_INT_ON:
+               st->chip_config.dmp_event_int_on = !!data;
+               break;
+       case ATTR_DMP_OUTPUT_RATE:
+               if (data <= 0 || data > MAX_DMP_OUTPUT_RATE) {
+                       result = -EINVAL;
+                       goto dmp_attr_store_fail;
+               }
+               st->chip_config.dmp_output_rate = data;
+               if (st->chip_config.has_compass) {
+                       st->compass_dmp_divider = COMPASS_RATE_SCALE * data /
+                                                       ONE_K_HZ;
+                       if (st->compass_dmp_divider > 0)
+                               st->compass_dmp_divider -= 1;
+                       st->compass_counter = 0;
+               }
+               break;
+       case ATTR_DMP_QUATERNION_ON:
+               result = inv_quaternion_on(st, indio_dev->buffer, !!data);
+               break;
+#ifdef CONFIG_INV_TESTING
+       case ATTR_DEBUG_SMD_ENABLE_TESTP1:
+       {
+               u8 d[] = {0x42};
+               result = st->set_power_state(st, true);
+               if (result)
+                       goto dmp_attr_store_fail;
+               result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d);
+               if (result)
+                       goto dmp_attr_store_fail;
+       }
+               break;
+       case ATTR_DEBUG_SMD_ENABLE_TESTP2:
+       {
+               u8 d[] = {0x42};
+               result = st->set_power_state(st, true);
+               if (result)
+                       goto dmp_attr_store_fail;
+               result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d);
+               if (result)
+                       goto dmp_attr_store_fail;
+       }
+               break;
+#endif
+       default:
+               result = -EINVAL;
+               goto dmp_attr_store_fail;
+       }
+
+dmp_attr_store_fail:
+       if ((this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) &&
+                                       (!st->chip_config.enable))
+               result |= st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+       if (result)
+               return result;
+
+       return count;
+}
+
+/**
+ * inv_attr_show() -  calling this function will show current
+ *                        dmp parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+       int result;
+       s8 *m;
+
+       switch (this_attr->address) {
+       case ATTR_DMP_SMD_ENABLE:
+               return sprintf(buf, "%d\n", st->chip_config.smd_enable);
+       case ATTR_DMP_SMD_THLD:
+               return sprintf(buf, "%d\n", st->smd.threshold);
+       case ATTR_DMP_SMD_DELAY_THLD:
+               return sprintf(buf, "%d\n", st->smd.delay);
+       case ATTR_DMP_SMD_DELAY_THLD2:
+               return sprintf(buf, "%d\n", st->smd.delay2);
+       case ATTR_DMP_TAP_ON:
+               return sprintf(buf, "%d\n", st->chip_config.tap_on);
+       case ATTR_DMP_TAP_THRESHOLD:
+               return sprintf(buf, "%d\n", st->tap.thresh);
+       case ATTR_DMP_TAP_MIN_COUNT:
+               return sprintf(buf, "%d\n", st->tap.min_count);
+       case ATTR_DMP_TAP_TIME:
+               return sprintf(buf, "%d\n", st->tap.time);
+       case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+               return sprintf(buf, "%d\n",
+                       st->chip_config.display_orient_on);
+
+       case ATTR_DMP_ON:
+               return sprintf(buf, "%d\n", st->chip_config.dmp_on);
+       case ATTR_DMP_INT_ON:
+               return sprintf(buf, "%d\n", st->chip_config.dmp_int_on);
+       case ATTR_DMP_EVENT_INT_ON:
+               return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on);
+       case ATTR_DMP_OUTPUT_RATE:
+               return sprintf(buf, "%d\n",
+                               st->chip_config.dmp_output_rate);
+       case ATTR_DMP_QUATERNION_ON:
+               return sprintf(buf, "%d\n", st->chip_config.quaternion_on);
+
+       case ATTR_MOTION_LPA_ON:
+               return sprintf(buf, "%d\n", st->mot_int.mot_on);
+       case ATTR_MOTION_LPA_FREQ:{
+               const char *f[] = {"1.25", "5", "20", "40"};
+               return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]);
+       }
+       case ATTR_MOTION_LPA_THRESHOLD:
+               return sprintf(buf, "%d\n", st->mot_int.mot_thr);
+
+       case ATTR_SELF_TEST_SAMPLES:
+               return sprintf(buf, "%d\n", st->self_test.samples);
+       case ATTR_SELF_TEST_THRESHOLD:
+               return sprintf(buf, "%d\n", st->self_test.threshold);
+       case ATTR_GYRO_ENABLE:
+               return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+       case ATTR_ACCL_ENABLE:
+               return sprintf(buf, "%d\n", st->chip_config.accl_enable);
+       case ATTR_COMPASS_ENABLE:
+               return sprintf(buf, "%d\n", st->chip_config.compass_enable);
+       case ATTR_POWER_STATE:
+               return sprintf(buf, "%d\n", !fake_asleep);
+       case ATTR_FIRMWARE_LOADED:
+               return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+       case ATTR_SAMPLING_FREQ:
+               return sprintf(buf, "%d\n", st->chip_config.new_fifo_rate);
+
+       case ATTR_SELF_TEST:
+               if (st->chip_config.enable)
+                       return -EBUSY;
+               mutex_lock(&indio_dev->mlock);
+               if (INV_MPU3050 == st->chip_type)
+                       result = 1;
+               else
+                       result = inv_hw_self_test(st);
+               mutex_unlock(&indio_dev->mlock);
+               return sprintf(buf, "%d\n", result);
+
+       case ATTR_GYRO_MATRIX:
+               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]);
+       case ATTR_ACCL_MATRIX:
+               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]);
+       case ATTR_COMPASS_MATRIX:
+               if (st->plat_data.sec_slave_type ==
+                               SECONDARY_SLAVE_TYPE_COMPASS)
+                       m = st->plat_data.secondary_orientation;
+               else
+                       return -ENODEV;
+               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]);
+       case ATTR_SECONDARY_NAME:{
+       const char *n[] = {"0", "AK8975", "AK8972", "AK8963", "BMA250"};
+       if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+               return sprintf(buf, "%s\n", n[1]);
+       else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+               return sprintf(buf, "%s\n", n[2]);
+       else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+               return sprintf(buf, "%s\n", n[3]);
+       else if (ACCEL_ID_BMA250 == st->plat_data.sec_slave_id)
+               return sprintf(buf, "%s\n", n[4]);
+       else
+               return sprintf(buf, "%s\n", n[0]);
+       }
+
+#ifdef CONFIG_INV_TESTING
+       case ATTR_REG_WRITE:
+               return sprintf(buf, "1\n");
+       case ATTR_DEBUG_SMD_EXE_STATE:
+       {
+               u8 d[2];
+
+               result = st->set_power_state(st, true);
+               mpu_memory_read(st, st->i2c_addr,
+                               inv_dmp_get_address(KEY_SMD_EXE_STATE), 2, d);
+               return sprintf(buf, "%d\n", (short)be16_to_cpup((__be16 *)(d)));
+       }
+       case ATTR_DEBUG_SMD_DELAY_CNTR:
+       {
+               u8 d[4];
+
+               result = st->set_power_state(st, true);
+               mpu_memory_read(st, st->i2c_addr,
+                               inv_dmp_get_address(KEY_SMD_DELAY_CNTR), 4, d);
+               return sprintf(buf, "%d\n", (int)be32_to_cpup((__be32 *)(d)));
+       }
+#endif
+       default:
+               return -EPERM;
+       }
+}
+
+/**
+ * inv_dmp_display_orient_show() -  calling this function will
+ *                     show orientation This event must use poll.
+ */
+static ssize_t inv_dmp_display_orient_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct inv_mpu_iio_s *st = iio_priv(dev_get_drvdata(dev));
+       return sprintf(buf, "%d\n", st->display_orient_data);
+}
+
+/**
+ * inv_accel_motion_show() -  calling this function showes motion interrupt.
+ *                         This event must use poll.
+ */
+static ssize_t inv_accel_motion_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "1\n");
+}
+
+/**
+ * inv_smd_show() -  calling this function showes smd interrupt.
+ *                         This event must use poll.
+ */
+static ssize_t inv_smd_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "1\n");
+}
+
+/**
+ *  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 iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct inv_reg_map_s *reg;
+       int result, cur_scale, cur_off;
+       short temp;
+       long scale_t;
+       u8 data[2];
+       const long scale[] = {3834792L, 3158064L, 3340827L};
+       const long offset[] = {5383314L, 2394184L, 1376256L};
+
+       reg = &st->reg;
+       mutex_lock(&indio_dev->mlock);
+       if (!st->chip_config.enable)
+               result = st->set_power_state(st, true);
+       else
+               result = 0;
+       if (result) {
+               mutex_unlock(&indio_dev->mlock);
+               return result;
+       }
+       result = inv_i2c_read(st, reg->temperature, 2, data);
+       if (!st->chip_config.enable)
+               result |= st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+       if (result) {
+               pr_err("Could not read temperature register.\n");
+               return result;
+       }
+       temp = (signed short)(be16_to_cpup((short *)&data[0]));
+       switch (st->chip_type) {
+       case INV_MPU3050:
+               cur_scale = scale[0];
+               cur_off   = offset[0];
+               break;
+       case INV_MPU6050:
+               cur_scale = scale[1];
+               cur_off   = offset[1];
+               break;
+       case INV_MPU6500:
+               cur_scale = scale[2];
+               cur_off   = offset[2];
+               break;
+       default:
+               return -EINVAL;
+       };
+       scale_t = cur_off +
+               inv_q30_mult((int)temp << MPU_TEMP_SHIFT, cur_scale);
+
+       INV_I2C_INC_TEMPREAD(1);
+
+       return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns());
+}
+
+/**
+ * inv_firmware_loaded() -  calling this function will change
+ *                        firmware load
+ */
+static int inv_firmware_loaded(struct inv_mpu_iio_s *st, int data)
+{
+       if (data)
+               return -EINVAL;
+       st->chip_config.firmware_loaded = 0;
+       st->chip_config.dmp_on = 0;
+       st->chip_config.quaternion_on = 0;
+
+       return 0;
+}
+
+static int inv_switch_gyro_engine(struct inv_mpu_iio_s *st, bool en)
+{
+       return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY);
+}
+
+static int inv_switch_accl_engine(struct inv_mpu_iio_s *st, bool en)
+{
+       return inv_switch_engine(st, en, BIT_PWR_ACCL_STBY);
+}
+
+/**
+ *  inv_gyro_enable() - Enable/disable gyro.
+ */
+static int inv_gyro_enable(struct inv_mpu_iio_s *st,
+                                struct iio_buffer *ring, bool en)
+{
+       if (en == st->chip_config.gyro_enable)
+               return 0;
+       if (!en) {
+               st->chip_config.gyro_fifo_enable = 0;
+               clear_bit(INV_MPU_SCAN_GYRO_X, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_GYRO_Y, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_GYRO_Z, ring->scan_mask);
+       }
+       st->chip_config.gyro_enable = en;
+
+       return 0;
+}
+
+/**
+ *  inv_accl_enable() - Enable/disable accl.
+ */
+static int inv_accl_enable(struct inv_mpu_iio_s *st,
+                                struct iio_buffer *ring, bool en)
+{
+       if (en == st->chip_config.accl_enable)
+               return 0;
+       if (!en) {
+               st->chip_config.accl_fifo_enable = 0;
+               clear_bit(INV_MPU_SCAN_ACCL_X, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_ACCL_Y, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_ACCL_Z, ring->scan_mask);
+       }
+       st->chip_config.accl_enable = en;
+
+       return 0;
+}
+
+/**
+ * inv_compass_enable() -  calling this function will store compass
+ *                         enable
+ */
+static int inv_compass_enable(struct inv_mpu_iio_s *st,
+                                struct iio_buffer *ring, bool en)
+{
+       if (en == st->chip_config.compass_enable)
+               return 0;
+       if (!en) {
+               st->chip_config.compass_fifo_enable = 0;
+               clear_bit(INV_MPU_SCAN_MAGN_X, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_MAGN_Y, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_MAGN_Z, ring->scan_mask);
+       }
+       st->chip_config.compass_enable = en;
+
+       return 0;
+}
+
+/**
+ * inv_attr_store() -  calling this function will store current
+ *                        non-dmp parameter settings
+ */
+static ssize_t inv_attr_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct iio_buffer *ring = indio_dev->buffer;
+       struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+       int data;
+       u8  d;
+       int result;
+
+       mutex_lock(&indio_dev->mlock);
+       if (st->chip_config.enable) {
+               result = -EBUSY;
+               goto attr_store_fail;
+       }
+       if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) {
+               result = st->set_power_state(st, true);
+               if (result)
+                       goto attr_store_fail;
+       }
+
+       result = kstrtoint(buf, 10, &data);
+       if (result)
+               goto attr_store_fail;
+       switch (this_attr->address) {
+       case ATTR_MOTION_LPA_ON:
+               if (INV_MPU6500 == st->chip_type) {
+                       if (data)
+                               /* enable and put in MPU6500 mode */
+                               d = BIT_ACCEL_INTEL_ENABLE
+                                       | BIT_ACCEL_INTEL_MODE;
+                       else
+                               d = 0;
+                       result = inv_i2c_single_write(st,
+                                               REG_6500_ACCEL_INTEL_CTRL, d);
+                       if (result)
+                               goto attr_store_fail;
+               }
+               st->mot_int.mot_on = !!data;
+               st->chip_config.lpa_mode = !!data;
+               break;
+       case ATTR_MOTION_LPA_FREQ:
+               result = inv_lpa_freq(st, data);
+               break;
+       case ATTR_MOTION_LPA_THRESHOLD:
+               if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) {
+                       result = -EINVAL;
+                       goto attr_store_fail;
+               }
+               d = (u8)(data >> MPU6XXX_MOTION_THRESH_SHIFT);
+               data = (d << MPU6XXX_MOTION_THRESH_SHIFT);
+               result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d);
+               if (result)
+                       goto attr_store_fail;
+               st->mot_int.mot_thr = data;
+               break;
+       /* from now on, power is not turned on */
+       case ATTR_SELF_TEST_SAMPLES:
+               if (data > ST_MAX_SAMPLES || data < 0) {
+                       result = -EINVAL;
+                       goto attr_store_fail;
+               }
+               st->self_test.samples = data;
+               break;
+       case ATTR_SELF_TEST_THRESHOLD:
+               if (data > ST_MAX_THRESHOLD || data < 0) {
+                       result = -EINVAL;
+                       goto attr_store_fail;
+               }
+               st->self_test.threshold = data;
+       case ATTR_GYRO_ENABLE:
+               result = st->gyro_en(st, ring, !!data);
+               break;
+       case ATTR_ACCL_ENABLE:
+               result = st->accl_en(st, ring, !!data);
+               break;
+       case ATTR_COMPASS_ENABLE:
+               result = inv_compass_enable(st, ring, !!data);
+               break;
+       case ATTR_POWER_STATE:
+               fake_asleep = !data;
+               break;
+       case ATTR_FIRMWARE_LOADED:
+               result = inv_firmware_loaded(st, data);
+               break;
+       case ATTR_SAMPLING_FREQ:
+               result = inv_fifo_rate_store(st, data);
+               break;
+       default:
+               result = -EINVAL;
+               goto attr_store_fail;
+       };
+
+attr_store_fail:
+       if ((this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) &&
+                                       (!st->chip_config.enable))
+               result |= st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+       if (result)
+               return result;
+
+       return count;
+}
+
+#ifdef CONFIG_INV_TESTING
+/**
+ * inv_reg_write_store() - register write command for testing.
+ *                         Format: WSRRDD, where RR is the register in hex,
+ *                                         and DD is the data in hex.
+ */
+static ssize_t inv_reg_write_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       u32 result;
+       u8 wreg, wval;
+       int temp;
+       char local_buf[10];
+
+       if ((buf[0] != 'W' && buf[0] != 'w') ||
+           (buf[1] != 'S' && buf[1] != 's'))
+               return -EINVAL;
+       if (strlen(buf) < 6)
+               return -EINVAL;
+
+       strncpy(local_buf, buf, 7);
+       local_buf[6] = 0;
+       result = sscanf(&local_buf[4], "%x", &temp);
+       if (result == 0)
+               return -EINVAL;
+       wval = temp;
+       local_buf[4] = 0;
+       sscanf(&local_buf[2], "%x", &temp);
+       if (result == 0)
+               return -EINVAL;
+       wreg = temp;
+
+       result = inv_i2c_single_write(st, wreg, wval);
+       if (result)
+               return result;
+
+       return count;
+}
+#endif /* CONFIG_INV_TESTING */
+
+#define IIO_ST(si, rb, sb, sh)                                          \
+       { .sign = si, .realbits = rb, .storagebits = sb, .shift = sh }
+#define INV_MPU_CHAN(_type, _channel2, _index)                \
+       {                                                         \
+               .type = _type,                                        \
+               .modified = 1,                                        \
+               .channel2 = _channel2,                                \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS), \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+               .scan_index = _index,                                 \
+               .scan_type  = IIO_ST('s', 16, 16, 0)                  \
+       }
+
+#define INV_ACCL_CHAN(_type, _channel2, _index)                \
+       {                                                         \
+               .type = _type,                                        \
+               .modified = 1,                                        \
+               .channel2 = _channel2,                                \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+                                     BIT(IIO_CHAN_INFO_OFFSET), \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+               .scan_index = _index,                                 \
+               .scan_type  = IIO_ST('s', 16, 16, 0)                  \
+       }
+
+#define INV_MPU_QUATERNION_CHAN(_channel2, _index)            \
+       {                                                         \
+               .type = IIO_QUATERNION,                               \
+               .modified = 1,                                        \
+               .channel2 = _channel2,                                \
+               .scan_index = _index,                                 \
+               .scan_type  = IIO_ST('s', 32, 32, 0)                  \
+       }
+
+#define INV_MPU_MAGN_CHAN(_channel2, _index)                  \
+       {                                                         \
+               .type = IIO_MAGN,                                     \
+               .modified = 1,                                        \
+               .channel2 = _channel2,                                \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+               .scan_index = _index,                                 \
+               .scan_type  = IIO_ST('s', 16, 16, 0)                  \
+       }
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+       IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+       INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU_SCAN_GYRO_X),
+       INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU_SCAN_GYRO_Y),
+       INV_MPU_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU_SCAN_GYRO_Z),
+
+       INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU_SCAN_ACCL_X),
+       INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU_SCAN_ACCL_Y),
+       INV_ACCL_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU_SCAN_ACCL_Z),
+
+       INV_MPU_QUATERNION_CHAN(IIO_MOD_R, INV_MPU_SCAN_QUAT_R),
+       INV_MPU_QUATERNION_CHAN(IIO_MOD_X, INV_MPU_SCAN_QUAT_X),
+       INV_MPU_QUATERNION_CHAN(IIO_MOD_Y, INV_MPU_SCAN_QUAT_Y),
+       INV_MPU_QUATERNION_CHAN(IIO_MOD_Z, INV_MPU_SCAN_QUAT_Z),
+
+       INV_MPU_MAGN_CHAN(IIO_MOD_X, INV_MPU_SCAN_MAGN_X),
+       INV_MPU_MAGN_CHAN(IIO_MOD_Y, INV_MPU_SCAN_MAGN_Y),
+       INV_MPU_MAGN_CHAN(IIO_MOD_Z, INV_MPU_SCAN_MAGN_Z),
+};
+
+
+/*constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+
+/* special sysfs */
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+
+/* event based sysfs, needs poll to read */
+static DEVICE_ATTR(event_display_orientation, S_IRUGO,
+       inv_dmp_display_orient_show, NULL);
+static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL);
+static DEVICE_ATTR(event_smd, S_IRUGO, inv_smd_show, NULL);
+
+/* DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(smd_enable, S_IRUGO | S_IWUSR,
+       inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_ENABLE);
+static IIO_DEVICE_ATTR(smd_threshold, S_IRUGO | S_IWUSR,
+       inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold, S_IRUGO | S_IWUSR,
+       inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold2, S_IRUGO | S_IWUSR,
+       inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD2);
+static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR,
+       inv_attr_show, inv_dmp_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+
+/* DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_dmp_attr_store, ATTR_DMP_ON);
+static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_dmp_attr_store, ATTR_DMP_INT_ON);
+static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON);
+static IIO_DEVICE_ATTR(dmp_output_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_dmp_attr_store, ATTR_DMP_OUTPUT_RATE);
+static IIO_DEVICE_ATTR(quaternion_on, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_dmp_attr_store, ATTR_DMP_QUATERNION_ON);
+
+/* non DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_MOTION_LPA_ON);
+static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_MOTION_LPA_FREQ);
+static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_MOTION_LPA_THRESHOLD);
+
+/* non DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(self_test_samples, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_SELF_TEST_SAMPLES);
+static IIO_DEVICE_ATTR(self_test_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_SELF_TEST_THRESHOLD);
+static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_GYRO_ENABLE);
+static IIO_DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_ACCL_ENABLE);
+static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_COMPASS_ENABLE);
+static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_POWER_STATE);
+static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_FIRMWARE_LOADED);
+static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_attr_store, ATTR_SAMPLING_FREQ);
+
+/* show method only sysfs but with power on/off */
+static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL,
+       ATTR_SELF_TEST);
+
+/* show method only sysfs */
+static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+       ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(accl_matrix, S_IRUGO, inv_attr_show, NULL,
+       ATTR_ACCL_MATRIX);
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL,
+       ATTR_COMPASS_MATRIX);
+static IIO_DEVICE_ATTR(secondary_name, S_IRUGO, inv_attr_show, NULL,
+       ATTR_SECONDARY_NAME);
+
+#ifdef CONFIG_INV_TESTING
+static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+       inv_reg_write_store, ATTR_REG_WRITE);
+/* smd debug related sysfs */
+static IIO_DEVICE_ATTR(debug_smd_enable_testp1, S_IWUSR, NULL,
+       inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP1);
+static IIO_DEVICE_ATTR(debug_smd_enable_testp2, S_IWUSR, NULL,
+       inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP2);
+static IIO_DEVICE_ATTR(debug_smd_exe_state, S_IRUGO, inv_attr_show,
+       NULL, ATTR_DEBUG_SMD_EXE_STATE);
+static IIO_DEVICE_ATTR(debug_smd_delay_cntr, S_IRUGO, inv_attr_show,
+       NULL, ATTR_DEBUG_SMD_DELAY_CNTR);
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+       &dev_attr_reg_dump.attr,
+       &dev_attr_temperature.attr,
+       &iio_dev_attr_self_test_samples.dev_attr.attr,
+       &iio_dev_attr_self_test_threshold.dev_attr.attr,
+       &iio_dev_attr_gyro_enable.dev_attr.attr,
+       &iio_dev_attr_power_state.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       &iio_dev_attr_self_test.dev_attr.attr,
+       &iio_dev_attr_gyro_matrix.dev_attr.attr,
+       &iio_dev_attr_secondary_name.dev_attr.attr,
+#ifdef CONFIG_INV_TESTING
+       &iio_dev_attr_reg_write.dev_attr.attr,
+       &iio_dev_attr_debug_smd_enable_testp1.dev_attr.attr,
+       &iio_dev_attr_debug_smd_enable_testp2.dev_attr.attr,
+       &iio_dev_attr_debug_smd_exe_state.dev_attr.attr,
+       &iio_dev_attr_debug_smd_delay_cntr.dev_attr.attr,
+#endif
+};
+
+static const struct attribute *inv_mpu6050_attributes[] = {
+       &dev_attr_event_display_orientation.attr,
+       &dev_attr_event_smd.attr,
+       &iio_dev_attr_smd_enable.dev_attr.attr,
+       &iio_dev_attr_smd_threshold.dev_attr.attr,
+       &iio_dev_attr_smd_delay_threshold.dev_attr.attr,
+       &iio_dev_attr_smd_delay_threshold2.dev_attr.attr,
+       &iio_dev_attr_display_orientation_on.dev_attr.attr,
+       &iio_dev_attr_dmp_on.dev_attr.attr,
+       &iio_dev_attr_dmp_int_on.dev_attr.attr,
+       &iio_dev_attr_dmp_event_int_on.dev_attr.attr,
+       &iio_dev_attr_dmp_output_rate.dev_attr.attr,
+       &iio_dev_attr_quaternion_on.dev_attr.attr,
+       &iio_dev_attr_accl_enable.dev_attr.attr,
+       &iio_dev_attr_firmware_loaded.dev_attr.attr,
+       &iio_dev_attr_accl_matrix.dev_attr.attr,
+
+};
+
+static const struct attribute *inv_mpu6500_attributes[] = {
+       &dev_attr_event_accel_motion.attr,
+       &iio_dev_attr_motion_lpa_on.dev_attr.attr,
+       &iio_dev_attr_motion_lpa_freq.dev_attr.attr,
+       &iio_dev_attr_motion_lpa_threshold.dev_attr.attr,
+};
+
+static const struct attribute *inv_compass_attributes[] = {
+       &iio_dev_attr_compass_enable.dev_attr.attr,
+       &iio_dev_attr_compass_matrix.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu3050_attributes[] = {
+       &iio_dev_attr_accl_enable.dev_attr.attr,
+       &iio_dev_attr_accl_matrix.dev_attr.attr,
+};
+
+static struct attribute *inv_attributes[ARRAY_SIZE(inv_gyro_attributes) +
+                               ARRAY_SIZE(inv_mpu6050_attributes) +
+                               ARRAY_SIZE(inv_mpu6500_attributes) +
+                               ARRAY_SIZE(inv_compass_attributes) + 1];
+
+static const struct attribute_group inv_attribute_group = {
+       .name = "mpu",
+       .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = &mpu_read_raw,
+       .write_raw = &mpu_write_raw,
+       .attrs = &inv_attribute_group,
+};
+
+/**
+ *  inv_setup_compass() - Configure compass.
+ */
+static int inv_setup_compass(struct inv_mpu_iio_s *st)
+{
+       int result;
+       u8 data[4];
+
+       if (INV_MPU6050 == st->chip_type) {
+               result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data);
+               if (result)
+                       return result;
+               data[0] &= ~BIT_I2C_MST_VDDIO;
+               if (st->plat_data.level_shifter)
+                       data[0] |= BIT_I2C_MST_VDDIO;
+               /*set up VDDIO register */
+               result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]);
+               if (result)
+                       return result;
+       }
+       /* set to bypass mode */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+                               st->plat_data.int_config | BIT_BYPASS_EN);
+       if (result)
+               return result;
+       /*read secondary i2c ID register */
+       result = inv_secondary_read(REG_AKM_ID, 1, 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_FR);
+       if (result)
+               return result;
+       result = inv_secondary_read(REG_AKM_SENSITIVITY, THREE_AXIS,
+                                       st->chip_info.compass_sens);
+       if (result)
+               return result;
+       /*revert to power down mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+       if (result)
+               return result;
+       pr_debug("%s senx=%d, seny=%d, senz=%d\n",
+                st->hw->name,
+                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,
+                       st->plat_data.int_config);
+       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 |
+                               NUM_BYTES_COMPASS_SLAVE);
+       if (result)
+               return result;
+       /*slave 1 is used for AKM mode change only*/
+       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 = AKM8975_ST_Upper;
+               st->compass_st_lower = AKM8975_ST_Lower;
+               data[0] = DATA_AKM_MODE_SM;
+       } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+               st->compass_st_upper = AKM8972_ST_Upper;
+               st->compass_st_lower = AKM8972_ST_Lower;
+               data[0] = DATA_AKM_MODE_SM;
+       } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+               st->compass_st_upper = AKM8963_ST_Upper;
+               st->compass_st_lower = AKM8963_ST_Lower;
+               data[0] = DATA_AKM_MODE_SM |
+                         (st->compass_scale << AKM8963_SCALE_SHIFT);
+       }
+       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 void inv_setup_func_ptr(struct inv_mpu_iio_s *st)
+{
+       if (st->chip_type == INV_MPU3050) {
+               st->set_power_state    = set_power_mpu3050;
+               st->switch_gyro_engine = inv_switch_3050_gyro_engine;
+               st->switch_accl_engine = inv_switch_3050_accl_engine;
+               st->init_config        = inv_init_config_mpu3050;
+               st->setup_reg          = inv_setup_reg_mpu3050;
+       } else {
+               st->set_power_state    = set_power_itg;
+               st->switch_gyro_engine = inv_switch_gyro_engine;
+               st->switch_accl_engine = inv_switch_accl_engine;
+               st->init_config        = inv_init_config;
+               st->setup_reg          = inv_setup_reg;
+               /*MPU6XXX special functions */
+               st->compass_en         = inv_compass_enable;
+               st->quaternion_en      = inv_quaternion_on;
+       }
+       st->gyro_en            = inv_gyro_enable;
+       st->accl_en            = inv_accl_enable;
+}
+
+static int inv_detect_6xxx(struct inv_mpu_iio_s *st)
+{
+       int result;
+       u8 d;
+
+       result = inv_i2c_read(st, REG_WHOAMI, 1, &d);
+       if (result)
+               return result;
+       if ((d == MPU6500_ID) || (d == MPU6515_ID)) {
+               st->chip_type = INV_MPU6500;
+               strcpy(st->name, "mpu6500");
+       } else {
+               strcpy(st->name, "mpu6050");
+       }
+
+       return 0;
+}
+
+/**
+ *  inv_check_chip_type() - check and setup chip type.
+ */
+static int inv_check_chip_type(struct inv_mpu_iio_s *st,
+               const struct i2c_device_id *id)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       int t_ind;
+
+       if (!strcmp(id->name, "itg3500"))
+               st->chip_type = INV_ITG3500;
+       else if (!strcmp(id->name, "mpu3050"))
+               st->chip_type = INV_MPU3050;
+       else if (!strcmp(id->name, "mpu6050"))
+               st->chip_type = INV_MPU6050;
+       else if (!strcmp(id->name, "mpu9150"))
+               st->chip_type = INV_MPU6050;
+       else if (!strcmp(id->name, "mpu6500"))
+               st->chip_type = INV_MPU6500;
+       else if (!strcmp(id->name, "mpu9250"))
+               st->chip_type = INV_MPU6500;
+       else if (!strcmp(id->name, "mpu6xxx"))
+               st->chip_type = INV_MPU6050;
+       else if (!strcmp(id->name, "mpu6515"))
+               st->chip_type = INV_MPU6500;
+       else
+               return -EPERM;
+       inv_setup_func_ptr(st);
+       st->hw  = &hw_info[st->chip_type];
+       st->mpu_slave = NULL;
+       reg = &st->reg;
+       st->setup_reg(reg);
+       /* reset to make sure previous state are not there */
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+       if (result)
+               return result;
+       msleep(POWER_UP_TIME);
+       /* toggle power state */
+       result = st->set_power_state(st, false);
+       if (result)
+               return result;
+
+       result = st->set_power_state(st, true);
+       if (result)
+               return result;
+
+       if (!strcmp(id->name, "mpu6xxx")) {
+               /* for MPU6500, reading register need more time */
+               msleep(POWER_UP_TIME);
+               result = inv_detect_6xxx(st);
+               if (result)
+                       return result;
+       }
+
+       switch (st->chip_type) {
+       case INV_ITG3500:
+               st->num_channels = INV_CHANNEL_NUM_GYRO;
+               break;
+       case INV_MPU6050:
+       case INV_MPU6500:
+               if (SECONDARY_SLAVE_TYPE_COMPASS ==
+                   st->plat_data.sec_slave_type) {
+                       st->chip_config.has_compass = 1;
+                       st->num_channels =
+                               INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN;
+               } else {
+                       st->chip_config.has_compass = 0;
+                       st->num_channels =
+                               INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION;
+               }
+               break;
+       case INV_MPU3050:
+               if (SECONDARY_SLAVE_TYPE_ACCEL ==
+                   st->plat_data.sec_slave_type) {
+                       if (ACCEL_ID_BMA250 == st->plat_data.sec_slave_id)
+                               inv_register_mpu3050_slave(st);
+                       st->num_channels = INV_CHANNEL_NUM_GYRO_ACCL;
+               } else {
+                       st->num_channels = INV_CHANNEL_NUM_GYRO;
+               }
+               break;
+       default:
+               result = st->set_power_state(st, false);
+               return -ENODEV;
+       }
+       st->chip_info.multi = 1;
+       switch (st->chip_type) {
+       case INV_MPU6050:
+               result = inv_get_silicon_rev_mpu6050(st);
+               break;
+       case INV_MPU6500:
+               result = inv_get_silicon_rev_mpu6500(st);
+               break;
+       default:
+               result = 0;
+               break;
+       }
+       if (result) {
+               pr_err("read silicon rev error\n");
+               st->set_power_state(st, false);
+               return result;
+       }
+       /* turn off the gyro engine after OTP reading */
+       result = st->switch_gyro_engine(st, false);
+       if (result)
+               return result;
+       result = st->switch_accl_engine(st, false);
+       if (result)
+               return result;
+       if (st->chip_config.has_compass) {
+               result = inv_setup_compass(st);
+               if (result) {
+                       pr_err("compass setup failed\n");
+                       st->set_power_state(st, false);
+                       return result;
+               }
+       }
+
+       t_ind = 0;
+       memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+               sizeof(inv_gyro_attributes));
+       t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+       if (INV_MPU3050 == st->chip_type && st->mpu_slave != NULL) {
+               memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes,
+                      sizeof(inv_mpu3050_attributes));
+               t_ind += ARRAY_SIZE(inv_mpu3050_attributes);
+               inv_attributes[t_ind] = NULL;
+               return 0;
+       }
+
+       if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) {
+               memcpy(&inv_attributes[t_ind], inv_mpu6050_attributes,
+                      sizeof(inv_mpu6050_attributes));
+               t_ind += ARRAY_SIZE(inv_mpu6050_attributes);
+       }
+
+       if (INV_MPU6500 == st->chip_type) {
+               memcpy(&inv_attributes[t_ind], inv_mpu6500_attributes,
+                      sizeof(inv_mpu6500_attributes));
+               t_ind += ARRAY_SIZE(inv_mpu6500_attributes);
+       }
+
+       if (st->chip_config.has_compass) {
+               memcpy(&inv_attributes[t_ind], inv_compass_attributes,
+                      sizeof(inv_compass_attributes));
+               t_ind += ARRAY_SIZE(inv_compass_attributes);
+       }
+       inv_attributes[t_ind] = NULL;
+
+       return 0;
+}
+
+/**
+ *  inv_create_dmp_sysfs() - create binary sysfs dmp entry.
+ */
+static const 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 inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+       int result;
+       result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware);
+
+       return result;
+}
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+
+static struct mpu_platform_data mpu_data = {
+       .int_config  = 0x00,
+       .level_shifter = 0,
+       .orientation = {
+                       0,  1,  0,
+                       -1,  0,  0,
+                       0,  0, -1,
+       },
+       /*
+       .key = {
+               221,  22, 205,   7, 217, 186, 151, 55,
+               206, 254,  35, 144, 225, 102,  47, 50,
+       },
+       */
+};
+
+static int of_inv_parse_platform_data(struct i2c_client *client,
+                                     struct mpu_platform_data *pdata)
+{
+       struct device_node *np = client->dev.of_node;
+       unsigned long irq_flags;
+       int irq_pin;
+       int gpio_pin;
+
+       gpio_pin = of_get_named_gpio_flags(np, "irq-gpio", 0, (enum of_gpio_flags *)&irq_flags);
+       gpio_request(gpio_pin, "mpu6500");
+       irq_pin = gpio_to_irq(gpio_pin);
+       client->irq = irq_pin;
+       i2c_set_clientdata(client, &mpu_data);
+
+       pr_info("%s: %s, %x, %x\n", __func__, client->name, client->addr, client->irq);
+
+       return 0;
+}
+
+/**
+ *  inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct inv_mpu_iio_s *st;
+       struct iio_dev *indio_dev;
+       int result;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               result = -ENOSYS;
+               pr_err("I2c function error\n");
+               goto out_no_free;
+       }
+#ifdef INV_KERNEL_3_10
+    indio_dev = iio_device_alloc(sizeof(*st));
+#else
+       indio_dev = iio_allocate_device(sizeof(*st));
+#endif
+       if (indio_dev == NULL) {
+               pr_err("memory allocation failed\n");
+               result =  -ENOMEM;
+               goto out_no_free;
+       }
+       st = iio_priv(indio_dev);
+       st->client = client;
+       st->sl_handle = client->adapter;
+       st->i2c_addr = client->addr;
+       if (client->dev.of_node) {
+               result = of_inv_parse_platform_data(client, &st->plat_data);
+               if (result)
+                       goto out_free;
+               st->plat_data = mpu_data;
+               pr_info("secondary_i2c_addr=%x\n", st->plat_data.secondary_i2c_addr);
+       } else
+               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;
+
+       result = st->init_config(indio_dev);
+       if (result) {
+               dev_err(&client->adapter->dev,
+                       "Could not initialize device.\n");
+               goto out_free;
+       }
+       result = st->set_power_state(st, false);
+       if (result) {
+               dev_err(&client->adapter->dev,
+                       "%s could not be turned off.\n", st->hw->name);
+               goto out_free;
+       }
+
+       /* Make state variables available to all _show and _store functions. */
+       i2c_set_clientdata(client, indio_dev);
+       indio_dev->dev.parent = &client->dev;
+       if (!strcmp(id->name, "mpu6xxx"))
+               indio_dev->name = st->name;
+       else
+               indio_dev->name = id->name;
+       indio_dev->channels = inv_mpu_channels;
+       indio_dev->num_channels = st->num_channels;
+
+       indio_dev->info = &mpu_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+       result = inv_mpu_configure_ring(indio_dev);
+       if (result) {
+               pr_err("configure ring buffer fail\n");
+               goto out_free;
+       }
+       st->irq = client->irq;
+       result = inv_mpu_probe_trigger(indio_dev);
+       if (result) {
+               pr_err("trigger probe fail\n");
+               goto out_unreg_ring;
+       }
+
+       /* Tell the i2c counter, we have an IRQ */
+       INV_I2C_SETIRQ(IRQ_MPU, client->irq);
+
+       result = iio_device_register(indio_dev);
+       if (result) {
+               pr_err("IIO device register fail\n");
+               goto out_remove_trigger;
+       }
+
+       if (INV_MPU6050 == st->chip_type ||
+           INV_MPU6500 == st->chip_type) {
+               result = inv_create_dmp_sysfs(indio_dev);
+               if (result) {
+                       pr_err("create dmp sysfs failed\n");
+                       goto out_unreg_iio;
+               }
+       }
+
+       INIT_KFIFO(st->timestamps);
+       spin_lock_init(&st->time_stamp_lock);
+       dev_info(&client->dev, "%s is ready to go!\n",
+                                       indio_dev->name);
+
+       return 0;
+out_unreg_iio:
+       iio_device_unregister(indio_dev);
+out_remove_trigger:
+       if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+               inv_mpu_remove_trigger(indio_dev);
+out_unreg_ring:
+       inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+#ifdef INV_KERNEL_3_10
+    iio_device_free(indio_dev);
+#else
+       iio_free_device(indio_dev);
+#endif
+out_no_free:
+       dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+
+       return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct inv_reg_map_s *reg;
+       int result;
+
+       reg = &st->reg;
+       dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name);
+
+       /* reset to make sure previous state are not there */
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+       if (result)
+               dev_err(&client->adapter->dev, "Failed to reset %s\n",
+                       st->hw->name);
+       msleep(POWER_UP_TIME);
+       /* turn off power to ensure gyro engine is off */
+       result = st->set_power_state(st, false);
+       if (result)
+               dev_err(&client->adapter->dev, "Failed to turn off %s\n",
+                       st->hw->name);
+}
+
+/**
+ *  inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       kfifo_free(&st->timestamps);
+       iio_device_unregister(indio_dev);
+       if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+               inv_mpu_remove_trigger(indio_dev);
+       inv_mpu_unconfigure_ring(indio_dev);
+#ifdef INV_KERNEL_3_10
+    iio_device_free(indio_dev);
+#else
+       iio_free_device(indio_dev);
+#endif
+
+       dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n");
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int inv_mpu_resume(struct device *dev)
+{
+       struct inv_mpu_iio_s *st =
+                       iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+       pr_debug("%s inv_mpu_resume\n", st->hw->name);
+       return st->set_power_state(st, true);
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       int result;
+
+       pr_debug("%s inv_mpu_suspend\n", st->hw->name);
+       mutex_lock(&indio_dev->mlock);
+       result = 0;
+       if ((!st->chip_config.dmp_on) ||
+               (!st->chip_config.enable) ||
+               (!st->chip_config.dmp_event_int_on))
+               result = st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+
+       return result;
+}
+static const struct dev_pm_ops inv_mpu_pmops = {
+       SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
+};
+#define INV_MPU_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU_PMOPS NULL
+#endif /* CONFIG_PM */
+
+static const u16 normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+       {"itg3500", INV_ITG3500},
+       {"mpu3050", INV_MPU3050},
+       {"mpu6050", INV_MPU6050},
+       {"mpu9150", INV_MPU9150},
+       {"mpu6500", INV_MPU6500},
+       {"mpu9250", INV_MPU9250},
+       {"mpu6xxx", INV_MPU6XXX},
+       {"mpu6515", INV_MPU6515},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static const struct of_device_id inv_mpu_of_match[] = {
+       { .compatible = "invensense,itg3500", },
+       { .compatible = "invensense,mpu3050", },
+       { .compatible = "invensense,mpu6050", },
+       { .compatible = "invensense,mpu9150", },
+       { .compatible = "invensense,mpu6500", },
+       { .compatible = "invensense,mpu9250", },
+       { .compatible = "invensense,mpu6xxx", },
+       { .compatible = "invensense,mpu9350", },
+       { .compatible = "invensense,mpu6515", },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, inv_mpu_of_match);
+
+static struct i2c_driver inv_mpu_driver = {
+       .class = I2C_CLASS_HWMON,
+       .probe          =       inv_mpu_probe,
+       .remove         =       inv_mpu_remove,
+       .shutdown       =       inv_mpu_shutdown,
+       .id_table       =       inv_mpu_id,
+       .driver = {
+               .owner  =       THIS_MODULE,
+               .name   =       "inv-mpu-iio",
+               .pm     =       INV_MPU_PMOPS,
+               .of_match_table = of_match_ptr(inv_mpu_of_match),
+       },
+       .address_list = normal_i2c,
+};
+
+static int __init inv_mpu_init(void)
+{
+       int result = i2c_add_driver(&inv_mpu_driver);
+    pr_info("%s:%d\n", __func__, __LINE__);
+       if (result) {
+               pr_err("failed\n");
+               return result;
+       }
+       return 0;
+}
+
+static void __exit inv_mpu_exit(void)
+{
+       i2c_del_driver(&inv_mpu_driver);
+}
+
+module_init(inv_mpu_init);
+module_exit(inv_mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-mpu-iio");
+
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h
new file mode 100644 (file)
index 0000000..18b32e8
--- /dev/null
@@ -0,0 +1,910 @@
+/*
+* 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_mpu_iio.h
+ *      @brief Struct definitions for the Invensense mpu driver.
+ */
+
+#ifndef _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#ifdef INV_KERNEL_3_10
+#include <linux/mpu.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#else
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#endif
+
+#include "dmpKey.h"
+
+/**
+ *  struct inv_reg_map_s - Notable slave registers.
+ *  @sample_rate_div:  Divider applied to gyro output rate.
+ *  @lpf:              Configures internal LPF.
+ *  @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 {
+       u8 sample_rate_div;
+       u8 lpf;
+       u8 bank_sel;
+       u8 user_ctrl;
+       u8 fifo_en;
+       u8 gyro_config;
+       u8 accl_config;
+       u8 fifo_count_h;
+       u8 fifo_r_w;
+       u8 raw_gyro;
+       u8 raw_accl;
+       u8 temperature;
+       u8 int_enable;
+       u8 int_status;
+       u8 pwr_mgmt_1;
+       u8 pwr_mgmt_2;
+       u8 mem_start_addr;
+       u8 mem_r_w;
+       u8 prgm_strt_addrh;
+};
+/*device enum */
+enum inv_devices {
+       INV_ITG3500,
+       INV_MPU3050,
+       INV_MPU6050,
+       INV_MPU9150,
+       INV_MPU6500,
+       INV_MPU9250,
+       INV_MPU6XXX,
+       INV_MPU6515,
+       INV_NUM_PARTS
+};
+
+/**
+ *  struct test_setup_t - set up parameters for self test.
+ *  @gyro_sens: 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;
+       u32 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 {
+       u8 num_reg;
+       u8 *name;
+};
+
+/**
+ *  struct inv_chip_config_s - Cached chip configuration data.
+ *  @fsr:              Full scale range.
+ *  @lpf:              Digital low pass filter frequency.
+ *  @accl_fs:          accel full scale range.
+ *  @self_test_run_once flag for self test run ever.
+ *  @has_footer:       MPU3050 specific work around.
+ *  @has_compass:      has compass or not.
+ *  @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
+ *  @compass_fifo_enable: enable compass data output
+ *  @is_asleep:                1 if chip is powered down.
+ *  @dmp_on:           dmp is on/off.
+ *  @dmp_int_on:        dmp interrupt on/off.
+ *  @dmp_event_int_on:  dmp event interrupt on/off.
+ *  @firmware_loaded:  flag indicate firmware loaded or not.
+ *  @lpa_mod:          low power mode.
+ *  @tap_on:           tap on/off.
+ *  @quaternion_on:    send quaternion data on/off.
+ *  @display_orient_on:        display orientation on/off.
+ *  @normal_compass_measure: discard first compass data after reset.
+ *  @smd_enable: disable/enable SMD function.
+ *  @lpa_freq:         low power frequency
+ *  @prog_start_addr:  firmware program start address.
+ *  @fifo_rate:                current FIFO update rate.
+ *  @new_fifo_rate:    set FIFO update rate
+ *  @dmp_output_rate:   current dmp output rate.
+ */
+struct inv_chip_config_s {
+       u32 fsr:2;
+       u32 lpf:3;
+       u32 accl_fs:2;
+       u32 self_test_run_once:1;
+       u32 has_footer:1;
+       u32 has_compass:1;
+       u32 enable:1;
+       u32 accl_enable:1;
+       u32 accl_fifo_enable:1;
+       u32 gyro_enable:1;
+       u32 gyro_fifo_enable:1;
+       u32 compass_enable:1;
+       u32 compass_fifo_enable:1;
+       u32 is_asleep:1;
+       u32 dmp_on:1;
+       u32 dmp_int_on:1;
+       u32 dmp_event_int_on:1;
+       u32 firmware_loaded:1;
+       u32 lpa_mode:1;
+       u32 tap_on:1;
+       u32 quaternion_on:1;
+       u32 display_orient_on:1;
+       u32 normal_compass_measure:1;
+       u32 smd_enable:1;
+       u16 lpa_freq;
+       u16  prog_start_addr;
+       u16 fifo_rate;
+       u16 new_fifo_rate;
+       u16 dmp_output_rate;
+};
+
+/**
+ *  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 {
+       u8 product_id;
+       u8 product_revision;
+       u8 silicon_revision;
+       u8 software_revision;
+       u8 multi;
+       u8 compass_sens[3];
+       u32 gyro_sens_trim;
+       u32 accl_sens_trim;
+};
+
+enum inv_channel_num {
+       INV_CHANNEL_NUM_GYRO = 4,
+       INV_CHANNEL_NUM_GYRO_ACCL = 7,
+       INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION = 11,
+       INV_CHANNEL_NUM_GYRO_ACCL_QUANTERNION_MAGN = 14,
+};
+
+/**
+ *  struct inv_tap_s structure to store tap data.
+ *  @min_count:  minimum taps counted.
+ *  @thresh:    tap threshold.
+ *  @time:     tap time.
+ */
+struct inv_tap_s {
+       u16 min_count;
+       u16 thresh;
+       u16 time;
+};
+
+/**
+ *  struct accel_mot_int_s structure to store motion interrupt data
+ *  @mot_thr:    motion threshold.
+ *  @mot_dur:    motion duration.
+ *  @mot_on:     flag to indicate motion detection on;
+ */
+struct accel_mot_int_s {
+       u16 mot_thr;
+       u32 mot_dur;
+       u8 mot_on:1;
+};
+
+/**
+ * struct self_test_setting - self test settables from sysfs
+ * samples: number of samples used in self test.
+ * threshold: threshold fail/pass criterion in self test.
+ *            This value is in the percentage multiplied by 100.
+ *            So 14% would be 14.
+ */
+struct self_test_setting {
+       u16 samples;
+       u16 threshold;
+};
+
+/**
+ * struct inv_smd_s significant motion detection structure.
+ * threshold: accel threshold for motion detection.
+ * delay: delay time to confirm 2nd motion.
+ * delay2: delay window parameter.
+ */
+struct inv_smd_s {
+       u32 threshold;
+       u32 delay;
+       u32 delay2;
+};
+
+struct inv_mpu_slave;
+/**
+ *  struct inv_mpu_iio_s - Driver state variables.
+ *  @chip_config:      Cached attribute information.
+ *  @chip_info:                Chip information from read-only registers.
+ *  @trig;              iio trigger.
+ *  @tap:               tap data structure.
+ *  @smd:               SMD data structure.
+ *  @reg:              Map of important registers.
+ *  @self_test:         self test settings.
+ *  @hw:               Other hardware-specific information.
+ *  @chip_type:                chip type.
+ *  @time_stamp_lock:  spin lock to time stamp.
+ *  @client:           i2c client handle.
+ *  @plat_data:                platform data.
+ *  @mpu_slave:                mpu slave handle.
+ *  (*set_power_state)(struct inv_mpu_iio_s *, int on): function ptr
+ *  (*switch_gyro_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  (*switch_accl_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  (*compass_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool);
+ *  (*quaternion_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool)
+ *  (*gyro_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool): func ptr.
+ *  (*accl_en)(struct inv_mpu_iio_s *, struct iio_buffer *, bool): func ptr.
+ *  (*init_config)(struct iio_dev *indio_dev): function ptr
+ * void (*setup_reg)(struct inv_reg_map_s *reg): function ptr
+ *  @timestamps:        kfifo queue to store time stamp.
+ *  @compass_st_upper:  compass self test upper limit.
+ *  @compass_st_lower:  compass self test lower limit.
+ *  @irq:               irq number store.
+ *  @accel_bias:        accel bias store.
+ *  @gyro_bias:         gyro bias store.
+ *  @raw_gyro:          raw gyro data.
+ *  @raw_accel:         raw accel data.
+ *  @raw_compass:       raw compass.
+ *  @raw_quaternion     raw quaternion data.
+ *  @int input_accel_bias[3]: accel bias from sysfs.
+ *  @compass_scale:     compass scale.
+ *  @i2c_addr:          i2c address.
+ *  @compass_divider:   slow down compass rate.
+ *  @compass_dmp_divider: slow down compass rate for dmp.
+ *  @compass_counter:   slow down compass rate.
+ *  @sample_divider:    sample divider for dmp.
+ *  @fifo_divider:      fifo divider for dmp.
+ *  @display_orient_data:display orient data.
+ *  @tap_data:          tap data.
+ *  @num_channels:      number of channels for current chip.
+ *  @sl_handle:         Handle to I2C port.
+ *  @irq_dur_ns:        duration between each irq.
+ *  @last_isr_time:     last isr time.
+ *  @mpu6500_last_motion_time: MPU6500 last real motion interrupt time.
+ *  @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX.
+ */
+struct inv_mpu_iio_s {
+#define TIMESTAMP_FIFO_SIZE 16
+       struct inv_chip_config_s chip_config;
+       struct inv_chip_info_s chip_info;
+       struct iio_trigger  *trig;
+       struct inv_tap_s   tap;
+       struct inv_smd_s smd;
+       struct inv_reg_map_s reg;
+       struct self_test_setting self_test;
+       const struct inv_hw_s *hw;
+       enum   inv_devices chip_type;
+       spinlock_t time_stamp_lock;
+       struct i2c_client *client;
+       struct mpu_platform_data plat_data;
+       struct inv_mpu_slave *mpu_slave;
+       struct accel_mot_int_s mot_int;
+       int (*set_power_state)(struct inv_mpu_iio_s *, bool on);
+       int (*switch_gyro_engine)(struct inv_mpu_iio_s *, bool on);
+       int (*switch_accl_engine)(struct inv_mpu_iio_s *, bool on);
+       int (*compass_en)(struct inv_mpu_iio_s *,
+                               struct iio_buffer *ring, bool on);
+       int (*quaternion_en)(struct inv_mpu_iio_s *,
+                               struct iio_buffer *ring, bool on);
+       int (*gyro_en)(struct inv_mpu_iio_s *,
+                               struct iio_buffer *ring, bool on);
+       int (*accl_en)(struct inv_mpu_iio_s *,
+                               struct iio_buffer *ring, bool on);
+       int (*init_config)(struct iio_dev *indio_dev);
+       void (*setup_reg)(struct inv_reg_map_s *reg);
+       DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE);
+       const short *compass_st_upper;
+       const short *compass_st_lower;
+       short irq;
+       int accel_bias[3];
+       int gyro_bias[3];
+       short raw_gyro[3];
+       short raw_accel[3];
+       short raw_compass[3];
+       int raw_quaternion[4];
+       int input_accel_bias[3];
+       u8 compass_scale;
+       u8 i2c_addr;
+       u8 compass_divider;
+       u8 compass_counter;
+       u8 compass_dmp_divider;
+       u8 sample_divider;
+       u8 fifo_divider;
+       u8 display_orient_data;
+       u8 tap_data;
+       enum inv_channel_num num_channels;
+       void *sl_handle;
+       u32 irq_dur_ns;
+       u64 last_isr_time;
+       u64 mpu6500_last_motion_time;
+       u8 name[20];
+       u8 secondary_name[20];
+};
+
+/* produces an unique identifier for each device based on the
+   combination of product version and product revision */
+struct prod_rev_map_t {
+       u16 mpl_product_key;
+       u8 silicon_rev;
+       u16 gyro_trim;
+       u16 accel_trim;
+};
+
+/**
+ *  struct inv_mpu_slave - MPU slave structure.
+ *  @suspend:          suspend operation.
+ *  @resume:           resume operation.
+ *  @setup:            setup chip. initialization.
+ *  @combine_data:     combine raw data into meaningful data.
+ *  @get_mode:         get current chip mode.
+ *  @set_lpf            set low pass filter.
+ *  @set_fs             set full scale
+ */
+struct inv_mpu_slave {
+       int (*suspend)(struct inv_mpu_iio_s *);
+       int (*resume)(struct inv_mpu_iio_s *);
+       int (*setup)(struct inv_mpu_iio_s *);
+       int (*combine_data)(u8 *in, short *out);
+       int (*get_mode)(void);
+       int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
+       int (*set_fs)(struct inv_mpu_iio_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_PD        0x00
+#define DATA_AKM_MODE_SM        0x01
+#define DATA_AKM_MODE_ST        0x08
+#define DATA_AKM_MODE_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 * (1L << 15))
+#define DATA_AKM8972_SCALE       (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE0      (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE1      (4915 * (1L << 15))
+#define AKM8963_SCALE_SHIFT      4
+#define NUM_BYTES_COMPASS_SLAVE  8
+
+/*register and associated bit definition*/
+#define REG_3050_FIFO_EN         0x12
+#define BITS_3050_ACCL_OUT             0x0E
+
+#define REG_3050_AUX_VDDIO       0x13
+#define BIT_3050_VDDIO                 0x04
+
+#define REG_3050_SLAVE_ADDR      0x14
+#define REG_3050_SAMPLE_RATE_DIV 0x15
+#define REG_3050_LPF             0x16
+#define REG_3050_INT_ENABLE      0x17
+#define REG_3050_AUX_BST_ADDR    0x18
+#define REG_3050_INT_STATUS      0x1A
+#define REG_3050_TEMPERATURE     0x1B
+#define REG_3050_RAW_GYRO        0x1D
+#define REG_3050_AUX_XOUT_H      0x23
+#define REG_3050_FIFO_COUNT_H    0x3A
+#define REG_3050_FIFO_R_W        0x3C
+
+#define REG_3050_USER_CTRL       0x3D
+#define BIT_3050_AUX_IF_EN             0x20
+#define BIT_3050_AUX_IF_RST            0x08
+#define BIT_3050_FIFO_RST              0x02
+
+#define REG_3050_PWR_MGMT_1      0x3E
+#define BITS_3050_POWER1               0x30
+#define BITS_3050_POWER2               0x10
+#define BITS_3050_GYRO_STANDBY         0x38
+
+#define REG_3500_OTP            0x0
+
+#define REG_YGOFFS_TC           0x1
+#define BIT_I2C_MST_VDDIO              0x80
+
+#define REG_XA_OFFS_L_TC        0x7
+#define REG_PRODUCT_ID          0xC
+#define REG_ST_GCT_X            0xD
+#define REG_SAMPLE_RATE_DIV     0x19
+#define REG_CONFIG              0x1A
+
+#define REG_GYRO_CONFIG         0x1B
+#define BITS_SELF_TEST_EN              0xE0
+
+#define REG_ACCEL_CONFIG       0x1C
+#define REG_ACCEL_MOT_THR       0x1F
+#define REG_ACCEL_MOT_DUR       0x20
+
+#define REG_FIFO_EN             0x23
+#define BIT_ACCEL_OUT                  0x08
+#define BITS_GYRO_OUT                  0x70
+
+
+#define REG_I2C_MST_CTRL        0x24
+#define BIT_WAIT_FOR_ES                        0x40
+
+#define REG_I2C_SLV0_ADDR       0x25
+#define BIT_I2C_READ                   0x80
+
+#define REG_I2C_SLV0_REG        0x26
+
+#define REG_I2C_SLV0_CTRL       0x27
+#define BIT_SLV_EN                     0x80
+
+#define REG_I2C_SLV1_ADDR       0x28
+#define REG_I2C_SLV1_REG        0x29
+#define REG_I2C_SLV1_CTRL       0x2A
+
+#define REG_I2C_SLV2_ADDR       0x2B
+#define REG_I2C_SLV2_REG        0x2C
+#define REG_I2C_SLV2_CTRL       0x2D
+
+#define REG_I2C_SLV4_CTRL       0x34
+
+#define REG_INT_PIN_CFG         0x37
+#define BIT_BYPASS_EN                   0x2
+
+#define REG_INT_ENABLE          0x38
+#define BIT_DATA_RDY_EN                 0x01
+#define BIT_DMP_INT_EN                  0x02
+#define BIT_ZMOT_EN                     0x20
+#define BIT_MOT_EN                      0x40
+#define BIT_6500_WOM_EN                 0x40
+
+#define REG_DMP_INT_STATUS      0x39
+#define SMD_INT_ON              0x04
+
+#define REG_INT_STATUS          0x3A
+#define BIT_MOT_INT                     0x40
+#define BIT_ZMOT_INT                    0x20
+
+#define REG_RAW_ACCEL           0x3B
+#define REG_TEMPERATURE         0x41
+#define REG_RAW_GYRO            0x43
+#define REG_EXT_SENS_DATA_00    0x49
+
+#define REG_ACCEL_INTEL_STATUS  0x61
+
+#define REG_I2C_SLV1_DO         0x64
+
+#define REG_I2C_MST_DELAY_CTRL  0x67
+#define BIT_SLV0_DLY_EN                 0x01
+#define BIT_SLV1_DLY_EN                 0x02
+#define BIT_SLV2_DLY_EN                 0x04
+
+#define REG_USER_CTRL           0x6A
+#define BIT_FIFO_RST                    0x04
+#define BIT_DMP_RST                     0x08
+#define BIT_I2C_MST_EN                  0x20
+#define BIT_FIFO_EN                     0x40
+#define BIT_DMP_EN                      0x80
+
+#define REG_PWR_MGMT_1          0x6B
+#define BIT_H_RESET                     0x80
+#define BIT_SLEEP                       0x40
+#define BIT_CYCLE                       0x20
+#define BIT_CLK_MASK                    0x7
+
+#define REG_PWR_MGMT_2          0x6C
+#define BIT_PWR_ACCL_STBY               0x38
+#define BIT_PWR_GYRO_STBY               0x07
+#define BIT_LPA_FREQ                    0xC0
+
+#define REG_BANK_SEL            0x6D
+#define REG_MEM_START_ADDR      0x6E
+#define REG_MEM_RW              0x6F
+#define REG_PRGM_STRT_ADDRH     0x70
+#define REG_FIFO_COUNT_H        0x72
+#define REG_FIFO_R_W            0x74
+#define REG_WHOAMI              0x75
+
+#define REG_6500_XG_ST_DATA     0x0
+#define REG_6500_XA_ST_DATA     0xD
+#define REG_6500_ACCEL_CONFIG2  0x1D
+#define BIT_ACCEL_FCHOCIE_B              0x08
+#define BIT_FIFO_SIZE_1K                 0x40
+
+
+#define REG_6500_LP_ACCEL_ODR   0x1E
+#define REG_6500_ACCEL_WOM_THR  0x1F
+
+#define REG_6500_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_ENABLE          0x80
+#define BIT_ACCEL_INTEL_MODE            0x40
+
+/* data definitions */
+#define DMP_START_ADDR           0x400
+#define DMP_MASK_TAP             0x3f
+#define DMP_MASK_DIS_ORIEN       0xC0
+#define DMP_DIS_ORIEN_SHIFT      6
+
+#define BYTES_FOR_DMP            16
+#define BYTES_FOR_EVENTS         4
+#define QUATERNION_BYTES         16
+#define BYTES_PER_SENSOR         6
+#define MPU3050_FOOTER_SIZE      2
+#define FIFO_COUNT_BYTE          2
+#define FIFO_THRESHOLD           500
+#define POWER_UP_TIME            100
+#define SENSOR_UP_TIME           30
+#define REG_UP_TIME              5
+#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
+#define MPU_MEM_BANK_SIZE        256
+
+#define MPU6XXX_MAX_MOTION_THRESH (255*4)
+#define MPU6XXX_MOTION_THRESH_SHIFT 5
+#define MPU6050_MOTION_DUR_DEFAULT  1
+#define MPU6050_ID               0x68
+#define MPU6050_MAX_MOTION_DUR   255
+#define MPU_TEMP_SHIFT           16
+#define LPA_FREQ_SHIFT           6
+#define COMPASS_RATE_SCALE       10
+#define MAX_GYRO_FS_PARAM        3
+#define MAX_ACCL_FS_PARAM        3
+#define MAX_LPA_FREQ_PARAM       3
+#define MPU6XXX_MAX_MPU_MEM      (256 * 12)
+
+#define INIT_MOT_DUR             128
+#define INIT_MOT_THR             128
+#define INIT_ZMOT_DUR            128
+#define INIT_ZMOT_THR            128
+#define INIT_ST_SAMPLES          50
+#define INIT_ST_THRESHOLD        14
+#define ST_THRESHOLD_MULTIPLIER  10
+#define ST_MAX_SAMPLES           500
+#define ST_MAX_THRESHOLD         100
+
+/*---- MPU6500 ----*/
+#define MPU6500_ID               0x70      /* unique WHOAMI */
+#define MPU6500_PRODUCT_REVISION 1
+#define MPU6500_MEM_REV_ADDR     0x16
+#define INV_MPU_REV_MASK         0xF
+#define MPU6500_REV              2
+
+/*---- MPU6515 ----*/
+#define MPU6515_ID               0x74      /* unique WHOAMI */
+
+/*---- MPU9250 ----*/
+#define MPU9250_ID               0x71      /* unique WHOAMI */
+
+#define THREE_AXIS               3
+#define GYRO_CONFIG_FSR_SHIFT    3
+#define ACCL_CONFIG_FSR_SHIFT    3
+#define GYRO_DPS_SCALE           250
+#define MEM_ADDR_PROD_REV        0x6
+#define SOFT_PROD_VER_BYTES      5
+#define CRC_FIRMWARE_SEED        0
+#define SELF_TEST_SUCCESS        1
+#define MS_PER_DMP_TICK          20
+
+/* init parameters */
+#define INIT_FIFO_RATE           50
+#define INIT_DMP_OUTPUT_RATE     25
+#define INIT_DUR_TIME           ((1000 / INIT_FIFO_RATE) * 1000 * 1000)
+#define INIT_TAP_THRESHOLD       100
+#define INIT_TAP_TIME            100
+#define INIT_TAP_MIN_COUNT       2
+#define MPU_INIT_SMD_DELAY_THLD  3
+#define MPU_INIT_SMD_DELAY2_THLD 1
+#define MPU_INIT_SMD_THLD        1500
+#define MPU_DEFAULT_DMP_FREQ     200
+#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
+
+#define TIME_STAMP_TOR                        5
+#define MAX_CATCH_UP                          5
+#define DEFAULT_ACCL_TRIM                     16384
+#define DEFAULT_GYRO_TRIM                     131
+#define MAX_FIFO_RATE                         1000
+#define MAX_DMP_OUTPUT_RATE                   200
+#define MIN_FIFO_RATE                         4
+#define ONE_K_HZ                              1000
+#define NS_PER_MS_SHIFT                       20
+
+/*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_DISPLAY_ORIENT  0x08
+#define INT_SRC_SHAKE           0x10
+
+#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
+#define INV_ACCL_MASK                     0x70
+#define INV_GYRO_MASK                     0xE
+/* scan element definition */
+enum inv_mpu_scan {
+       INV_MPU_SCAN_QUAT_R = 0,
+       INV_MPU_SCAN_QUAT_X,
+       INV_MPU_SCAN_QUAT_Y,
+       INV_MPU_SCAN_QUAT_Z,
+       INV_MPU_SCAN_ACCL_X,
+       INV_MPU_SCAN_ACCL_Y,
+       INV_MPU_SCAN_ACCL_Z,
+       INV_MPU_SCAN_GYRO_X,
+       INV_MPU_SCAN_GYRO_Y,
+       INV_MPU_SCAN_GYRO_Z,
+       INV_MPU_SCAN_MAGN_X,
+       INV_MPU_SCAN_MAGN_Y,
+       INV_MPU_SCAN_MAGN_Z,
+       INV_MPU_SCAN_TIMESTAMP,
+};
+
+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
+};
+
+enum inv_slave_mode {
+       INV_MODE_SUSPEND,
+       INV_MODE_NORMAL,
+};
+
+/*==== 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
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+       ATTR_DMP_SMD_ENABLE,
+       ATTR_DMP_SMD_THLD,
+       ATTR_DMP_SMD_DELAY_THLD,
+       ATTR_DMP_SMD_DELAY_THLD2,
+       ATTR_DMP_TAP_ON,
+       ATTR_DMP_TAP_THRESHOLD,
+       ATTR_DMP_TAP_MIN_COUNT,
+       ATTR_DMP_TAP_TIME,
+       ATTR_DMP_DISPLAY_ORIENTATION_ON,
+/* *****above this line, are DMP features, power needs on/off */
+/* *****below this line, are DMP features, no power needed */
+       ATTR_DMP_ON,
+       ATTR_DMP_INT_ON,
+       ATTR_DMP_EVENT_INT_ON,
+       ATTR_DMP_OUTPUT_RATE,
+       ATTR_DMP_QUATERNION_ON,
+/*  *****above this line, it is all DMP related features */
+/*  *****below this line, it is all non-DMP related features */
+       ATTR_MOTION_LPA_ON,
+       ATTR_MOTION_LPA_FREQ,
+       ATTR_MOTION_LPA_THRESHOLD,
+/*  *****above this line, it is non-DMP, power needs on/off */
+/*  *****below this line, it is non-DMP, no needs to on/off power */
+       ATTR_SELF_TEST_SAMPLES,
+       ATTR_SELF_TEST_THRESHOLD,
+       ATTR_GYRO_ENABLE,
+       ATTR_ACCL_ENABLE,
+       ATTR_COMPASS_ENABLE,
+       ATTR_POWER_STATE, /* this is fake sysfs for compatibility */
+       ATTR_FIRMWARE_LOADED,
+       ATTR_SAMPLING_FREQ,
+/*  *****below this line, it is attributes only has show methods */
+       ATTR_SELF_TEST, /* this has show-only methods but needs power on/off */
+       ATTR_GYRO_MATRIX,
+       ATTR_ACCL_MATRIX,
+       ATTR_COMPASS_MATRIX,
+       ATTR_SECONDARY_NAME,
+#ifdef CONFIG_INV_TESTING
+       ATTR_I2C_COUNTERS,
+       ATTR_REG_WRITE,
+       ATTR_DEBUG_SMD_ENABLE_TESTP1,
+       ATTR_DEBUG_SMD_ENABLE_TESTP2,
+       ATTR_DEBUG_SMD_EXE_STATE,
+       ATTR_DEBUG_SMD_DELAY_CNTR
+#endif
+};
+
+enum inv_accl_fs_e {
+       INV_FS_02G = 0,
+       INV_FS_04G,
+       INV_FS_08G,
+       INV_FS_16G,
+       NUM_ACCL_FSR
+};
+
+enum inv_fsr_e {
+       INV_FSR_250DPS = 0,
+       INV_FSR_500DPS,
+       INV_FSR_1000DPS,
+       INV_FSR_2000DPS,
+       NUM_FSR
+};
+
+enum inv_clock_sel_e {
+       INV_CLK_INTERNAL = 0,
+       INV_CLK_PLL,
+       NUM_CLK
+};
+
+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);
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+int inv_init_config_mpu3050(struct iio_dev *indio_dev);
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st);
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st);
+int set_3050_bypass(struct inv_mpu_iio_s *st, bool enable);
+int inv_register_mpu3050_slave(struct inv_mpu_iio_s *st);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_switch_3050_gyro_engine(struct inv_mpu_iio_s *st, bool en);
+int inv_switch_3050_accl_engine(struct inv_mpu_iio_s *st, bool en);
+int set_power_mpu3050(struct inv_mpu_iio_s *st, bool power_on);
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on);
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on);
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate);
+u16 inv_dmp_get_address(u16 key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+                               u32 axis, u16 threshold);
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st, u16 min_taps);
+int  inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time);
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, u16 i2c_addr,
+       u8 reg, u16 length, u8 *data);
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+       u16 i2c_addr, u8 reg, u8 data);
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+               int *gyro_result, int *accl_result);
+int inv_hw_self_test(struct inv_mpu_iio_s *st);
+void inv_recover_setting(struct inv_mpu_iio_s *st);
+int inv_power_up_self_test(struct inv_mpu_iio_s *st);
+s64 get_time_ns(void);
+int write_be32_key_to_mem(struct inv_mpu_iio_s *st,
+                                       u32 data, int key);
+int inv_set_accel_bias_dmp(struct inv_mpu_iio_s *st);
+int inv_send_sensor_data(struct inv_mpu_iio_s *st, u16 elements);
+int inv_send_interrupt_word(struct inv_mpu_iio_s *st, bool on);
+int mpu_memory_write(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+                    u32 len, u8 const *data);
+int mpu_memory_read(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+                   u32 len, u8 *data);
+int mpu_memory_write_unaligned(struct inv_mpu_iio_s *st, u16 key, int len,
+                                                       u8 const *d);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+#define mem_w(a, b, c) \
+       mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c)
+#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)
+
+#endif  /* #ifndef _INV_MPU_IIO_H_ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c
new file mode 100644 (file)
index 0000000..0774e18
--- /dev/null
@@ -0,0 +1,2162 @@
+/*
+* 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_mpu_misc.c
+ *      @brief   A sysfs device driver for Invensense mpu.
+ *      @details This file is part of invensense mpu driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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/crc32.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_counters.h"
+
+/* DMP defines */
+#define DMP_ORIENTATION_TIME           500
+#define DMP_ORIENTATION_ANGLE          60
+#define DMP_DEFAULT_FIFO_RATE           200
+#define DMP_TAP_SCALE                   (767603923 / 5)
+#define DMP_MULTI_SHIFT                 30
+#define DMP_MULTI_TAP_TIME              500
+#define DMP_SHAKE_REJECT_THRESH         100
+#define DMP_SHAKE_REJECT_TIME           10
+#define DMP_SHAKE_REJECT_TIMEOUT        10
+#define DMP_ANGLE_SCALE                 15
+#define DMP_PRECISION                   1000
+#define DMP_MAX_DIVIDER                 4
+#define DMP_MAX_MIN_TAPS                4
+#define DMP_IMAGE_CRC_VALUE             0x665f5a73
+#define DMP_IMAGE_SIZE                  2913
+
+/*--- 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
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FS         (0 << 3)
+#define DEF_SELFTEST_ACCEL_FS         (2 << 3)
+#define DEF_SELFTEST_GYRO_SENS            (32768 / 250)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME          10
+#define DEF_ST_STABLE_TIME          200
+#define DEF_ST_6500_STABLE_TIME     20
+#define DEF_GYRO_SCALE              131
+#define DEF_ST_PRECISION            1000
+#define DEF_ST_ACCEL_FS_MG          8000UL
+#define DEF_ST_SCALE                (1L << 15)
+#define DEF_ST_TRY_TIMES            2
+#define DEF_ST_COMPASS_RESULT_SHIFT 2
+#define DEF_ST_ACCEL_RESULT_SHIFT   1
+#define DEF_ST_OTP0_THRESH          60
+#define DEF_ST_ABS_THRESH           20
+#define DEF_ST_TOR                  2
+
+#define DEF_ST_COMPASS_WAIT_MIN     (10 * 1000)
+#define DEF_ST_COMPASS_WAIT_MAX     (15 * 1000)
+#define DEF_ST_COMPASS_TRY_TIMES    10
+#define DEF_ST_COMPASS_8963_SHIFT   2
+
+#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
+
+/*---- MPU6500 Self Test Pass/Fail Criteria ----*/
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX             20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL                  60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN             225
+#define DEF_ACCEL_ST_AL_MAX             675
+#define DEF_6500_ACCEL_ST_SHIFT_DELTA   500
+#define DEF_6500_GYRO_CT_SHIFT_DELTA    500
+#define DEF_ST_MPU6500_ACCEL_LPF        2
+#define DEF_ST_6500_ACCEL_FS_MG         2000UL
+#define DEF_SELFTEST_6500_ACCEL_FS      (0 << 3)
+
+/* Note: The ST_AL values are only used when ST_OTP = 0,
+ * i.e no factory self test values for reference
+ */
+
+/* NOTE: product entries are in chronological order */
+static const 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},
+       /* prod_ver = 1 */
+       {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},
+       {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},
+       /* prod_ver = 1 */
+       {MPL_PROD_KEY(1,  16), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,  17), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,  18), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,  19), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,  20), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,  28), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   1), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   2), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   3), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   4), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   5), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(1,   6), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 2 */
+       {MPL_PROD_KEY(2,   7), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,   8), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,   9), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,  10), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,  11), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,  12), MPU_SILICON_REV_B1, 131, 16384},
+       {MPL_PROD_KEY(2,  29), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 3 */
+       {MPL_PROD_KEY(3,  30), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 4 */
+       {MPL_PROD_KEY(4,  31), MPU_SILICON_REV_B1, 131,  8192},
+       {MPL_PROD_KEY(4,   1), MPU_SILICON_REV_B1, 131,  8192},
+       {MPL_PROD_KEY(4,   3), MPU_SILICON_REV_B1, 131,  8192},
+       /* prod_ver = 5 */
+       {MPL_PROD_KEY(5,   3), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 6 */
+       {MPL_PROD_KEY(6,  19), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 7 */
+       {MPL_PROD_KEY(7,  19), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 8 */
+       {MPL_PROD_KEY(8,  19), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 9 */
+       {MPL_PROD_KEY(9,  19), MPU_SILICON_REV_B1, 131, 16384},
+       /* prod_ver = 10 */
+       {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}
+};
+
+/*
+*   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 const 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 const u16 mpu_6500_st_tb[256] = {
+       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
+};
+
+static const 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 const 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 const 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};
+
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string)
+{
+       int ii;
+       string = kmalloc(len * 2 + 1, GFP_KERNEL);
+       for (ii = 0; ii < len; ii++)
+               sprintf(&string[ii * 2], "%02X", data[ii]);
+       string[len * 2] = 0;
+       return string;
+}
+
+char *wr_pr_debug_end(char *string)
+{
+       kfree(string);
+       return "";
+}
+
+int mpu_memory_write(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+                    u32 len, u8 const *data)
+{
+       u8 bank[2];
+       u8 addr[2];
+       u8 buf[513];
+
+       struct i2c_msg msg;
+       int res;
+
+       if (!data || !st)
+               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;
+       addr[1] = mem_addr & 0xFF;
+
+       buf[0] = REG_MEM_RW;
+       memcpy(buf + 1, data, len);
+
+       /* write message */
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = bank;
+       msg.len = sizeof(bank);
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(3);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = addr;
+       msg.len = sizeof(addr);
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(3);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = (u8 *)buf;
+       msg.len = len + 1;
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(2+len);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       {
+               char *write = 0;
+               pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+                        mpu_addr, bank[1], addr[1],
+                        wr_pr_debug_begin(data, len, write),
+                        wr_pr_debug_end(write),
+                        len);
+       }
+       return 0;
+}
+
+int mpu_memory_read(struct inv_mpu_iio_s *st, u8 mpu_addr, u16 mem_addr,
+                   u32 len, u8 *data)
+{
+       u8 bank[2];
+       u8 addr[2];
+       u8 buf;
+
+       struct i2c_msg msg;
+       int res;
+
+       if (!data || !st)
+               return -EINVAL;
+
+       bank[0] = REG_BANK_SEL;
+       bank[1] = mem_addr >> 8;
+
+       addr[0] = REG_MEM_START_ADDR;
+       addr[1] = mem_addr & 0xFF;
+
+       buf = REG_MEM_RW;
+
+       /* write message */
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = bank;
+       msg.len = sizeof(bank);
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(3);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = addr;
+       msg.len = sizeof(addr);
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(3);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       msg.addr = mpu_addr;
+       msg.flags = 0;
+       msg.buf = &buf;
+       msg.len = 1;
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUWRITE(3);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+
+       msg.addr = mpu_addr;
+       msg.flags = I2C_M_RD;
+       msg.buf = data;
+       msg.len = len;
+       /* msg.scl_rate = 200*1000; */
+       INV_I2C_INC_MPUREAD(len);
+       res = i2c_transfer(st->sl_handle, &msg, 1);
+       if (res < 1) {
+               if (res >= 0)
+                       res = -EIO;
+               return res;
+       }
+       {
+               char *read = 0;
+               pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+                        mpu_addr, bank[1], addr[1], len,
+                        wr_pr_debug_begin(data, len, read),
+                        wr_pr_debug_end(read));
+       }
+
+       return 0;
+}
+
+int mpu_memory_write_unaligned(struct inv_mpu_iio_s *st, u16 key, int len,
+                                                               u8 const *d)
+{
+       u32 addr;
+       int start, end;
+       int len1, len2;
+       int result = 0;
+       if (len > MPU_MEM_BANK_SIZE)
+               return -EINVAL;
+       addr = inv_dmp_get_address(key);
+       if (addr > MPU6XXX_MAX_MPU_MEM)
+               return -EINVAL;
+       start = (addr >> 8);
+       end   = ((addr + len - 1) >> 8);
+       if (start == end) {
+               result = mpu_memory_write(st, st->i2c_addr, addr, len, d);
+       } else {
+               end <<= 8;
+               len1 = end - addr;
+               len2 = len - len1;
+               result = mpu_memory_write(st, st->i2c_addr, addr, len1, d);
+               result |= mpu_memory_write(st, st->i2c_addr, end, len2,
+                                                               d + len1);
+       }
+
+       return result;
+}
+
+/**
+ *  index_of_key()- Inverse lookup of the index of an MPL product key .
+ *  @key: the MPL product indentifier also referred to as 'key'.
+ */
+static short index_of_key(u16 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 -EINVAL;
+}
+
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_iio_s *st)
+{
+       struct inv_chip_info_s *chip_info = &st->chip_info;
+       int result;
+       u8 whoami, sw_rev;
+
+       result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami);
+       if (result)
+               return result;
+       if (whoami != MPU6500_ID && whoami != MPU9250_ID &&
+                       whoami != MPU6515_ID)
+               return -EINVAL;
+
+       /*memory read need more time after power up */
+       msleep(POWER_UP_TIME);
+       result = mpu_memory_read(st, st->i2c_addr,
+                       MPU6500_MEM_REV_ADDR, 1, &sw_rev);
+       sw_rev &= INV_MPU_REV_MASK;
+       if (result)
+               return result;
+       if (sw_rev != 0)
+               return -EINVAL;
+
+       /* these values are place holders and not real values */
+       chip_info->product_id = MPU6500_PRODUCT_REVISION;
+       chip_info->product_revision = MPU6500_PRODUCT_REVISION;
+       chip_info->silicon_revision = MPU6500_PRODUCT_REVISION;
+       chip_info->software_revision = sw_rev;
+       chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM;
+       chip_info->accl_sens_trim = DEFAULT_ACCL_TRIM;
+       chip_info->multi = 1;
+
+       return 0;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st)
+{
+       int result;
+       struct inv_reg_map_s *reg;
+       u8 prod_ver = 0x00, prod_rev = 0x00;
+       struct prod_rev_map_t *p_rev;
+       u8 bank =
+           (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+       u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV);
+       u16 key;
+       u8 regs[5];
+       u16 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;
+       /*memory read need more time after power up */
+       msleep(POWER_UP_TIME);
+       result = mpu_memory_read(st, 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, REG_XA_OFFS_L_TC,
+                               SOFT_PROD_VER_BYTES, 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 -EINVAL;
+               index = index_of_key(key);
+               if (index < 0 || index >= NUM_OF_PROD_REVS)
+                       return -EINVAL;
+               /* check MPL is compiled for this device */
+               if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+                       return -EINVAL;
+               p_rev = (struct prod_rev_map_t *)&prod_rev_map[index];
+       /* if valid, use the software product key */
+       } else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+               p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev];
+       } else {
+               return -EINVAL;
+       }
+       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)
+               pr_info("multi is %d\n", chip_info->multi);
+       return result;
+}
+
+/**
+ *  read_accel_hw_self_test_prod_shift()- read the accelerometer hardware
+ *                                         self-test bias shift calculated
+ *                                         during final production test and
+ *                                         stored in chip non-volatile memory.
+ *  @st:  main data structure.
+ *  @st_prod:   A pointer to an array of 3 elements to hold the values
+ *              for production hardware self-test bias shifts returned to the
+ *              user.
+ *  @accl_sens: accel sensitivity.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_mpu_iio_s *st,
+                                       int *st_prod, int *accl_sens)
+{
+       u8 regs[4];
+       u8 shift_code[3];
+       int result, i;
+
+       for (i = 0; i < 3; i++)
+               st_prod[i] = 0;
+
+       result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs);
+
+       if (result)
+               return result;
+       if ((0 == regs[0])  && (0 == regs[1]) &&
+           (0 == regs[2]) && (0 == regs[3]))
+               return -EINVAL;
+       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] = accl_sens[i] *
+                               accl_st_tb[shift_code[i] - 1];
+       }
+
+       return 0;
+}
+
+/**
+* inv_check_accl_self_test()- check accel self test. this function returns
+*                              zero as success. A non-zero return value
+*                              indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_accl_self_test(struct inv_mpu_iio_s *st,
+       int *reg_avg, int *st_avg){
+       int gravity, reg_z_avg, g_z_sign, j, ret_val;
+       int tmp;
+       int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS];
+       int st_shift_ratio[THREE_AXIS];
+       int accel_sens[THREE_AXIS];
+
+       if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+           st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+               return 0;
+       g_z_sign = 1;
+       ret_val = 0;
+       tmp = DEF_ST_SCALE * DEF_ST_PRECISION / DEF_ST_ACCEL_FS_MG;
+       for (j = 0; j < 3; j++)
+               accel_sens[j] = tmp;
+
+       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 */
+               accel_sens[Z] /= 2;
+       } else {
+               /* half sensitivity X, Y, Z accelerometer parts */
+               accel_sens[X] /= st->chip_info.multi;
+               accel_sens[Y] /= st->chip_info.multi;
+               accel_sens[Z] /= st->chip_info.multi;
+       }
+       gravity = accel_sens[Z];
+       reg_z_avg = reg_avg[Z] - g_z_sign * gravity * DEF_ST_PRECISION;
+       ret_val = read_accel_hw_self_test_prod_shift(st, st_shift_prod,
+                                                       accel_sens);
+       if (ret_val)
+               return ret_val;
+
+       for (j = 0; j < 3; j++) {
+               st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+               if (st_shift_prod[j]) {
+                       tmp = st_shift_prod[j] / DEF_ST_PRECISION;
+                       st_shift_ratio[j] = abs(st_shift_cust[j] / tmp
+                               - DEF_ST_PRECISION);
+                       if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+                               ret_val = 1;
+               } else {
+                       if (st_shift_cust[j] <
+                               DEF_ACCEL_ST_SHIFT_MIN * gravity)
+                               ret_val = 1;
+                       if (st_shift_cust[j] >
+                               DEF_ACCEL_ST_SHIFT_MAX * gravity)
+                               ret_val = 1;
+               }
+       }
+
+       return ret_val;
+}
+
+/**
+* inv_check_3500_gyro_self_test() check gyro self test. this function returns
+*                                 zero as success. A non-zero return value
+*                                 indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+
+static int inv_check_3500_gyro_self_test(struct inv_mpu_iio_s *st,
+       int *reg_avg, int *st_avg){
+       int result;
+       int gst[3], ret_val;
+       int gst_otp[3], i;
+       u8 st_code[THREE_AXIS];
+       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, THREE_AXIS, st_code);
+       if (result)
+               return result;
+       gst_otp[0] = 0;
+       gst_otp[1] = 0;
+       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];
+       }
+       /* check self test value passing criterion. Using the DEF_ST_TOR
+        * for certain degree of tolerance */
+       for (i = 0; i < 3; i++) {
+               if (gst_otp[i] == 0) {
+                       if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH *
+                                                       DEF_ST_PRECISION *
+                                                       DEF_GYRO_SCALE)
+                               ret_val |= (1 << i);
+               } else {
+                       if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) >
+                                       DEF_GYRO_CT_SHIFT_DELTA)
+                               ret_val |= (1 << i);
+               }
+       }
+       /* check for absolute value passing criterion. Using DEF_ST_TOR
+        * for certain degree of tolerance */
+       for (i = 0; i < 3; i++) {
+               if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+                   DEF_ST_PRECISION * DEF_GYRO_SCALE)
+                       ret_val |= (1 << i);
+       }
+
+       return ret_val;
+}
+
+/**
+* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function
+*                                   returns zero as success. A non-zero return
+*                                   value indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_6050_gyro_self_test(struct inv_mpu_iio_s *st,
+       int *reg_avg, int *st_avg){
+       int result;
+       int ret_val;
+       int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+       u8 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);
+       if (result)
+               return result;
+       regs[X] &= 0x1f;
+       regs[Y] &= 0x1f;
+       regs[Z] &= 0x1f;
+       for (i = 0; i < 3; i++) {
+               if (regs[i] != 0)
+                       st_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+               else
+                       st_shift_prod[i] = 0;
+       }
+       st_shift_prod[1] = -st_shift_prod[1];
+
+       for (i = 0; i < 3; i++) {
+               st_shift_cust[i] =  st_avg[i] - reg_avg[i];
+               if (st_shift_prod[i]) {
+                       st_shift_ratio[i] = abs(st_shift_cust[i] /
+                               st_shift_prod[i] - DEF_ST_PRECISION);
+                       if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+                               ret_val = 1;
+               } else {
+                       if (st_shift_cust[i] < DEF_ST_PRECISION *
+                               DEF_GYRO_CT_SHIFT_MIN * DEF_SELFTEST_GYRO_SENS)
+                               ret_val = 1;
+                       if (st_shift_cust[i] > DEF_ST_PRECISION *
+                               DEF_GYRO_CT_SHIFT_MAX * DEF_SELFTEST_GYRO_SENS)
+                               ret_val = 1;
+               }
+       }
+       /* check for absolute value passing criterion. Using DEF_ST_TOR
+        * for certain degree of tolerance */
+       for (i = 0; i < 3; i++)
+               if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+                   DEF_ST_PRECISION * DEF_GYRO_SCALE)
+                       ret_val = 1;
+
+       return ret_val;
+}
+
+/**
+* inv_check_6500_gyro_self_test() - check 6500 gyro self test. this function
+*                                   returns zero as success. A non-zero return
+*                                   value indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_6500_gyro_self_test(struct inv_mpu_iio_s *st,
+       int *reg_avg, int *st_avg)
+{
+       u8 regs[3];
+       int ret_val, result;
+       int otp_value_zero = 0;
+       int st_shift_prod[3], st_shift_cust[3], i;
+
+       ret_val = 0;
+       result = inv_i2c_read(st, REG_6500_XG_ST_DATA, 3, regs);
+       if (result)
+               return result;
+       pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
+                st->hw->name, regs[0], regs[1], regs[2]);
+
+       for (i = 0; i < 3; i++) {
+               if (regs[i] != 0) {
+                       st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+               } else {
+                       st_shift_prod[i] = 0;
+                       otp_value_zero = 1;
+               }
+       }
+       pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
+                st->hw->name, st_shift_prod[0], st_shift_prod[1],
+                st_shift_prod[2]);
+
+       for (i = 0; i < 3; i++) {
+               st_shift_cust[i] = st_avg[i] - reg_avg[i];
+               if (!otp_value_zero) {
+                       /* Self Test Pass/Fail Criteria A */
+                       if (st_shift_cust[i] < DEF_6500_GYRO_CT_SHIFT_DELTA
+                                               * st_shift_prod[i])
+                                       ret_val = 1;
+               } else {
+                       /* Self Test Pass/Fail Criteria B */
+                       if (st_shift_cust[i] < DEF_GYRO_ST_AL *
+                                               DEF_SELFTEST_GYRO_SENS *
+                                               DEF_ST_PRECISION)
+                               ret_val = 1;
+               }
+       }
+       pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
+                st->hw->name, st_shift_cust[0], st_shift_cust[1],
+                st_shift_cust[2]);
+
+       if (ret_val == 0) {
+               /* Self Test Pass/Fail Criteria C */
+               for (i = 0; i < 3; i++)
+                       if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
+                                               DEF_SELFTEST_GYRO_SENS *
+                                               DEF_ST_PRECISION)
+                               ret_val = 1;
+       }
+
+       return ret_val;
+}
+
+/**
+* inv_check_6500_accel_self_test() - check 6500 accel self test. this function
+*                                   returns zero as success. A non-zero return
+*                                   value indicates failure in self test.
+*  @*st: main data structure.
+*  @*reg_avg: average value of normal test.
+*  @*st_avg:  average value of self test
+*/
+static int inv_check_6500_accel_self_test(struct inv_mpu_iio_s *st,
+                                               int *reg_avg, int *st_avg) {
+       int ret_val, result;
+       int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+       u8 regs[3];
+       int otp_value_zero = 0;
+
+#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
+                                / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
+                                / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+
+       ret_val = 0;
+       result = inv_i2c_read(st, REG_6500_XA_ST_DATA, 3, regs);
+       if (result)
+               return result;
+       pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
+                st->hw->name, regs[0], regs[1], regs[2]);
+
+       for (i = 0; i < 3; i++) {
+               if (regs[i] != 0) {
+                       st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+               } else {
+                       st_shift_prod[i] = 0;
+                       otp_value_zero = 1;
+               }
+       }
+       pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
+                st->hw->name, st_shift_prod[0], st_shift_prod[1],
+                st_shift_prod[2]);
+
+       if (!otp_value_zero) {
+               /* Self Test Pass/Fail Criteria A */
+               for (i = 0; i < 3; i++) {
+                       st_shift_cust[i] = st_avg[i] - reg_avg[i];
+                       st_shift_ratio[i] = abs(st_shift_cust[i] /
+                                       st_shift_prod[i] - DEF_ST_PRECISION);
+                       if (st_shift_ratio[i] > DEF_6500_ACCEL_ST_SHIFT_DELTA)
+                               ret_val = 1;
+               }
+       } else {
+               /* Self Test Pass/Fail Criteria B */
+               for (i = 0; i < 3; i++) {
+                       st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
+                       if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
+                                       st_shift_cust[i] > ACCEL_ST_AL_MAX)
+                               ret_val = 1;
+               }
+       }
+       pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
+                st->hw->name, st_shift_cust[0], st_shift_cust[1],
+                st_shift_cust[2]);
+
+       return ret_val;
+}
+
+/*
+ *  inv_do_test() - do the actual test of self testing
+ */
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+               int *gyro_result, int *accl_result)
+{
+       struct inv_reg_map_s *reg;
+       int result, i, j, packet_size;
+       u8 data[BYTES_PER_SENSOR * 2], d;
+       bool has_accl;
+       int fifo_count, packet_count, ind, s;
+
+       reg = &st->reg;
+       has_accl = (st->chip_type != INV_ITG3500);
+       if (has_accl)
+               packet_size = BYTES_PER_SENSOR * 2;
+       else
+               packet_size = BYTES_PER_SENSOR;
+
+       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;
+       /* setup parameters */
+       result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_98HZ);
+       if (result)
+               return result;
+
+       if (INV_MPU6500 == st->chip_type) {
+               /* config accel LPF register for MPU6500 */
+               result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2,
+                                               DEF_ST_MPU6500_ACCEL_LPF |
+                                               BIT_FIFO_SIZE_1K);
+               if (result)
+                       return result;
+       }
+
+       result = inv_i2c_single_write(st, reg->sample_rate_div,
+                       DEF_SELFTEST_SAMPLE_RATE);
+       if (result)
+               return result;
+       /* wait for the sampling rate change to stabilize */
+       mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+       result = inv_i2c_single_write(st, reg->gyro_config,
+               self_test_flag | DEF_SELFTEST_GYRO_FS);
+       if (result)
+               return result;
+       if (has_accl) {
+               if (INV_MPU6500 == st->chip_type)
+                       d = DEF_SELFTEST_6500_ACCEL_FS;
+               else
+                       d = DEF_SELFTEST_ACCEL_FS;
+               d |= self_test_flag;
+               result = inv_i2c_single_write(st, reg->accl_config, d);
+               if (result)
+                       return result;
+       }
+       /* wait for the output to get stable */
+       if (self_test_flag) {
+               if (INV_MPU6500 == st->chip_type)
+                       msleep(DEF_ST_6500_STABLE_TIME);
+               else
+                       msleep(DEF_ST_STABLE_TIME);
+       }
+
+       /* enable FIFO reading */
+       result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+       if (result)
+               return result;
+       /* enable sensor output to FIFO */
+       if (has_accl)
+               d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
+       else
+               d = BITS_GYRO_OUT;
+       for (i = 0; i < THREE_AXIS; i++) {
+               gyro_result[i] = 0;
+               accl_result[i] = 0;
+       }
+       s = 0;
+       while (s < st->self_test.samples) {
+               result = inv_i2c_single_write(st, reg->fifo_en, d);
+               if (result)
+                       return result;
+               mdelay(DEF_GYRO_WAIT_TIME);
+               result = inv_i2c_single_write(st, reg->fifo_en, 0);
+               if (result)
+                       return result;
+
+               result = inv_i2c_read(st, reg->fifo_count_h,
+                                       FIFO_COUNT_BYTE, data);
+               if (result)
+                       return result;
+               fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+               pr_debug("%s self_test fifo_count - %d\n",
+                        st->hw->name, fifo_count);
+               packet_count = fifo_count / packet_size;
+               i = 0;
+               while ((i < packet_count) && (s < st->self_test.samples)) {
+                       short vals[3];
+                       result = inv_i2c_read(st, reg->fifo_r_w,
+                               packet_size, data);
+                       if (result)
+                               return result;
+                       ind = 0;
+                       if (has_accl) {
+                               for (j = 0; j < THREE_AXIS; j++) {
+                                       vals[j] = (short)be16_to_cpup(
+                                           (__be16 *)(&data[ind + 2 * j]));
+                                       accl_result[j] += vals[j];
+                               }
+                               ind += BYTES_PER_SENSOR;
+                               pr_debug(
+                                   "%s self_test accel data - %d %+d %+d %+d",
+                                   st->hw->name, s, vals[0], vals[1], vals[2]);
+                       }
+
+                       for (j = 0; j < THREE_AXIS; j++) {
+                               vals[j] = (short)be16_to_cpup(
+                                       (__be16 *)(&data[ind + 2 * j]));
+                               gyro_result[j] += vals[j];
+                       }
+                       pr_debug("%s self_test gyro data - %d %+d %+d %+d",
+                                st->hw->name, s, vals[0], vals[1], vals[2]);
+
+                       s++;
+                       i++;
+               }
+       }
+
+       if (has_accl) {
+               for (j = 0; j < THREE_AXIS; j++) {
+                       accl_result[j] = accl_result[j] / s;
+                       accl_result[j] *= DEF_ST_PRECISION;
+               }
+       }
+       for (j = 0; j < THREE_AXIS; j++) {
+               gyro_result[j] = gyro_result[j] / s;
+               gyro_result[j] *= DEF_ST_PRECISION;
+       }
+
+       return 0;
+}
+
+/*
+ *  inv_recover_setting() recover the old settings after everything is done
+ */
+void inv_recover_setting(struct inv_mpu_iio_s *st)
+{
+       struct inv_reg_map_s *reg;
+       int data;
+
+       reg = &st->reg;
+       inv_i2c_single_write(st, reg->gyro_config,
+                            st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT);
+       inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+       data = ONE_K_HZ/st->chip_config.new_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 <<
+                                    ACCL_CONFIG_FSR_SHIFT));
+       }
+       st->switch_gyro_engine(st, false);
+       st->switch_accl_engine(st, false);
+       st->set_power_state(st, false);
+}
+
+static int inv_check_compass_self_test(struct inv_mpu_iio_s *st)
+{
+       int result;
+       u8 data[6];
+       u8 counter, cntl;
+       short x, y, z;
+       u8 *sens;
+       sens = st->chip_info.compass_sens;
+
+       /* set to bypass mode */
+       result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+                               st->plat_data.int_config | BIT_BYPASS_EN);
+       if (result) {
+               result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+                               st->plat_data.int_config);
+               return result;
+       }
+       /* set to power down mode */
+       result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PD);
+       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_ST);
+       if (result)
+               goto AKM_fail;
+       counter = DEF_ST_COMPASS_TRY_TIMES;
+       while (counter > 0) {
+               usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX);
+               result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+               if (result)
+                       goto AKM_fail;
+               if ((data[0] & DATA_AKM_DRDY) == 0)
+                       counter--;
+               else
+                       counter = 0;
+       }
+       if ((data[0] & DATA_AKM_DRDY) == 0) {
+               result = -EINVAL;
+               goto AKM_fail;
+       }
+       result = inv_secondary_read(REG_AKM_MEASURE_DATA,
+                                       BYTES_PER_SENSOR, data);
+       if (result)
+               goto AKM_fail;
+
+       x = le16_to_cpup((__le16 *)(&data[0]));
+       y = le16_to_cpup((__le16 *)(&data[2]));
+       z = le16_to_cpup((__le16 *)(&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 <<= DEF_ST_COMPASS_8963_SHIFT;
+                       y <<= DEF_ST_COMPASS_8963_SHIFT;
+                       z <<= DEF_ST_COMPASS_8963_SHIFT;
+               }
+       }
+       result = -EINVAL;
+       if (x > st->compass_st_upper[X] || x < st->compass_st_lower[X])
+               goto AKM_fail;
+       if (y > st->compass_st_upper[Y] || y < st->compass_st_lower[Y])
+               goto AKM_fail;
+       if (z > st->compass_st_upper[Z] || z < st->compass_st_lower[Z])
+               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_PD);
+       /*restore to non-bypass mode */
+       result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+                       st->plat_data.int_config);
+       return result;
+}
+
+int inv_power_up_self_test(struct inv_mpu_iio_s *st)
+{
+       int result;
+
+       result = st->set_power_state(st, true);
+       if (result)
+               return result;
+       result = st->switch_accl_engine(st, true);
+       if (result)
+               return result;
+       result = st->switch_gyro_engine(st, true);
+       if (result)
+               return result;
+
+       return 0;
+}
+
+/*
+ *  inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_iio_s *st)
+{
+       int result;
+       int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+       int accl_bias_st[THREE_AXIS], accl_bias_regular[THREE_AXIS];
+       int test_times;
+       char compass_result, accel_result, gyro_result;
+
+       result = inv_power_up_self_test(st);
+       if (result)
+               return result;
+       compass_result = 0;
+       accel_result   = 0;
+       gyro_result    = 0;
+       test_times = DEF_ST_TRY_TIMES;
+       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 = DEF_ST_TRY_TIMES;
+       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->chip_config.has_compass)
+                       compass_result = !inv_check_compass_self_test(st);
+
+                if (INV_MPU6050 == st->chip_type) {
+                       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);
+               } else if (INV_MPU6500 == st->chip_type) {
+                       accel_result = !inv_check_6500_accel_self_test(st,
+                               accl_bias_regular, accl_bias_st);
+                       gyro_result = !inv_check_6500_gyro_self_test(st,
+                               gyro_bias_regular, gyro_bias_st);
+               }
+       }
+test_fail:
+       inv_recover_setting(st);
+
+       return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) |
+               (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
+
+static int inv_load_firmware(struct inv_mpu_iio_s *st,
+       u8 *data, int size)
+{
+       int bank, write_size;
+       int result;
+       u16 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_mpu_iio_s *st,
+       u8 *data, int size)
+{
+       int bank, write_size;
+       int result;
+       u16 memaddr;
+       u8 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,
+                       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_mpu_iio_s *st,
+               u16 fifoRate)
+{
+       u8 regs[2];
+       int result = 0;
+       /*For some reason DINAC4 is defined as 0xb8, but DINBC4 is not*/
+       const u8 regs_end[] = {DINAFE, DINAF2, DINAAB, 0xc4,
+                                       DINAAA, DINAF1, DINADF, DINADF,
+                                       0xbb, 0xaf, DINADF, DINADF};
+
+       regs[0] = (u8)((fifoRate >> 8) & 0xff);
+       regs[1] = (u8)(fifoRate & 0xff);
+       result = mem_w_key(KEY_D_0_22, ARRAY_SIZE(regs), 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, ARRAY_SIZE(regs_end), regs_end);
+
+       return result;
+}
+
+int inv_send_quaternion(struct inv_mpu_iio_s *st, bool on)
+{
+       const u8 regs_on[] = {DINBC0, DINBC2,
+                                        DINBC4, DINBC6};
+       const u8 regs_off[] = {DINA80, DINA80,
+                                         DINA80, DINA80};
+       const u8 *regs;
+       u8 result;
+       if (on)
+               regs = regs_on;
+       else
+               regs = regs_off;
+       result = mem_w_key(KEY_CFG_LP_QUAT, ARRAY_SIZE(regs_on), regs);
+
+       return result;
+}
+
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_iio_s *st,
+                                               bool on)
+{
+       /*Turn on the display orientation interrupt in the DMP*/
+       int result;
+       u8  regs[] = {0xd8};
+
+       if (on)
+               regs[0] = 0xd9;
+       result = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, 1, regs);
+       return result;
+}
+
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, u16 fifo_rate)
+{
+       u8 divider;
+       int result;
+
+       divider = (u8)(ONE_K_HZ / fifo_rate) - 1;
+       if (divider > DMP_MAX_DIVIDER) {
+               st->sample_divider = DMP_MAX_DIVIDER;
+               st->fifo_divider =
+                       (u8)(DMP_DEFAULT_FIFO_RATE / 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_mpu_iio_s *st,
+       u8 on)
+{
+       int result;
+       u8  regs[] = {0};
+
+       if (on)
+               regs[0] = 0xf8;
+       else
+               regs[0] = DINAD8;
+       result = mem_w_key(KEY_CFG_20, ARRAY_SIZE(regs), regs);
+       if (result)
+               return result;
+       return result;
+}
+
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+                               u32 axis, u16 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 */
+       u8 data[2];
+       int sampleDivider;
+       int scaledThreshold;
+       u32 dmpThreshold;
+       u8 sample_div;
+       const u32  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 = (u32)(scaledThreshold * accel_sens);
+       else
+               return -EINVAL;
+       dmpThreshold = dmpThreshold / DMP_PRECISION;
+
+       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, ARRAY_SIZE(data), 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, ARRAY_SIZE(data), 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, ARRAY_SIZE(data), data);
+               if (result)
+                       return result;
+       }
+       if (axis & INV_TAP_AXIS_Z) {
+               result = mem_w_key(KEY_DMP_TAP_THR_Z, ARRAY_SIZE(data), 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, ARRAY_SIZE(data), data);
+               if (result)
+                       return result;
+       }
+       return 0;
+}
+
+static int inv_set_tap_axes_dmp(struct inv_mpu_iio_s *st,
+                               u32 axes)
+{
+       /* Sets a mask in the DMP that indicates what tap events
+       should result in an interrupt */
+       u8 regs[4];
+       u8 result;
+
+       /* check if any spurious bit other the ones expected are set */
+       if (axes & (~(INV_TAP_ALL_DIRECTIONS)))
+               return -EINVAL;
+
+       regs[0] = (u8)axes;
+       result = mem_w_key(KEY_D_1_72, 1, regs);
+
+       return result;
+}
+
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st,
+                               u16 min_taps) {
+       /*Indicates the minimum number of consecutive taps required
+               before the DMP will generate an interrupt */
+       u8 regs[1];
+       u8 result;
+       /* check if any spurious bit other the ones expected are set */
+       if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1))
+               return -EINVAL;
+       regs[0] = (u8)(min_taps-1);
+       result = mem_w_key(KEY_D_1_79, ARRAY_SIZE(regs), regs);
+
+       return result;
+}
+
+int  inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, u16 time)
+{
+       /* Determines how long after a tap the DMP requires before
+         another tap can be registered*/
+       int result;
+       /* DMP Algorithm */
+       u16 dmpTime;
+       u8 data[2];
+       u8 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, ARRAY_SIZE(data), data);
+
+       return result;
+}
+
+static int inv_set_multiple_tap_time_dmp(struct inv_mpu_iio_s *st,
+                                       u32 time)
+{
+       /*Determines how close together consecutive taps must occur
+       to be considered double/triple taps*/
+       int result;
+       /* DMP Algorithm */
+       u16 dmpTime;
+       u8 data[2];
+       u8 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, ARRAY_SIZE(data), data);
+
+       return result;
+}
+
+int inv_q30_mult(int a, int b)
+{
+       u64 temp;
+       int result;
+
+       temp = (u64)a * b;
+       result = (int)(temp >> DMP_MULTI_SHIFT);
+
+       return result;
+}
+
+static u16 inv_row_2_scale(const s8 *row)
+{
+       u16 b;
+
+       if (row[0] > 0)
+               b = 0;
+       else if (row[0] < 0)
+               b = 4;
+       else if (row[1] > 0)
+               b = 1;
+       else if (row[1] < 0)
+               b = 5;
+       else if (row[2] > 0)
+               b = 2;
+       else if (row[2] < 0)
+               b = 6;
+       else
+               b = 7;
+
+       return b;
+}
+
+/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar
+*      representation.
+* @param[in] mtx Orientation matrix to convert to a scalar.
+* @return Description of orientation matrix. The lowest 2 bits (0 and 1)
+* represent the column the one is on for the
+* first row, with the bit number 2 being the sign. The next 2 bits
+* (3 and 4) represent
+* the column the one is on for the second row with bit number 5 being
+* the sign.
+* The next 2 bits (6 and 7) represent the column the one is on for the
+* third row with
+* bit number 8 being the sign. In binary the identity matrix would therefor
+* be: 010_001_000 or 0x88 in hex.
+*/
+static u16 inv_orientation_matrix_to_scaler(const signed char *mtx)
+{
+
+       u16 scalar;
+       scalar = inv_row_2_scale(mtx);
+       scalar |= inv_row_2_scale(mtx + 3) << 3;
+       scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+       return scalar;
+}
+#if 0
+static int inv_disable_gyro_cal(struct inv_mpu_iio_s *st)
+{
+       const u8 regs[] = {
+               0xb8, 0xaa, 0xaa, 0xaa,
+               0xb0, 0x88, 0xc3, 0xc5,
+               0xc7
+       };
+       return mem_w_key(KEY_CFG_MOTION_BIAS, ARRAY_SIZE(regs), regs);
+}
+#endif
+
+static int inv_gyro_dmp_cal(struct inv_mpu_iio_s *st)
+{
+       int inv_gyro_orient;
+       u8 regs[3];
+       int result;
+
+       u8 tmpD = DINA4C;
+       u8 tmpE = DINACD;
+       u8 tmpF = DINA6C;
+
+       inv_gyro_orient =
+               inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+       if ((inv_gyro_orient & 3) == 0)
+               regs[0] = tmpD;
+       else if ((inv_gyro_orient & 3) == 1)
+               regs[0] = tmpE;
+       else if ((inv_gyro_orient & 3) == 2)
+               regs[0] = tmpF;
+       if ((inv_gyro_orient & 0x18) == 0)
+               regs[1] = tmpD;
+       else if ((inv_gyro_orient & 0x18) == 0x8)
+               regs[1] = tmpE;
+       else if ((inv_gyro_orient & 0x18) == 0x10)
+               regs[1] = tmpF;
+       if ((inv_gyro_orient & 0xc0) == 0)
+               regs[2] = tmpD;
+       else if ((inv_gyro_orient & 0xc0) == 0x40)
+               regs[2] = tmpE;
+       else if ((inv_gyro_orient & 0xc0) == 0x80)
+               regs[2] = tmpF;
+
+       result = mem_w_key(KEY_FCFG_1, ARRAY_SIZE(regs), regs);
+       if (result)
+               return result;
+
+       if (inv_gyro_orient & 4)
+               regs[0] = DINA36 | 1;
+       else
+               regs[0] = DINA36;
+       if (inv_gyro_orient & 0x20)
+               regs[1] = DINA56 | 1;
+       else
+               regs[1] = DINA56;
+       if (inv_gyro_orient & 0x100)
+               regs[2] = DINA76 | 1;
+       else
+               regs[2] = DINA76;
+
+       result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs);
+
+       return result;
+}
+
+static int inv_accel_dmp_cal(struct inv_mpu_iio_s *st)
+{
+       int inv_accel_orient;
+       int result;
+       u8 regs[3];
+       const u8 tmp[3] = { DINA0C, DINAC9, DINA2C };
+       inv_accel_orient =
+               inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+       regs[0] = tmp[inv_accel_orient & 3];
+       regs[1] = tmp[(inv_accel_orient >> 3) & 3];
+       regs[2] = tmp[(inv_accel_orient >> 6) & 3];
+       result = mem_w_key(KEY_FCFG_2, ARRAY_SIZE(regs), regs);
+       if (result)
+               return result;
+
+       regs[0] = DINA26;
+       regs[1] = DINA46;
+       regs[2] = DINA66;
+       if (inv_accel_orient & 4)
+               regs[0] |= 1;
+       if (inv_accel_orient & 0x20)
+               regs[1] |= 1;
+       if (inv_accel_orient & 0x100)
+               regs[2] |= 1;
+       result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs);
+
+       return result;
+}
+
+static u16 inv_orientation_matrix_to_scalar(const s8 *mtx)
+{
+
+       u16 scalar;
+
+       /*
+       XYZ  010_001_000 Identity Matrix
+       XZY  001_010_000
+       YXZ  010_000_001
+       YZX  000_010_001
+       ZXY  001_000_010
+       ZYX  000_001_010
+       */
+
+       scalar = inv_row_2_scale(mtx);
+       scalar |= inv_row_2_scale(mtx + 3) << 3;
+       scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+       return scalar;
+}
+
+int inv_set_accel_bias_dmp(struct inv_mpu_iio_s *st)
+{
+       int inv_accel_orient, result, i, accel_bias_body[3], out[3];
+       int tmp[] = {1, 1, 1};
+       int mask[] = {4, 0x20, 0x100};
+       int accel_sf = 0x20000000;/* 536870912 */
+       u8 *regs;
+
+       inv_accel_orient =
+               inv_orientation_matrix_to_scalar(st->plat_data.orientation);
+
+       for (i = 0; i < 3; i++)
+               if (inv_accel_orient & mask[i])
+                       tmp[i] = -1;
+
+       for (i = 0; i < 3; i++)
+               accel_bias_body[i] = st->input_accel_bias[(inv_accel_orient >>
+                                       (i * 3)) & 3] * tmp[i];
+       for (i = 0; i < 3; i++)
+               accel_bias_body[i] = inv_q30_mult(accel_sf,
+                                       accel_bias_body[i]);
+       for (i = 0; i < 3; i++)
+               out[i] = cpu_to_be32p(&accel_bias_body[i]);
+       regs = (u8 *)out;
+       result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs);
+
+       return result;
+}
+
+static int inv_set_gyro_sf_dmp(struct inv_mpu_iio_s *st)
+{
+       /*The gyro threshold, in dps, above which taps will be rejected*/
+       int result;
+       /* DMP Algorithm */
+       u8 sampleDivider;
+       u32 gyro_sf;
+       const u32 gyro_sens = 0x03e80000;
+
+       sampleDivider = st->sample_divider;
+       gyro_sf = inv_q30_mult(gyro_sens,
+                       (int)(DMP_TAP_SCALE * (sampleDivider + 1)));
+       result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104);
+
+       return result;
+}
+
+static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_iio_s *st,
+                                               int thresh)
+{      /*THIS FUNCTION FAILS MEM_W*/
+       /*The gyro threshold, in dps, above which taps will be rejected */
+       int result;
+       /* DMP Algorithm */
+       u8 sampleDivider;
+       int thresh_scaled;
+       u32 gyro_sf;
+       const u32 gyro_sens = 0x03e80000;
+       sampleDivider = st->sample_divider;
+       gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE *
+                       (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;
+       result = write_be32_key_to_mem(st, thresh_scaled, KEY_D_1_92);
+
+       return result;
+}
+
+static int inv_set_shake_reject_time_dmp(struct inv_mpu_iio_s *st,
+                                               u32 time)
+{
+       /* How long a gyro axis must remain above its threshold
+       before taps are rejected */
+       int result;
+       /* DMP Algorithm */
+       u16 dmpTime;
+       u8 data[2];
+       u8 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, ARRAY_SIZE(data), data);
+       return result;
+}
+
+static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_iio_s *st,
+                                               u32 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 */
+       u16 dmpTime;
+       u8 data[2];
+       u8 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, ARRAY_SIZE(data), data);
+       return result;
+}
+
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_iio_s *st, bool on)
+{
+       u8 result;
+       const u8 regs_on[] = {DINADA, DINAB1, DINAB9,
+                                        DINAF3, DINA8B, DINAA3, DINA91,
+                                        DINAB6, DINADA, DINAB4, DINADA};
+       const u8 regs_off[] = {0xd8, 0xb1, 0xb9, 0xf3, 0x8b,
+                                         0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9};
+       /*For some reason DINAC4 is defined as 0xb8,
+       but DINBC4 is not defined.*/
+       const u8 regs_end[] = {DINAFE, DINAF2, DINAAB, 0xc4,
+                                       DINAAA, DINAF1, DINADF, DINADF,
+                                       0xbb, 0xaf, DINADF, DINADF};
+       const u8 regs[] = {0, 0};
+       /* reset fifo count to zero */
+       result = mem_w_key(KEY_D_1_178, ARRAY_SIZE(regs), regs);
+       if (result)
+               return result;
+
+       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, ARRAY_SIZE(regs_on),
+                                       regs_on);
+       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, ARRAY_SIZE(regs_off),
+                                       regs_off);
+       if (result)
+               return result;
+
+       result = mem_w_key(KEY_CFG_6, ARRAY_SIZE(regs_end), regs_end);
+
+       return result;
+}
+
+/**
+ * inv_enable_tap_dmp() -  calling this function will enable/disable tap function.
+ */
+int inv_enable_tap_dmp(struct inv_mpu_iio_s *st, bool 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,
+                                                  st->tap.thresh);
+               if (result)
+                       return result;
+               result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Y,
+                                                  st->tap.thresh);
+               if (result)
+                       return result;
+               result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Z,
+                                                  st->tap.thresh);
+               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, st->tap.min_count);
+       if (result)
+               return result;
+
+       result = inv_set_tap_time_dmp(st, st->tap.time);
+       if (result)
+               return result;
+
+       result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME);
+       if (result)
+               return result;
+
+       result = inv_set_gyro_sf_dmp(st);
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH);
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME);
+       if (result)
+               return result;
+
+       result = inv_set_shake_reject_timeout_dmp(st,
+                                                 DMP_SHAKE_REJECT_TIMEOUT);
+       return result;
+}
+
+int inv_send_sensor_data(struct inv_mpu_iio_s *st, u16 elements)
+{
+       int result;
+       u8 regs[] = {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, ARRAY_SIZE(regs), regs);
+       return result;
+}
+
+int inv_send_interrupt_word(struct inv_mpu_iio_s *st, bool on)
+{
+       const u8 regs_on[] = { DINA20 };
+       const u8 regs_off[] = { DINAA3 };
+       u8 result;
+
+       if (on)
+               result = mem_w_key(KEY_CFG_27, ARRAY_SIZE(regs_on), regs_on);
+       else
+               result = mem_w_key(KEY_CFG_27, ARRAY_SIZE(regs_off), regs_off);
+
+       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)
+{
+       u8 *firmware;
+       int result;
+       struct inv_reg_map_s *reg;
+       struct iio_dev *indio_dev;
+       struct inv_mpu_iio_s *st;
+
+       indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+       st = iio_priv(indio_dev);
+
+       if (st->chip_config.firmware_loaded)
+               return -EINVAL;
+       if (st->chip_config.enable)
+               return -EBUSY;
+
+       reg = &st->reg;
+       if (DMP_IMAGE_SIZE != size) {
+               pr_err("wrong DMP image size\n");
+               return -EINVAL;
+       }
+
+       firmware = kmalloc(size, GFP_KERNEL);
+       if (!firmware)
+               return -ENOMEM;
+
+       mutex_lock(&indio_dev->mlock);
+
+       memcpy(firmware, buf, size);
+       result = crc32(CRC_FIRMWARE_SEED, firmware, size);
+       if (DMP_IMAGE_CRC_VALUE != result) {
+               pr_err("firmware CRC error - 0x%08x vs 0x%08x\n",
+                       result, DMP_IMAGE_CRC_VALUE);
+               result = -EINVAL;
+               goto firmware_write_fail;
+       }
+
+       result = st->set_power_state(st, true);
+       if (result)
+               goto firmware_write_fail;
+
+       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_set_fifo_rate(st, DMP_DEFAULT_FIFO_RATE);
+       if (result)
+               goto firmware_write_fail;
+       result = inv_gyro_dmp_cal(st);
+       if (result)
+               goto firmware_write_fail;
+       result = inv_accel_dmp_cal(st);
+       if (result)
+               goto firmware_write_fail;
+       /* result = inv_disable_gyro_cal(st);*/
+       if (result)
+               goto firmware_write_fail;
+
+       st->chip_config.firmware_loaded = 1;
+
+firmware_write_fail:
+       result |= st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+       kfree(firmware);
+       if (result)
+               return result;
+       return 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)
+{
+       int bank, write_size, size, data, result;
+       u16 memaddr;
+       struct iio_dev *indio_dev;
+       struct inv_mpu_iio_s *st;
+
+       size = count;
+       indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+       st = iio_priv(indio_dev);
+
+       data = 0;
+       mutex_lock(&indio_dev->mlock);
+       if (!st->chip_config.enable) {
+               result = st->set_power_state(st, true);
+               if (result) {
+                       mutex_unlock(&indio_dev->mlock);
+                       return result;
+               }
+       }
+       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);
+               result = mpu_memory_read(st,
+                       st->i2c_addr, memaddr, write_size, &buf[data]);
+               if (result) {
+                       mutex_unlock(&indio_dev->mlock);
+                       return result;
+               }
+       }
+       if (!st->chip_config.enable)
+               result = st->set_power_state(st, false);
+       mutex_unlock(&indio_dev->mlock);
+       if (result)
+               return result;
+
+       return count;
+}
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c
new file mode 100644 (file)
index 0000000..82d4d87
--- /dev/null
@@ -0,0 +1,1161 @@
+/*
+* 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_mpu_ring.c
+ *      @brief   A sysfs device driver for Invensense gyroscopes.
+ *      @details This file is part of inv mpu iio driver code
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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>
+#ifdef INV_KERNEL_3_10
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/sysfs.h>
+#else
+#include "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+#endif
+
+#include "inv_mpu_iio.h"
+
+/**
+ *  reset_fifo_mpu3050() - Reset FIFO related registers
+ *  @st:       Device driver instance.
+ */
+static int reset_fifo_mpu3050(struct iio_dev *indio_dev)
+{
+       struct inv_reg_map_s *reg;
+       int result;
+       u8 val, user_ctrl;
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       reg = &st->reg;
+
+       /* disable interrupt */
+       result = inv_i2c_single_write(st, reg->int_enable,
+                               st->plat_data.int_config);
+       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->chip_config.has_footer = 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;
+       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,
+                               st->plat_data.int_config | 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,
+                               st->plat_data.int_config | 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,
+                            st->plat_data.int_config | val);
+       pr_err("reset fifo failed\n");
+
+       return result;
+}
+
+/**
+ *  inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_iio_s *st, int rate)
+{
+       const short hz[] = {188, 98, 42, 20, 10, 5};
+       const int   d[] = {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 < ARRAY_SIZE(d) - 1))
+               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 << GYRO_CONFIG_FSR_SHIFT));
+       } else {
+               result = inv_i2c_single_write(st, reg->lpf, data);
+       }
+       if (result)
+               return result;
+       st->chip_config.lpf = data;
+
+       return 0;
+}
+
+/**
+ *  set_fifo_rate_reg() - Set fifo rate in hardware register
+ */
+static int set_fifo_rate_reg(struct inv_mpu_iio_s *st)
+{
+       u8 data;
+       u16 fifo_rate;
+       int result;
+       struct inv_reg_map_s *reg;
+
+       reg = &st->reg;
+       fifo_rate = st->chip_config.new_fifo_rate;
+       data = ONE_K_HZ / fifo_rate - 1;
+       result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+       if (result)
+               return result;
+       result = inv_set_lpf(st, fifo_rate);
+       if (result)
+               return result;
+       st->chip_config.fifo_rate = fifo_rate;
+
+       return 0;
+}
+
+/**
+ *  inv_lpa_mode() - store current low power mode settings
+ */
+static int inv_lpa_mode(struct inv_mpu_iio_s *st, int lpa_mode)
+{
+       unsigned long result;
+       u8 d;
+       struct inv_reg_map_s *reg;
+
+       reg = &st->reg;
+       result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+       if (result)
+               return result;
+       if (lpa_mode)
+               d |= BIT_CYCLE;
+       else
+               d &= ~BIT_CYCLE;
+
+       result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+       if (result)
+               return result;
+       if (INV_MPU6500 == st->chip_type) {
+               if (lpa_mode)
+                       d = BIT_ACCEL_FCHOCIE_B;
+               else
+                       d = 0;
+               result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, d);
+               if (result)
+                       return result;
+       }
+
+       return 0;
+}
+
+/**
+ *  reset_fifo_itg() - Reset FIFO related registers.
+ *  @st:       Device driver instance.
+ */
+static int reset_fifo_itg(struct iio_dev *indio_dev)
+{
+       struct inv_reg_map_s *reg;
+       int result, data;
+       u8 val, int_word;
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       reg = &st->reg;
+
+       if (st->chip_config.lpa_mode) {
+               result = inv_lpa_mode(st, 0);
+               if (result) {
+                       pr_err("reset lpa mode failed\n");
+                       return result;
+               }
+       }
+       /* disable interrupt */
+       result = inv_i2c_single_write(st, reg->int_enable, 0);
+       if (result) {
+               pr_err("int_enable write failed\n");
+               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;
+       int_word = 0;
+
+       /* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */
+       if (st->mot_int.mot_on)
+               int_word |= BIT_MOT_EN;
+
+       if (st->chip_config.dmp_on) {
+               val = (BIT_FIFO_RST | BIT_DMP_RST);
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+               st->last_isr_time = get_time_ns();
+               if (st->chip_config.dmp_int_on) {
+                       int_word |= BIT_DMP_INT_EN;
+                       result = inv_i2c_single_write(st, reg->int_enable,
+                                                       int_word);
+                       if (result)
+                               return result;
+               }
+               val = (BIT_DMP_EN | BIT_FIFO_EN);
+               if (st->chip_config.compass_enable &
+                       (!st->chip_config.dmp_event_int_on))
+                       val |= BIT_I2C_MST_EN;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+
+               if (st->chip_config.compass_enable) {
+                       /* I2C_MST_DLY is set according to sample rate,
+                          slow down the power*/
+                       data = max(COMPASS_RATE_SCALE *
+                               st->chip_config.new_fifo_rate / ONE_K_HZ,
+                               st->chip_config.new_fifo_rate /
+                               st->chip_config.dmp_output_rate);
+                       if (data > 0)
+                               data -= 1;
+                       result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+                                                       data);
+                       if (result)
+                               return result;
+               }
+               val = 0;
+               if (st->chip_config.accl_fifo_enable)
+                       val |= INV_ACCL_MASK;
+               if (st->chip_config.gyro_fifo_enable)
+                       val |= INV_GYRO_MASK;
+               result = inv_send_sensor_data(st, val);
+               if (result)
+                       return result;
+               if (st->chip_config.display_orient_on || st->chip_config.tap_on)
+                       result = inv_send_interrupt_word(st, true);
+               else
+                       result = inv_send_interrupt_word(st, false);
+       } else {
+               /* reset FIFO and possibly reset I2C*/
+               val = BIT_FIFO_RST;
+               result = inv_i2c_single_write(st, reg->user_ctrl, val);
+               if (result)
+                       goto reset_fifo_fail;
+               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) {
+                       int_word |= BIT_DATA_RDY_EN;
+               }
+               result = inv_i2c_single_write(st, reg->int_enable, int_word);
+               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;
+               if (st->chip_config.compass_enable) {
+                       /* I2C_MST_DLY is set according to sample rate,
+                          slow down the power*/
+                       data = COMPASS_RATE_SCALE *
+                               st->chip_config.new_fifo_rate / ONE_K_HZ;
+                       if (data > 0)
+                               data -= 1;
+                       result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL,
+                                                       data);
+                       if (result)
+                               return result;
+               }
+               /* 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;
+       }
+       st->chip_config.normal_compass_measure = 0;
+       result = inv_lpa_mode(st, st->chip_config.lpa_mode);
+       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("reset fifo failed\n");
+
+       return result;
+}
+
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:       Device driver instance.
+ */
+static void inv_clear_kfifo(struct inv_mpu_iio_s *st)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&st->time_stamp_lock, flags);
+       kfifo_reset(&st->timestamps);
+       spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:       Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+       inv_clear_kfifo(st);
+       if (INV_MPU3050 == st->chip_type)
+               return reset_fifo_mpu3050(indio_dev);
+       else
+               return reset_fifo_itg(indio_dev);
+}
+
+static int inv_set_dmp_sysfs(struct inv_mpu_iio_s *st)
+{
+       int result;
+
+       result = inv_set_fifo_rate(st, st->chip_config.dmp_output_rate);
+       if (result)
+               return result;
+       result = inv_set_interrupt_on_gesture_event(st,
+                               st->chip_config.dmp_event_int_on);
+
+       return result;
+}
+
+/**
+ *  set_inv_enable() - Reset FIFO related registers.
+ *                     This also powers on the chip if needed.
+ *  @st:       Device driver instance.
+ *  @fifo_enable: enable/disable
+ */
+static int set_inv_enable(struct iio_dev *indio_dev,
+                       bool enable) {
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct inv_reg_map_s *reg;
+       int result;
+
+       reg = &st->reg;
+       if (enable) {
+               if (st->chip_config.new_fifo_rate !=
+                               st->chip_config.fifo_rate) {
+                       result = set_fifo_rate_reg(st);
+                       if (result)
+                               return result;
+               }
+               if (st->chip_config.dmp_on) {
+                       result = inv_set_dmp_sysfs(st);
+                       if (result)
+                               return result;
+               }
+
+               if (st->chip_config.gyro_enable) {
+                       result = st->switch_gyro_engine(st, true);
+                       if (result)
+                               return result;
+               }
+               if (st->chip_config.accl_enable) {
+                       result = st->switch_accl_engine(st, true);
+                       if (result)
+                               return result;
+               }
+
+               result = inv_reset_fifo(indio_dev);
+               if (result)
+                       return result;
+       } else {
+               if ((INV_MPU3050 != st->chip_type)
+                       && st->chip_config.lpa_mode) {
+                       /* if the chip is in low power mode,
+                               register write/read could fail */
+                       result = inv_lpa_mode(st, 0);
+                       if (result)
+                               return result;
+               }
+               result = inv_i2c_single_write(st, reg->fifo_en, 0);
+               if (result)
+                       return result;
+               /* disable fifo reading */
+               if (INV_MPU3050 != st->chip_type) {
+                       result = inv_i2c_single_write(st, reg->int_enable, 0);
+                       if (result)
+                               return result;
+                       result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+               } else {
+                       result = inv_i2c_single_write(st, reg->int_enable,
+                               st->plat_data.int_config);
+               }
+               if (result)
+                       return result;
+               /* turn off the gyro/accl engine during disable phase */
+               result = st->switch_gyro_engine(st, false);
+               if (result)
+                       return result;
+               result = st->switch_accl_engine(st, false);
+               if (result)
+                       return result;
+               result = st->set_power_state(st, false);
+               if (result)
+                       return result;
+       }
+       st->chip_config.enable = enable;
+
+       return 0;
+}
+
+/**
+ *  inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+       struct inv_mpu_iio_s *st;
+       u64 timestamp;
+       int catch_up;
+       u64 time_since_last_irq;
+
+       st = (struct inv_mpu_iio_s *)dev_id;
+       timestamp = get_time_ns();
+       time_since_last_irq = timestamp - st->last_isr_time;
+       spin_lock(&st->time_stamp_lock);
+       catch_up = 0;
+       while ((time_since_last_irq > st->irq_dur_ns * 2) &&
+              (catch_up < MAX_CATCH_UP) &&
+              (!st->chip_config.lpa_mode) &&
+              (!st->chip_config.dmp_on)) {
+               st->last_isr_time += st->irq_dur_ns;
+               kfifo_in(&st->timestamps,
+                        &st->last_isr_time, 1);
+               time_since_last_irq = timestamp - st->last_isr_time;
+               catch_up++;
+       }
+       kfifo_in(&st->timestamps, &timestamp, 1);
+       st->last_isr_time = timestamp;
+       spin_unlock(&st->time_stamp_lock);
+
+       return IRQ_WAKE_THREAD;
+}
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, u8 *d,
+                               short *s, int scan_index, int d_ind) {
+       struct iio_buffer *ring = indio_dev->buffer;
+       int st;
+       int i;
+       for (i = 0; i < 3; i++) {
+               st = iio_scan_mask_query(indio_dev, ring, scan_index + i);
+               if (st) {
+                       memcpy(&d[d_ind], &s[i], sizeof(s[i]));
+                       d_ind += sizeof(s[i]);
+               }
+       }
+       return d_ind;
+}
+
+static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t,
+                       int has_footer, u8 *data)
+{
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct iio_buffer *ring = indio_dev->buffer;
+       int ind, i, d_ind;
+       struct inv_chip_config_s *conf;
+       short g[THREE_AXIS], a[THREE_AXIS];
+       s64 buf[8];
+       u8 *tmp;
+       int bytes_per_datum, scan_count;
+       conf = &st->chip_config;
+
+       scan_count = bitmap_weight(indio_dev->active_scan_mask,
+                                      indio_dev->masklength);
+       bytes_per_datum = scan_count * 2;
+
+       ind = 0;
+       if (has_footer)
+               ind += 2;
+       tmp = (u8 *)buf;
+       d_ind = 0;
+
+       if (conf->gyro_fifo_enable) {
+               for (i = 0; i < ARRAY_SIZE(g); i++) {
+                       g[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+                       st->raw_gyro[i] = g[i];
+               }
+               ind += BYTES_PER_SENSOR;
+       }
+       if (conf->accl_fifo_enable) {
+               st->mpu_slave->combine_data(&data[ind], a);
+               for (i = 0; i < ARRAY_SIZE(a); i++)
+                       st->raw_accel[i] = a[i];
+
+               ind += BYTES_PER_SENSOR;
+               d_ind = put_scan_to_buf(indio_dev, tmp, a,
+                       INV_MPU_SCAN_ACCL_X, d_ind);
+       }
+       if (conf->gyro_fifo_enable)
+               d_ind = put_scan_to_buf(indio_dev, tmp, g,
+                       INV_MPU_SCAN_GYRO_X, d_ind);
+
+       i = (bytes_per_datum + 7) / 8;
+       if (ring->scan_timestamp)
+               buf[i] = t;
+#ifdef INV_KERNEL_3_10
+       ring->access->store_to(indio_dev->buffer, (u8 *)buf);
+#else
+       ring->access->store_to(indio_dev->buffer, (u8 *)buf, t);
+#endif
+}
+
+/**
+ *  inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for
+ *                            mpu3050.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+
+       struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+       struct iio_dev *indio_dev = iio_priv_to_dev(st);
+       int bytes_per_datum;
+       u8 data[64];
+       int result;
+       short fifo_count, byte_read;
+       u32 copied;
+       s64 timestamp;
+       struct inv_reg_map_s *reg;
+       reg = &st->reg;
+       /* It is impossible that chip is asleep or enable is
+       zero when interrupt is on
+       *  because interrupt is now connected with enable */
+       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->chip_config.has_footer)
+               byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+       else
+               byte_read = bytes_per_datum;
+
+       fifo_count = 0;
+       if (byte_read != 0) {
+               result = inv_i2c_read(st, reg->fifo_count_h,
+                               FIFO_COUNT_BYTE, data);
+               if (result)
+                       goto end_session;
+               fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+               if (fifo_count < byte_read)
+                       goto end_session;
+               if (fifo_count & 1)
+                       goto flush_fifo;
+               if (fifo_count > FIFO_THRESHOLD)
+                       goto flush_fifo;
+               /* Timestamp mismatch. */
+               if (kfifo_len(&st->timestamps) <
+                       fifo_count / byte_read)
+                       goto flush_fifo;
+               if (kfifo_len(&st->timestamps) >
+                       fifo_count / byte_read + TIME_STAMP_TOR) {
+                       if (st->chip_config.dmp_on) {
+                               result = kfifo_to_user(&st->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->timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+               inv_report_data_3050(indio_dev, timestamp,
+                                    st->chip_config.has_footer, data);
+               fifo_count -= byte_read;
+               if (st->chip_config.has_footer == 0) {
+                       st->chip_config.has_footer = 1;
+                       byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+               }
+       }
+
+end_session:
+       return IRQ_HANDLED;
+
+flush_fifo:
+       /* Flush HW and SW FIFOs. */
+       inv_reset_fifo(indio_dev);
+       inv_clear_kfifo(st);
+       return IRQ_HANDLED;
+}
+
+static int inv_report_gyro_accl_compass(struct iio_dev *indio_dev,
+                                       u8 *data, s64 t)
+{
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       short g[THREE_AXIS], a[THREE_AXIS], c[THREE_AXIS];
+       int q[4];
+       int result, ind;
+       u32 word;
+       u8 d[8], compass_divider;
+       u8 buf[64];
+       u64 *tmp;
+       int source, i;
+       struct inv_chip_config_s *conf;
+
+       conf = &st->chip_config;
+       ind = 0;
+
+       if (conf->quaternion_on & conf->dmp_on) {
+               for (i = 0; i < ARRAY_SIZE(q); i++) {
+                       q[i] = be32_to_cpup((__be32 *)(&data[ind + i * 4]));
+                       st->raw_quaternion[i] = q[i];
+                       memcpy(&buf[ind + i * sizeof(q[i])], &q[i],
+                                               sizeof(q[i]));
+               }
+               ind += QUATERNION_BYTES;
+       }
+
+       if (conf->accl_fifo_enable) {
+               for (i = 0; i < ARRAY_SIZE(a); i++) {
+                       a[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+                       memcpy(&buf[ind + i * sizeof(a[i])], &a[i],
+                                               sizeof(a[i]));
+               }
+               ind += BYTES_PER_SENSOR;
+       }
+
+       if (conf->gyro_fifo_enable) {
+               for (i = 0; i < ARRAY_SIZE(g); i++) {
+                       g[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+                       memcpy(&buf[ind + i * sizeof(g[i])], &g[i],
+                                               sizeof(g[i]));
+               }
+               ind += BYTES_PER_SENSOR;
+       }
+
+       if (conf->dmp_on && (conf->tap_on || conf->display_orient_on)) {
+               word = (u32)(be32_to_cpup((u32 *)&data[ind]));
+               source = ((word >> 16) & 0xff);
+               if (source) {
+                       st->tap_data = (DMP_MASK_TAP & (word & 0xff));
+                       st->display_orient_data =
+                       ((DMP_MASK_DIS_ORIEN & (word & 0xff)) >>
+                         DMP_DIS_ORIEN_SHIFT);
+               }
+
+               /* report tap information */
+               if (source & INT_SRC_TAP)
+                       sysfs_notify(&indio_dev->dev.kobj, NULL, "event_tap");
+               /* report orientation information */
+               if (source & INT_SRC_DISPLAY_ORIENT)
+                       sysfs_notify(&indio_dev->dev.kobj, NULL,
+                                    "event_display_orientation");
+       }
+       /*divider and counter is used to decrease the speed of read in
+               high frequency sample rate*/
+       if (conf->compass_fifo_enable) {
+               c[0] = 0;
+               c[1] = 0;
+               c[2] = 0;
+               if (conf->dmp_on)
+                       compass_divider = st->compass_dmp_divider;
+               else
+                       compass_divider = st->compass_divider;
+               if (compass_divider <= st->compass_counter) {
+                       /*read from external sensor data register */
+                       result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+                                             NUM_BYTES_COMPASS_SLAVE, d);
+                       /* d[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 == d[0]) &&
+                           (0 == (d[7] & DATA_AKM_STAT_MASK)) &&
+                           (!result)) {
+                               u8 *sens;
+                               sens = st->chip_info.compass_sens;
+                               c[0] = (short)((d[2] << 8) | d[1]);
+                               c[1] = (short)((d[4] << 8) | d[3]);
+                               c[2] = (short)((d[6] << 8) | d[5]);
+                               c[0] = (short)(((int)c[0] *
+                                              (sens[0] + 128)) >> 8);
+                               c[1] = (short)(((int)c[1] *
+                                              (sens[1] + 128)) >> 8);
+                               c[2] = (short)(((int)c[2] *
+                                              (sens[2] + 128)) >> 8);
+                               st->raw_compass[0] = c[0];
+                               st->raw_compass[1] = c[1];
+                               st->raw_compass[2] = c[2];
+                       }
+                       st->compass_counter = 0;
+               } else if (compass_divider != 0) {
+                       st->compass_counter++;
+               }
+               if (!conf->normal_compass_measure) {
+                       c[0] = 0;
+                       c[1] = 0;
+                       c[2] = 0;
+                       conf->normal_compass_measure = 1;
+               }
+               for (i = 0; i < 3; i++)
+                       memcpy(&buf[ind + i * sizeof(c[i])], &c[i],
+                                               sizeof(c[i]));
+               ind += BYTES_PER_SENSOR;
+       }
+       tmp = (u64 *)buf;
+       tmp[DIV_ROUND_UP(ind, 8)] = t;
+
+       if (ind > 0)
+#ifdef INV_KERNEL_3_10
+               iio_push_to_buffers(indio_dev, buf);
+#else
+               iio_push_to_buffer(indio_dev->buffer, buf, t);
+#endif
+
+       return 0;
+}
+
+static void inv_process_motion(struct inv_mpu_iio_s *st)
+{
+       struct iio_dev *indio_dev = iio_priv_to_dev(st);
+       s32 diff, true_motion;
+       s64 timestamp;
+       int result;
+       u8 data[1];
+
+       /* motion interrupt */
+       result = inv_i2c_read(st, REG_INT_STATUS, 1, data);
+       if (result)
+               return;
+
+       if (data[0] & BIT_MOT_INT) {
+               timestamp = get_time_ns();
+               diff = (int)(((timestamp - st->mpu6500_last_motion_time) >>
+                       NS_PER_MS_SHIFT));
+               if (diff > st->mot_int.mot_dur) {
+                       st->mpu6500_last_motion_time = timestamp;
+                       true_motion = 1;
+               } else {
+                       true_motion = 0;
+               }
+               if (true_motion)
+                       sysfs_notify(&indio_dev->dev.kobj, NULL,
+                                       "event_accel_motion");
+       }
+}
+
+static int get_bytes_per_datum(struct inv_mpu_iio_s *st)
+{
+       int bytes_per_datum;
+
+       bytes_per_datum = 0;
+       if (st->chip_config.dmp_on) {
+               if (st->chip_config.quaternion_on)
+                       bytes_per_datum += QUATERNION_BYTES;
+               if (st->chip_config.tap_on ||
+                       st->chip_config.display_orient_on)
+                       bytes_per_datum += BYTES_FOR_EVENTS;
+       }
+       if (st->chip_config.accl_fifo_enable)
+               bytes_per_datum += BYTES_PER_SENSOR;
+       if (st->chip_config.gyro_fifo_enable)
+               bytes_per_datum += BYTES_PER_SENSOR;
+
+       return bytes_per_datum;
+}
+
+/**
+ *  inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+
+       struct inv_mpu_iio_s *st = (struct inv_mpu_iio_s *)dev_id;
+       struct iio_dev *indio_dev = iio_priv_to_dev(st);
+       size_t bytes_per_datum;
+       int result;
+       u8 data[BYTES_FOR_DMP + QUATERNION_BYTES];
+       u16 fifo_count;
+       u32 copied;
+       s64 timestamp;
+       struct inv_reg_map_s *reg;
+       s64 buf[8];
+       s8 *tmp;
+
+       mutex_lock(&indio_dev->mlock);
+       if (!(iio_buffer_enabled(indio_dev)))
+               goto end_session;
+
+       reg = &st->reg;
+       if (!(st->chip_config.accl_fifo_enable |
+               st->chip_config.gyro_fifo_enable |
+               st->chip_config.dmp_on |
+               st->chip_config.compass_fifo_enable |
+               st->mot_int.mot_on))
+               goto end_session;
+       if (st->mot_int.mot_on)
+               inv_process_motion(st);
+       if (st->chip_config.dmp_on && st->chip_config.smd_enable) {
+               /* dmp interrupt status */
+               result = inv_i2c_read(st, REG_DMP_INT_STATUS, 1, data);
+               if (!result)
+                       if (data[0] & SMD_INT_ON) {
+                               sysfs_notify(&indio_dev->dev.kobj, NULL,
+                                               "event_smd");
+                               st->chip_config.smd_enable = 0;
+                       }
+       }
+       if (st->chip_config.lpa_mode) {
+               result = inv_i2c_read(st, reg->raw_accl,
+                                     BYTES_PER_SENSOR, data);
+               if (result)
+                       goto end_session;
+               inv_report_gyro_accl_compass(indio_dev, data,
+                                            get_time_ns());
+               goto end_session;
+       }
+       bytes_per_datum = get_bytes_per_datum(st);
+       fifo_count = 0;
+       if (bytes_per_datum != 0) {
+               result = inv_i2c_read(st, reg->fifo_count_h,
+                               FIFO_COUNT_BYTE, data);
+               if (result)
+                       goto end_session;
+               fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+               if (fifo_count == 0)
+                       goto flush_fifo;
+               if (fifo_count < bytes_per_datum)
+                       goto end_session;
+               /* fifo count can't be odd number */
+               if (fifo_count & 1)
+                       goto flush_fifo;
+               if (fifo_count >  FIFO_THRESHOLD)
+                       goto flush_fifo;
+               /* timestamp mismatch. */
+               if (kfifo_len(&st->timestamps) <
+                       fifo_count / bytes_per_datum)
+                       goto flush_fifo;
+               if (kfifo_len(&st->timestamps) >
+                       fifo_count / bytes_per_datum + TIME_STAMP_TOR) {
+                       if (st->chip_config.dmp_on) {
+                               result = kfifo_to_user(&st->timestamps,
+                               &timestamp, sizeof(timestamp), &copied);
+                               if (result)
+                                       goto flush_fifo;
+                       } else {
+                               goto flush_fifo;
+                       }
+               }
+       } else {
+               result = kfifo_to_user(&st->timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+       }
+       tmp = (s8 *)buf;
+       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->timestamps,
+                       &timestamp, sizeof(timestamp), &copied);
+               if (result)
+                       goto flush_fifo;
+               inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+               fifo_count -= bytes_per_datum;
+       }
+       if (bytes_per_datum == 0 && st->chip_config.compass_fifo_enable)
+               inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+
+end_session:
+       mutex_unlock(&indio_dev->mlock);
+
+       return IRQ_HANDLED;
+
+flush_fifo:
+       /* Flush HW and SW FIFOs. */
+       inv_reset_fifo(indio_dev);
+       inv_clear_kfifo(st);
+       mutex_unlock(&indio_dev->mlock);
+
+       return IRQ_HANDLED;
+}
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       free_irq(st->client->irq, st);
+       iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_postenable(struct iio_dev *indio_dev)
+{
+       return set_inv_enable(indio_dev, true);
+}
+
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+       return set_inv_enable(indio_dev, false);
+}
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       struct iio_buffer *ring = indio_dev->buffer;
+       int result;
+
+       if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_X) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Y) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_GYRO_Z))
+               st->chip_config.gyro_fifo_enable = 1;
+       else
+               st->chip_config.gyro_fifo_enable = 0;
+
+       if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_X) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Y) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_ACCL_Z))
+               st->chip_config.accl_fifo_enable = 1;
+       else
+               st->chip_config.accl_fifo_enable = 0;
+
+       if (iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_X) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Y) ||
+           iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_MAGN_Z))
+               st->chip_config.compass_fifo_enable = 1;
+       else
+               st->chip_config.compass_fifo_enable = 0;
+
+       /* check to make sure engine is turned on if fifo is turned on */
+       if (st->chip_config.gyro_fifo_enable &&
+               (!st->chip_config.gyro_enable)) {
+               result = st->switch_gyro_engine(st, true);
+               if (result)
+                       return;
+               st->chip_config.gyro_enable = true;
+       }
+       if (st->chip_config.accl_fifo_enable &&
+               (!st->chip_config.accl_enable)) {
+               result = st->switch_accl_engine(st, true);
+               if (result)
+                       return;
+               st->chip_config.accl_enable = true;
+       }
+}
+
+static int inv_check_quaternion(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       struct iio_buffer *ring = indio_dev->buffer;
+       int result;
+
+       if (st->chip_config.dmp_on) {
+               if (
+                 iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_R) ||
+                 iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_X) ||
+                 iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_Y) ||
+                 iio_scan_mask_query(indio_dev, ring, INV_MPU_SCAN_QUAT_Z))
+                       st->chip_config.quaternion_on = 1;
+               else
+                       st->chip_config.quaternion_on = 0;
+
+               result = inv_send_quaternion(st,
+                               st->chip_config.quaternion_on);
+               if (result)
+                       return result;
+       } else {
+               st->chip_config.quaternion_on = 0;
+               clear_bit(INV_MPU_SCAN_QUAT_R, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_X, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_Y, ring->scan_mask);
+               clear_bit(INV_MPU_SCAN_QUAT_Z, ring->scan_mask);
+       }
+
+       return 0;
+}
+
+static int inv_check_conflict_sysfs(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+       struct iio_buffer *ring = indio_dev->buffer;
+       int result;
+
+       if (st->chip_config.lpa_mode) {
+               /* dmp cannot run with low power mode on */
+               st->chip_config.dmp_on = 0;
+               result = st->gyro_en(st, ring, false);
+               if (result)
+                       return result;
+               result = st->compass_en(st, ring, false);
+               if (result)
+                       return result;
+               result = st->quaternion_en(st, ring, false);
+               if (result)
+                       return result;
+
+               result = st->accl_en(st, ring, true);
+               if (result)
+                       return result;
+       }
+       result = inv_check_quaternion(indio_dev);
+       if (result)
+               return result;
+
+       return result;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+       int result;
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+       result = st->set_power_state(st, true);
+       if (result)
+               return result;
+
+       result = inv_check_conflict_sysfs(indio_dev);
+       if (result)
+               return result;
+       inv_scan_query(indio_dev);
+
+       return result;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+       .preenable = &inv_preenable,
+       .postenable = &inv_postenable,
+       .predisable = &inv_predisable,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+       int ret;
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+       struct iio_buffer *ring;
+
+       ring = iio_kfifo_allocate();
+       if (!ring)
+               return -ENOMEM;
+       indio_dev->buffer = ring;
+       /* setup ring buffer */
+       ring->scan_timestamp = true;
+       indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
+       /*scan count double count timestamp. should subtract 1. but
+       number of channels still includes timestamp*/
+       if (INV_MPU3050 == st->chip_type)
+               ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+                       inv_read_fifo_mpu3050,
+                       IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+       else
+               ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+                       inv_read_fifo,
+                       IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+       if (ret)
+               goto error_iio_sw_rb_free;
+
+       indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+       return 0;
+error_iio_sw_rb_free:
+       iio_kfifo_free(indio_dev->buffer);
+       return ret;
+}
+
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c
new file mode 100644 (file)
index 0000000..947ca09
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+* 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_mpu_trigger.c
+ *      @brief   A sysfs device driver for Invensense devices
+ *      @details This file is part of inv mpu iio 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>
+
+#ifdef INV_KERNEL_3_10
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#else
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+#endif
+
+#include "inv_mpu_iio.h"
+
+/**
+ * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state
+ **/
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+                                               bool state)
+{
+/*
+       --yd    struct iio_dev *indio_dev = trig->private_data;
+
+       --yd    dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+*/
+       return 0;
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+       .owner = THIS_MODULE,
+       .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev)
+{
+       int ret;
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+#ifdef INV_KERNEL_3_10
+       st->trig = iio_trigger_alloc("%s-dev%d",
+#else
+       st->trig = iio_allocate_trigger("%s-dev%d",
+#endif
+                                       indio_dev->name,
+                                       indio_dev->id);
+       if (st->trig == NULL)
+               return -ENOMEM;
+       st->trig->dev.parent = &st->client->dev;
+#ifdef INV_KERNEL_3_10
+       iio_trigger_set_drvdata(st->trig, indio_dev);
+#else
+       st->trig->private_data = indio_dev;
+#endif
+       st->trig->ops = &inv_mpu_trigger_ops;
+       ret = iio_trigger_register(st->trig);
+
+       if (ret) {
+#ifdef INV_KERNEL_3_10
+               iio_trigger_free(st->trig);
+#else
+               iio_free_trigger(st->trig);
+#endif
+               return -EPERM;
+       }
+       indio_dev->trig = st->trig;
+
+       return 0;
+}
+
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev)
+{
+       struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+
+       iio_trigger_unregister(st->trig);
+#ifdef INV_KERNEL_3_10
+       iio_trigger_free(st->trig);
+#else
+       iio_free_trigger(st->trig);
+#endif
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c
new file mode 100644 (file)
index 0000000..bd84f63
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+* 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 invensense mpu 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_mpu_iio.h"
+#define BMA250_CHIP_ID                 3
+#define BMA250_RANGE_SET               0
+#define BMA250_BW_SET                  4
+
+/* range and bandwidth */
+#define BMA250_RANGE_2G                 3
+#define BMA250_RANGE_4G                 5
+#define BMA250_RANGE_8G                 8
+#define BMA250_RANGE_16G                12
+#define BMA250_RANGE_MAX                4
+#define BMA250_RANGE_MASK               0xF0
+
+#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
+#define BMA250_MAX_BW_SIZE      8
+#define BMA250_BW_REG_MASK      0xE0
+
+/*      register definitions */
+#define BMA250_X_AXIS_LSB_REG                   0x02
+#define BMA250_RANGE_SEL_REG                    0x0F
+#define BMA250_BW_SEL_REG                       0x10
+#define BMA250_MODE_CTRL_REG                    0x11
+
+/* mode settings */
+#define BMA250_MODE_NORMAL     0
+#define BMA250_MODE_LOWPOWER   1
+#define BMA250_MODE_SUSPEND    2
+#define BMA250_MODE_MAX        3
+#define BMA250_MODE_MASK       0x3F
+#define BMA250_BIT_SUSPEND     0x80
+#define BMA250_BIT_LP          0x40
+
+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_mpu_iio_s *st, u8 bw)
+{
+       int res;
+       u8 data;
+       int bandwidth;
+       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:
+               return -EINVAL;
+       }
+       res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+       if (res)
+               return res;
+       data &= BMA250_BW_REG_MASK;
+       data |= bandwidth;
+       res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+       return res;
+}
+
+static int bma250_set_range(struct inv_mpu_iio_s *st, u8 range)
+{
+       int res;
+       u8 orig, data;
+       switch (range) {
+       case 0:
+               data  = BMA250_RANGE_2G;
+               break;
+       case 1:
+               data  = BMA250_RANGE_4G;
+               break;
+       case 2:
+               data  = BMA250_RANGE_8G;
+               break;
+       case 3:
+               data  = BMA250_RANGE_16G;
+               break;
+       default:
+               return -EINVAL;
+       }
+       res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+       if (res)
+               return res;
+       orig &= BMA250_RANGE_MASK;
+       data |= orig;
+       res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+       if (res)
+               return res;
+       bma_static_property.range = range;
+
+       return 0;
+}
+
+static int setup_slave_bma250(struct inv_mpu_iio_s *st)
+{
+       int result;
+       u8 data[2];
+       result = set_3050_bypass(st, true);
+       if (result)
+               return result;
+       /*read secondary i2c ID register */
+       result = inv_secondary_read(0, 1, data);
+       if (result)
+               return result;
+       if (BMA250_CHIP_ID != data[0])
+               return -EINVAL;
+       result = set_3050_bypass(st, false);
+       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, REG_3050_AUX_BST_ADDR,
+                                       BMA250_X_AXIS_LSB_REG);
+
+       return result;
+}
+
+static int bma250_set_mode(struct inv_mpu_iio_s *st, u8 mode)
+{
+       int res;
+       u8 data;
+
+       res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+       if (res)
+               return res;
+       data &= BMA250_MODE_MASK;
+       switch (mode) {
+       case BMA250_MODE_NORMAL:
+               break;
+       case BMA250_MODE_LOWPOWER:
+               data |= BMA250_BIT_LP;
+               break;
+       case BMA250_MODE_SUSPEND:
+               data |= BMA250_BIT_SUSPEND;
+               break;
+       default:
+               return -EINVAL;
+       }
+       res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+       if (res)
+               return res;
+       bma_static_property.mode = mode;
+
+       return 0;
+}
+
+static int suspend_slave_bma250(struct inv_mpu_iio_s *st)
+{
+       int result;
+       if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+               return 0;
+       /*set to bypass mode */
+       result = set_3050_bypass(st, true);
+       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 0;
+}
+
+static int resume_slave_bma250(struct inv_mpu_iio_s *st)
+{
+       int result;
+       if (bma_static_property.mode == BMA250_MODE_NORMAL)
+               return 0;
+       /*set to bypass mode */
+       result = set_3050_bypass(st, true);
+       if (result)
+               return result;
+       result = bma250_set_mode(st, BMA250_MODE_NORMAL);
+       /* recover bypass mode */
+       result |= set_3050_bypass(st, false);
+
+       return result ? (-EINVAL) : 0;
+}
+
+static int combine_data_slave_bma250(u8 *in, short *out)
+{
+       out[0] = le16_to_cpup((__le16 *)(&in[0]));
+       out[1] = le16_to_cpup((__le16 *)(&in[2]));
+       out[2] = le16_to_cpup((__le16 *)(&in[4]));
+
+       return 0;
+}
+
+static int get_mode_slave_bma250(void)
+{
+       switch (bma_static_property.mode) {
+       case BMA250_MODE_SUSPEND:
+               return INV_MODE_SUSPEND;
+       case BMA250_MODE_NORMAL:
+               return INV_MODE_NORMAL;
+       default:
+               return -EINVAL;
+       }
+}
+
+/**
+ *  set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_mpu_iio_s *st, int rate)
+{
+       const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7};
+       const int   d[] = {7, 6, 5, 4, 3, 2, 1, 0};
+       int i, h, data, result;
+       h = (rate >> 1);
+       i = 0;
+       while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1))
+               i++;
+       data = d[i];
+
+       result = set_3050_bypass(st, true);
+       if (result)
+               return result;
+       result = bma250_set_bandwidth(st, (u8) data);
+       result |= set_3050_bypass(st, false);
+
+       return result ? (-EINVAL) : 0;
+}
+/**
+ *  set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_mpu_iio_s *st, int fs)
+{
+       int result;
+       result = set_3050_bypass(st, true);
+       if (result)
+               return result;
+       result = bma250_set_range(st, (u8) fs);
+       result |= set_3050_bypass(st, false);
+
+       return result ? (-EINVAL) : 0;
+}
+
+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_mpu3050_slave(struct inv_mpu_iio_s *st)
+{
+       st->mpu_slave = &slave_bma250;
+
+       return 0;
+}
+/**
+ *  @}
+ */
+
index 1e4ecd56b66ac69fa60df71a9b2cba5551700d4e..f4749623936664cd198cd7ed5a95d69ceb2d03fe 100755 (executable)
@@ -1,8 +1,15 @@
 /*
- $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.
+*/
 
 #ifndef __MPU_H_
 #define __MPU_H_
 #ifdef __KERNEL__
 #include <linux/types.h>
 #include <linux/ioctl.h>
-#elif defined LINUX
-#include <sys/ioctl.h>
-#include <linux/types.h>
-#else
-#include "mltypes.h"
 #endif
 
-/* Number of axes on each sensor */
-#define GYRO_NUM_AXES               (3)
-#define ACCEL_NUM_AXES              (3)
-#define COMPASS_NUM_AXES            (3)
-
-struct mpu_read_write {
-       /* Memory address or register address depending on ioctl */
-       __u16 address;
-       __u16 length;
-       __u8 *data;
-};
-
-enum mpuirq_data_type {
-       MPUIRQ_DATA_TYPE_MPU_IRQ,
-       MPUIRQ_DATA_TYPE_SLAVE_IRQ,
-       MPUIRQ_DATA_TYPE_PM_EVENT,
-       MPUIRQ_DATA_TYPE_NUM_TYPES,
-};
-
-/* User space PM event notification */
-#define MPU_PM_EVENT_SUSPEND_PREPARE (3)
-#define MPU_PM_EVENT_POST_SUSPEND    (4)
-
-struct mpuirq_data {
-       __u32 interruptcount;
-       __u64 irqtime;
-       __u32 data_type;
-       __s32 data;
-};
-
-enum ext_slave_config_key {
-       MPU_SLAVE_CONFIG_ODR_SUSPEND,
-       MPU_SLAVE_CONFIG_ODR_RESUME,
-       MPU_SLAVE_CONFIG_FSR_SUSPEND,
-       MPU_SLAVE_CONFIG_FSR_RESUME,
-       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_WRITE_REGISTERS,
-       MPU_SLAVE_READ_REGISTERS,
-       MPU_SLAVE_CONFIG_INTERNAL_REFERENCE,
-       /* AMI 306 specific config keys */
-       MPU_SLAVE_PARAM,
-       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,
-       MPU_SLAVE_FULL_SCALE,
-       MPU_SLAVE_LPF,
-       MPU_SLAVE_CLK_SRC,
-       MPU_SLAVE_DIVIDER,
-       MPU_SLAVE_DMP_ENABLE,
-       MPU_SLAVE_FIFO_ENABLE,
-       MPU_SLAVE_DMP_CFG1,
-       MPU_SLAVE_DMP_CFG2,
-       MPU_SLAVE_TC,
-       MPU_SLAVE_GYRO,
-       MPU_SLAVE_ADDR,
-       MPU_SLAVE_PRODUCT_REVISION,
-       MPU_SLAVE_SILICON_REVISION,
-       MPU_SLAVE_PRODUCT_ID,
-       MPU_SLAVE_GYRO_SENS_TRIM,
-       MPU_SLAVE_ACCEL_SENS_TRIM,
-       MPU_SLAVE_RAM,
-       /* -------------------------- */
-       MPU_SLAVE_CONFIG_NUM_CONFIG_KEYS
-};
-
-/* For the MPU_SLAVE_CONFIG_IRQ_SUSPEND and MPU_SLAVE_CONFIG_IRQ_RESUME */
-enum ext_slave_config_irq_type {
-       MPU_SLAVE_IRQ_TYPE_NONE,
-       MPU_SLAVE_IRQ_TYPE_MOTION,
-       MPU_SLAVE_IRQ_TYPE_DATA_READY,
-};
-
-/* Structure for the following IOCTS's
- * MPU_CONFIG_GYRO
- * MPU_CONFIG_ACCEL
- * MPU_CONFIG_COMPASS
- * MPU_CONFIG_PRESSURE
- * MPU_GET_CONFIG_GYRO
- * MPU_GET_CONFIG_ACCEL
- * MPU_GET_CONFIG_COMPASS
- * MPU_GET_CONFIG_PRESSURE
- *
- * @key one of enum ext_slave_config_key
- * @len length of data pointed to by data
- * @apply zero if communication with the chip is not necessary, false otherwise
- *        This flag can be used to select cached data or to refresh cashed data
- *        cache data to be pushed later or push immediately.  If true and the
- *        slave is on the secondary bus the MPU will first enger bypass mode
- *        before calling the slaves .config or .get_config funcion
- * @data pointer to the data to confgure or get
- */
-struct ext_slave_config {
-       __u8 key;
-       __u16 len;
-       __u8 apply;
-       void *data;
-};
-
-enum ext_slave_type {
-       EXT_SLAVE_TYPE_GYROSCOPE,
-       EXT_SLAVE_TYPE_ACCEL,
-       EXT_SLAVE_TYPE_COMPASS,
-       EXT_SLAVE_TYPE_PRESSURE,
-       /*EXT_SLAVE_TYPE_TEMPERATURE */
+enum secondary_slave_type {
+       SECONDARY_SLAVE_TYPE_NONE,
+       SECONDARY_SLAVE_TYPE_ACCEL,
+       SECONDARY_SLAVE_TYPE_COMPASS,
+       SECONDARY_SLAVE_TYPE_PRESSURE,
+       SECONDARY_SLAVE_TYPE_ALS,
 
-       EXT_SLAVE_NUM_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,
@@ -151,9 +50,9 @@ enum ext_slave_id {
        ACCEL_ID_MMA845X,
        ACCEL_ID_MPU6050,
 
+       COMPASS_ID_AK8963,
        COMPASS_ID_AK8975,
        COMPASS_ID_AK8972,
-       COMPASS_ID_AK8963,
        COMPASS_ID_AMI30X,
        COMPASS_ID_AMI306,
        COMPASS_ID_YAS529,
@@ -162,138 +61,36 @@ enum ext_slave_id {
        COMPASS_ID_LSM303DLH,
        COMPASS_ID_LSM303DLM,
        COMPASS_ID_MMC314X,
-       COMPASS_ID_MMC328X,
        COMPASS_ID_HSCDTD002B,
        COMPASS_ID_HSCDTD004A,
+       COMPASS_ID_MLX90399,
+       COMPASS_ID_AK09911,
+       COMPASS_ID_AK09912,
+       COMPASS_ID_ST480M,
 
-       PRESSURE_ID_BMA085,
-};
-
-enum ext_slave_endian {
-       EXT_SLAVE_BIG_ENDIAN,
-       EXT_SLAVE_LITTLE_ENDIAN,
-       EXT_SLAVE_FS8_BIG_ENDIAN,
-       EXT_SLAVE_FS16_BIG_ENDIAN,
-};
-
-enum ext_slave_bus {
-       EXT_SLAVE_BUS_INVALID = -1,
-       EXT_SLAVE_BUS_PRIMARY = 0,
-       EXT_SLAVE_BUS_SECONDARY = 1
-};
-
-
-/**
- *  struct ext_slave_platform_data - Platform data for mpu3050 and mpu6050
- *  slave devices
- *
- *  @type: the type of slave device based on the enum ext_slave_type
- *         definitions.
- *  @irq: the irq number attached to the slave if any.
- *  @adapt_num: the I2C adapter number.
- *  @bus: the bus the slave is attached to: enum ext_slave_bus
- *  @address: the I2C slave address of the slave device.
- *  @orientation: the mounting matrix of the device relative to MPU.
- *  @irq_data: private data for the slave irq handler
- *  @private_data: additional data, user customizable.  Not touched by the MPU
- *                 driver.
- *
- * The orientation matricies are 3x3 rotation matricies
- * that are applied to the data to rotate from the mounting orientation to the
- * platform orientation.  The values must be one of 0, 1, or -1 and each row and
- * column should have exactly 1 non-zero value.
- */
-struct ext_slave_platform_data {
-       __u8 type;
-       __u32 irq;
-       __u32 adapt_num;
-       __u32 bus;
-       __u8 address;
-       __s8 orientation[9];
-       void *irq_data;
-       void *private_data;
-};
-
-struct fix_pnt_range {
-       __s32 mantissa;
-       __s32 fraction;
-};
-
-static inline long range_fixedpoint_to_long_mg(struct fix_pnt_range rng)
-{
-       return (long)(rng.mantissa * 1000 + rng.fraction / 10);
-}
-
-struct ext_slave_read_trigger {
-       __u8 reg;
-       __u8 value;
-};
-
-/**
- *  struct ext_slave_descr - Description of the slave device for programming.
- *
- *  @suspend:  function pointer to put the device in suspended state
- *  @resume:   function pointer to put the device in running state
- *  @read:     function that reads the device data
- *  @init:     function used to preallocate memory used by the driver
- *  @exit:     function used to free memory allocated for the driver
- *  @config:   function used to configure the device
- *  @get_config:function used to get the device's configuration
- *
- *  @name:     text name of the device
- *  @type:     device type. enum ext_slave_type
- *  @id:       enum ext_slave_id
- *  @read_reg: starting register address to retrieve data.
- *  @read_len: length in bytes of the sensor data.  Typically  6.
- *  @endian:   byte order of the data. enum ext_slave_endian
- *  @range:    full scale range of the slave ouput: struct fix_pnt_range
- *  @trigger:  If reading data first requires writing a register this is the
- *             data to write.
- *
- *  Defines the functions and information about the slave the mpu3050 and
- *  mpu6050 needs to use the slave device.
- */
-struct ext_slave_descr {
-       int (*init) (void *mlsl_handle,
-                    struct ext_slave_descr *slave,
-                    struct ext_slave_platform_data *pdata);
-       int (*exit) (void *mlsl_handle,
-                    struct ext_slave_descr *slave,
-                    struct ext_slave_platform_data *pdata);
-       int (*suspend) (void *mlsl_handle,
-                       struct ext_slave_descr *slave,
-                       struct ext_slave_platform_data *pdata);
-       int (*resume) (void *mlsl_handle,
-                      struct ext_slave_descr *slave,
-                      struct ext_slave_platform_data *pdata);
-       int (*read) (void *mlsl_handle,
-                    struct ext_slave_descr *slave,
-                    struct ext_slave_platform_data *pdata,
-                    __u8 *data);
-       int (*config) (void *mlsl_handle,
-                      struct ext_slave_descr *slave,
-                      struct ext_slave_platform_data *pdata,
-                      struct ext_slave_config *config);
-       int (*get_config) (void *mlsl_handle,
-                          struct ext_slave_descr *slave,
-                          struct ext_slave_platform_data *pdata,
-                          struct ext_slave_config *config);
+       PRESSURE_ID_BMP085,
+       PRESSURE_ID_BMP280,
 
-       char *name;
-       __u8 type;
-       __u8 id;
-       __u8 read_reg;
-       __u8 read_len;
-       __u8 endian;
-       struct fix_pnt_range range;
-       struct ext_slave_read_trigger *trigger;
+       ALS_ID_APDS_9900,
+       ALS_ID_APDS_9930,
 };
 
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
 /**
  * struct mpu_platform_data - Platform data for the mpu driver
  * @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
+ * @aux_slave_type: auxiliary slave. Another slave device type
+ * @aux_slave_id: auxiliary slave ID.
+ * @aux_i2c_addr: auxiliary device I2C address.
+ * @read_only_slave_type: read only slave type.
+ * @read_only_slave_id: read only slave device ID.
+ * @read_only_i2c_addr: read only slave device address.
  *
  * Contains platform specific information on how to configure the MPU3050 to
  * work on this platform.  The orientation matricies are 3x3 rotation matricies
@@ -302,63 +99,25 @@ struct ext_slave_descr {
  * column should have exactly 1 non-zero value.
  */
 struct mpu_platform_data {
-       __u32 int_config;
-       __u32 level_shifter;
-       __s8 orientation[GYRO_NUM_AXES * GYRO_NUM_AXES];
-};
-
-#if defined __KERNEL__ || defined LINUX
-#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)
-
+       __u8 int_config;
+       __u8 level_shifter;
+       __s8 orientation[9];
+       enum secondary_slave_type sec_slave_type;
+       enum ext_slave_id sec_slave_id;
+       __u16 secondary_i2c_addr;
+       __s8 secondary_orientation[9];
+       enum secondary_slave_type aux_slave_type;
+       enum ext_slave_id aux_slave_id;
+       __u16 aux_i2c_addr;
+       enum secondary_slave_type read_only_slave_type;
+       enum ext_slave_id read_only_slave_id;
+       __u16 read_only_i2c_addr;
+#ifdef CONFIG_DTS_INV_MPU_IIO
+       int (*power_on)(struct mpu_platform_data *);
+       int (*power_off)(struct mpu_platform_data *);
+       struct regulator *vdd_ana;
+       struct regulator *vdd_i2c;
 #endif
+};
 
-#endif                         /* __MPU_H_ */
+#endif /* __MPU_H_ */
index 7c63bd67c36e274f933a857b23ee00e5e1c2daed..b06db7e3bcd6f967a94c065eab867a08d10895ef 100644 (file)
@@ -37,6 +37,7 @@ enum iio_chan_type {
        IIO_VELOCITY,
        IIO_CONCENTRATION,
        IIO_RESISTANCE,
+       IIO_QUATERNION,
 };
 
 enum iio_modifier {
@@ -76,6 +77,7 @@ enum iio_modifier {
        IIO_MOD_Q,
        IIO_MOD_CO2,
        IIO_MOD_VOC,
+       IIO_MOD_R,
 };
 
 enum iio_event_type {