linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit
@ 2012-06-28 18:30 Ge Gao
  2012-07-06  9:07 ` Jonathan Cameron
  0 siblings, 1 reply; 4+ messages in thread
From: Ge Gao @ 2012-06-28 18:30 UTC (permalink / raw)
  To: linux-iio; +Cc: linux-iio-owner, Jonathan Cameron


[-- Attachment #1.1: Type: text/plain, Size: 119 bytes --]

Hi All,

                Attached is the Invensense MPU6050/9150 IIO driver submit.
Thanks.



Best Regards,



Ge GAO

[-- Attachment #1.2: Type: text/html, Size: 1565 bytes --]

[-- Attachment #2: 0001-Invensense-MPU6050-MPU9150-driver.patch --]
[-- Type: application/octet-stream, Size: 96997 bytes --]

From 6d4093a59d77a68d4972f648ea9eba69e8598159 Mon Sep 17 00:00:00 2001
From: Ge Gao <ggao@invensense.com>
Date: Thu, 28 Jun 2012 10:58:12 -0700
Subject: [PATCH 1/4]     Invensense MPU6050/MPU9150 driver.

      --MPU6050/MPU9150 driver.
      --Secondary bus for AKM8975/AKM8963/AKM8972 support.
      --kernel fix for Kfifo poll support from Jonanthan.
      --Kfifo bug fix. Need to check available space before store.
      --add new IIO type for quaternion.

Change-Id: I4023d322193fa89df8ceb84e6b95611be4264e49
Signed-off-by: Ge Gao <ggao@invensense.com>
---
 drivers/staging/iio/imu/Kconfig                    |    1 +
 drivers/staging/iio/imu/Makefile                   |    3 +
 drivers/staging/iio/imu/mpu6050/Kconfig            |   13 +
 drivers/staging/iio/imu/mpu6050/Makefile           |   11 +
 drivers/staging/iio/imu/mpu6050/README             |  296 ++++++++
 .../staging/iio/imu/mpu6050/dmpDefaultMPU6050.c    |  290 ++++++++
 drivers/staging/iio/imu/mpu6050/dmpKey.h           |  494 +++++++++++++
 drivers/staging/iio/imu/mpu6050/dmpmap.h           |  266 +++++++
 drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h      |  727 ++++++++++++++++++++
 drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c     |  563 +++++++++++++++
 drivers/staging/iio/imu/mpu6050/mpu.h              |  107 +++
 11 files changed, 2771 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/imu/mpu6050/Kconfig
 create mode 100644 drivers/staging/iio/imu/mpu6050/Makefile
 create mode 100644 drivers/staging/iio/imu/mpu6050/README
 create mode 100644 drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
 create mode 100644 drivers/staging/iio/imu/mpu6050/dmpKey.h
 create mode 100644 drivers/staging/iio/imu/mpu6050/dmpmap.h
 create mode 100644 drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
 create mode 100644 drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
 create mode 100644 drivers/staging/iio/imu/mpu6050/mpu.h

diff --git a/drivers/staging/iio/imu/Kconfig b/drivers/staging/iio/imu/Kconfig
index 2c2f47d..e27c26e 100644
--- a/drivers/staging/iio/imu/Kconfig
+++ b/drivers/staging/iio/imu/Kconfig
@@ -14,4 +14,5 @@ config ADIS16400
 	  adis16365, adis16400 and adis16405 triaxial inertial sensors
 	  (adis16400 series also have magnetometers).
 
+source "drivers/staging/iio/imu/mpu6050/Kconfig"
 endmenu
diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile
index 3400a13..920eb13 100644
--- a/drivers/staging/iio/imu/Makefile
+++ b/drivers/staging/iio/imu/Makefile
@@ -5,3 +5,6 @@
 adis16400-y             := adis16400_core.o
 adis16400-$(CONFIG_IIO_BUFFER) += adis16400_ring.o adis16400_trigger.o
 obj-$(CONFIG_ADIS16400) += adis16400.o
+
+obj-y += mpu6050/
+
diff --git a/drivers/staging/iio/imu/mpu6050/Kconfig b/drivers/staging/iio/imu/mpu6050/Kconfig
new file mode 100644
index 0000000..c4fa2c2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/Kconfig
@@ -0,0 +1,13 @@
+#
+# inv-mpu-6050 drivers for Invensense MPU devices and combos
+#
+
+config INV_MPU6050_IIO
+	tristate "Invensense MPU6050 devices"
+	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && !INV_MPU && !INV_MPU_IIO
+	default n
+	help
+	  This driver supports the Invensense MPU6050/MPU9150 devices. It also
+	  supports AKM8975/AKM8963/AKM8972 in the secondary bus.
+	  This driver can be built as a module. The module will be called
+	  inv-mpu6050-iio.
diff --git a/drivers/staging/iio/imu/mpu6050/Makefile b/drivers/staging/iio/imu/mpu6050/Makefile
new file mode 100644
index 0000000..1a5e746
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for Invensense inv-mpu6050-iio device.
+#
+
+obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050-iio.o
+
+inv-mpu6050-iio-objs := inv_mpu_core.o
+inv-mpu6050-iio-objs += inv_mpu_ring.o
+inv-mpu6050-iio-objs += inv_mpu_misc.o
+inv-mpu6050-iio-objs += dmpDefaultMPU6050.o
+
diff --git a/drivers/staging/iio/imu/mpu6050/README b/drivers/staging/iio/imu/mpu6050/README
new file mode 100644
index 0000000..806a956
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/README
@@ -0,0 +1,296 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Description
+-----------
+This document describes how to install the Invensense device driver into a
+Linux kernel. At the moment, this driver supports the
+MPU6050/MPU9150. The slave address of these four chips are
+0x68 or 0x69. However, the actual slave address depends on the board
+configuration. The driver does not assume anything about it.
+
+Files included in this package:
+Kconfig
+Makefile
+inv_mpu_core.c
+inv_mpu_misc.c
+inv_mpu_iio.h
+inv_mpu_ring.c
+dmpDefaultMPU6050.c
+dmpkey.h
+dmpmap.h
+mpu.h
+Including the driver in the Linux kernel
+----------------------------------------
+mpu.h should be added to "kernel/include/linux".
+Other files listed should be added to the drivers/staging/iio/imu/mpu
+directory (or another directory of your choosing). When building the
+kernel, the driver will not appear in menuconfig without modifications
+similar to those below:
+
+modify "drivers/staging/iio/imu/Kconfig" like
+source "drivers/staging/iio/imu/mpu/Kconfig"
+
+modify "drivers/staging/iio/imu/Makefile"
+obj-y += mpu/
+
+Board and Platform Data
+-----------------------
+The board file needs to be modified to register the device on an I2C bus. An
+i2c_board_info instance must be defined as seen below. The hardcoded value of
+140 corresponds to the GPIO input pin wired to the device's interrupt pin.
+This pin will most likely be different for your platform.
+platform data is for orientation matrix,  and secondary bus situations.
+For MPU9150, it is regarded as a MPU9150 and AKM8975 in the secondary.
+So the secondary i2c address must be filled.
+-----------------------------------------------------------------
+The board file is arch/arm/mach-omap2/board-omap4panda.c for panda board or
+modify the board file in your system as below:
+--------------------------------------------------------
+For AKM8963 in the secondary i2c bus of MPU6050,
+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_AK8963,
+	.secondary_i2c_addr = 0x0E
+};
+-----------------------------------------------------------
+For MPU9150, the secondary i2c bus address must be filled as below.
+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 = 0x0E
+};
+-----------------------------------------------------------
+for 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,
+	},
+};
+for 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,
+	},
+};
+
+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));
+
+IIO subsystem
+----------------------------------------------
+successful installation will create two directories under /sys/bus/iio/devices
+iio:device0
+trigger0
+Under /dev/ diretory, a file "iio:device0" will also be created(or iio:deviceX, if
+you have more than one iio devices).
+Communicating with the driver in userspace
+------------------------------------------
+Upon installation, the driver generates several files in sysfs. If your
+platform is configured as detailed above, navigate to the following path to
+find these files:
+/sys/bus/iio/devices/iio:device0
+
+The list below provides a brief description for each file.
+--------------------------------------
+For MPU6050:
+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 orient matrix obtained from board file.
+
+gyro_enable (read/write)
+enable/disable gyro functionality. affect raw_gyro. turn off this will
+shut down gyro and save power.
+
+accl_enable (read/write)
+enable/disable accelerometer functionality. affect raw_accl.
+turn off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+Flag indicate 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. 1 is updated internally.
+
+dmp_on(read/write)
+This entry controls whether to run DMP or not. To enable DMP ,
+firmware_loaded must be 1. write 1 to enable DMP and write 0 to disable dmp.
+
+dmp_in_on(read/write)
+This entry controls whether dmp interrupt is on/off. firmware_loaded must
+be 1. sometimes, it is desirable that interrupt is off while DMP is running.
+
+dmp_event_int_on(read/write)
+This entry controls whether dmp event interrupt is on/off. Setting this on
+would turn off the data interrupt and turn on the event interrupt. No data
+interrupt would be generated. Only when event happens, does an interrupt
+generate. This can be used in power saving mode when system is waiting for
+a special event to wake up.
+
+dmp_firmware (write only binary file)
+This is the entry that firmware code is loaded into. If the action is succeful,
+firmware_loaded will be updated as 1. In order to load new firmware,
+firmware_loaded flag should be set 0.
+
+lpa_mode(read-write)
+Low power  accelerometer mode
+lpa_freq(read-write)
+low power acceleromter frequency.
+
+accel_matrix
+orient matrix for accel
+
+flick_lower,
+flick_upper,
+flick_counter,
+flick_message_on,
+flick_int_on,
+flick_axis,
+Flick related entry
+
+pedometer_time
+pedometer_steps,
+Pedometer related entry
+
+event_flick
+event_tap
+event_orientation
+event_display_orientation
+event related entry. These entry must use poll to read.
+
+tap_on
+control tap function of DMP
+
+dmp_int_on
+turn on/off dmp interrupt.
+
+dmp_output_rate
+control dmp output rate when dmp is on.
+
+tap_time
+tap_min_count
+tap_threshold
+tap related entries. control various parameters of tap function.
+
+orientation_on
+turn on/off orientation function of DMP.
+
+display_orientation_on
+turn on/off display orientation function of DMP.
+
+quaternion_on
+turn on/off quaterniion data output. must use DMP.
+-------------------------------------------------------------------
+for MPU9150 and secondary compass
+MPU9150 has every entry MPU6050 has. It has additional entries:
+
+compass_enable (read/write)
+enable this will enable compass function.
+
+compass_matrix (read-only)
+compass orient matrix
+----------------------------------------------------------------------------------
+low power accelerometer mode
+Lower power accelerometer mode is a special mode. It works only for accelerometer.
+It has two entries, lpa_mode and lpa_freq. Only MPU6050 and MPU9150 has this mode.
+To run low power accel mode, set lpa_mode to 1, set lpa_freq to 0~3, which corresponds
+to 1.25Hz, 5Hz, 20Hz, 40Hz. "gyro_enable" and "compass_enable" must be zero. "dmp_on"
+must be zero.
+-----------------------------------------------------------------------------------
+dmp event.
+dmp event is event out by the DMP unit inside MPU. Only MPU6050 and MPU9150 supports this.
+There are four sysfs entreis, event_flick, event_tap and event_orientation and
+event_display_orientation. These four events must
+be polled before read. The proper method to poll sysfs is:
+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.
+------------------------------------------------------------------------------
+If streaming to a userspace application, the recommended way to access gyro/accel/compass
+data is via /dev/iio:device0. Follow these steps to get constant readings 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, using "dmp_on" to enable/disable dmp .
+7. If compass is enabled, output will have compass data.
+===========================================================================
+                    Recommended sysfs entry setup senquence
+1. without DMP firmware
+1.1 set "power_state" to 1,
+1.2 change scale and fifo rate value to your need.
+1.3 change gyro_enable and accle_enable and compass_enable to your needs. For example,
+if you want gyro only, set accl_enable to 0 or set accl_enable to zero and compass_enable to zero.
+If you want accel only, set gyro_enable to 0 or set gyro_enable to zero and compass_enable to zero.
+If you want compass only, disable gyro and accel.
+1.4 set "enable" to 1. you will get output you want.
+
+2. With DMP firmware
+2.1 set "power_state" to 1,
+2.2 write "0" to firmware_loaded if it is not zero already.
+2.3 load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware image.
+2.4 make sure firmware_loaded is 1 after loading.
+2.5 make other configurations similar to the situation as without DMP firmware.
+2.6 set dmp_on to 1.
+2.7 set "enable" to 1.
+=======================================================
+The enable function is using enable entry under "/sys/bus/iio/devices/iio:device0/buffer"
+==========================================================
+test applications:
+Test application is mpu_iio
+------------------------------------------
+To run with MPU9150/MPU6050:
+using the following command:
+for orientation/tap/flick/display orientation event:
+mpu_iio  -c 10 -l 3 -p
+for normal data print
+mpu_iio  -c 10 -l 3 -r
+----------------------------------------
+Please use mpu_iio.c and iio_utils.h as the sample code for your development.
diff --git a/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
new file mode 100644
index 0000000..5803643
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ *
+ */
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_27                  (2745)
+#define CFG_20                  (2078)
+#define CFG_23                  (2748)
+#define CFG_FIFO_ON_EVENT       (2694)
+#define CFG_ORIENT_IRQ_1        (2533)
+#define CGNOTICE_INTR           (2636)
+#define X_GRT_Y_TMP             (1318)
+#define CFG_DR_INT              (1029)
+#define CFG_AUTH                (1035)
+#define FCFG_1                  (1062)
+#define SKIP_X_GRT_Y_TMP        (1319)
+#define SKIP_END_COMPARE        (1395)
+#define FCFG_3                  (1110)
+#define FCFG_2                  (1066)
+#define END_COMPARE_Y_X_TMP2    (1415)
+#define CFG_DISPLAY_ORIENT_INT  (1706)
+#define FCFG_7                  (1076)
+#define FCFG_6                  (1128)
+#define NO_ORIENT_INTERRUPT     (1725)
+#define CFG_8                   (2723)
+#define CFG_15                  (2731)
+#define CFG_16                  (2749)
+#define END_COMPARE_Y_X_TMP     (1367)
+#define CFG_6                   (2756)
+#define END_ORIENT_1            (1709)
+#define END_COMPARE_Y_X         (1444)
+#define CFG_LP_QUAT             (2717)
+#define END_ORIENT              (1738)
+#define CFG_FLICK_IN            (2589)
+#define CFG_7                   (1221)
+#define CFG_MOTION_BIAS         (1224)
+#define X_GRT_Y                 (1368)
+#define TEMPLABEL               (2178)
+#define NOT_TIME_MINUS_1        (1528)
+#define END_COMPARE_Y_X_TMP3    (1394)
+#define X_GRT_Y_TMP2            (1339)
+
+#define D_0_22                  (22+512)
+#define D_0_24                  (24+512)
+
+#define D_0_36                  (36)
+#define D_0_52                  (52)
+#define D_0_96                  (96)
+#define D_0_104                 (104)
+#define D_0_108                 (108)
+#define D_0_163                 (163)
+#define D_0_188                 (188)
+#define D_0_192                 (192)
+#define D_0_224                 (224)
+#define D_0_228                 (228)
+#define D_0_232                 (232)
+#define D_0_236                 (236)
+
+#define D_1_2                   (256 + 2)
+#define D_1_4                   (256 + 4)
+#define D_1_8                   (256 + 8)
+#define D_1_10                  (256 + 10)
+#define D_1_24                  (256 + 24)
+#define D_1_28                  (256 + 28)
+#define D_1_36                  (256 + 36)
+#define D_1_40                  (256 + 40)
+#define D_1_44                  (256 + 44)
+#define D_1_72                  (256 + 72)
+#define D_1_74                  (256 + 74)
+#define D_1_79                  (256 + 79)
+#define D_1_88                  (256 + 88)
+#define D_1_90                  (256 + 90)
+#define D_1_92                  (256 + 92)
+#define D_1_96                  (256 + 96)
+#define D_1_98                  (256 + 98)
+#define D_1_106                 (256 + 106)
+#define D_1_108                 (256 + 108)
+#define D_1_112                 (256 + 112)
+#define D_1_128                 (256 + 144)
+#define D_1_152                 (256 + 12)
+#define D_1_160                 (256 + 160)
+#define D_1_176                 (256 + 176)
+#define D_1_178                 (256 + 178)
+#define D_1_218                 (256 + 218)
+#define D_1_232                 (256 + 232)
+#define D_1_236                 (256 + 236)
+#define D_1_240                 (256 + 240)
+#define D_1_244                 (256 + 244)
+#define D_1_250                 (256 + 250)
+#define D_1_252                 (256 + 252)
+#define D_2_12                  (512 + 12)
+#define D_2_96                  (512 + 96)
+#define D_2_108                 (512 + 108)
+#define D_2_208                 (512 + 208)
+#define D_2_224                 (512 + 224)
+#define D_2_236                 (512 + 236)
+#define D_2_244                 (512 + 244)
+#define D_2_248                 (512 + 248)
+#define D_2_252                 (512 + 252)
+
+#define CPASS_BIAS_X            (35 * 16 + 4)
+#define CPASS_BIAS_Y            (35 * 16 + 8)
+#define CPASS_BIAS_Z            (35 * 16 + 12)
+#define CPASS_MTX_00            (36 * 16)
+#define CPASS_MTX_01            (36 * 16 + 4)
+#define CPASS_MTX_02            (36 * 16 + 8)
+#define CPASS_MTX_10            (36 * 16 + 12)
+#define CPASS_MTX_11            (37 * 16)
+#define CPASS_MTX_12            (37 * 16 + 4)
+#define CPASS_MTX_20            (37 * 16 + 8)
+#define CPASS_MTX_21            (37 * 16 + 12)
+#define CPASS_MTX_22            (43 * 16 + 12)
+#define D_ACT0                  (40 * 16)
+#define D_ACSX                  (40 * 16 + 4)
+#define D_ACSY                  (40 * 16 + 8)
+#define D_ACSZ                  (40 * 16 + 12)
+
+#define FLICK_MSG               (45 * 16 + 4)
+#define FLICK_COUNTER           (45 * 16 + 8)
+#define FLICK_LOWER             (45 * 16 + 12)
+#define FLICK_UPPER             (46 * 16 + 12)
+
+#define D_AUTH_OUT               (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)
+
+static const struct tKeyLabel dmpTConfig[] = {
+	{KEY_CFG_27,                    CFG_27},
+	{KEY_CFG_20,                    CFG_20},
+	{KEY_CFG_23,                    CFG_23},
+	{KEY_CFG_FIFO_ON_EVENT,         CFG_FIFO_ON_EVENT},
+	{KEY_CFG_ORIENT_IRQ_1,          CFG_ORIENT_IRQ_1},
+	{KEY_CGNOTICE_INTR,             CGNOTICE_INTR},
+	{KEY_X_GRT_Y_TMP,               X_GRT_Y_TMP},
+	{KEY_CFG_DR_INT,                CFG_DR_INT},
+	{KEY_CFG_AUTH,                  CFG_AUTH},
+	{KEY_FCFG_1,                    FCFG_1},
+	{KEY_SKIP_X_GRT_Y_TMP,          SKIP_X_GRT_Y_TMP},
+	{KEY_SKIP_END_COMPARE,          SKIP_END_COMPARE},
+	{KEY_FCFG_3,                    FCFG_3},
+	{KEY_FCFG_2,                    FCFG_2},
+	{KEY_END_COMPARE_Y_X_TMP2,      END_COMPARE_Y_X_TMP2},
+	{KEY_CFG_DISPLAY_ORIENT_INT,    CFG_DISPLAY_ORIENT_INT},
+	{KEY_FCFG_7,                    FCFG_7},
+	{KEY_FCFG_6,                    FCFG_6},
+	{KEY_NO_ORIENT_INTERRUPT,       NO_ORIENT_INTERRUPT},
+	{KEY_CFG_8,                     CFG_8},
+	{KEY_CFG_15,                    CFG_15},
+	{KEY_CFG_16,                    CFG_16},
+	{KEY_END_COMPARE_Y_X_TMP,       END_COMPARE_Y_X_TMP},
+	{KEY_CFG_6,                     CFG_6},
+	{KEY_END_ORIENT_1,              END_ORIENT_1},
+	{KEY_END_COMPARE_Y_X,           END_COMPARE_Y_X},
+	{KEY_CFG_LP_QUAT,               CFG_LP_QUAT},
+	{KEY_END_ORIENT,                END_ORIENT},
+	{KEY_CFG_FLICK_IN,              CFG_FLICK_IN},
+	{KEY_CFG_7,                     CFG_7},
+	{KEY_CFG_MOTION_BIAS,           CFG_MOTION_BIAS},
+	{KEY_X_GRT_Y,                   X_GRT_Y},
+	{KEY_TEMPLABEL,                 TEMPLABEL},
+	{KEY_NOT_TIME_MINUS_1,          NOT_TIME_MINUS_1},
+	{KEY_END_COMPARE_Y_X_TMP3,      END_COMPARE_Y_X_TMP3},
+	{KEY_X_GRT_Y_TMP2,              X_GRT_Y_TMP2},
+	{KEY_D_0_22,                D_0_22},
+	{KEY_D_0_96,                D_0_96},
+	{KEY_D_0_104,               D_0_104},
+	{KEY_D_0_108,               D_0_108},
+	{KEY_D_1_36,               D_1_36},
+	{KEY_D_1_40,               D_1_40},
+	{KEY_D_1_44,               D_1_44},
+	{KEY_D_1_72,               D_1_72},
+	{KEY_D_1_74,               D_1_74},
+	{KEY_D_1_79,               D_1_79},
+	{KEY_D_1_88,               D_1_88},
+	{KEY_D_1_90,               D_1_90},
+	{KEY_D_1_92,               D_1_92},
+	{KEY_D_1_160,               D_1_160},
+	{KEY_D_1_176,               D_1_176},
+	{KEY_D_1_218,               D_1_218},
+	{KEY_D_1_232,               D_1_232},
+	{KEY_D_1_250,               D_1_250},
+	{KEY_DMP_TAPW_MIN,          DMP_TAPW_MIN},
+	{KEY_DMP_TAP_THR_X,         DMP_TAP_THX},
+	{KEY_DMP_TAP_THR_Y,         DMP_TAP_THY},
+	{KEY_DMP_TAP_THR_Z,         DMP_TAP_THZ},
+	{KEY_DMP_SH_TH_Y,           DMP_SH_TH_Y},
+	{KEY_DMP_SH_TH_X,           DMP_SH_TH_X},
+	{KEY_DMP_SH_TH_Z,           DMP_SH_TH_Z},
+	{KEY_DMP_ORIENT,            DMP_ORIENT},
+	{KEY_D_AUTH_OUT,            D_AUTH_OUT},
+	{KEY_D_AUTH_IN,             D_AUTH_IN},
+	{KEY_D_AUTH_A,              D_AUTH_A},
+	{KEY_D_AUTH_B,              D_AUTH_B},
+	{KEY_CPASS_BIAS_X,          CPASS_BIAS_X},
+	{KEY_CPASS_BIAS_Y,          CPASS_BIAS_Y},
+	{KEY_CPASS_BIAS_Z,          CPASS_BIAS_Z},
+	{KEY_CPASS_MTX_00,          CPASS_MTX_00},
+	{KEY_CPASS_MTX_01,          CPASS_MTX_01},
+	{KEY_CPASS_MTX_02,          CPASS_MTX_02},
+	{KEY_CPASS_MTX_10,          CPASS_MTX_10},
+	{KEY_CPASS_MTX_11,          CPASS_MTX_11},
+	{KEY_CPASS_MTX_12,          CPASS_MTX_12},
+	{KEY_CPASS_MTX_20,          CPASS_MTX_20},
+	{KEY_CPASS_MTX_21,          CPASS_MTX_21},
+	{KEY_CPASS_MTX_22,          CPASS_MTX_22},
+	{KEY_D_ACT0,                D_ACT0},
+	{KEY_D_ACSX,                D_ACSX},
+	{KEY_D_ACSY,                D_ACSY},
+	{KEY_D_ACSZ,                D_ACSZ},
+	{KEY_FLICK_MSG,             FLICK_MSG},
+	{KEY_FLICK_COUNTER,         FLICK_COUNTER},
+	{KEY_FLICK_LOWER,           FLICK_LOWER},
+	{KEY_FLICK_UPPER,           FLICK_UPPER},
+	{KEY_D_PEDSTD_BP_B, D_PEDSTD_BP_B},
+	{KEY_D_PEDSTD_HP_A, D_PEDSTD_HP_A},
+	{KEY_D_PEDSTD_HP_B, D_PEDSTD_HP_B},
+	{KEY_D_PEDSTD_BP_A4, D_PEDSTD_BP_A4},
+	{KEY_D_PEDSTD_BP_A3, D_PEDSTD_BP_A3},
+	{KEY_D_PEDSTD_BP_A2, D_PEDSTD_BP_A2},
+	{KEY_D_PEDSTD_BP_A1, D_PEDSTD_BP_A1},
+	{KEY_D_PEDSTD_INT_THRSH, D_PEDSTD_INT_THRSH},
+	{KEY_D_PEDSTD_CLIP, D_PEDSTD_CLIP},
+	{KEY_D_PEDSTD_SB, D_PEDSTD_SB},
+	{KEY_D_PEDSTD_SB_TIME, D_PEDSTD_SB_TIME},
+	{KEY_D_PEDSTD_PEAKTHRSH, D_PEDSTD_PEAKTHRSH},
+	{KEY_D_PEDSTD_TIML,      D_PEDSTD_TIML},
+	{KEY_D_PEDSTD_TIMH,      D_PEDSTD_TIMH},
+	{KEY_D_PEDSTD_PEAK,      D_PEDSTD_PEAK},
+	{KEY_D_PEDSTD_STEPCTR,   D_PEDSTD_STEPCTR},
+	{KEY_D_PEDSTD_TIMECTR,  D_PEDSTD_TIMECTR},
+	{KEY_D_PEDSTD_DECI,  D_PEDSTD_DECI},
+	{KEY_D_HOST_NO_MOT,  D_HOST_NO_MOT}
+};
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key)
+{
+	static int isSorted;
+	if (!isSorted) {
+		int kk;
+		for (kk = 0; kk < NUM_KEYS; ++kk) {
+			keys[kk].addr = 0xffff;
+			keys[kk].key = kk;
+		}
+		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+			keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr;
+		isSorted = 1;
+	}
+	if (key >= NUM_KEYS)
+		return 0xffff;
+	return keys[key].addr;
+}
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/mpu6050/dmpKey.h b/drivers/staging/iio/imu/mpu6050/dmpKey.h
new file mode 100644
index 0000000..e8e1951
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpKey.h
@@ -0,0 +1,494 @@
+/*
+ * 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 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)
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER        (KEY_CFG_LP_QUAT + 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_GYRO_ENABLE               (KEY_D_GYRO_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_CR_TIME_G                 (KEY_D_QUAT_ENABLE + 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_BIAS_X            (KEY_D_CS_TQ3 + 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)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT              (KEY_X_GRT_Y_TMP2 + 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)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT           (KEY_D_PEDSTD_DECI + 1)
+
+/* EIS keys */
+#define KEY_P_EIS_FIFO_FOOTER       (KEY_D_HOST_NO_MOT + 1)
+#define KEY_P_EIS_FIFO_YSHIFT       (KEY_P_EIS_FIFO_FOOTER + 1)
+#define KEY_P_EIS_DATA_RATE         (KEY_P_EIS_FIFO_YSHIFT + 1)
+#define KEY_P_EIS_FIFO_XSHIFT       (KEY_P_EIS_DATA_RATE + 1)
+#define KEY_P_EIS_FIFO_SYNC         (KEY_P_EIS_FIFO_XSHIFT + 1)
+#define KEY_P_EIS_FIFO_ZSHIFT       (KEY_P_EIS_FIFO_SYNC + 1)
+#define KEY_P_EIS_FIFO_READY        (KEY_P_EIS_FIFO_ZSHIFT + 1)
+#define KEY_DMP_FOOTER              (KEY_P_EIS_FIFO_READY + 1)
+#define KEY_DMP_INTX_HC             (KEY_DMP_FOOTER + 1)
+#define KEY_DMP_INTX_PH             (KEY_DMP_INTX_HC + 1)
+#define KEY_DMP_INTX_SH             (KEY_DMP_INTX_PH + 1)
+#define KEY_DMP_AINV_SH             (KEY_DMP_INTX_SH + 1)
+#define KEY_DMP_A_INV_XH            (KEY_DMP_AINV_SH + 1)
+#define KEY_DMP_AINV_PH             (KEY_DMP_A_INV_XH + 1)
+#define KEY_DMP_CTHX_H              (KEY_DMP_AINV_PH + 1)
+#define KEY_DMP_CTHY_H              (KEY_DMP_CTHX_H + 1)
+#define KEY_DMP_CTHZ_H              (KEY_DMP_CTHY_H + 1)
+#define KEY_DMP_NCTHX_H             (KEY_DMP_CTHZ_H + 1)
+#define KEY_DMP_NCTHY_H             (KEY_DMP_NCTHX_H + 1)
+#define KEY_DMP_NCTHZ_H             (KEY_DMP_NCTHY_H + 1)
+#define KEY_DMP_CTSQ_XH             (KEY_DMP_NCTHZ_H + 1)
+#define KEY_DMP_CTSQ_YH             (KEY_DMP_CTSQ_XH + 1)
+#define KEY_DMP_CTSQ_ZH             (KEY_DMP_CTSQ_YH + 1)
+#define KEY_DMP_INTX_H              (KEY_DMP_CTSQ_ZH + 1)
+#define KEY_DMP_INTY_H              (KEY_DMP_INTX_H + 1)
+#define KEY_DMP_INTZ_H              (KEY_DMP_INTY_H + 1)
+#define KEY_DMP_HPX_H               (KEY_DMP_INTZ_H + 1)
+#define KEY_DMP_HPY_H               (KEY_DMP_HPX_H + 1)
+#define KEY_DMP_HPZ_H               (KEY_DMP_HPY_H + 1)
+
+/* Stream keys */
+#define KEY_STREAM_P_GYRO_Z         (KEY_DMP_HPZ_H + 1)
+#define KEY_STREAM_P_GYRO_Y         (KEY_STREAM_P_GYRO_Z + 1)
+#define KEY_STREAM_P_GYRO_X         (KEY_STREAM_P_GYRO_Y + 1)
+#define KEY_STREAM_P_TEMP           (KEY_STREAM_P_GYRO_X + 1)
+#define KEY_STREAM_P_AUX_Y          (KEY_STREAM_P_TEMP + 1)
+#define KEY_STREAM_P_AUX_X          (KEY_STREAM_P_AUX_Y + 1)
+#define KEY_STREAM_P_AUX_Z          (KEY_STREAM_P_AUX_X + 1)
+#define KEY_STREAM_P_ACCEL_Y        (KEY_STREAM_P_AUX_Z + 1)
+#define KEY_STREAM_P_ACCEL_X        (KEY_STREAM_P_ACCEL_Y + 1)
+#define KEY_STREAM_P_FOOTER         (KEY_STREAM_P_ACCEL_X + 1)
+#define KEY_STREAM_P_ACCEL_Z        (KEY_STREAM_P_FOOTER + 1)
+
+#define NUM_KEYS                    (KEY_STREAM_P_ACCEL_Z + 1)
+
+struct tKeyLabel  {
+	unsigned short key;
+	unsigned short addr;
+};
+
+#define DINA0A 0x0a
+#define DINA22 0x22
+#define DINA42 0x42
+#define DINA5A 0x5a
+
+#define DINA06 0x06
+#define DINA0E 0x0e
+#define DINA16 0x16
+#define DINA1E 0x1e
+#define DINA26 0x26
+#define DINA2E 0x2e
+#define DINA36 0x36
+#define DINA3E 0x3e
+#define DINA46 0x46
+#define DINA4E 0x4e
+#define DINA56 0x56
+#define DINA5E 0x5e
+#define DINA66 0x66
+#define DINA6E 0x6e
+#define DINA76 0x76
+#define DINA7E 0x7e
+
+#define DINA00 0x00
+#define DINA08 0x08
+#define DINA10 0x10
+#define DINA18 0x18
+#define DINA20 0x20
+#define DINA28 0x28
+#define DINA30 0x30
+#define DINA38 0x38
+#define DINA40 0x40
+#define DINA48 0x48
+#define DINA50 0x50
+#define DINA58 0x58
+#define DINA60 0x60
+#define DINA68 0x68
+#define DINA70 0x70
+#define DINA78 0x78
+
+#define DINA04 0x04
+#define DINA0C 0x0c
+#define DINA14 0x14
+#define DINA1C 0x1C
+#define DINA24 0x24
+#define DINA2C 0x2c
+#define DINA34 0x34
+#define DINA3C 0x3c
+#define DINA44 0x44
+#define DINA4C 0x4c
+#define DINA54 0x54
+#define DINA5C 0x5c
+#define DINA64 0x64
+#define DINA6C 0x6c
+#define DINA74 0x74
+#define DINA7C 0x7c
+
+#define DINA01 0x01
+#define DINA09 0x09
+#define DINA11 0x11
+#define DINA19 0x19
+#define DINA21 0x21
+#define DINA29 0x29
+#define DINA31 0x31
+#define DINA39 0x39
+#define DINA41 0x41
+#define DINA49 0x49
+#define DINA51 0x51
+#define DINA59 0x59
+#define DINA61 0x61
+#define DINA69 0x69
+#define DINA71 0x71
+#define DINA79 0x79
+
+#define DINA25 0x25
+#define DINA2D 0x2d
+#define DINA35 0x35
+#define DINA3D 0x3d
+#define DINA4D 0x4d
+#define DINA55 0x55
+#define DINA5D 0x5D
+#define DINA6D 0x6d
+#define DINA75 0x75
+#define DINA7D 0x7d
+
+#define DINADC 0xdc
+#define DINAF2 0xf2
+#define DINAAB 0xab
+#define DINAAA 0xaa
+#define DINAF1 0xf1
+#define DINADF 0xdf
+#define DINADA 0xda
+#define DINAB1 0xb1
+#define DINAB9 0xb9
+#define DINAF3 0xf3
+#define DINA8B 0x8b
+#define DINAA3 0xa3
+#define DINA91 0x91
+#define DINAB6 0xb6
+#define DINAB4 0xb4
+
+
+#define DINC00 0x00
+#define DINC01 0x01
+#define DINC02 0x02
+#define DINC03 0x03
+#define DINC08 0x08
+#define DINC09 0x09
+#define DINC0A 0x0a
+#define DINC0B 0x0b
+#define DINC10 0x10
+#define DINC11 0x11
+#define DINC12 0x12
+#define DINC13 0x13
+#define DINC18 0x18
+#define DINC19 0x19
+#define DINC1A 0x1a
+#define DINC1B 0x1b
+
+#define DINC20 0x20
+#define DINC21 0x21
+#define DINC22 0x22
+#define DINC23 0x23
+#define DINC28 0x28
+#define DINC29 0x29
+#define DINC2A 0x2a
+#define DINC2B 0x2b
+#define DINC30 0x30
+#define DINC31 0x31
+#define DINC32 0x32
+#define DINC33 0x33
+#define DINC38 0x38
+#define DINC39 0x39
+#define DINC3A 0x3a
+#define DINC3B 0x3b
+
+#define DINC40 0x40
+#define DINC41 0x41
+#define DINC42 0x42
+#define DINC43 0x43
+#define DINC48 0x48
+#define DINC49 0x49
+#define DINC4A 0x4a
+#define DINC4B 0x4b
+#define DINC50 0x50
+#define DINC51 0x51
+#define DINC52 0x52
+#define DINC53 0x53
+#define DINC58 0x58
+#define DINC59 0x59
+#define DINC5A 0x5a
+#define DINC5B 0x5b
+
+#define DINC60 0x60
+#define DINC61 0x61
+#define DINC62 0x62
+#define DINC63 0x63
+#define DINC68 0x68
+#define DINC69 0x69
+#define DINC6A 0x6a
+#define DINC6B 0x6b
+#define DINC70 0x70
+#define DINC71 0x71
+#define DINC72 0x72
+#define DINC73 0x73
+#define DINC78 0x78
+#define DINC79 0x79
+#define DINC7A 0x7a
+#define DINC7B 0x7b
+#define DIND40 0x40
+#define DINA80 0x80
+#define DINA90 0x90
+#define DINAA0 0xa0
+#define DINAC9 0xc9
+#define DINACB 0xcb
+#define DINACD 0xcd
+#define DINACF 0xcf
+#define DINAC8 0xc8
+#define DINACA 0xca
+#define DINACC 0xcc
+#define DINACE 0xce
+#define DINAD8 0xd8
+#define DINADD 0xdd
+#define DINAF8 0xf0
+#define DINAFE 0xfe
+
+#define DINBF8 0xf8
+#define DINAC0 0xb0
+#define DINAC1 0xb1
+#define DINAC2 0xb4
+#define DINAC3 0xb5
+#define DINAC4 0xb8
+#define DINAC5 0xb9
+#define DINBC0 0xc0
+#define DINBC2 0xc2
+#define DINBC4 0xc4
+#define DINBC6 0xc6
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/dmpmap.h b/drivers/staging/iio/imu/mpu6050/dmpmap.h
new file mode 100644
index 0000000..420e19d
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpmap.h
@@ -0,0 +1,266 @@
+/*
+* 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 DMPMAP_H
+#define DMPMAP_H
+
+#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
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
new file mode 100644
index 0000000..0958d4a
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
@@ -0,0 +1,727 @@
+/*
+* 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 _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include "./mpu.h"
+#include "../../iio.h"
+#include "../../buffer.h"
+#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 {
+	unsigned char sample_rate_div;
+	unsigned char lpf;
+	unsigned char bank_sel;
+	unsigned char user_ctrl;
+	unsigned char fifo_en;
+	unsigned char gyro_config;
+	unsigned char accl_config;
+	unsigned char fifo_count_h;
+	unsigned char fifo_r_w;
+	unsigned char raw_gyro;
+	unsigned char raw_accl;
+	unsigned char temperature;
+	unsigned char int_enable;
+	unsigned char int_status;
+	unsigned char pwr_mgmt_1;
+	unsigned char pwr_mgmt_2;
+	unsigned char mem_start_addr;
+	unsigned char mem_r_w;
+	unsigned char prgm_strt_addrh;
+};
+/*device enum */
+enum inv_devices {
+	INV_MPU6050,
+	INV_MPU9150,
+	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;
+	unsigned int accl_sens[3];
+};
+
+/**
+ *  struct inv_hw_s - Other important hardware information.
+ *  @num_reg:	Number of registers on device.
+ *  @name:      name of the chip
+ */
+struct inv_hw_s {
+	unsigned char num_reg;
+	unsigned char *name;
+};
+
+/**
+ *  struct inv_chip_config_s - Cached chip configuration data.
+ *  @fsr:		Full scale range.
+ *  @lpf:		Digital low pass filter frequency.
+ *  @clk_src:		Clock source.
+ *  @accl_fs:		accel full scale range.
+ *  @self_test_run_once flag for self test run ever.
+ *  @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.
+ *  @orientation_on:	dmp is on/off.
+ *  @firmware_loaded:	flag indicate firmware loaded or not.
+ *  @lpa_mod:		low power mode.
+ *  @tap_on:		tap on/off.
+ *  @flick_int_on:	flick interrupt on/off.
+ *  @quaternion_on:	send quaternion data on/off.
+ *  @display_orient_on:	display orientation on/off.
+ *  @lpa_freq:		low power frequency
+ *  @prog_start_addr:	firmware program start address.
+ *  @dmp_output_rate:   dmp output rate.
+ *  @fifo_rate:		FIFO update rate.
+ */
+struct inv_chip_config_s {
+	unsigned int fsr:2;
+	unsigned int lpf:3;
+	unsigned int clk_src:1;
+	unsigned int accl_fs:2;
+	unsigned int self_test_run_once:1;
+	unsigned int has_compass:1;
+	unsigned int enable:1;
+	unsigned int accl_enable:1;
+	unsigned int accl_fifo_enable:1;
+	unsigned int gyro_enable:1;
+	unsigned int gyro_fifo_enable:1;
+	unsigned int compass_enable:1;
+	unsigned int compass_fifo_enable:1;
+	unsigned int is_asleep:1;
+	unsigned int dmp_on:1;
+	unsigned int dmp_int_on:1;
+	unsigned int dmp_event_int_on:1;
+	unsigned int orientation_on:1;
+	unsigned int firmware_loaded:1;
+	unsigned int lpa_mode:1;
+	unsigned int tap_on:1;
+	unsigned int flick_int_on:1;
+	unsigned int quaternion_on:1;
+	unsigned int display_orient_on:1;
+	unsigned short lpa_freq;
+	unsigned short  prog_start_addr;
+	unsigned short fifo_rate;
+	unsigned short 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 {
+	unsigned char product_id;
+	unsigned char product_revision;
+	unsigned char silicon_revision;
+	unsigned char software_revision;
+	unsigned char multi;
+	unsigned char compass_sens[3];
+	unsigned long gyro_sens_trim;
+	unsigned long accl_sens_trim;
+};
+
+/**
+ *  struct inv_flick_s structure to store flick data.
+ *  @lower:	lower bound of flick.
+ *  @upper:     upper bound of flick.
+ *  @counter:	counter of flick.
+ *  @msg_on;    message to carry flick
+ *  @axis:      axis of flick
+ */
+struct inv_flick_s {
+	int lower;
+	int upper;
+	int counter;
+	int msg_on;
+	int axis;
+};
+
+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 {
+	unsigned short min_count;
+	unsigned short thresh;
+	unsigned short time;
+};
+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.
+ *  @flick:		flick data structure
+ *  @tap:               tap data structure
+ *  @reg:		Map of important registers.
+ *  @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.
+ *  int (*set_power_state)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*switch_gyro_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*switch_accl_engine)(struct inv_mpu_iio_s *, int on): function ptr
+ *  int (*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.
+ *  @compass_scale:     compass scale.
+ *  @i2c_addr:          i2c address.
+ *  @compass_divider:   slow down compass rate.
+ *  @compass_counter:   slow down compass rate.
+ *  @sample_divider:    sample divider for dmp.
+ *  @fifo_divider:      fifo divider for dmp.
+ *  @orient_data:       orientation data.
+ *  @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.
+ */
+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_flick_s flick;
+	struct inv_tap_s   tap;
+	struct inv_reg_map_s reg;
+	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;
+	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 (*init_config)(struct iio_dev *indio_dev);
+	void (*setup_reg)(struct inv_reg_map_s *reg);
+	DECLARE_KFIFO(timestamps, long long, 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];
+	unsigned char compass_scale;
+	unsigned char i2c_addr;
+	unsigned char compass_divider;
+	unsigned char compass_counter;
+	unsigned char sample_divider;
+	unsigned char fifo_divider;
+	unsigned char orient_data;
+	unsigned char display_orient_data;
+	unsigned char tap_data;
+	enum inv_channel_num num_channels;
+	void *sl_handle;
+	unsigned int irq_dur_ns;
+	long long last_isr_time;
+#ifdef CONFIG_INV_TESTING
+	unsigned long i2c_readcount;
+	unsigned long i2c_writecount;
+#endif
+};
+
+/* produces an unique identifier for each device based on the
+   combination of product version and product revision */
+struct prod_rev_map_t {
+	unsigned short mpl_product_key;
+	unsigned char silicon_rev;
+	unsigned short gyro_trim;
+	unsigned short accel_trim;
+};
+
+/**
+ *  struct inv_mpu_slave - MPU slave structure.
+ *  @suspend:		suspend operation.
+ *  @resume:		resume operation.
+ *  @setup:		setup chip. initialization.
+ *  @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)(unsigned char *in, short *out);
+	int (*get_mode)(struct inv_mpu_iio_s *);
+	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_PW_DN      0x00
+#define DATA_AKM_MODE_PW_SM      0x01
+#define DATA_AKM_MODE_PW_ST      0x08
+#define DATA_AKM_MODE_PW_FR      0x0F
+#define DATA_AKM_SELF_TEST       0x40
+#define DATA_AKM_DRDY            0x01
+#define DATA_AKM8963_BIT         0x10
+#define DATA_AKM_STAT_MASK       0x0C
+
+#define DATA_AKM8975_SCALE       9830
+#define DATA_AKM8972_SCALE       19661
+#define DATA_AKM8963_SCALE0      19661
+#define DATA_AKM8963_SCALE1      4915
+#define AKM8963_SCALE_SHIFT      4
+#define NUM_BYTES_COMPASS_SLAVE  8
+
+/*register and associated bit definition*/
+#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_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_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 REG_DMP_INT_STATUS      0x39
+#define REG_INT_STATUS          0x3A
+#define REG_RAW_ACCEL           0x3B
+#define REG_TEMPERATURE         0x41
+#define REG_RAW_GYRO            0x43
+#define REG_EXT_SENS_DATA_00    0x49
+#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 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 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
+
+/* 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 QUATERNION_BYTES         16
+#define BYTES_PER_SENSOR         6
+#define FIFO_COUNT_BYTE          2
+#define FIFO_THRESHOLD           500
+#define POWER_UP_TIME            100
+#define SENSOR_UP_TIME           30
+#define MPU_MEM_BANK_SIZE        256
+#define MPU6050_TEMP_OFFSET	 2462307L
+#define MPU6050_TEMP_SCALE       2977653L
+#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 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
+
+/* init parameters */
+#define INIT_FIFO_RATE           50
+#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 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 MAX_FIFO_RATE                         1000
+#define MIN_FIFO_RATE                         4
+#define ONE_K_HZ                              1000
+
+/* flick related defines */
+#define DATA_INT                              2097
+#define DATA_MSG_ON                           262144
+#define FLICK_INT_STATUS                      8
+
+/*tap related defines */
+#define INV_TAP                               0x08
+#define INV_NUM_TAP_AXES                      3
+
+#define INV_TAP_AXIS_X_POS                    0x20
+#define INV_TAP_AXIS_X_NEG                    0x10
+#define INV_TAP_AXIS_Y_POS                    0x08
+#define INV_TAP_AXIS_Y_NEG                    0x04
+#define INV_TAP_AXIS_Z_POS                    0x02
+#define INV_TAP_AXIS_Z_NEG                    0x01
+#define INV_TAP_ALL_DIRECTIONS                0x3f
+
+#define INV_TAP_AXIS_X                        0x1
+#define INV_TAP_AXIS_Y                        0x2
+#define INV_TAP_AXIS_Z                        0x4
+
+#define INV_TAP_AXIS_ALL                      \
+		(INV_TAP_AXIS_X            |   \
+		INV_TAP_AXIS_Y             |   \
+		INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP    0x01
+#define INT_SRC_ORIENT 0x02
+#define INT_SRC_DISPLAY_ORIENT  0x08
+#define INT_SRC_SHAKE           0x10
+
+
+/*orientation related */
+#define INV_X_UP                          0x01
+#define INV_X_DOWN                        0x02
+#define INV_Y_UP                          0x04
+#define INV_Y_DOWN                        0x08
+#define INV_Z_UP                          0x10
+#define INV_Z_DOWN                        0x20
+#define INV_ORIENTATION_ALL               0x3F
+
+#define INV_ORIENTATION_FLIP              0x40
+#define INV_X_AXIS_INDEX                  0x00
+#define INV_Y_AXIS_INDEX                  0x01
+#define INV_Z_AXIS_INDEX                  0x02
+
+#define INV_ELEMENT_1                     0x0001
+#define INV_ELEMENT_2                     0x0002
+#define INV_ELEMENT_3                     0x0004
+#define INV_ELEMENT_4                     0x0008
+#define INV_ELEMENT_5                     0x0010
+#define INV_ELEMENT_6                     0x0020
+#define INV_ELEMENT_7                     0x0040
+#define INV_ELEMENT_8                     0x0080
+#define INV_ALL                           0xFFFF
+#define INV_ELEMENT_MASK                  0x00FF
+#define INV_GYRO_ACC_MASK                 0x007E
+/* 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_GYRO_X,
+	INV_MPU_SCAN_GYRO_Y,
+	INV_MPU_SCAN_GYRO_Z,
+	INV_MPU_SCAN_ACCL_X,
+	INV_MPU_SCAN_ACCL_Y,
+	INV_MPU_SCAN_ACCL_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_FLICK_LOWER,
+	ATTR_DMP_FLICK_UPPER,
+	ATTR_DMP_FLICK_COUNTER,
+	ATTR_DMP_FLICK_INT_ON,
+	ATTR_DMP_FLICK_AXIS,
+	ATTR_DMP_FLICK_MSG_ON,
+	ATTR_DMP_PEDOMETER_STEPS,
+	ATTR_DMP_PEDOMETER_TIME,
+	ATTR_DMP_TAP_THRESHOLD,
+	ATTR_DMP_TAP_MIN_COUNT,
+	ATTR_DMP_TAP_ON,
+	ATTR_DMP_TAP_TIME,
+	ATTR_DMP_ON,
+	ATTR_DMP_INT_ON,
+	ATTR_DMP_EVENT_INT_ON,
+	ATTR_DMP_OUTPUT_RATE,
+	ATTR_DMP_ORIENTATION_ON,
+	ATTR_DMP_QUATERNION_ON,
+	ATTR_DMP_DISPLAY_ORIENTATION_ON,
+	ATTR_LPA_MODE,
+	ATTR_LPA_FREQ,
+	ATTR_CLK_SRC,
+	ATTR_SELF_TEST,
+	ATTR_KEY,
+	ATTR_GYRO_MATRIX,
+	ATTR_ACCL_MATRIX,
+	ATTR_COMPASS_MATRIX,
+	ATTR_GYRO_ENABLE,
+	ATTR_ACCL_ENABLE,
+	ATTR_COMPASS_ENABLE,
+	ATTR_POWER_STATE,
+	ATTR_FIRMWARE_LOADED,
+#ifdef CONFIG_INV_TESTING
+	ATTR_I2C_COUNTERS,
+	ATTR_REG_WRITE,
+#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_get_silicon_rev_mpu6050(struct inv_mpu_iio_s *st);
+int set_inv_enable(struct iio_dev *indio_dev, bool enable);
+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_enable_orientation_dmp(struct inv_mpu_iio_s *st, bool on);
+int inv_set_fifo_rate(struct inv_mpu_iio_s *st, unsigned short fifo_rate);
+unsigned short inv_dmp_get_address(unsigned short key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_iio_s *st,
+				unsigned int axis, unsigned short threshold);
+int inv_set_min_taps_dmp(struct inv_mpu_iio_s *st, unsigned short min_taps);
+int  inv_set_tap_time_dmp(struct inv_mpu_iio_s *st, unsigned short 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, unsigned short i2c_addr,
+	unsigned char reg, unsigned short length, unsigned char *data);
+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+	unsigned short i2c_addr, unsigned char reg, unsigned char data);
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+		int *gyro_result, int *accl_result);
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+			    unsigned char mpu_addr,
+			    unsigned short mem_addr,
+			    unsigned int len, unsigned char const *data);
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+			   unsigned char mpu_addr,
+			   unsigned short mem_addr,
+			   unsigned int len, unsigned char *data);
+int inv_hw_self_test(struct inv_mpu_iio_s *st);
+
+#define mem_w(a, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, inv_dmp_get_address(key), b, c)
+#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/mpu6050/inv_mpu_ring.c b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
new file mode 100644
index 0000000..528da5f
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
@@ -0,0 +1,563 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include "inv_mpu_iio.h"
+#include "../../iio.h"
+#include "../../kfifo_buf.h"
+#include "../../trigger_consumer.h"
+#include "../../sysfs.h"
+
+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;
+
+	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;
+}
+
+/**
+ *  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;
+	unsigned char val;
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
+	reg = &st->reg;
+
+	inv_scan_query(indio_dev);
+	/* 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;
+
+	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 = iio_get_time_ns();
+		if (st->chip_config.dmp_int_on) {
+			result = inv_i2c_single_write(st, reg->int_enable,
+							BIT_DMP_INT_EN);
+			if (result)
+				return result;
+		}
+		val = (BIT_DMP_EN | BIT_FIFO_EN);
+		if (st->chip_config.compass_enable)
+			val |= BIT_I2C_MST_EN;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+	} else {
+		/* reset FIFO and possibly reset I2C*/
+		val = BIT_FIFO_RST;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+		st->last_isr_time = iio_get_time_ns();
+		/* enable interrupt */
+		if (st->chip_config.accl_fifo_enable ||
+		    st->chip_config.gyro_fifo_enable ||
+		    st->chip_config.compass_enable) {
+			result = inv_i2c_single_write(st, reg->int_enable,
+						BIT_DATA_RDY_EN);
+			if (result)
+				return result;
+		}
+		/* enable FIFO reading and I2C master interface*/
+		val = BIT_FIFO_EN;
+		if (st->chip_config.compass_enable)
+			val |= BIT_I2C_MST_EN;
+		result = inv_i2c_single_write(st, reg->user_ctrl, val);
+		if (result)
+			goto reset_fifo_fail;
+		/* enable sensor output to FIFO */
+		val = 0;
+		if (st->chip_config.gyro_fifo_enable)
+			val |= BITS_GYRO_OUT;
+		if (st->chip_config.accl_fifo_enable)
+			val |= BIT_ACCEL_OUT;
+		result = inv_i2c_single_write(st, reg->fifo_en, val);
+		if (result)
+			goto reset_fifo_fail;
+	}
+	return 0;
+reset_fifo_fail:
+	if (st->chip_config.dmp_on)
+		val = BIT_DMP_INT_EN;
+	else
+		val = BIT_DATA_RDY_EN;
+	inv_i2c_single_write(st, reg->int_enable, val);
+	pr_err("reset fifo failed\n");
+	return result;
+}
+
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:	Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+	return reset_fifo_itg(indio_dev);
+}
+
+/**
+ *  set_inv_enable() - Reset FIFO related registers.
+ *			This also powers on the chip if needed.
+ *  @st:	Device driver instance.
+ *  @fifo_enable: enable/disable
+ */
+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;
+
+	if (st->chip_config.is_asleep)
+		return -EINVAL;
+	reg = &st->reg;
+	if (enable) {
+		result = inv_reset_fifo(indio_dev);
+		if (result)
+			return result;
+	} else {
+		result = inv_i2c_single_write(st, reg->fifo_en, 0);
+		if (result)
+			return result;
+		/* disable fifo reading */
+		result = inv_i2c_single_write(st, reg->int_enable, 0);
+		if (result)
+			return result;
+		result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+		if (result)
+			return result;
+	}
+	st->chip_config.enable = !!enable;
+
+	return 0;
+}
+
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:	Device driver instance.
+ */
+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_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;
+	long long timestamp;
+	int catch_up;
+	unsigned int time_since_last_irq;
+
+	st = (struct inv_mpu_iio_s *)dev_id;
+	timestamp = iio_get_time_ns();
+	time_since_last_irq = (unsigned int)(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 = ((unsigned int)(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, unsigned char *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 int put_scan_to_buf_q(struct iio_dev *indio_dev, unsigned char *d,
+				int *s, int scan_index, int d_ind) {
+	struct iio_buffer *ring = indio_dev->buffer;
+	int st;
+	int i;
+	for (i = 0; i < 4; 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 int inv_report_gyro_accl_compass(struct iio_dev *indio_dev,
+					unsigned char *data, s64 t)
+{
+	struct inv_mpu_iio_s *st = iio_priv(indio_dev);
+	struct iio_buffer *ring = indio_dev->buffer;
+	short g[THREE_AXIS], a[THREE_AXIS], c[THREE_AXIS];
+	int q[4];
+	int result, ind, d_ind;
+	s64 buf[8];
+	unsigned int word;
+	unsigned char d[8];
+	unsigned char *tmp;
+	int source;
+	struct inv_chip_config_s *conf;
+
+	conf = &st->chip_config;
+	ind = 0;
+	if (conf->quaternion_on & conf->dmp_on) {
+		q[0] = be32_to_cpup((__be32 *)(&data[ind]));
+		q[1] = be32_to_cpup((__be32 *)(&data[ind + 4]));
+		q[2] = be32_to_cpup((__be32 *)(&data[ind + 8]));
+		q[3] = be32_to_cpup((__be32 *)(&data[ind + 12]));
+		ind += QUATERNION_BYTES;
+	}
+	if (conf->accl_fifo_enable | conf->dmp_on) {
+		a[0] = be16_to_cpup((__be16 *)(&data[ind]));
+		a[1] = be16_to_cpup((__be16 *)(&data[ind + 2]));
+		a[2] = be16_to_cpup((__be16 *)(&data[ind + 4]));
+
+		a[0] *= st->chip_info.multi;
+		a[1] *= st->chip_info.multi;
+		a[2] *= st->chip_info.multi;
+		st->raw_accel[0] = a[0];
+		st->raw_accel[1] = a[1];
+		st->raw_accel[2] = a[2];
+		ind += BYTES_PER_SENSOR;
+	}
+	if (conf->gyro_fifo_enable | conf->dmp_on) {
+		g[0] = be16_to_cpup((__be16 *)(&data[ind]));
+		g[1] = be16_to_cpup((__be16 *)(&data[ind + 2]));
+		g[2] = be16_to_cpup((__be16 *)(&data[ind + 4]));
+
+		st->raw_gyro[0] = g[0];
+		st->raw_gyro[1] = g[1];
+		st->raw_gyro[2] = g[2];
+		ind += BYTES_PER_SENSOR;
+	}
+	if (conf->dmp_on) {
+		word = (unsigned int)(be32_to_cpup((unsigned int *)&data[ind]));
+		source = ((word >> 16) & 0xff);
+		if (source) {
+			st->tap_data = (DMP_MASK_TAP & (word & 0xff));
+			st->orient_data = ((word >> 8) & 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_ORIENT)
+			sysfs_notify(&indio_dev->dev.kobj, NULL,
+				     "event_orientation");
+		/* 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 (st->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)) {
+				unsigned char *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 (st->compass_divider != 0) {
+			st->compass_counter++;
+		}
+	}
+
+	tmp = (unsigned char *)buf;
+	d_ind = 0;
+	if (conf->quaternion_on & conf->dmp_on)
+		d_ind = put_scan_to_buf_q(indio_dev, tmp, q,
+				INV_MPU_SCAN_QUAT_R, d_ind);
+	if (conf->gyro_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, g,
+				INV_MPU_SCAN_GYRO_X, d_ind);
+	if (conf->accl_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, a,
+				INV_MPU_SCAN_ACCL_X, d_ind);
+	if (conf->compass_fifo_enable)
+		d_ind = put_scan_to_buf(indio_dev, tmp, c,
+				INV_MPU_SCAN_MAGN_X, d_ind);
+	if (ring->scan_timestamp)
+		buf[(d_ind + 7) / 8] = t;
+	ring->access->store_to(indio_dev->buffer, (u8 *)buf, t);
+
+	return 0;
+}
+
+/**
+ *  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;
+	unsigned char data[BYTES_FOR_DMP + QUATERNION_BYTES];
+	unsigned short fifo_count;
+	unsigned int copied;
+	s64 timestamp;
+	struct inv_reg_map_s *reg;
+	s64 buf[8];
+	unsigned char *tmp;
+	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))
+		goto end_session;
+	if (st->chip_config.dmp_on && st->chip_config.flick_int_on) {
+		/*dmp interrupt status */
+		inv_i2c_read(st, REG_DMP_INT_STATUS, 1, data);
+		if (data[0] & FLICK_INT_STATUS)
+			sysfs_notify(&indio_dev->dev.kobj, NULL, "event_flick");
+	}
+	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,
+					     iio_get_time_ns());
+		goto end_session;
+	}
+
+	if (st->chip_config.dmp_on)
+		if (st->chip_config.quaternion_on)
+			bytes_per_datum = BYTES_FOR_DMP + QUATERNION_BYTES;
+		else
+			bytes_per_datum = BYTES_FOR_DMP;
+	else
+		bytes_per_datum = (st->chip_config.accl_fifo_enable +
+		st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR;
+	fifo_count = 0;
+	if (bytes_per_datum != 0) {
+		result = inv_i2c_read(st, reg->fifo_count_h,
+				FIFO_COUNT_BYTE, data);
+		if (result)
+			goto end_session;
+		fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+		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 = (char *)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)
+		inv_report_gyro_accl_compass(indio_dev, data, timestamp);
+end_session:
+	return IRQ_HANDLED;
+flush_fifo:
+	/* Flush HW and SW FIFOs. */
+	inv_reset_fifo(indio_dev);
+	inv_clear_kfifo(st);
+	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);
+};
+
+int inv_postenable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, true);
+
+}
+
+int inv_predisable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+	.preenable = &iio_sw_buffer_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(indio_dev);
+	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*/
+	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;
+
+	return 0;
+error_iio_sw_rb_free:
+	iio_kfifo_free(indio_dev->buffer);
+	return ret;
+}
+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu6050/mpu.h b/drivers/staging/iio/imu/mpu6050/mpu.h
new file mode 100644
index 0000000..5105fb2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/mpu.h
@@ -0,0 +1,107 @@
+/*
+* 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    mpu.h
+ *      @brief   mpu definition
+ */
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+	SECONDARY_SLAVE_TYPE_NONE,
+	SECONDARY_SLAVE_TYPE_ACCEL,
+	SECONDARY_SLAVE_TYPE_COMPASS,
+	SECONDARY_SLAVE_TYPE_PRESSURE,
+
+	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,
+	ACCEL_ID_LIS3DH,
+	ACCEL_ID_KXSD9,
+	ACCEL_ID_KXTF9,
+	ACCEL_ID_BMA150,
+	ACCEL_ID_BMA222,
+	ACCEL_ID_BMA250,
+	ACCEL_ID_ADXL34X,
+	ACCEL_ID_MMA8450,
+	ACCEL_ID_MMA845X,
+	ACCEL_ID_MPU6050,
+
+	COMPASS_ID_AK8963,
+	COMPASS_ID_AK8975,
+	COMPASS_ID_AK8972,
+	COMPASS_ID_AMI30X,
+	COMPASS_ID_AMI306,
+	COMPASS_ID_YAS529,
+	COMPASS_ID_YAS530,
+	COMPASS_ID_HMC5883,
+	COMPASS_ID_LSM303DLH,
+	COMPASS_ID_LSM303DLM,
+	COMPASS_ID_MMC314X,
+	COMPASS_ID_HSCDTD002B,
+	COMPASS_ID_HSCDTD004A,
+
+	PRESSURE_ID_BMA085,
+};
+
+#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
+ *
+ * Contains platform specific information on how to configure the MPU3050 to
+ * work on this platform.  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 mpu_platform_data {
+	__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];
+	__u8 key[16];
+};
+
+#endif	/* __MPU_H_ */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit
  2012-06-28 18:30 [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit Ge Gao
@ 2012-07-06  9:07 ` Jonathan Cameron
  2012-07-06 17:40   ` Ge Gao
  0 siblings, 1 reply; 4+ messages in thread
From: Jonathan Cameron @ 2012-07-06  9:07 UTC (permalink / raw)
  To: Ge Gao; +Cc: linux-iio

Hi Ge,

Please don't get disheartened by this partial review.
Getting use to approaches to getting kernel drivers merged
can take a considerable time. It's not a trivial thing
to do even with a 'perfect' driver.  There is a lot of
nice / interesting stuff in your driver so I look forward
to seeing it move forward!  I doubt we'll get any more complex
hardware anytime soon!

Couple of major comments.

1) This first patch could benefit from breaking up futher. Right
now it's too big and cumbersome to review remotely quickly.
First separate off the docs into their own patch.
Then I'd pull out the DMP code into a follow up patch.
That way we can get the core stuff sorted relatively quickly
before it gets to complex.

Actually the best way to get this in would be to strip nearly
everything back so just have your basic data flow through
the fifo. No events or motion processing or anything unusual in
the first patch.  Then we can review that very quickly and get
something in place to make incremental extensions to.

All in all it is too big to get reviewed. Sorry, but patches
really need to be inline emails if you want to have a reasonable
chance of getting eyes on them. There are a number of people on the
mailing list who will take a look at new drivers when they
have a few minutes spare.  This first file has taken me over
2 hours to review and what I have done is far from thorough.

Anyhow some comments in line. Mostly the code looks pretty clean
and there are some good docs which always helps.

2) I may not have made it clear enough before, but you are
pretty much not going to succeeed in having your own versions
of drivers that already exist.  That sort of code replication gets
a lot of friction upstream.

What you need to do is to write a i2c master driver and then
attach your secondary devices to this.  Where you need additional
features in those drivers, propose them and if they are not too
invasive I doubt anyone will mind.  Although you only have a couple
of slave devices here, seeing your table of id's shows there will
be a lot more to come.

If there is an absolute reason why this approach won't work, you
need to convince people with a careful brief arguement 'before' this
goes anywhere.

Anyhow Ge good luck with this one. I might get a chance to take a look
at the other patches, but it'll be a while give their size. Sorry, but
the short easy to review ones tend to get attended to first!

Jonathan

 From 6d4093a59d77a68d4972f648ea9eba69e8598159 Mon Sep 17 00:00:00 2001
From: Ge Gao <ggao@invensense.com>
Date: Thu, 28 Jun 2012 10:58:12 -0700
Subject: [PATCH 1/4]     Invensense MPU6050/MPU9150 driver.

       --MPU6050/MPU9150 driver.
       --Secondary bus for AKM8975/AKM8963/AKM8972 support.
       --kernel fix for Kfifo poll support from Jonanthan.
       --Kfifo bug fix. Need to check available space before store.
       --add new IIO type for quaternion.

...
index 0000000..c4fa2c2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/Kconfig
@@ -0,0 +1,6 @@
+#
+# inv-mpu-6050 drivers for Invensense MPU devices and combos
+#
+
+config INV_MPU6050_IIO
+	tristate "Invensense MPU6050 devices"
Will need to drop the !INV_MPU bit from the patch (and put it back in 
your local tree).
+	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && !INV_MPU && 
!INV_MPU_IIO
+	default n
+	help
+	  This driver supports the Invensense MPU6050/MPU9150 devices. It also
+	  supports AKM8975/AKM8963/AKM8972 in the secondary bus.
Ideally you want to build this text up as stuff is added (guessing not 
all of this
is in the initial patch!)
+	  This driver can be built as a module. The module will be called
+	  inv-mpu6050-iio.
Not keen on the naming, inv-mpu6050 then if you need to change it for
your local tree do it in a patch there.

...

diff --git a/drivers/staging/iio/imu/mpu6050/README 
b/drivers/staging/iio/imu/mpu6050/README
new file mode 100644
index 0000000..806a956
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/README
@@ -0,0 +1,11 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Description
+-----------
+This document describes how to install the Invensense device driver into a
+Linux kernel. At the moment, this driver supports the
+MPU6050/MPU9150. The slave address of these four chips are
+0x68 or 0x69. However, the actual slave address depends on the board
+configuration. The driver does not assume anything about it.
+
I would have prefered the documentation in a separate patch.
Also, lots of this needs 'adjusting' to take into account that the
driver will be in the main tree.

+Files included in this package:
+Kconfig
+Makefile
+inv_mpu_core.c
+inv_mpu_misc.c
+inv_mpu_iio.h
+inv_mpu_ring.c
+dmpDefaultMPU6050.c
+dmpkey.h
+dmpmap.h
+mpu.h
+Including the driver in the Linux kernel
+----------------------------------------
+mpu.h should be added to "kernel/include/linux".
+Other files listed should be added to the drivers/staging/iio/imu/mpu
+directory (or another directory of your choosing). When building the
+kernel, the driver will not appear in menuconfig without modifications
+similar to those below:
+
+modify "drivers/staging/iio/imu/Kconfig" like
+source "drivers/staging/iio/imu/mpu/Kconfig"
+
+modify "drivers/staging/iio/imu/Makefile"
+obj-y += mpu/
+
+Board and Platform Data
+-----------------------
+The board file needs to be modified to register the device on an I2C 
bus. An
+i2c_board_info instance must be defined as seen below. The hardcoded 
value of
+140 corresponds to the GPIO input pin wired to the device's interrupt pin.
+This pin will most likely be different for your platform.
+platform data is for orientation matrix,  and secondary bus situations.
+For MPU9150, it is regarded as a MPU9150 and AKM8975 in the secondary.
+So the secondary i2c address must be filled.
+-----------------------------------------------------------------
+The board file is arch/arm/mach-omap2/board-omap4panda.c for panda board or
+modify the board file in your system as below:
+--------------------------------------------------------
good examples for the platform data.  I'm not sure I'd spend
as much time talking about how to register it for a given board.
That stuff is pretty standard.
+For AKM8963 in the secondary i2c bus of MPU6050,
+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_AK8963,
+	.secondary_i2c_addr = 0x0E
+};
...
+
+IIO subsystem
+----------------------------------------------
+successful installation will create two directories under 
/sys/bus/iio/devices
+iio:device0
+trigger0
+Under /dev/ diretory, a file "iio:device0" will also be created(or 
iio:deviceX, if
+you have more than one iio devices).
This stuff is all good, but ideally it would go in a more generic document
where it isn't already covered.  That way we don't have to edit more than
one if anything changes!
+Communicating with the driver in userspace
+------------------------------------------
+Upon installation, the driver generates several files in sysfs. If your
+platform is configured as detailed above, navigate to the following path to
+find these files:
+/sys/bus/iio/devices/iio:device0
+
+The list below provides a brief description for each file.
+--------------------------------------
Firstly these need documenting in 
Documentation/abi/testing/syfs-bus-iio-mpu6050 or
similar using the standard formats found in there.

+For MPU6050:
+temperature (Read-only)
+Read temperature data directly from the temperature register.
Not abi compliant in_temp_input probably.
+
+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.
What controls this?  I'd be tempted to drop it as not
terribly useful to the user if they can't change it.
Probably a debugging aid?
+
+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.
That's in the magic value category.  If you do need a self test
like this, make it output a nice string to say what happened.

+
+key (read-only)
+show the key value of this driver. Used by MPL.
Define the acronym!
+
+gyro_matrix (read-only)
+show the orient matrix obtained from board file.
why?
+
+gyro_enable (read/write)
+enable/disable gyro functionality. affect raw_gyro. turn off this will
+shut down gyro and save power.
If the startup time is not horendous do this dynamically. E.g. bring it
up when a reading is requested or if it in use by the buffered interfaces.
+
+accl_enable (read/write)
+enable/disable accelerometer functionality. affect raw_accl.
+turn off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+Flag indicate 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. 1 is updated internally.
Not sure this should be exposed to the users.   Firmware should always be
loaded and done automatically before the device interfaces appear.
If there is another reason for this, please give details.
+
+dmp_on(read/write)
+This entry controls whether to run DMP or not. To enable DMP ,
+firmware_loaded must be 1. write 1 to enable DMP and write 0 to disable 
dmp.
+
+dmp_in_on(read/write)
+This entry controls whether dmp interrupt is on/off. firmware_loaded must
+be 1. sometimes, it is desirable that interrupt is off while DMP is 
running.
This is definitely not something that should be exposed to userspace. Work
out when it is 'desirable' and do the appropriate control in driver.
+
+dmp_event_int_on(read/write)
+This entry controls whether dmp event interrupt is on/off. Setting this on
+would turn off the data interrupt and turn on the event interrupt. No data
+interrupt would be generated. Only when event happens, does an interrupt
+generate. This can be used in power saving mode when system is waiting for
+a special event to wake up.
Again, raw interrupt controls like this are not abi compliant and should not
be exposed to the user.  It is far from obvious when users will want 
this and when
they will not. If it is useful in power saving mode then enable it, if 
not don't..
+
+dmp_firmware (write only binary file)
+This is the entry that firmware code is loaded into. If the action is 
succeful,
+firmware_loaded will be updated as 1. In order to load new firmware,
+firmware_loaded flag should be set 0.
No. Sorry, but the kernel has standard ways of loading firmware. Those 
are what
you need to use.
+
+lpa_mode(read-write)
+Low power  accelerometer mode
+lpa_freq(read-write)
+low power acceleromter frequency.
Hmm.. There are drivers doing this sort of thing but it generalises very 
badly.
What result does it actually have on the accelerometer?  I'd much rather it
was controlled by dropping into this if they frequency was set appropriately
rather than via this exta control
+
+accel_matrix
+orient matrix for accel
in_accel_orientation_matrix perhaps?  Needs to be associated with the 
channels
explicitly.
+
+flick_lower,
+flick_upper,
+flick_counter,
+flick_message_on,
+flick_int_on,
+flick_axis,
+Flick related entry
That's pretty opaque. Thes probably want to be mapped to the standard 
event types
and those extended if necessary. Certainly don't want to be here.
+
+pedometer_time
+pedometer_steps,
+Pedometer related entry
Cool. Not see that before. Probably want to do that with a new channel 
type. No
reason we won't get more pedometers at some point.

+
+event_flick
+event_tap
+event_orientation
+event_display_orientation
+event related entry. These entry must use poll to read.
Using poll in sysfs is pretty heavily frowned upon except for extremely 
unlikely
error conditions.  These need to go through the event interface of iio. 
If we
need to extend that to handle them then by all means send patches to do so.
+
+tap_on
+control tap function of DMP
+
+dmp_int_on
+turn on/off dmp interrupt.
you have both dmp_in_on and dmp_int_on.  I don't like either ;)
+
+dmp_output_rate
+control dmp output rate when dmp is on.
That's another 'sampling frequency' for the relevant channels.
+
+tap_time
+tap_min_count
+tap_threshold
+tap related entries. control various parameters of tap function.
More stuff that needs to go through the event interface.
+
+orientation_on
+turn on/off orientation function of DMP.
Hmm.. Is this for power reasons?  We really need to start a discussion
about fine grained power control for this sort of device.  Right now I
think you may have to drop it or control it via the enabled channels on
the buffered interface.
+
+display_orientation_on
+turn on/off display orientation function of DMP.
+
+quaternion_on
+turn on/off quaterniion data output. must use DMP.
+-------------------------------------------------------------------
+for MPU9150 and secondary compass
+MPU9150 has every entry MPU6050 has. It has additional entries:
+
+compass_enable (read/write)
+enable this will enable compass function.
+
+compass_matrix (read-only)
+compass orient matrix
Need to map to normal channel types (or add one if this doesn't map
to a magnetometer?)
+----------------------------------------------------------------------------------
+low power accelerometer mode
+Lower power accelerometer mode is a special mode. It works only for 
accelerometer.
+It has two entries, lpa_mode and lpa_freq. Only MPU6050 and MPU9150 has 
this mode.
+To run low power accel mode, set lpa_mode to 1, set lpa_freq to 0~3, 
which corresponds
+to 1.25Hz, 5Hz, 20Hz, 40Hz. "gyro_enable" and "compass_enable" must be 
zero. "dmp_on"
+must be zero.
Then enable it automatically if the acceleration is all that is enabled 
and the frequency is
one of those.
+-----------------------------------------------------------------------------------
+dmp event.
+dmp event is event out by the DMP unit inside MPU. Only MPU6050 and 
MPU9150 supports this.
+There are four sysfs entreis, event_flick, event_tap and 
event_orientation and
entries
+event_display_orientation. These four events must
+be polled before read. The proper method to poll sysfs is:
+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.
Nope. These need to come out of a proper event interface.  The load 
involved in doing
polling on sysfs is huge and gets very rude responses from GregKH and 
others.
As this patch will go via Greg after me, you really don't want to do this!
+------------------------------------------------------------------------------
+If streaming to a userspace application, the recommended way to access 
gyro/accel/compass
+data is via /dev/iio:device0. Follow these steps to get constant 
readings from
+the driver:
It will only be /dev/iio:device[0] if this is the first iio device added....
+
+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.
err. fifo_rate is documented.  It is probably what we would normally map to
sampling_frequency. I'm guessing there may be additional sampling 
frequencies
for the motion calculation units?  We may need to think hard about how 
to control
those?  To my mind there ought to be an 'ideal' set of values for a 
given output
rate from the firmware processing.
+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, using "dmp_on" to enable/disable dmp .
+7. If compass is enabled, output will have compass data.
+===========================================================================
+                    Recommended sysfs entry setup senquence
+1. without DMP firmware
+1.1 set "power_state" to 1,
+1.2 change scale and fifo rate value to your need.
+1.3 change gyro_enable and accle_enable and compass_enable to your 
needs. For example,
+if you want gyro only, set accl_enable to 0 or set accl_enable to zero 
and compass_enable to zero.
+If you want accel only, set gyro_enable to 0 or set gyro_enable to zero 
and compass_enable to zero.
+If you want compass only, disable gyro and accel.
+1.4 set "enable" to 1. you will get output you want.
+
+2. With DMP firmware
+2.1 set "power_state" to 1,
+2.2 write "0" to firmware_loaded if it is not zero already.
+2.3 load firmware into "dmp_firmware" as a whole. Don't split the DMP 
firmware image.
+2.4 make sure firmware_loaded is 1 after loading.
+2.5 make other configurations similar to the situation as without DMP 
firmware.
+2.6 set dmp_on to 1.
+2.7 set "enable" to 1.
+=======================================================
+The enable function is using enable entry under 
"/sys/bus/iio/devices/iio:device0/buffer"
+==========================================================
+test applications:
+Test application is mpu_iio
provided here?
+------------------------------------------
+To run with MPU9150/MPU6050:
+using the following command:
+for orientation/tap/flick/display orientation event:
+mpu_iio  -c 10 -l 3 -p
+for normal data print
+mpu_iio  -c 10 -l 3 -r
+----------------------------------------
+Please use mpu_iio.c and iio_utils.h as the sample code for your 
development.
diff --git a/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c 
b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
new file mode 100644
index 0000000..5803643
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ *
+ */
+#include "dmpKey.h"
+#include "dmpmap.h"
+
Some of these are less than entirely obvious. Perhaps some comments
would help?
+#define CFG_27                  (2745)
...
+
I hate to point out the obvious, but a macro might
serve better here.
#define D(a, b) (a*256 + b)
+#define D_1_2                   (256 + 2)
+#define D_1_4                   (256 + 4)
...

+
+#define D_HOST_NO_MOT          (976)
+
So a lot of the above is about providing the config?
Loose the defines and put them straight into here
e.g.

static const struct tkeyLabel dmpTConfig[] = {
        {KEY_CFG_27, 2745}, etc.
+static const struct tKeyLabel dmpTConfig[] = {
+	{KEY_CFG_27,                    CFG_27},
+	{KEY_CFG_20,                    CFG_20},
....

+};
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
kernel has ARRAY_SIZE macro
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key)
+{
+	static int isSorted;
Is this going to play well if you have more than one of your chips present?
Possibly, I haven't really thought it through!
+	if (!isSorted) {
+		int kk;
+		for (kk = 0; kk < NUM_KEYS; ++kk) {
+			keys[kk].addr = 0xffff;
+			keys[kk].key = kk;
+		}
+		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+			keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr;
+		isSorted = 1;
+	}
+	if (key >= NUM_KEYS)
+		return 0xffff;
+	return keys[key].addr;
+}
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/mpu6050/dmpKey.h 
b/drivers/staging/iio/imu/mpu6050/dmpKey.h
new file mode 100644
index 0000000..e8e1951
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpKey.h
@@ -0,0 +1,16 @@
+/*
+ * 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 DMPKEY_H__
+#define DMPKEY_H__
+
Not sure the stringing them off the previous is a good idea.
If they really are always like this, then use an enum instead.
+#define KEY_CFG_25                  (0)
+#define KEY_CFG_24                  (KEY_CFG_25 + 1)
,,,

+
+struct tKeyLabel  {
+	unsigned short key;
+	unsigned short addr;
+};
+
This is an 'interesting' set of defines.
Why not just use the value in the code?
Or for that matter put them in order...

+#define DINA0A 0x0a
+#define DINA22 0x22
...
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/dmpmap.h 
b/drivers/staging/iio/imu/mpu6050/dmpmap.h
new file mode 100644
index 0000000..420e19d
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpmap.h
@@ -0,0 +1,17 @@
+/*
+* 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 DMPMAP_H
+#define DMPMAP_H
+
If we are going to have this, perhaps have some documenation?
Also it's only included from one place that I can see. Just flatten
it in there and drop this header.

+#define DMP_PTAT    0
+#define DMP_XGYR    2
...
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h 
b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
new file mode 100644
index 0000000..0958d4a
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
@@ -0,0 +1,25 @@
+/*
+* 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 _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include "./mpu.h"
+#include "../../iio.h"
+#include "../../buffer.h"
Do these as <linux/iio/iio.h> etc.
Actually you seem to be on a somewhat out of data tree.  You'll
want to rebase on staging/staging-next before the next submission.

+#include "dmpKey.h"
+/**
Nice docs. Thanks.
+ *  struct inv_reg_map_s - Notable slave registers.
+ *  @sample_rate_div:	Divider applied to gyro output rate.
+ *  @lpf:		Configures internal LPF.
Spell it out, low_pass_filter (I guess?)
+ *  @bank_sel:		Selects between memory banks.
+ *  @user_ctrl:		Enables/resets the FIFO.
+ *  @fifo_en:		Determines which data will appear in FIFO.
+ *  @gyro_config:	gyro config register.
+ *  @accl_config:	accel config register
+ *  @fifo_count_h:	Upper byte of FIFO count.
+ *  @fifo_r_w:		FIFO register.
+ *  @raw_gyro		Address of first gyro register.
+ *  @raw_accl		Address of first accel register.
+ *  @temperature	temperature register
+ *  @int_enable:	Interrupt enable register.
+ *  @int_status:	Interrupt flags.
+ *  @pwr_mgmt_1:	Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:	Controls power state of individual sensors.
+ *  @mem_start_addr:	Address of first memory read.
+ *  @mem_r_w:		Access to memory.
+ *  @prgm_strt_addrh	firmware program start address register
+ */
+struct inv_reg_map_s {
+	unsigned char sample_rate_div;
As in kernel, u8 is the right type to use.
+	unsigned char lpf;
....
+	const short *compass_st_lower;
+	short irq;
+	int accel_bias[3];
+	int gyro_bias[3];
I'd use fixed length types s16 perhaps?
+	short raw_gyro[3];
+	short raw_accel[3];
+	short raw_compass[3];
+	unsigned char compass_scale;
+	unsigned char i2c_addr;
+	unsigned char compass_divider;
+	unsigned char compass_counter;
+	unsigned char sample_divider;
+	unsigned char fifo_divider;
+	unsigned char orient_data;
+	unsigned char display_orient_data;
+	unsigned char tap_data;
+	enum inv_channel_num num_channels;
+	void *sl_handle;
+	unsigned int irq_dur_ns;
+	long long last_isr_time;
+#ifdef CONFIG_INV_TESTING
+	unsigned long i2c_readcount;
+	unsigned long i2c_writecount;
+#endif
+};
+

+	int (*setup)(struct inv_mpu_iio_s *);
+	int (*combine_data)(unsigned char *in, short *out);
+	int (*get_mode)(struct inv_mpu_iio_s *);
+	int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
+	int (*set_fs)(struct inv_mpu_iio_s *, int fs);
+};
+
Are these specific to your handling or to the AKM chips themselves?
If the akm chips, should be in a separate header.
I'd much rather see a generic driver for these with the extra
hooks you need rather than rolling them into your driver and having
lots of code repitition.

+/* 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

...
+	ATTR_COMPASS_ENABLE,
+	ATTR_POWER_STATE,
+	ATTR_FIRMWARE_LOADED,
Clear these out, or do them as a debugfs interface as some
other drivers use.
+#ifdef CONFIG_INV_TESTING
+	ATTR_I2C_COUNTERS,
+	ATTR_REG_WRITE,
+#endif
+};
+
+enum inv_accl_fs_e {
+	INV_FS_02G = 0,
+	INV_FS_04G,
+	INV_FS_08G,
+	INV_FS_16G,
+	NUM_ACCL_FSR
+};
...
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, unsigned short i2c_addr,
+	unsigned char reg, unsigned short length, unsigned char *data);
This isn't defined anywhere that I can see? Each patch should stand on its
own. If I can't build them one at a time then I reject them.  This is
vital in a more general sense for kernel development as we HAVE to have
a bisectable set of patches.

+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+	unsigned short i2c_addr, unsigned char reg, unsigned char data);
+int inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+		int *gyro_result, int *accl_result);
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+			    unsigned char mpu_addr,
+			    unsigned short mem_addr,
+			    unsigned int len, unsigned char const *data);
+int mpu_memory_read(struct i2c_adapter *i2c_adap,
+			   unsigned char mpu_addr,
+			   unsigned short mem_addr,
+			   unsigned int len, unsigned char *data);
+int inv_hw_self_test(struct inv_mpu_iio_s *st);
+
+#define mem_w(a, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, inv_dmp_get_address(key), b, c)
+#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/mpu6050/inv_mpu_ring.c 
b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
new file mode 100644
index 0000000..528da5f
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
@@ -0,0 +1,44 @@
+/*
+* 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
...

+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;
+
+	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;
st->chip_config.gyro_fifo_enable =
	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);
+	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;
+}
+
+/**
+ *  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;
+	unsigned char val;
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
I'd just use st->reg directly, can't see why taking a copy
helps other than confusing me slightly ;)
+	reg = &st->reg;
+
+	inv_scan_query(indio_dev);
+	/* 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;
....
+	return result;
+}
+

As functions go, this one currently doesn't seem to have
a great deal of point... drop it, you can always introduce
it later if it has more to do.
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:	Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+	return reset_fifo_itg(indio_dev);
+}
+
....
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:	Device driver instance.
+ */
+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_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+
Bonus blank line here.  Yup, I'm getting nitpicky ;)
+	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;
+	unsigned char data[BYTES_FOR_DMP + QUATERNION_BYTES];
+	unsigned short fifo_count;
+	unsigned int copied;
+	s64 timestamp;
+	struct inv_reg_map_s *reg;
+	s64 buf[8];
+	unsigned char *tmp;
+	reg = &st->reg;
Given the number of repeats of checking if anything is turned on,
perhaps a inv_anything_enabled inline function might make for shorter 
easier to read
code.
+	if (!(st->chip_config.accl_fifo_enable |
+		st->chip_config.gyro_fifo_enable |
+		st->chip_config.dmp_on |
+		st->chip_config.compass_fifo_enable))
+		goto end_session;
...
+
+int inv_postenable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, true);
bonus blank line.  This formatting stuff does matter if you
are reviewing a lot of code. Makes it simpler to do.
+
+}
+
+int inv_predisable(struct iio_dev *indio_dev)
+{
+	return set_inv_enable(indio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+	.preenable = &iio_sw_buffer_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(indio_dev);
+	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*/
+	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;
+
+	return 0;
+error_iio_sw_rb_free:
+	iio_kfifo_free(indio_dev->buffer);
+	return ret;
+}

+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu6050/mpu.h 
b/drivers/staging/iio/imu/mpu6050/mpu.h
new file mode 100644
index 0000000..5105fb2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/mpu.h
@@ -0,0 +1,15 @@
+/*
+* 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.
+*
+*/
+
+/**
I may be missing something but is this kerneldoc? If not it doesn't
belong in a kernel driver...
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    mpu.h
+ *      @brief   mpu definition
+ */
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+	SECONDARY_SLAVE_TYPE_NONE,
+	SECONDARY_SLAVE_TYPE_ACCEL,
+	SECONDARY_SLAVE_TYPE_COMPASS,
+	SECONDARY_SLAVE_TYPE_PRESSURE,
+
+	SECONDARY_SLAVE_TYPE_TYPES
+};
+
+};
+
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
blank line here please.
+/**
+ * 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
+ *
+ * Contains platform specific information on how to configure the 
MPU3050 to
+ * work on this platform.  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 mpu_platform_data {
why the __ versions?
+	__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];
+	__u8 key[16];
+};
+
+#endif	/* __MPU_H_ */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* RE: [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit
  2012-07-06  9:07 ` Jonathan Cameron
@ 2012-07-06 17:40   ` Ge Gao
  2012-07-07  8:51     ` Jonathan Cameron
  0 siblings, 1 reply; 4+ messages in thread
From: Ge Gao @ 2012-07-06 17:40 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio

Dear Jonathan,
	Thank you very much for the advice. I think it is a great idea! I
will put it as two steps. First the data out, second the DMP(Digital
Motion Processing). Actually, data out is most useful one and that's what
most people would use.
	What makes this driver so complex is the DMP. Without it, the
driver would be much simpler. I will rewrite it and re-submit it.
	For the code base, I am basing the code on the release version
3.4.4 from the Linux website. I notice that the newest release candidate
is 3. 5-rc5. Shall I use this as the base or I should always use the
latest release candidate?
	 I found that the 3.5-rc5 has moved the iio driver out of staging
and KFIFO become the only choice. However, it still lacks the poll
feature.
	As for the DMP loading, I was trying to use the standard way,
request_firmware. However, it is a hotplug mechanism and need a "Daemon"
to function. I run my system on Panda board and I didn't see hotplug being
triggered when I try to run it. That's why I switched to a more "manual"
way to load firmware. MPU6050/MPU9150 can run without firmware. The
firmware is only for DMP purpose; if no DMP function is required, no
firmware should be loaded. So it can be chosen load or not. Without DMP,
MPU6050/9150 would just output data. Nothing else.
	I will rewrite the code again as we discussed and resubmit. Thank
you for advice.

Best Regards,

Ge GAO


-----Original Message-----
From: Jonathan Cameron [mailto:jic23@kernel.org]
Sent: Friday, July 06, 2012 2:07 AM
To: Ge Gao
Cc: linux-iio@vger.kernel.org
Subject: Re: [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit

Hi Ge,

Please don't get disheartened by this partial review.
Getting use to approaches to getting kernel drivers merged can take a
considerable time. It's not a trivial thing to do even with a 'perfect'
driver.  There is a lot of nice / interesting stuff in your driver so I
look forward to seeing it move forward!  I doubt we'll get any more
complex hardware anytime soon!

Couple of major comments.

1) This first patch could benefit from breaking up futher. Right now it's
too big and cumbersome to review remotely quickly.
First separate off the docs into their own patch.
Then I'd pull out the DMP code into a follow up patch.
That way we can get the core stuff sorted relatively quickly before it
gets to complex.

Actually the best way to get this in would be to strip nearly everything
back so just have your basic data flow through the fifo. No events or
motion processing or anything unusual in the first patch.  Then we can
review that very quickly and get something in place to make incremental
extensions to.

All in all it is too big to get reviewed. Sorry, but patches really need
to be inline emails if you want to have a reasonable chance of getting
eyes on them. There are a number of people on the mailing list who will
take a look at new drivers when they have a few minutes spare.  This first
file has taken me over
2 hours to review and what I have done is far from thorough.

Anyhow some comments in line. Mostly the code looks pretty clean and there
are some good docs which always helps.

2) I may not have made it clear enough before, but you are pretty much not
going to succeeed in having your own versions of drivers that already
exist.  That sort of code replication gets a lot of friction upstream.

What you need to do is to write a i2c master driver and then attach your
secondary devices to this.  Where you need additional features in those
drivers, propose them and if they are not too invasive I doubt anyone will
mind.  Although you only have a couple of slave devices here, seeing your
table of id's shows there will be a lot more to come.

If there is an absolute reason why this approach won't work, you need to
convince people with a careful brief arguement 'before' this goes
anywhere.

Anyhow Ge good luck with this one. I might get a chance to take a look at
the other patches, but it'll be a while give their size. Sorry, but the
short easy to review ones tend to get attended to first!

Jonathan

 From 6d4093a59d77a68d4972f648ea9eba69e8598159 Mon Sep 17 00:00:00 2001
From: Ge Gao <ggao@invensense.com>
Date: Thu, 28 Jun 2012 10:58:12 -0700
Subject: [PATCH 1/4]     Invensense MPU6050/MPU9150 driver.

       --MPU6050/MPU9150 driver.
       --Secondary bus for AKM8975/AKM8963/AKM8972 support.
       --kernel fix for Kfifo poll support from Jonanthan.
       --Kfifo bug fix. Need to check available space before store.
       --add new IIO type for quaternion.

...
index 0000000..c4fa2c2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/Kconfig
@@ -0,0 +1,6 @@
+#
+# inv-mpu-6050 drivers for Invensense MPU devices and combos #
+
+config INV_MPU6050_IIO
+	tristate "Invensense MPU6050 devices"
Will need to drop the !INV_MPU bit from the patch (and put it back in your
local tree).
+	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && !INV_MPU &&
!INV_MPU_IIO
+	default n
+	help
+	  This driver supports the Invensense MPU6050/MPU9150 devices. It
also
+	  supports AKM8975/AKM8963/AKM8972 in the secondary bus.
Ideally you want to build this text up as stuff is added (guessing not all
of this is in the initial patch!)
+	  This driver can be built as a module. The module will be called
+	  inv-mpu6050-iio.
Not keen on the naming, inv-mpu6050 then if you need to change it for your
local tree do it in a patch there.

...

diff --git a/drivers/staging/iio/imu/mpu6050/README
b/drivers/staging/iio/imu/mpu6050/README
new file mode 100644
index 0000000..806a956
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/README
@@ -0,0 +1,11 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Description
+-----------
+This document describes how to install the Invensense device driver
+into a Linux kernel. At the moment, this driver supports the
+MPU6050/MPU9150. The slave address of these four chips are
+0x68 or 0x69. However, the actual slave address depends on the board
+configuration. The driver does not assume anything about it.
+
I would have prefered the documentation in a separate patch.
Also, lots of this needs 'adjusting' to take into account that the driver
will be in the main tree.

+Files included in this package:
+Kconfig
+Makefile
+inv_mpu_core.c
+inv_mpu_misc.c
+inv_mpu_iio.h
+inv_mpu_ring.c
+dmpDefaultMPU6050.c
+dmpkey.h
+dmpmap.h
+mpu.h
+Including the driver in the Linux kernel
+----------------------------------------
+mpu.h should be added to "kernel/include/linux".
+Other files listed should be added to the drivers/staging/iio/imu/mpu
+directory (or another directory of your choosing). When building the
+kernel, the driver will not appear in menuconfig without modifications
+similar to those below:
+
+modify "drivers/staging/iio/imu/Kconfig" like source
+"drivers/staging/iio/imu/mpu/Kconfig"
+
+modify "drivers/staging/iio/imu/Makefile"
+obj-y += mpu/
+
+Board and Platform Data
+-----------------------
+The board file needs to be modified to register the device on an I2C
bus. An
+i2c_board_info instance must be defined as seen below. The hardcoded
value of
+140 corresponds to the GPIO input pin wired to the device's interrupt
pin.
+This pin will most likely be different for your platform.
+platform data is for orientation matrix,  and secondary bus situations.
+For MPU9150, it is regarded as a MPU9150 and AKM8975 in the secondary.
+So the secondary i2c address must be filled.
+-----------------------------------------------------------------
+The board file is arch/arm/mach-omap2/board-omap4panda.c for panda
+board or modify the board file in your system as below:
+--------------------------------------------------------
good examples for the platform data.  I'm not sure I'd spend as much time
talking about how to register it for a given board.
That stuff is pretty standard.
+For AKM8963 in the secondary i2c bus of MPU6050, 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_AK8963,
+	.secondary_i2c_addr = 0x0E
+};
...
+
+IIO subsystem
+----------------------------------------------
+successful installation will create two directories under
/sys/bus/iio/devices
+iio:device0
+trigger0
+Under /dev/ diretory, a file "iio:device0" will also be created(or
iio:deviceX, if
+you have more than one iio devices).
This stuff is all good, but ideally it would go in a more generic document
where it isn't already covered.  That way we don't have to edit more than
one if anything changes!
+Communicating with the driver in userspace
+------------------------------------------
+Upon installation, the driver generates several files in sysfs. If your
+platform is configured as detailed above, navigate to the following
+path to find these files:
+/sys/bus/iio/devices/iio:device0
+
+The list below provides a brief description for each file.
+--------------------------------------
Firstly these need documenting in
Documentation/abi/testing/syfs-bus-iio-mpu6050 or similar using the
standard formats found in there.

+For MPU6050:
+temperature (Read-only)
+Read temperature data directly from the temperature register.
Not abi compliant in_temp_input probably.
+
+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.
What controls this?  I'd be tempted to drop it as not terribly useful to
the user if they can't change it.
Probably a debugging aid?
+
+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.
That's in the magic value category.  If you do need a self test like this,
make it output a nice string to say what happened.

+
+key (read-only)
+show the key value of this driver. Used by MPL.
Define the acronym!
+
+gyro_matrix (read-only)
+show the orient matrix obtained from board file.
why?
+
+gyro_enable (read/write)
+enable/disable gyro functionality. affect raw_gyro. turn off this will
+shut down gyro and save power.
If the startup time is not horendous do this dynamically. E.g. bring it up
when a reading is requested or if it in use by the buffered interfaces.
+
+accl_enable (read/write)
+enable/disable accelerometer functionality. affect raw_accl.
+turn off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+Flag indicate 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. 1 is updated internally.
Not sure this should be exposed to the users.   Firmware should always be
loaded and done automatically before the device interfaces appear.
If there is another reason for this, please give details.
+
+dmp_on(read/write)
+This entry controls whether to run DMP or not. To enable DMP ,
+firmware_loaded must be 1. write 1 to enable DMP and write 0 to disable
dmp.
+
+dmp_in_on(read/write)
+This entry controls whether dmp interrupt is on/off. firmware_loaded
+must be 1. sometimes, it is desirable that interrupt is off while DMP
+is
running.
This is definitely not something that should be exposed to userspace. Work
out when it is 'desirable' and do the appropriate control in driver.
+
+dmp_event_int_on(read/write)
+This entry controls whether dmp event interrupt is on/off. Setting this
+on would turn off the data interrupt and turn on the event interrupt.
+No data interrupt would be generated. Only when event happens, does an
+interrupt generate. This can be used in power saving mode when system
+is waiting for a special event to wake up.
Again, raw interrupt controls like this are not abi compliant and should
not be exposed to the user.  It is far from obvious when users will want
this and when they will not. If it is useful in power saving mode then
enable it, if not don't..
+
+dmp_firmware (write only binary file)
+This is the entry that firmware code is loaded into. If the action is
succeful,
+firmware_loaded will be updated as 1. In order to load new firmware,
+firmware_loaded flag should be set 0.
No. Sorry, but the kernel has standard ways of loading firmware. Those are
what you need to use.
+
+lpa_mode(read-write)
+Low power  accelerometer mode
+lpa_freq(read-write)
+low power acceleromter frequency.
Hmm.. There are drivers doing this sort of thing but it generalises very
badly.
What result does it actually have on the accelerometer?  I'd much rather
it was controlled by dropping into this if they frequency was set
appropriately rather than via this exta control
+
+accel_matrix
+orient matrix for accel
in_accel_orientation_matrix perhaps?  Needs to be associated with the
channels explicitly.
+
+flick_lower,
+flick_upper,
+flick_counter,
+flick_message_on,
+flick_int_on,
+flick_axis,
+Flick related entry
That's pretty opaque. Thes probably want to be mapped to the standard
event types and those extended if necessary. Certainly don't want to be
here.
+
+pedometer_time
+pedometer_steps,
+Pedometer related entry
Cool. Not see that before. Probably want to do that with a new channel
type. No reason we won't get more pedometers at some point.

+
+event_flick
+event_tap
+event_orientation
+event_display_orientation
+event related entry. These entry must use poll to read.
Using poll in sysfs is pretty heavily frowned upon except for extremely
unlikely error conditions.  These need to go through the event interface
of iio.
If we
need to extend that to handle them then by all means send patches to do
so.
+
+tap_on
+control tap function of DMP
+
+dmp_int_on
+turn on/off dmp interrupt.
you have both dmp_in_on and dmp_int_on.  I don't like either ;)
+
+dmp_output_rate
+control dmp output rate when dmp is on.
That's another 'sampling frequency' for the relevant channels.
+
+tap_time
+tap_min_count
+tap_threshold
+tap related entries. control various parameters of tap function.
More stuff that needs to go through the event interface.
+
+orientation_on
+turn on/off orientation function of DMP.
Hmm.. Is this for power reasons?  We really need to start a discussion
about fine grained power control for this sort of device.  Right now I
think you may have to drop it or control it via the enabled channels on
the buffered interface.
+
+display_orientation_on
+turn on/off display orientation function of DMP.
+
+quaternion_on
+turn on/off quaterniion data output. must use DMP.
+-------------------------------------------------------------------
+for MPU9150 and secondary compass
+MPU9150 has every entry MPU6050 has. It has additional entries:
+
+compass_enable (read/write)
+enable this will enable compass function.
+
+compass_matrix (read-only)
+compass orient matrix
Need to map to normal channel types (or add one if this doesn't map to a
magnetometer?)
+-----------------------------------------------------------------------
+-----------
+low power accelerometer mode
+Lower power accelerometer mode is a special mode. It works only for
accelerometer.
+It has two entries, lpa_mode and lpa_freq. Only MPU6050 and MPU9150 has
this mode.
+To run low power accel mode, set lpa_mode to 1, set lpa_freq to 0~3,
which corresponds
+to 1.25Hz, 5Hz, 20Hz, 40Hz. "gyro_enable" and "compass_enable" must be
zero. "dmp_on"
+must be zero.
Then enable it automatically if the acceleration is all that is enabled
and the frequency is one of those.
+-----------------------------------------------------------------------
+------------
+dmp event.
+dmp event is event out by the DMP unit inside MPU. Only MPU6050 and
MPU9150 supports this.
+There are four sysfs entreis, event_flick, event_tap and
event_orientation and
entries
+event_display_orientation. These four events must be polled before
+read. The proper method to poll sysfs is:
+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.
Nope. These need to come out of a proper event interface.  The load
involved in doing polling on sysfs is huge and gets very rude responses
from GregKH and others.
As this patch will go via Greg after me, you really don't want to do this!
+-----------------------------------------------------------------------
+------- If streaming to a userspace application, the recommended way to
+access
gyro/accel/compass
+data is via /dev/iio:device0. Follow these steps to get constant
readings from
+the driver:
It will only be /dev/iio:device[0] if this is the first iio device
added....
+
+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.
err. fifo_rate is documented.  It is probably what we would normally map
to sampling_frequency. I'm guessing there may be additional sampling
frequencies for the motion calculation units?  We may need to think hard
about how to control those?  To my mind there ought to be an 'ideal' set
of values for a given output rate from the firmware processing.
+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, using "dmp_on" to enable/disable dmp .
+7. If compass is enabled, output will have compass data.
+=========================================================================
==
+                    Recommended sysfs entry setup senquence 1. without
+DMP firmware
+1.1 set "power_state" to 1,
+1.2 change scale and fifo rate value to your need.
+1.3 change gyro_enable and accle_enable and compass_enable to your
needs. For example,
+if you want gyro only, set accl_enable to 0 or set accl_enable to zero
and compass_enable to zero.
+If you want accel only, set gyro_enable to 0 or set gyro_enable to zero
and compass_enable to zero.
+If you want compass only, disable gyro and accel.
+1.4 set "enable" to 1. you will get output you want.
+
+2. With DMP firmware
+2.1 set "power_state" to 1,
+2.2 write "0" to firmware_loaded if it is not zero already.
+2.3 load firmware into "dmp_firmware" as a whole. Don't split the DMP
firmware image.
+2.4 make sure firmware_loaded is 1 after loading.
+2.5 make other configurations similar to the situation as without DMP
firmware.
+2.6 set dmp_on to 1.
+2.7 set "enable" to 1.
+=======================================================
+The enable function is using enable entry under
"/sys/bus/iio/devices/iio:device0/buffer"
+==========================================================
+test applications:
+Test application is mpu_iio
provided here?
+------------------------------------------
+To run with MPU9150/MPU6050:
+using the following command:
+for orientation/tap/flick/display orientation event:
+mpu_iio  -c 10 -l 3 -p
+for normal data print
+mpu_iio  -c 10 -l 3 -r
+----------------------------------------
+Please use mpu_iio.c and iio_utils.h as the sample code for your
development.
diff --git a/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
new file mode 100644
index 0000000..5803643
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ *
+ */
+#include "dmpKey.h"
+#include "dmpmap.h"
+
Some of these are less than entirely obvious. Perhaps some comments would
help?
+#define CFG_27                  (2745)
...
+
I hate to point out the obvious, but a macro might serve better here.
#define D(a, b) (a*256 + b)
+#define D_1_2                   (256 + 2)
+#define D_1_4                   (256 + 4)
...

+
+#define D_HOST_NO_MOT          (976)
+
So a lot of the above is about providing the config?
Loose the defines and put them straight into here e.g.

static const struct tkeyLabel dmpTConfig[] = {
        {KEY_CFG_27, 2745}, etc.
+static const struct tKeyLabel dmpTConfig[] = {
+	{KEY_CFG_27,                    CFG_27},
+	{KEY_CFG_20,                    CFG_20},
....

+};
+#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
kernel has ARRAY_SIZE macro
+
+static struct tKeyLabel keys[NUM_KEYS];
+
+unsigned short inv_dmp_get_address(unsigned short key) {
+	static int isSorted;
Is this going to play well if you have more than one of your chips
present?
Possibly, I haven't really thought it through!
+	if (!isSorted) {
+		int kk;
+		for (kk = 0; kk < NUM_KEYS; ++kk) {
+			keys[kk].addr = 0xffff;
+			keys[kk].key = kk;
+		}
+		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
+			keys[dmpTConfig[kk].key].addr =
dmpTConfig[kk].addr;
+		isSorted = 1;
+	}
+	if (key >= NUM_KEYS)
+		return 0xffff;
+	return keys[key].addr;
+}
+/**
+ *  @}
+ */
diff --git a/drivers/staging/iio/imu/mpu6050/dmpKey.h
b/drivers/staging/iio/imu/mpu6050/dmpKey.h
new file mode 100644
index 0000000..e8e1951
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpKey.h
@@ -0,0 +1,16 @@
+/*
+ * 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 DMPKEY_H__
+#define DMPKEY_H__
+
Not sure the stringing them off the previous is a good idea.
If they really are always like this, then use an enum instead.
+#define KEY_CFG_25                  (0)
+#define KEY_CFG_24                  (KEY_CFG_25 + 1)
,,,

+
+struct tKeyLabel  {
+	unsigned short key;
+	unsigned short addr;
+};
+
This is an 'interesting' set of defines.
Why not just use the value in the code?
Or for that matter put them in order...

+#define DINA0A 0x0a
+#define DINA22 0x22
...
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/dmpmap.h
b/drivers/staging/iio/imu/mpu6050/dmpmap.h
new file mode 100644
index 0000000..420e19d
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/dmpmap.h
@@ -0,0 +1,17 @@
+/*
+* 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 DMPMAP_H
+#define DMPMAP_H
+
If we are going to have this, perhaps have some documenation?
Also it's only included from one place that I can see. Just flatten it in
there and drop this header.

+#define DMP_PTAT    0
+#define DMP_XGYR    2
...
+
+#endif
diff --git a/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
new file mode 100644
index 0000000..0958d4a
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
@@ -0,0 +1,25 @@
+/*
+* 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 _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include "./mpu.h"
+#include "../../iio.h"
+#include "../../buffer.h"
Do these as <linux/iio/iio.h> etc.
Actually you seem to be on a somewhat out of data tree.  You'll want to
rebase on staging/staging-next before the next submission.

+#include "dmpKey.h"
+/**
Nice docs. Thanks.
+ *  struct inv_reg_map_s - Notable slave registers.
+ *  @sample_rate_div:	Divider applied to gyro output rate.
+ *  @lpf:		Configures internal LPF.
Spell it out, low_pass_filter (I guess?)
+ *  @bank_sel:		Selects between memory banks.
+ *  @user_ctrl:		Enables/resets the FIFO.
+ *  @fifo_en:		Determines which data will appear in FIFO.
+ *  @gyro_config:	gyro config register.
+ *  @accl_config:	accel config register
+ *  @fifo_count_h:	Upper byte of FIFO count.
+ *  @fifo_r_w:		FIFO register.
+ *  @raw_gyro		Address of first gyro register.
+ *  @raw_accl		Address of first accel register.
+ *  @temperature	temperature register
+ *  @int_enable:	Interrupt enable register.
+ *  @int_status:	Interrupt flags.
+ *  @pwr_mgmt_1:	Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:	Controls power state of individual sensors.
+ *  @mem_start_addr:	Address of first memory read.
+ *  @mem_r_w:		Access to memory.
+ *  @prgm_strt_addrh	firmware program start address register
+ */
+struct inv_reg_map_s {
+	unsigned char sample_rate_div;
As in kernel, u8 is the right type to use.
+	unsigned char lpf;
....
+	const short *compass_st_lower;
+	short irq;
+	int accel_bias[3];
+	int gyro_bias[3];
I'd use fixed length types s16 perhaps?
+	short raw_gyro[3];
+	short raw_accel[3];
+	short raw_compass[3];
+	unsigned char compass_scale;
+	unsigned char i2c_addr;
+	unsigned char compass_divider;
+	unsigned char compass_counter;
+	unsigned char sample_divider;
+	unsigned char fifo_divider;
+	unsigned char orient_data;
+	unsigned char display_orient_data;
+	unsigned char tap_data;
+	enum inv_channel_num num_channels;
+	void *sl_handle;
+	unsigned int irq_dur_ns;
+	long long last_isr_time;
+#ifdef CONFIG_INV_TESTING
+	unsigned long i2c_readcount;
+	unsigned long i2c_writecount;
+#endif
+};
+

+	int (*setup)(struct inv_mpu_iio_s *);
+	int (*combine_data)(unsigned char *in, short *out);
+	int (*get_mode)(struct inv_mpu_iio_s *);
+	int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
+	int (*set_fs)(struct inv_mpu_iio_s *, int fs); };
+
Are these specific to your handling or to the AKM chips themselves?
If the akm chips, should be in a separate header.
I'd much rather see a generic driver for these with the extra hooks you
need rather than rolling them into your driver and having lots of code
repitition.

+/* 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

...
+	ATTR_COMPASS_ENABLE,
+	ATTR_POWER_STATE,
+	ATTR_FIRMWARE_LOADED,
Clear these out, or do them as a debugfs interface as some other drivers
use.
+#ifdef CONFIG_INV_TESTING
+	ATTR_I2C_COUNTERS,
+	ATTR_REG_WRITE,
+#endif
+};
+
+enum inv_accl_fs_e {
+	INV_FS_02G = 0,
+	INV_FS_04G,
+	INV_FS_08G,
+	INV_FS_16G,
+	NUM_ACCL_FSR
+};
...
+int inv_i2c_read_base(struct inv_mpu_iio_s *st, unsigned short i2c_addr,
+	unsigned char reg, unsigned short length, unsigned char *data);
This isn't defined anywhere that I can see? Each patch should stand on its
own. If I can't build them one at a time then I reject them.  This is
vital in a more general sense for kernel development as we HAVE to have a
bisectable set of patches.

+int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
+	unsigned short i2c_addr, unsigned char reg, unsigned char data);
int
+inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
+		int *gyro_result, int *accl_result);
+int mpu_memory_write(struct i2c_adapter *i2c_adap,
+			    unsigned char mpu_addr,
+			    unsigned short mem_addr,
+			    unsigned int len, unsigned char const *data);
int
+mpu_memory_read(struct i2c_adapter *i2c_adap,
+			   unsigned char mpu_addr,
+			   unsigned short mem_addr,
+			   unsigned int len, unsigned char *data); int
+inv_hw_self_test(struct inv_mpu_iio_s *st);
+
+#define mem_w(a, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle,\
+			st->i2c_addr, inv_dmp_get_address(key), b, c)
#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/mpu6050/inv_mpu_ring.c
b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
new file mode 100644
index 0000000..528da5f
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
@@ -0,0 +1,44 @@
+/*
+* 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
...

+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;
+
+	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;
st->chip_config.gyro_fifo_enable =
	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);
+	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; }
+
+/**
+ *  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;
+	unsigned char val;
+	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
I'd just use st->reg directly, can't see why taking a copy helps other
than confusing me slightly ;)
+	reg = &st->reg;
+
+	inv_scan_query(indio_dev);
+	/* 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;
....
+	return result;
+}
+

As functions go, this one currently doesn't seem to have a great deal of
point... drop it, you can always introduce it later if it has more to do.
+/**
+ *  inv_reset_fifo() - Reset FIFO related registers.
+ *  @st:	Device driver instance.
+ */
+static int inv_reset_fifo(struct iio_dev *indio_dev) {
+	return reset_fifo_itg(indio_dev);
+}
+
....
+/**
+ *  inv_clear_kfifo() - clear time stamp fifo
+ *  @st:	Device driver instance.
+ */
+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_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+irqreturn_t inv_read_fifo(int irq, void *dev_id) {
+
Bonus blank line here.  Yup, I'm getting nitpicky ;)
+	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;
+	unsigned char data[BYTES_FOR_DMP + QUATERNION_BYTES];
+	unsigned short fifo_count;
+	unsigned int copied;
+	s64 timestamp;
+	struct inv_reg_map_s *reg;
+	s64 buf[8];
+	unsigned char *tmp;
+	reg = &st->reg;
Given the number of repeats of checking if anything is turned on, perhaps
a inv_anything_enabled inline function might make for shorter easier to
read code.
+	if (!(st->chip_config.accl_fifo_enable |
+		st->chip_config.gyro_fifo_enable |
+		st->chip_config.dmp_on |
+		st->chip_config.compass_fifo_enable))
+		goto end_session;
...
+
+int inv_postenable(struct iio_dev *indio_dev) {
+	return set_inv_enable(indio_dev, true);
bonus blank line.  This formatting stuff does matter if you are reviewing
a lot of code. Makes it simpler to do.
+
+}
+
+int inv_predisable(struct iio_dev *indio_dev) {
+	return set_inv_enable(indio_dev, false); }
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+	.preenable = &iio_sw_buffer_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(indio_dev);
+	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*/
+	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;
+
+	return 0;
+error_iio_sw_rb_free:
+	iio_kfifo_free(indio_dev->buffer);
+	return ret;
+}

+/**
+ *  @}
+ */
+
diff --git a/drivers/staging/iio/imu/mpu6050/mpu.h
b/drivers/staging/iio/imu/mpu6050/mpu.h
new file mode 100644
index 0000000..5105fb2
--- /dev/null
+++ b/drivers/staging/iio/imu/mpu6050/mpu.h
@@ -0,0 +1,15 @@
+/*
+* 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.
+*
+*/
+
+/**
I may be missing something but is this kerneldoc? If not it doesn't belong
in a kernel driver...
+ *  @addtogroup  DRIVERS
+ *  @brief       Hardware drivers.
+ *
+ *  @{
+ *      @file    mpu.h
+ *      @brief   mpu definition
+ */
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+	SECONDARY_SLAVE_TYPE_NONE,
+	SECONDARY_SLAVE_TYPE_ACCEL,
+	SECONDARY_SLAVE_TYPE_COMPASS,
+	SECONDARY_SLAVE_TYPE_PRESSURE,
+
+	SECONDARY_SLAVE_TYPE_TYPES
+};
+
+};
+
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
blank line here please.
+/**
+ * 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
+ *
+ * Contains platform specific information on how to configure the
MPU3050 to
+ * work on this platform.  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 mpu_platform_data {
why the __ versions?
+	__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];
+	__u8 key[16];
+};
+
+#endif	/* __MPU_H_ */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit
  2012-07-06 17:40   ` Ge Gao
@ 2012-07-07  8:51     ` Jonathan Cameron
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2012-07-07  8:51 UTC (permalink / raw)
  To: Ge Gao; +Cc: linux-iio

On 07/06/2012 06:40 PM, Ge Gao wrote:
> Dear Jonathan,
> 	Thank you very much for the advice. I think it is a great idea! I
> will put it as two steps. First the data out, second the DMP(Digital
> Motion Processing). Actually, data out is most useful one and that's what
> most people would use.
Cool.
> 	What makes this driver so complex is the DMP. Without it, the
> driver would be much simpler. I will rewrite it and re-submit it.
> 	For the code base, I am basing the code on the release version
> 3.4.4 from the Linux website. I notice that the newest release candidate
> is 3. 5-rc5. Shall I use this as the base or I should always use the
> latest release candidate?
Ultimately the drivers will go through the staging-next tree
http://git.kernel.org/?p=linux/kernel/git/stable/staging.git;a=summary

I send pull requests to Greg KH and he deals with pushing them onwards.
For reference my tree is at
http://git.kernel.org/?p=linux/kernel/git/jic23/iio.git;a=summary
as stuff can sit there for a few days before going into Greg's tree.

Things will probably continue going through the staging tree (even if
out of staging) purely because so many patches effect stuff that is
still in there.
> 	 I found that the 3.5-rc5 has moved the iio driver out of staging
> and KFIFO become the only choice. However, it still lacks the poll
> feature.
I posted poll patches just the other day. Waiting for feedback.
Lars-Peter is testing them.  For now you'll have to pull them off the
mailing list (or I might push out a 'working' branch later if I get
time).

> 	As for the DMP loading, I was trying to use the standard way,
> request_firmware. However, it is a hotplug mechanism and need a "Daemon"
> to function. I run my system on Panda board and I didn't see hotplug being
> triggered when I try to run it. That's why I switched to a more "manual"
> way to load firmware. MPU6050/MPU9150 can run without firmware. The
> firmware is only for DMP purpose; if no DMP function is required, no
> firmware should be loaded. So it can be chosen load or not. Without DMP,
> MPU6050/9150 would just output data. Nothing else.
I'm afraid I'm not that familiar with the firmware loading stuff, but
there must be a way of doing it as it occurs on plenty of embedded
boards (typically for wifi chips).  Maybe ask on the panda board lists?
> 	I will rewrite the code again as we discussed and resubmit. Thank
> you for advice.
You are welcome. Looking forward to the next version.
> 
> Best Regards,
> 
> Ge GAO
> 
> 
> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: Friday, July 06, 2012 2:07 AM
> To: Ge Gao
> Cc: linux-iio@vger.kernel.org
> Subject: Re: [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit
> 
> Hi Ge,
> 
> Please don't get disheartened by this partial review.
> Getting use to approaches to getting kernel drivers merged can take a
> considerable time. It's not a trivial thing to do even with a 'perfect'
> driver.  There is a lot of nice / interesting stuff in your driver so I
> look forward to seeing it move forward!  I doubt we'll get any more
> complex hardware anytime soon!
> 
> Couple of major comments.
> 
> 1) This first patch could benefit from breaking up futher. Right now it's
> too big and cumbersome to review remotely quickly.
> First separate off the docs into their own patch.
> Then I'd pull out the DMP code into a follow up patch.
> That way we can get the core stuff sorted relatively quickly before it
> gets to complex.
> 
> Actually the best way to get this in would be to strip nearly everything
> back so just have your basic data flow through the fifo. No events or
> motion processing or anything unusual in the first patch.  Then we can
> review that very quickly and get something in place to make incremental
> extensions to.
> 
> All in all it is too big to get reviewed. Sorry, but patches really need
> to be inline emails if you want to have a reasonable chance of getting
> eyes on them. There are a number of people on the mailing list who will
> take a look at new drivers when they have a few minutes spare.  This first
> file has taken me over
> 2 hours to review and what I have done is far from thorough.
> 
> Anyhow some comments in line. Mostly the code looks pretty clean and there
> are some good docs which always helps.
> 
> 2) I may not have made it clear enough before, but you are pretty much not
> going to succeeed in having your own versions of drivers that already
> exist.  That sort of code replication gets a lot of friction upstream.
> 
> What you need to do is to write a i2c master driver and then attach your
> secondary devices to this.  Where you need additional features in those
> drivers, propose them and if they are not too invasive I doubt anyone will
> mind.  Although you only have a couple of slave devices here, seeing your
> table of id's shows there will be a lot more to come.
> 
> If there is an absolute reason why this approach won't work, you need to
> convince people with a careful brief arguement 'before' this goes
> anywhere.
> 
> Anyhow Ge good luck with this one. I might get a chance to take a look at
> the other patches, but it'll be a while give their size. Sorry, but the
> short easy to review ones tend to get attended to first!
> 
> Jonathan
> 
>  From 6d4093a59d77a68d4972f648ea9eba69e8598159 Mon Sep 17 00:00:00 2001
> From: Ge Gao <ggao@invensense.com>
> Date: Thu, 28 Jun 2012 10:58:12 -0700
> Subject: [PATCH 1/4]     Invensense MPU6050/MPU9150 driver.
> 
>        --MPU6050/MPU9150 driver.
>        --Secondary bus for AKM8975/AKM8963/AKM8972 support.
>        --kernel fix for Kfifo poll support from Jonanthan.
>        --Kfifo bug fix. Need to check available space before store.
>        --add new IIO type for quaternion.
> 
> ...
> index 0000000..c4fa2c2
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/Kconfig
> @@ -0,0 +1,6 @@
> +#
> +# inv-mpu-6050 drivers for Invensense MPU devices and combos #
> +
> +config INV_MPU6050_IIO
> +	tristate "Invensense MPU6050 devices"
> Will need to drop the !INV_MPU bit from the patch (and put it back in your
> local tree).
> +	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && !INV_MPU &&
> !INV_MPU_IIO
> +	default n
> +	help
> +	  This driver supports the Invensense MPU6050/MPU9150 devices. It
> also
> +	  supports AKM8975/AKM8963/AKM8972 in the secondary bus.
> Ideally you want to build this text up as stuff is added (guessing not all
> of this is in the initial patch!)
> +	  This driver can be built as a module. The module will be called
> +	  inv-mpu6050-iio.
> Not keen on the naming, inv-mpu6050 then if you need to change it for your
> local tree do it in a patch there.
> 
> ...
> 
> diff --git a/drivers/staging/iio/imu/mpu6050/README
> b/drivers/staging/iio/imu/mpu6050/README
> new file mode 100644
> index 0000000..806a956
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/README
> @@ -0,0 +1,11 @@
> +Kernel driver inv-mpu-iio
> +Author: Invensense <http://invensense.com>
> +
> +Description
> +-----------
> +This document describes how to install the Invensense device driver
> +into a Linux kernel. At the moment, this driver supports the
> +MPU6050/MPU9150. The slave address of these four chips are
> +0x68 or 0x69. However, the actual slave address depends on the board
> +configuration. The driver does not assume anything about it.
> +
> I would have prefered the documentation in a separate patch.
> Also, lots of this needs 'adjusting' to take into account that the driver
> will be in the main tree.
> 
> +Files included in this package:
> +Kconfig
> +Makefile
> +inv_mpu_core.c
> +inv_mpu_misc.c
> +inv_mpu_iio.h
> +inv_mpu_ring.c
> +dmpDefaultMPU6050.c
> +dmpkey.h
> +dmpmap.h
> +mpu.h
> +Including the driver in the Linux kernel
> +----------------------------------------
> +mpu.h should be added to "kernel/include/linux".
> +Other files listed should be added to the drivers/staging/iio/imu/mpu
> +directory (or another directory of your choosing). When building the
> +kernel, the driver will not appear in menuconfig without modifications
> +similar to those below:
> +
> +modify "drivers/staging/iio/imu/Kconfig" like source
> +"drivers/staging/iio/imu/mpu/Kconfig"
> +
> +modify "drivers/staging/iio/imu/Makefile"
> +obj-y += mpu/
> +
> +Board and Platform Data
> +-----------------------
> +The board file needs to be modified to register the device on an I2C
> bus. An
> +i2c_board_info instance must be defined as seen below. The hardcoded
> value of
> +140 corresponds to the GPIO input pin wired to the device's interrupt
> pin.
> +This pin will most likely be different for your platform.
> +platform data is for orientation matrix,  and secondary bus situations.
> +For MPU9150, it is regarded as a MPU9150 and AKM8975 in the secondary.
> +So the secondary i2c address must be filled.
> +-----------------------------------------------------------------
> +The board file is arch/arm/mach-omap2/board-omap4panda.c for panda
> +board or modify the board file in your system as below:
> +--------------------------------------------------------
> good examples for the platform data.  I'm not sure I'd spend as much time
> talking about how to register it for a given board.
> That stuff is pretty standard.
> +For AKM8963 in the secondary i2c bus of MPU6050, 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_AK8963,
> +	.secondary_i2c_addr = 0x0E
> +};
> ...
> +
> +IIO subsystem
> +----------------------------------------------
> +successful installation will create two directories under
> /sys/bus/iio/devices
> +iio:device0
> +trigger0
> +Under /dev/ diretory, a file "iio:device0" will also be created(or
> iio:deviceX, if
> +you have more than one iio devices).
> This stuff is all good, but ideally it would go in a more generic document
> where it isn't already covered.  That way we don't have to edit more than
> one if anything changes!
> +Communicating with the driver in userspace
> +------------------------------------------
> +Upon installation, the driver generates several files in sysfs. If your
> +platform is configured as detailed above, navigate to the following
> +path to find these files:
> +/sys/bus/iio/devices/iio:device0
> +
> +The list below provides a brief description for each file.
> +--------------------------------------
> Firstly these need documenting in
> Documentation/abi/testing/syfs-bus-iio-mpu6050 or similar using the
> standard formats found in there.
> 
> +For MPU6050:
> +temperature (Read-only)
> +Read temperature data directly from the temperature register.
> Not abi compliant in_temp_input probably.
> +
> +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.
> What controls this?  I'd be tempted to drop it as not terribly useful to
> the user if they can't change it.
> Probably a debugging aid?
> +
> +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.
> That's in the magic value category.  If you do need a self test like this,
> make it output a nice string to say what happened.
> 
> +
> +key (read-only)
> +show the key value of this driver. Used by MPL.
> Define the acronym!
> +
> +gyro_matrix (read-only)
> +show the orient matrix obtained from board file.
> why?
> +
> +gyro_enable (read/write)
> +enable/disable gyro functionality. affect raw_gyro. turn off this will
> +shut down gyro and save power.
> If the startup time is not horendous do this dynamically. E.g. bring it up
> when a reading is requested or if it in use by the buffered interfaces.
> +
> +accl_enable (read/write)
> +enable/disable accelerometer functionality. affect raw_accl.
> +turn off this will shut down accel and save power.
> +
> +firmware_loaded (read/write)
> +Flag indicate 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. 1 is updated internally.
> Not sure this should be exposed to the users.   Firmware should always be
> loaded and done automatically before the device interfaces appear.
> If there is another reason for this, please give details.
> +
> +dmp_on(read/write)
> +This entry controls whether to run DMP or not. To enable DMP ,
> +firmware_loaded must be 1. write 1 to enable DMP and write 0 to disable
> dmp.
> +
> +dmp_in_on(read/write)
> +This entry controls whether dmp interrupt is on/off. firmware_loaded
> +must be 1. sometimes, it is desirable that interrupt is off while DMP
> +is
> running.
> This is definitely not something that should be exposed to userspace. Work
> out when it is 'desirable' and do the appropriate control in driver.
> +
> +dmp_event_int_on(read/write)
> +This entry controls whether dmp event interrupt is on/off. Setting this
> +on would turn off the data interrupt and turn on the event interrupt.
> +No data interrupt would be generated. Only when event happens, does an
> +interrupt generate. This can be used in power saving mode when system
> +is waiting for a special event to wake up.
> Again, raw interrupt controls like this are not abi compliant and should
> not be exposed to the user.  It is far from obvious when users will want
> this and when they will not. If it is useful in power saving mode then
> enable it, if not don't..
> +
> +dmp_firmware (write only binary file)
> +This is the entry that firmware code is loaded into. If the action is
> succeful,
> +firmware_loaded will be updated as 1. In order to load new firmware,
> +firmware_loaded flag should be set 0.
> No. Sorry, but the kernel has standard ways of loading firmware. Those are
> what you need to use.
> +
> +lpa_mode(read-write)
> +Low power  accelerometer mode
> +lpa_freq(read-write)
> +low power acceleromter frequency.
> Hmm.. There are drivers doing this sort of thing but it generalises very
> badly.
> What result does it actually have on the accelerometer?  I'd much rather
> it was controlled by dropping into this if they frequency was set
> appropriately rather than via this exta control
> +
> +accel_matrix
> +orient matrix for accel
> in_accel_orientation_matrix perhaps?  Needs to be associated with the
> channels explicitly.
> +
> +flick_lower,
> +flick_upper,
> +flick_counter,
> +flick_message_on,
> +flick_int_on,
> +flick_axis,
> +Flick related entry
> That's pretty opaque. Thes probably want to be mapped to the standard
> event types and those extended if necessary. Certainly don't want to be
> here.
> +
> +pedometer_time
> +pedometer_steps,
> +Pedometer related entry
> Cool. Not see that before. Probably want to do that with a new channel
> type. No reason we won't get more pedometers at some point.
> 
> +
> +event_flick
> +event_tap
> +event_orientation
> +event_display_orientation
> +event related entry. These entry must use poll to read.
> Using poll in sysfs is pretty heavily frowned upon except for extremely
> unlikely error conditions.  These need to go through the event interface
> of iio.
> If we
> need to extend that to handle them then by all means send patches to do
> so.
> +
> +tap_on
> +control tap function of DMP
> +
> +dmp_int_on
> +turn on/off dmp interrupt.
> you have both dmp_in_on and dmp_int_on.  I don't like either ;)
> +
> +dmp_output_rate
> +control dmp output rate when dmp is on.
> That's another 'sampling frequency' for the relevant channels.
> +
> +tap_time
> +tap_min_count
> +tap_threshold
> +tap related entries. control various parameters of tap function.
> More stuff that needs to go through the event interface.
> +
> +orientation_on
> +turn on/off orientation function of DMP.
> Hmm.. Is this for power reasons?  We really need to start a discussion
> about fine grained power control for this sort of device.  Right now I
> think you may have to drop it or control it via the enabled channels on
> the buffered interface.
> +
> +display_orientation_on
> +turn on/off display orientation function of DMP.
> +
> +quaternion_on
> +turn on/off quaterniion data output. must use DMP.
> +-------------------------------------------------------------------
> +for MPU9150 and secondary compass
> +MPU9150 has every entry MPU6050 has. It has additional entries:
> +
> +compass_enable (read/write)
> +enable this will enable compass function.
> +
> +compass_matrix (read-only)
> +compass orient matrix
> Need to map to normal channel types (or add one if this doesn't map to a
> magnetometer?)
> +-----------------------------------------------------------------------
> +-----------
> +low power accelerometer mode
> +Lower power accelerometer mode is a special mode. It works only for
> accelerometer.
> +It has two entries, lpa_mode and lpa_freq. Only MPU6050 and MPU9150 has
> this mode.
> +To run low power accel mode, set lpa_mode to 1, set lpa_freq to 0~3,
> which corresponds
> +to 1.25Hz, 5Hz, 20Hz, 40Hz. "gyro_enable" and "compass_enable" must be
> zero. "dmp_on"
> +must be zero.
> Then enable it automatically if the acceleration is all that is enabled
> and the frequency is one of those.
> +-----------------------------------------------------------------------
> +------------
> +dmp event.
> +dmp event is event out by the DMP unit inside MPU. Only MPU6050 and
> MPU9150 supports this.
> +There are four sysfs entreis, event_flick, event_tap and
> event_orientation and
> entries
> +event_display_orientation. These four events must be polled before
> +read. The proper method to poll sysfs is:
> +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.
> Nope. These need to come out of a proper event interface.  The load
> involved in doing polling on sysfs is huge and gets very rude responses
> from GregKH and others.
> As this patch will go via Greg after me, you really don't want to do this!
> +-----------------------------------------------------------------------
> +------- If streaming to a userspace application, the recommended way to
> +access
> gyro/accel/compass
> +data is via /dev/iio:device0. Follow these steps to get constant
> readings from
> +the driver:
> It will only be /dev/iio:device[0] if this is the first iio device
> added....
> +
> +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.
> err. fifo_rate is documented.  It is probably what we would normally map
> to sampling_frequency. I'm guessing there may be additional sampling
> frequencies for the motion calculation units?  We may need to think hard
> about how to control those?  To my mind there ought to be an 'ideal' set
> of values for a given output rate from the firmware processing.
> +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, using "dmp_on" to enable/disable dmp .
> +7. If compass is enabled, output will have compass data.
> +=========================================================================
> ==
> +                    Recommended sysfs entry setup senquence 1. without
> +DMP firmware
> +1.1 set "power_state" to 1,
> +1.2 change scale and fifo rate value to your need.
> +1.3 change gyro_enable and accle_enable and compass_enable to your
> needs. For example,
> +if you want gyro only, set accl_enable to 0 or set accl_enable to zero
> and compass_enable to zero.
> +If you want accel only, set gyro_enable to 0 or set gyro_enable to zero
> and compass_enable to zero.
> +If you want compass only, disable gyro and accel.
> +1.4 set "enable" to 1. you will get output you want.
> +
> +2. With DMP firmware
> +2.1 set "power_state" to 1,
> +2.2 write "0" to firmware_loaded if it is not zero already.
> +2.3 load firmware into "dmp_firmware" as a whole. Don't split the DMP
> firmware image.
> +2.4 make sure firmware_loaded is 1 after loading.
> +2.5 make other configurations similar to the situation as without DMP
> firmware.
> +2.6 set dmp_on to 1.
> +2.7 set "enable" to 1.
> +=======================================================
> +The enable function is using enable entry under
> "/sys/bus/iio/devices/iio:device0/buffer"
> +==========================================================
> +test applications:
> +Test application is mpu_iio
> provided here?
> +------------------------------------------
> +To run with MPU9150/MPU6050:
> +using the following command:
> +for orientation/tap/flick/display orientation event:
> +mpu_iio  -c 10 -l 3 -p
> +for normal data print
> +mpu_iio  -c 10 -l 3 -r
> +----------------------------------------
> +Please use mpu_iio.c and iio_utils.h as the sample code for your
> development.
> diff --git a/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
> b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
> new file mode 100644
> index 0000000..5803643
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/dmpDefaultMPU6050.c
> @@ -0,0 +1,16 @@
> +/*
> + * 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.
> + *
> + */
> +#include "dmpKey.h"
> +#include "dmpmap.h"
> +
> Some of these are less than entirely obvious. Perhaps some comments would
> help?
> +#define CFG_27                  (2745)
> ...
> +
> I hate to point out the obvious, but a macro might serve better here.
> #define D(a, b) (a*256 + b)
> +#define D_1_2                   (256 + 2)
> +#define D_1_4                   (256 + 4)
> ...
> 
> +
> +#define D_HOST_NO_MOT          (976)
> +
> So a lot of the above is about providing the config?
> Loose the defines and put them straight into here e.g.
> 
> static const struct tkeyLabel dmpTConfig[] = {
>         {KEY_CFG_27, 2745}, etc.
> +static const struct tKeyLabel dmpTConfig[] = {
> +	{KEY_CFG_27,                    CFG_27},
> +	{KEY_CFG_20,                    CFG_20},
> ....
> 
> +};
> +#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0]))
> kernel has ARRAY_SIZE macro
> +
> +static struct tKeyLabel keys[NUM_KEYS];
> +
> +unsigned short inv_dmp_get_address(unsigned short key) {
> +	static int isSorted;
> Is this going to play well if you have more than one of your chips
> present?
> Possibly, I haven't really thought it through!
> +	if (!isSorted) {
> +		int kk;
> +		for (kk = 0; kk < NUM_KEYS; ++kk) {
> +			keys[kk].addr = 0xffff;
> +			keys[kk].key = kk;
> +		}
> +		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk)
> +			keys[dmpTConfig[kk].key].addr =
> dmpTConfig[kk].addr;
> +		isSorted = 1;
> +	}
> +	if (key >= NUM_KEYS)
> +		return 0xffff;
> +	return keys[key].addr;
> +}
> +/**
> + *  @}
> + */
> diff --git a/drivers/staging/iio/imu/mpu6050/dmpKey.h
> b/drivers/staging/iio/imu/mpu6050/dmpKey.h
> new file mode 100644
> index 0000000..e8e1951
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/dmpKey.h
> @@ -0,0 +1,16 @@
> +/*
> + * 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 DMPKEY_H__
> +#define DMPKEY_H__
> +
> Not sure the stringing them off the previous is a good idea.
> If they really are always like this, then use an enum instead.
> +#define KEY_CFG_25                  (0)
> +#define KEY_CFG_24                  (KEY_CFG_25 + 1)
> ,,,
> 
> +
> +struct tKeyLabel  {
> +	unsigned short key;
> +	unsigned short addr;
> +};
> +
> This is an 'interesting' set of defines.
> Why not just use the value in the code?
> Or for that matter put them in order...
> 
> +#define DINA0A 0x0a
> +#define DINA22 0x22
> ...
> +
> +#endif
> diff --git a/drivers/staging/iio/imu/mpu6050/dmpmap.h
> b/drivers/staging/iio/imu/mpu6050/dmpmap.h
> new file mode 100644
> index 0000000..420e19d
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/dmpmap.h
> @@ -0,0 +1,17 @@
> +/*
> +* 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 DMPMAP_H
> +#define DMPMAP_H
> +
> If we are going to have this, perhaps have some documenation?
> Also it's only included from one place that I can see. Just flatten it in
> there and drop this header.
> 
> +#define DMP_PTAT    0
> +#define DMP_XGYR    2
> ...
> +
> +#endif
> diff --git a/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
> b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
> new file mode 100644
> index 0000000..0958d4a
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_iio.h
> @@ -0,0 +1,25 @@
> +/*
> +* 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 _INV_MPU_IIO_H_
> +#define _INV_MPU_IIO_H_
> +
> +#include <linux/i2c.h>
> +#include <linux/kfifo.h>
> +#include <linux/miscdevice.h>
> +#include <linux/input.h>
> +#include <linux/spinlock.h>
> +#include "./mpu.h"
> +#include "../../iio.h"
> +#include "../../buffer.h"
> Do these as <linux/iio/iio.h> etc.
> Actually you seem to be on a somewhat out of data tree.  You'll want to
> rebase on staging/staging-next before the next submission.
> 
> +#include "dmpKey.h"
> +/**
> Nice docs. Thanks.
> + *  struct inv_reg_map_s - Notable slave registers.
> + *  @sample_rate_div:	Divider applied to gyro output rate.
> + *  @lpf:		Configures internal LPF.
> Spell it out, low_pass_filter (I guess?)
> + *  @bank_sel:		Selects between memory banks.
> + *  @user_ctrl:		Enables/resets the FIFO.
> + *  @fifo_en:		Determines which data will appear in FIFO.
> + *  @gyro_config:	gyro config register.
> + *  @accl_config:	accel config register
> + *  @fifo_count_h:	Upper byte of FIFO count.
> + *  @fifo_r_w:		FIFO register.
> + *  @raw_gyro		Address of first gyro register.
> + *  @raw_accl		Address of first accel register.
> + *  @temperature	temperature register
> + *  @int_enable:	Interrupt enable register.
> + *  @int_status:	Interrupt flags.
> + *  @pwr_mgmt_1:	Controls chip's power state and clock source.
> + *  @pwr_mgmt_2:	Controls power state of individual sensors.
> + *  @mem_start_addr:	Address of first memory read.
> + *  @mem_r_w:		Access to memory.
> + *  @prgm_strt_addrh	firmware program start address register
> + */
> +struct inv_reg_map_s {
> +	unsigned char sample_rate_div;
> As in kernel, u8 is the right type to use.
> +	unsigned char lpf;
> ....
> +	const short *compass_st_lower;
> +	short irq;
> +	int accel_bias[3];
> +	int gyro_bias[3];
> I'd use fixed length types s16 perhaps?
> +	short raw_gyro[3];
> +	short raw_accel[3];
> +	short raw_compass[3];
> +	unsigned char compass_scale;
> +	unsigned char i2c_addr;
> +	unsigned char compass_divider;
> +	unsigned char compass_counter;
> +	unsigned char sample_divider;
> +	unsigned char fifo_divider;
> +	unsigned char orient_data;
> +	unsigned char display_orient_data;
> +	unsigned char tap_data;
> +	enum inv_channel_num num_channels;
> +	void *sl_handle;
> +	unsigned int irq_dur_ns;
> +	long long last_isr_time;
> +#ifdef CONFIG_INV_TESTING
> +	unsigned long i2c_readcount;
> +	unsigned long i2c_writecount;
> +#endif
> +};
> +
> 
> +	int (*setup)(struct inv_mpu_iio_s *);
> +	int (*combine_data)(unsigned char *in, short *out);
> +	int (*get_mode)(struct inv_mpu_iio_s *);
> +	int (*set_lpf)(struct inv_mpu_iio_s *, int rate);
> +	int (*set_fs)(struct inv_mpu_iio_s *, int fs); };
> +
> Are these specific to your handling or to the AKM chips themselves?
> If the akm chips, should be in a separate header.
> I'd much rather see a generic driver for these with the extra hooks you
> need rather than rolling them into your driver and having lots of code
> repitition.
> 
> +/* 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
> 
> ...
> +	ATTR_COMPASS_ENABLE,
> +	ATTR_POWER_STATE,
> +	ATTR_FIRMWARE_LOADED,
> Clear these out, or do them as a debugfs interface as some other drivers
> use.
> +#ifdef CONFIG_INV_TESTING
> +	ATTR_I2C_COUNTERS,
> +	ATTR_REG_WRITE,
> +#endif
> +};
> +
> +enum inv_accl_fs_e {
> +	INV_FS_02G = 0,
> +	INV_FS_04G,
> +	INV_FS_08G,
> +	INV_FS_16G,
> +	NUM_ACCL_FSR
> +};
> ...
> +int inv_i2c_read_base(struct inv_mpu_iio_s *st, unsigned short i2c_addr,
> +	unsigned char reg, unsigned short length, unsigned char *data);
> This isn't defined anywhere that I can see? Each patch should stand on its
> own. If I can't build them one at a time then I reject them.  This is
> vital in a more general sense for kernel development as we HAVE to have a
> bisectable set of patches.
> 
> +int inv_i2c_single_write_base(struct inv_mpu_iio_s *st,
> +	unsigned short i2c_addr, unsigned char reg, unsigned char data);
> int
> +inv_do_test(struct inv_mpu_iio_s *st, int self_test_flag,
> +		int *gyro_result, int *accl_result);
> +int mpu_memory_write(struct i2c_adapter *i2c_adap,
> +			    unsigned char mpu_addr,
> +			    unsigned short mem_addr,
> +			    unsigned int len, unsigned char const *data);
> int
> +mpu_memory_read(struct i2c_adapter *i2c_adap,
> +			   unsigned char mpu_addr,
> +			   unsigned short mem_addr,
> +			   unsigned int len, unsigned char *data); int
> +inv_hw_self_test(struct inv_mpu_iio_s *st);
> +
> +#define mem_w(a, b, c) mpu_memory_write(st->sl_handle,\
> +			st->i2c_addr, a, b, c)
> +#define mem_w_key(key, b, c) mpu_memory_write(st->sl_handle,\
> +			st->i2c_addr, inv_dmp_get_address(key), b, c)
> #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/mpu6050/inv_mpu_ring.c
> b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
> new file mode 100644
> index 0000000..528da5f
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/inv_mpu_ring.c
> @@ -0,0 +1,44 @@
> +/*
> +* 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
> ...
> 
> +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;
> +
> +	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;
> st->chip_config.gyro_fifo_enable =
> 	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);
> +	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; }
> +
> +/**
> + *  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;
> +	unsigned char val;
> +	struct inv_mpu_iio_s  *st = iio_priv(indio_dev);
> I'd just use st->reg directly, can't see why taking a copy helps other
> than confusing me slightly ;)
> +	reg = &st->reg;
> +
> +	inv_scan_query(indio_dev);
> +	/* 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;
> ....
> +	return result;
> +}
> +
> 
> As functions go, this one currently doesn't seem to have a great deal of
> point... drop it, you can always introduce it later if it has more to do.
> +/**
> + *  inv_reset_fifo() - Reset FIFO related registers.
> + *  @st:	Device driver instance.
> + */
> +static int inv_reset_fifo(struct iio_dev *indio_dev) {
> +	return reset_fifo_itg(indio_dev);
> +}
> +
> ....
> +/**
> + *  inv_clear_kfifo() - clear time stamp fifo
> + *  @st:	Device driver instance.
> + */
> +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_read_fifo() - Transfer data from FIFO to ring buffer.
> + */
> +irqreturn_t inv_read_fifo(int irq, void *dev_id) {
> +
> Bonus blank line here.  Yup, I'm getting nitpicky ;)
> +	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;
> +	unsigned char data[BYTES_FOR_DMP + QUATERNION_BYTES];
> +	unsigned short fifo_count;
> +	unsigned int copied;
> +	s64 timestamp;
> +	struct inv_reg_map_s *reg;
> +	s64 buf[8];
> +	unsigned char *tmp;
> +	reg = &st->reg;
> Given the number of repeats of checking if anything is turned on, perhaps
> a inv_anything_enabled inline function might make for shorter easier to
> read code.
> +	if (!(st->chip_config.accl_fifo_enable |
> +		st->chip_config.gyro_fifo_enable |
> +		st->chip_config.dmp_on |
> +		st->chip_config.compass_fifo_enable))
> +		goto end_session;
> ...
> +
> +int inv_postenable(struct iio_dev *indio_dev) {
> +	return set_inv_enable(indio_dev, true);
> bonus blank line.  This formatting stuff does matter if you are reviewing
> a lot of code. Makes it simpler to do.
> +
> +}
> +
> +int inv_predisable(struct iio_dev *indio_dev) {
> +	return set_inv_enable(indio_dev, false); }
> +
> +static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
> +	.preenable = &iio_sw_buffer_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(indio_dev);
> +	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*/
> +	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;
> +
> +	return 0;
> +error_iio_sw_rb_free:
> +	iio_kfifo_free(indio_dev->buffer);
> +	return ret;
> +}
> 
> +/**
> + *  @}
> + */
> +
> diff --git a/drivers/staging/iio/imu/mpu6050/mpu.h
> b/drivers/staging/iio/imu/mpu6050/mpu.h
> new file mode 100644
> index 0000000..5105fb2
> --- /dev/null
> +++ b/drivers/staging/iio/imu/mpu6050/mpu.h
> @@ -0,0 +1,15 @@
> +/*
> +* 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.
> +*
> +*/
> +
> +/**
> I may be missing something but is this kerneldoc? If not it doesn't belong
> in a kernel driver...
> + *  @addtogroup  DRIVERS
> + *  @brief       Hardware drivers.
> + *
> + *  @{
> + *      @file    mpu.h
> + *      @brief   mpu definition
> + */
> +
> +#ifndef __MPU_H_
> +#define __MPU_H_
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/ioctl.h>
> +#endif
> +
> +enum secondary_slave_type {
> +	SECONDARY_SLAVE_TYPE_NONE,
> +	SECONDARY_SLAVE_TYPE_ACCEL,
> +	SECONDARY_SLAVE_TYPE_COMPASS,
> +	SECONDARY_SLAVE_TYPE_PRESSURE,
> +
> +	SECONDARY_SLAVE_TYPE_TYPES
> +};
> +
> +};
> +
> +#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
> blank line here please.
> +/**
> + * 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
> + *
> + * Contains platform specific information on how to configure the
> MPU3050 to
> + * work on this platform.  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 mpu_platform_data {
> why the __ versions?
> +	__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];
> +	__u8 key[16];
> +};
> +
> +#endif	/* __MPU_H_ */
> --
> 1.7.0.4
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-07-07  8:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-28 18:30 [PATCH 1/4] IIO: Invensense MPU6050/MPU9150 driver submit Ge Gao
2012-07-06  9:07 ` Jonathan Cameron
2012-07-06 17:40   ` Ge Gao
2012-07-07  8:51     ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).