summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/mtdnand.tmpl2
-rw-r--r--Documentation/devicetree/bindings/bus/ti-gpmc.txt1
-rw-r--r--Documentation/devicetree/bindings/input/atmel,maxtouch.txt25
-rw-r--r--Documentation/devicetree/bindings/mtd/gpmc-nand.txt24
-rw-r--r--Makefile2
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/boot/dts/Makefile1
-rw-r--r--arch/arm/boot/dts/omap3-h1-ev1.dts57
-rw-r--r--arch/arm/boot/dts/omap3.dtsi272
-rw-r--r--arch/arm/boot/dts/omap3430-sdp.dts1
-rw-r--r--arch/arm/boot/dts/omap36xx.dtsi35
-rw-r--r--arch/arm/boot/dts/omap3_h1.dts57
-rw-r--r--arch/arm/configs/omap3_h1_defconfig2952
-rw-r--r--arch/arm/configs/omap3_h1_defconfig.old2858
-rw-r--r--arch/arm/mach-omap2/Kconfig28
-rw-r--r--arch/arm/mach-omap2/Makefile2
-rw-r--r--arch/arm/mach-omap2/board-flash.c2
-rw-r--r--arch/arm/mach-omap2/board-omap3h1-bluetooth.c387
-rw-r--r--arch/arm/mach-omap2/board-omap3h1.c1267
-rw-r--r--arch/arm/mach-omap2/board-omap3h1.h30
-rw-r--r--arch/arm/mach-omap2/devices.c4
-rw-r--r--arch/arm/mach-omap2/fb.c5
-rw-r--r--arch/arm/mach-omap2/gpmc-nand.c4
-rw-r--r--arch/arm/mach-omap2/gpmc.c130
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_3xxx_data.c4
-rw-r--r--arch/arm/mach-omap2/opp.c2
-rw-r--r--arch/arm/mach-omap2/opp3xxx_data.c4
-rw-r--r--arch/arm/mach-omap2/pm-debug-regs.h2
-rw-r--r--arch/arm/mach-omap2/pm.c2
-rw-r--r--arch/arm/mach-omap2/pmu.c5
-rw-r--r--arch/arm/mach-omap2/sdram-micron-mt29c4g48.h63
-rw-r--r--arch/arm/mach-s5pv210/mach-goni.c8
-rw-r--r--arch/arm/tools/mach-types1
-rw-r--r--drivers/base/power/runtime.c1
-rw-r--r--drivers/gpio/gpio-tps65910.c4
-rw-r--r--drivers/iio/Kconfig3
-rw-r--r--drivers/iio/Makefile2
-rw-r--r--drivers/iio/imu-aosp/Kconfig (renamed from drivers/iio/imu/Kconfig)2
-rw-r--r--drivers/iio/imu-aosp/Makefile (renamed from drivers/iio/imu/Makefile)1
-rw-r--r--drivers/iio/imu-aosp/adis.c (renamed from drivers/iio/imu/adis.c)0
-rw-r--r--drivers/iio/imu-aosp/adis16400.h (renamed from drivers/iio/imu/adis16400.h)0
-rw-r--r--drivers/iio/imu-aosp/adis16400_buffer.c (renamed from drivers/iio/imu/adis16400_buffer.c)0
-rw-r--r--drivers/iio/imu-aosp/adis16400_core.c (renamed from drivers/iio/imu/adis16400_core.c)0
-rw-r--r--drivers/iio/imu-aosp/adis16480.c (renamed from drivers/iio/imu/adis16480.c)0
-rw-r--r--drivers/iio/imu-aosp/adis_buffer.c (renamed from drivers/iio/imu/adis_buffer.c)0
-rw-r--r--drivers/iio/imu-aosp/adis_trigger.c (renamed from drivers/iio/imu/adis_trigger.c)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/Kconfig (renamed from drivers/iio/imu/inv_mpu6050/Kconfig)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/Makefile (renamed from drivers/iio/imu/inv_mpu6050/Makefile)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_core.c (renamed from drivers/iio/imu/inv_mpu6050/inv_mpu_core.c)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_iio.h (renamed from drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_ring.c (renamed from drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c)0
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_trigger.c (renamed from drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c)0
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/Kconfig40
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/Makefile56
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/README659
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c384
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/dmpKey.h607
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/dmpmap.h263
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c271
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c3135
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c270
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h30
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h1078
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c2041
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c1899
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c94
-rw-r--r--drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c315
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c854
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c522
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig13
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile6
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c154
-rwxr-xr-xdrivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h72
-rw-r--r--drivers/iio/industrialio-core.c5
-rw-r--r--drivers/iio/light/Kconfig25
-rw-r--r--drivers/iio/light/Makefile2
-rw-r--r--drivers/iio/light/cm3391.c784
-rw-r--r--drivers/iio/magnetometer/Makefile2
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/Kconfig47
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/Makefile48
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/README176
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak09911_core.c512
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h115
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c139
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c75
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c590
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h144
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c138
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c75
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ami306_core.c570
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ami306_iio.h159
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ami306_ring.c163
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c90
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_yas53x_core.c969
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h172
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c165
-rwxr-xr-xdrivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c91
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c3295
-rw-r--r--drivers/leds/leds-lm3530.c67
-rw-r--r--drivers/mfd/tps65910.c2
-rw-r--r--drivers/mtd/nand/atmel_nand.c2
-rw-r--r--drivers/mtd/nand/au1550nd.c2
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c6
-rw-r--r--drivers/mtd/nand/gpio.c231
-rw-r--r--drivers/mtd/nand/jz4740_nand.c4
-rw-r--r--drivers/mtd/nand/lpc32xx_mlc.c2
-rw-r--r--drivers/mtd/nand/lpc32xx_slc.c2
-rw-r--r--drivers/mtd/nand/mxc_nand.c3
-rw-r--r--drivers/mtd/nand/nand_bbt.c33
-rw-r--r--drivers/mtd/nand/omap2.c662
-rw-r--r--drivers/mtd/nand/orion_nand.c5
-rw-r--r--drivers/mtd/nand/plat_nand.c4
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c124
-rw-r--r--drivers/mtd/nand/s3c2410.c2
-rw-r--r--drivers/mtd/nand/sh_flctl.c4
-rw-r--r--drivers/mtd/nand/sharpsl.c2
-rw-r--r--drivers/mtd/nand/tmio_nand.c2
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c8
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c1
-rw-r--r--drivers/power/bq27x00_battery.c1228
-rw-r--r--drivers/regulator/core.c13
-rw-r--r--drivers/regulator/tps65910-regulator.c2
-rw-r--r--drivers/staging/iio/Kconfig1
-rw-r--r--drivers/staging/iio/Makefile1
-rw-r--r--drivers/staging/iio/imu/inv_mpu/Kconfig32
-rw-r--r--drivers/staging/iio/imu/inv_mpu/Makefile37
-rw-r--r--drivers/staging/iio/imu/inv_mpu/README659
-rw-r--r--drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c381
-rw-r--r--drivers/staging/iio/imu/inv_mpu/dmpKey.h607
-rw-r--r--drivers/staging/iio/imu/inv_mpu/dmpmap.h263
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c271
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c3148
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c270
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h30
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h1068
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c2028
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c1868
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c80
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c315
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c852
-rw-r--r--drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c510
-rw-r--r--drivers/staging/iio/imu/inv_mpu/olio_specials.h17
-rw-r--r--drivers/staging/iio/inv_test/Kconfig13
-rw-r--r--drivers/staging/iio/inv_test/Makefile6
-rw-r--r--drivers/staging/iio/inv_test/inv_counters.c154
-rw-r--r--drivers/staging/iio/inv_test/inv_counters.h72
-rw-r--r--drivers/usb/phy/phy-ulpi.c2
-rw-r--r--drivers/video/omap2/displays/Kconfig8
-rw-r--r--drivers/video/omap2/displays/Makefile1
-rw-r--r--drivers/video/omap2/displays/panel-ili9342.c598
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c8
-rw-r--r--include/linux/i2c/atmel_mxt_ts.h33
-rw-r--r--include/linux/mpu.h111
-rw-r--r--include/linux/mtd/bbm.h2
-rw-r--r--include/linux/platform_data/mtd-nand-omap2.h20
-rw-r--r--include/linux/platform_data/mtd-onenand-omap2.h2
-rw-r--r--include/linux/tty.h5
-rw-r--r--sound/soc/codecs/dmic.c3
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/omap3h1.c147
162 files changed, 43930 insertions, 1681 deletions
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl
index fe122d6e686..a248f42a121 100644
--- a/Documentation/DocBook/mtdnand.tmpl
+++ b/Documentation/DocBook/mtdnand.tmpl
@@ -1224,8 +1224,6 @@ in this page</entry>
#define NAND_BBT_CREATE 0x00000200
/* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000400
-/* Scan block empty during good / bad block scan */
-#define NAND_BBT_SCANEMPTY 0x00000800
/* Write bbt if neccecary */
#define NAND_BBT_WRITE 0x00001000
/* Read and write back block contents when writing bbt */
diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt
index 4b87ea1194e..704be9306c9 100644
--- a/Documentation/devicetree/bindings/bus/ti-gpmc.txt
+++ b/Documentation/devicetree/bindings/bus/ti-gpmc.txt
@@ -95,7 +95,6 @@ GPMC chip-select settings properties for child nodes. All are optional.
- gpmc,burst-wrap Enables wrap bursting
- gpmc,burst-read Enables read page/burst mode
- gpmc,burst-write Enables write page/burst mode
-- gpmc,device-nand Device is NAND
- gpmc,device-width Total width of device(s) connected to a GPMC
chip-select in bytes. The GPMC supports 8-bit
and 16-bit devices and so this property must be
diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
new file mode 100644
index 00000000000..6319f07bfbc
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
@@ -0,0 +1,25 @@
+Atmel maXTouch touchscreen/touchpad
+
+Required properties:
+- compatible:
+ atmel,maxtouch
+
+- reg: The I2C address of the device
+
+- interrupts: The sink for the touch controller CHG/IRQ output
+ See ../interrupt-controller/interrupts.txt
+
+Optional properties for maXTouch device:
+
+- linux,gpio-keymap: An array of up to 4 entries indicating the Linux
+ keycode generated by each GPIO. Linux keycodes are defined in
+ <dt-bindings/input/input.h>.
+
+Example:
+
+ touch@4b {
+ compatible = "atmel,maxtouch";
+ reg = <0x4b>;
+ interrupt-parent = <&gpio>;
+ interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_LOW>;
+ };
diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
index 6a983c1d87c..5e1f31b5ff7 100644
--- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
@@ -22,15 +22,26 @@ Optional properties:
width of 8 is assumed.
- ti,nand-ecc-opt: A string setting the ECC layout to use. One of:
-
- "sw" Software method (default)
- "hw" Hardware method
- "hw-romcode" gpmc hamming mode method & romcode layout
+ "sw" <deprecated> use "ham1" instead
+ "hw" <deprecated> use "ham1" instead
+ "hw-romcode" <deprecated> use "ham1" instead
+ "ham1" 1-bit Hamming ecc code
"bch4" 4-bit BCH ecc code
"bch8" 8-bit BCH ecc code
- - elm_id: Specifies elm device node. This is required to support BCH
- error correction using ELM module.
+ - ti,nand-xfer-type: A string setting the data transfer type. One of:
+
+ "prefetch-polled" Prefetch polled mode (default)
+ "polled" Polled mode, without prefetch
+ "prefetch-dma" Prefetch enabled sDMA mode
+ "prefetch-irq" Prefetch enabled irq mode
+
+ - elm_id: <deprecated> use "ti,elm-id" instead
+ - ti,elm-id: Specifies phandle of the ELM devicetree node.
+ ELM is an on-chip hardware engine on TI SoC which is used for
+ locating ECC errors for BCHx algorithms. SoC devices which have
+ ELM hardware engines should specify this device node in .dtsi
+ Using ELM for ECC error correction frees some CPU cycles.
For inline partiton table parsing (optional):
@@ -55,6 +66,7 @@ Example for an AM33xx board:
reg = <0 0 0>; /* CS0, offset 0 */
nand-bus-width = <16>;
ti,nand-ecc-opt = "bch8";
+ ti,nand-xfer-type = "polled";
gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>;
diff --git a/Makefile b/Makefile
index dc8b8475cb1..79984a5f4cb 100644
--- a/Makefile
+++ b/Makefile
@@ -369,7 +369,7 @@ LINUXINCLUDE := \
KBUILD_CPPFLAGS := -D__KERNEL__
-KBUILD_CFLAGS := -Werror -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
+KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f414eaa38e5..7f7977cf934 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1596,6 +1596,7 @@ config ARCH_NR_GPIO
default 352 if ARCH_VT8500
default 288 if ARCH_SUNXI
default 264 if MACH_H4700
+ default 256 if MACH_OMAP3_H1
default 0
help
Maximum number of GPIOs in the system.
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index e2a70d9fb96..ebd3b406229 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -161,6 +161,7 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
am335x-evm.dtb \
am335x-evmsk.dtb \
am335x-bone.dtb
+dtb-$(CONFIG_MACH_OMAP3_H1) += omap3-h1-ev1.dtb
dtb-$(CONFIG_MACH_MINNOW) += omap3-minnow-p0.dtb omap3-casper-p1.dtb
dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb
dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
diff --git a/arch/arm/boot/dts/omap3-h1-ev1.dts b/arch/arm/boot/dts/omap3-h1-ev1.dts
new file mode 100644
index 00000000000..fc8a507b855
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-h1-ev1.dts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 Olio Devices, Inc.
+ * Authors: Evan Wilson <evan@oliodevices.com>
+ * Mattis Fjallstrom <mattis@oliodevices.com>
+ *
+ * Modified from omap3-beagle-xm.dts
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+
+/include/ "omap36xx.dtsi"
+/ {
+ model = "Olio OMAP3 H1 Board";
+ compatible = "olio,omap3-h1", "ti,omap3";
+
+ tusb_phy: tusb-usb@0 {
+ compatible = "ti,tusb-usb";
+ gpios = <&gpio5 14 0x1>; /* IRQ gpio-142 GPIOF_DIR_OUT|GPIOF_INIT_HIGH */
+ gpio-names = "tusb-cs";
+ clk_in = "periph_26mhz"; /* external 26MHz clock */
+ reg = <0 0>;
+ };
+
+ /* Needs to be modified for Olio H1 */
+
+ /* Consider setting up pad-wkup here, like in Minnow */
+
+
+};
+
+/* Lots of pinmux in the board file, as well */
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb_phy_pins>;
+
+ usb_phy_pins: pinmux_usb_phy_pins {
+ pinctrl-single,pins = <
+ 0x140 0x4104 /* MCBSP3_CLKX, MODE4 (GPIO-142) | INPUT | OFFWAKEUP */
+ 0x5ae 0x004 /* ETK_D1, MODE4 (GPIO_15) | OUTPUT */
+ 0x5b0 0x004 /* ETK_D2, MODE4 (GPIO_16) | OUTPUT */
+ 0x5c6 0x004 /* EDK_D13, MODE4 (GPIO_27) | OUTPUT */
+ >;
+ };
+};
+
+
+&usb_otg_hs {
+ interface-type = <0>;
+ usb-phy = <&tusb_phy>;
+ mode = <3>;
+ power = <50>;
+
+};
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index 2b8802164c1..4d45679361b 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -14,17 +14,18 @@
compatible = "ti,omap3430", "ti,omap3";
interrupt-parent = <&intc>;
- aliases {
+ aliases { /*
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
+ */
};
- cpus {
- cpu@0 {
+ cpus {
+ cpu@0 {
compatible = "arm,cortex-a8";
- };
- };
+ };
+ };
pmu {
compatible = "arm,cortex-a8-pmu";
@@ -43,14 +44,14 @@
ti,hwmods = "mpu";
};
- iva {
- compatible = "ti,iva2.2";
- ti,hwmods = "iva";
-
- dsp {
- compatible = "ti,omap3-c64";
- };
- };
+ iva {
+ compatible = "ti,iva2.2";
+ ti,hwmods = "iva";
+
+ dsp {
+ compatible = "ti,omap3-c64";
+ };
+ };
};
/*
@@ -111,7 +112,7 @@
pinctrl-single,register-width = <16>;
pinctrl-single,function-mask = <0x7f1f>;
#gpio-range-cells = <3>;
- };
+ };
gpio1: gpio@48310000 {
compatible = "ti,omap3-gpio";
@@ -123,24 +124,31 @@
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
- };
+ };
- gpio2: gpio@49050000 {
- compatible = "ti,omap3-gpio";
- reg = <0x49050000 0x200>;
- interrupts = <30>;
- ti,hwmods = "gpio2";
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- /* for gpio 32 33 there is no pnctrl controller defined */
- gpio-ranges = <&omap3_pmx_core 2 0x25 10>,
- <&omap3_pmx_core 12 0x37 8>,
- <&omap3_pmx_core 20 0x40 8>,
- <&omap3_pmx_core 28 0x4B 3>,
- <&omap3_pmx_core 31 0x4f 1>;
- };
+ /* disabling this one makes suspend work */
+ /* but it also causes the touch to NOT work */
+
+ gpio2: gpio@49050000 {
+ compatible = "ti,omap3-gpio";
+ reg = <0x49050000 0x200>;
+ interrupts = <30>;
+ ti,hwmods = "gpio2";
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ /* for gpio 32 33 there is no pnctrl controller defined */
+ /* gpio-ranges = <&omap3_pmx_core 2 0x25 10>, */
+ /* <&omap3_pmx_core 12 0x37 8>, */
+ /* <&omap3_pmx_core 20 0x40 8>, */
+ /* <&omap3_pmx_core 28 0x4B 3>, */
+ /* <&omap3_pmx_core 31 0x4f 1>; */
+ };
+
+ /* Removing this one disables touch,
+ * suspend works.
+ */
gpio3: gpio@49052000 {
compatible = "ti,omap3-gpio";
@@ -151,26 +159,34 @@
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
- gpio-ranges = <&omap3_pmx_core 0 0x50 32>;
- };
+ /* gpio-ranges = <&omap3_pmx_core 0 0x50 32>; */
+ };
- gpio4: gpio@49054000 {
- compatible = "ti,omap3-gpio";
+ /* Removing this one disables _both_ touch
+ * and suspend.
+ */
+
+ gpio4: gpio@49054000 {
+ compatible = "ti,omap3-gpio";
reg = <0x49054000 0x200>;
interrupts = <32>;
ti,hwmods = "gpio4";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <2>;
+ #interrupt-cells = <2>;
/*
- * gpio 127 in wkup mux, whcih we can not use (see
+ * gpio 127 in wkup mux, which we can not use (see
* sorce code for comment)
*/
- gpio-ranges = <&omap3_pmx_core 0 0x70 16>,
- <&omap3_pmx_core 16 0x82 14>,
- <&omap3_pmx_core 30 0x81 1>;
- };
+ /* gpio-ranges = <&omap3_pmx_core 0 0x70 16>, */
+ /* <&omap3_pmx_core 16 0x82 14>, */
+ /* <&omap3_pmx_core 30 0x81 1>; */
+ };
+
+ /* Removing this one causes touch to work but suspend to
+ * crash....
+ */
gpio5: gpio@49056000 {
compatible = "ti,omap3-gpio";
@@ -180,14 +196,18 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <2>;
+ #interrupt-cells = <2>;
/*
- * gpio 128 129 in wkup mux, whcih we can not use (see
- * sorce code for comment)
- */
- gpio-ranges = <&omap3_pmx_core 2 0x94 30>;
+ * gpio 128 129 in wkup mux, whcih we can not use (see
+ * sorce code for comment)
+ */
+ /* gpio-ranges = <&omap3_pmx_core 2 0x94 20>; */
};
+ /* Removing this one causes touch to work but suspend to
+ * crash....
+ */
+
gpio6: gpio@49058000 {
compatible = "ti,omap3-gpio";
reg = <0x49058000 0x200>;
@@ -196,36 +216,37 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <2>;
+ #interrupt-cells = <2>;
/* for gpio 187 there is no pnctrl controller defined */
- gpio-ranges = <&omap3_pmx_core 0 0xb2 7>,
- <&omap3_pmx_core 7 0x80 1>,
- <&omap3_pmx_core 8 0xc7 1>,
- <&omap3_pmx_core 9 0xc0 1>,
- <&omap3_pmx_core 10 0xcb 13>,
- <&omap3_pmx_core 23 0xc8 3>,
- <&omap3_pmx_core 26 0xd9 1>,
- <&omap3_pmx_core 28 0xc1 4>;
- };
+ /* gpio-ranges = <&omap3_pmx_core 0 0xb2 7>, */
+ /* <&omap3_pmx_core 7 0x80 1>, */
+ /* <&omap3_pmx_core 8 0xc7 1>, */
+ /* <&omap3_pmx_core 9 0xc0 1>, */
+ /* <&omap3_pmx_core 10 0xcb 13>, */
+ /* <&omap3_pmx_core 23 0xc8 3>, */
+ /* <&omap3_pmx_core 26 0xd9 1>, */
+ /* <&omap3_pmx_core 28 0xc1 4>; */
+ };
- uart1: serial@4806a000 {
- compatible = "ti,omap3-uart";
- ti,hwmods = "uart1";
- clock-frequency = <48000000>;
- };
+ /* uart1: serial@4806a000 { */
+ /* compatible = "ti,omap3-uart"; */
+ /* ti,hwmods = "uart1"; */
+ /* clock-frequency = <48000000>; */
+ /* }; */
- uart2: serial@4806c000 {
- compatible = "ti,omap3-uart";
- ti,hwmods = "uart2";
- clock-frequency = <48000000>;
- };
+ /* uart2: serial@4806c000 { */
+ /* compatible = "ti,omap3-uart"; */
+ /* ti,hwmods = "uart2"; */
+ /* clock-frequency = <48000000>; */
+ /* }; */
- uart3: serial@49020000 {
- compatible = "ti,omap3-uart";
- ti,hwmods = "uart3";
- clock-frequency = <48000000>;
- };
+ /* uart3: serial@49020000 { */
+ /* compatible = "ti,omap3-uart"; */
+ /* ti,hwmods = "uart3"; */
+ /* clock-frequency = <48000000>; */
+ /* }; */
+ /*
i2c1: i2c@48070000 {
compatible = "ti,omap3-i2c";
#address-cells = <1>;
@@ -245,8 +266,8 @@
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c3";
- };
-
+ }; */
+
mcspi1: spi@48098000 {
compatible = "ti,omap2-mcspi";
#address-cells = <1>;
@@ -299,7 +320,7 @@
ti,spi-num-cs = <1>;
dmas = <&sdma 70>, <&sdma 71>;
dma-names = "tx0", "rx0";
- };
+ };
mmc1: mmc@4809c000 {
compatible = "ti,omap3-hsmmc";
@@ -321,7 +342,7 @@
ti,hwmods = "mmc3";
dmas = <&sdma 77>, <&sdma 78>;
dma-names = "tx", "rx";
- };
+ };
wdt2: wdt@48314000 {
compatible = "ti,omap3-wdt";
@@ -331,11 +352,11 @@
mcbsp1: mcbsp@48074000 {
compatible = "ti,omap3-mcbsp";
reg = <0x48074000 0xff>;
- reg-names = "mpu";
- interrupts = <16>, /* OCP compliant interrupt */
- <59>, /* TX interrupt */
- <60>; /* RX interrupt */
- interrupt-names = "common", "tx", "rx";
+ reg-names = "mpu";
+ interrupts = <16>, /* OCP compliant interrupt */
+ <59>, /* TX interrupt */
+ <60>; /* RX interrupt */
+ interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp1";
dmas = <&sdma 31>,
@@ -348,11 +369,11 @@
reg = <0x49022000 0xff>,
<0x49028000 0xff>;
reg-names = "mpu", "sidetone";
- interrupts = <17>, /* OCP compliant interrupt */
- <62>, /* TX interrupt */
- <63>, /* RX interrupt */
- <4>; /* Sidetone */
- interrupt-names = "common", "tx", "rx", "sidetone";
+ interrupts = <17>, /* OCP compliant interrupt */
+ <62>, /* TX interrupt */
+ <63>, /* RX interrupt */
+ <4>; /* Sidetone */
+ interrupt-names = "common", "tx", "rx", "sidetone";
ti,buffer-size = <1280>;
ti,hwmods = "mcbsp2", "mcbsp2_sidetone";
dmas = <&sdma 33>,
@@ -365,11 +386,11 @@
reg = <0x49024000 0xff>,
<0x4902a000 0xff>;
reg-names = "mpu", "sidetone";
- interrupts = <22>, /* OCP compliant interrupt */
- <89>, /* TX interrupt */
- <90>, /* RX interrupt */
- <5>; /* Sidetone */
- interrupt-names = "common", "tx", "rx", "sidetone";
+ interrupts = <22>, /* OCP compliant interrupt */
+ <89>, /* TX interrupt */
+ <90>, /* RX interrupt */
+ <5>; /* Sidetone */
+ interrupt-names = "common", "tx", "rx", "sidetone";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp3", "mcbsp3_sidetone";
dmas = <&sdma 17>,
@@ -381,10 +402,10 @@
compatible = "ti,omap3-mcbsp";
reg = <0x49026000 0xff>;
reg-names = "mpu";
- interrupts = <23>, /* OCP compliant interrupt */
- <54>, /* TX interrupt */
- <55>; /* RX interrupt */
- interrupt-names = "common", "tx", "rx";
+ interrupts = <23>, /* OCP compliant interrupt */
+ <54>, /* TX interrupt */
+ <55>; /* RX interrupt */
+ interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp4";
dmas = <&sdma 19>,
@@ -396,16 +417,16 @@
compatible = "ti,omap3-mcbsp";
reg = <0x48096000 0xff>;
reg-names = "mpu";
- interrupts = <27>, /* OCP compliant interrupt */
- <81>, /* TX interrupt */
- <82>; /* RX interrupt */
- interrupt-names = "common", "tx", "rx";
+ interrupts = <27>, /* OCP compliant interrupt */
+ <81>, /* TX interrupt */
+ <82>; /* RX interrupt */
+ interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp5";
dmas = <&sdma 21>,
<&sdma 22>;
dma-names = "tx", "rx";
- };
+ };
timer1: timer@48318000 {
compatible = "ti,omap3430-timer";
@@ -501,13 +522,13 @@
ti,timer-alwon;
ti,timer-secure;
};
-
- usbhstll: usbhstll@48062000 {
- compatible = "ti,usbhs-tll";
- reg = <0x48062000 0x1000>;
- interrupts = <78>;
- ti,hwmods = "usb_tll_hs";
- };
+
+ /* usbhstll: usbhstll@48062000 { */
+ /* compatible = "ti,usbhs-tll"; */
+ /* reg = <0x48062000 0x1000>; */
+ /* interrupts = <78>; */
+ /* ti,hwmods = "usb_tll_hs"; */
+ /* }; */
usbhshost: usbhshost@48064000 {
compatible = "ti,usbhs-host";
@@ -517,20 +538,24 @@
#size-cells = <1>;
ranges;
- usbhsohci: ohci@48064400 {
- compatible = "ti,ohci-omap3", "usb-ohci";
- reg = <0x48064400 0x400>;
- interrupt-parent = <&intc>;
- interrupts = <76>;
- };
+ /* usbhsohci: ohci@48064400 { */
+ /* compatible = "ti,ohci-omap3", "usb-ohci"; */
+ /* reg = <0x48064400 0x400>; */
+ /* interrupt-parent = <&intc>; */
+ /* interrupts = <76>; */
+ /* }; */
- usbhsehci: ehci@48064800 {
- compatible = "ti,ehci-omap", "usb-ehci";
- reg = <0x48064800 0x400>;
- interrupt-parent = <&intc>;
- interrupts = <77>;
- };
- };
+ /* usbhsehci: ehci@48064800 { */
+ /* compatible = "ti,ehci-omap", "usb-ehci"; */
+ /* reg = <0x48064800 0x400>; */
+ /* interrupt-parent = <&intc>; */
+ /* interrupts = <77>; */
+ /* }; */
+ };
+
+ /* GPMC needs to be initialized here, or everything
+ * falls apart.
+ */
gpmc: gpmc@6e000000 {
compatible = "ti,omap3430-gpmc";
@@ -541,8 +566,9 @@
gpmc,num-waitpins = <4>;
#address-cells = <2>;
#size-cells = <1>;
- };
+ };
+ /*
usb_otg_hs: usb_otg_hs@480ab000 {
compatible = "ti,omap3-musb";
reg = <0x480ab000 0x1000>;
@@ -552,11 +578,11 @@
multipoint = <1>;
num-eps = <16>;
ram-bits = <12>;
- };
+ };*/
glbl_prm: glbl_prm@48307250 {
- /*
- * disabled by default. becasue two required
+ /*
+ * disabled by default. because two required
* values should be defined in board file
*/
status = "disabled";
diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts
index 144ae43453c..6076d016b47 100644
--- a/arch/arm/boot/dts/omap3430-sdp.dts
+++ b/arch/arm/boot/dts/omap3430-sdp.dts
@@ -105,7 +105,6 @@
nand-bus-width = <8>;
ti,nand-ecc-opt = "sw";
- gpmc,device-nand;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <36>;
gpmc,cs-wr-off-ns = <36>;
diff --git a/arch/arm/boot/dts/omap36xx.dtsi b/arch/arm/boot/dts/omap36xx.dtsi
index f3447bc1b03..2a25174167c 100644
--- a/arch/arm/boot/dts/omap36xx.dtsi
+++ b/arch/arm/boot/dts/omap36xx.dtsi
@@ -12,27 +12,28 @@
/ {
aliases {
- serial3 = &uart4;
+ /* serial3 = &uart4; */
};
cpus {
/* OMAP3630/OMAP37xx 'standard device' variants OPP50 to OPP130 */
- cpu@0 {
- operating-points = <
- /* kHz uV */
- 300000 1012500
- 600000 1200000
- 800000 1325000
- >;
- clock-latency = <300000>; /* From legacy driver */
- };
+ /* cpu@0 { */
+ /* operating-points = < */
+ /* /\* kHz uV *\/ */
+ /* 300000 1012500 */
+ /* 600000 1200000 */
+ /* 800000 1325000 */
+ /* 1000000 1375000 */
+ /* >; */
+ /* clock-latency = <300000>; /\* From legacy driver *\/ */
+ /* }; */
};
- ocp {
- uart4: serial@49042000 {
- compatible = "ti,omap3-uart";
- ti,hwmods = "uart4";
- clock-frequency = <48000000>;
- };
- };
+ /* ocp { */
+ /* uart4: serial@49042000 { */
+ /* compatible = "ti,omap3-uart"; */
+ /* ti,hwmods = "uart4"; */
+ /* clock-frequency = <48000000>; */
+ /* }; */
+ /* }; */
};
diff --git a/arch/arm/boot/dts/omap3_h1.dts b/arch/arm/boot/dts/omap3_h1.dts
new file mode 100644
index 00000000000..13a34e53ac7
--- /dev/null
+++ b/arch/arm/boot/dts/omap3_h1.dts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 Olio Devices, Inc.
+ * Authors: Evan Wilson <evan@oliodevices.com>
+ * Mattis Fjallstrom <mattis@oliodevices.com>
+ *
+ * Modified from omap3-beagle-xm.dts
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+
+/include/ "omap36xx.dtsi"
+/ {
+ model = "Olio OMAP3 H1 Board";
+ compatible = "olio,omap3-h1", "ti,omap3";
+
+ cpus {
+ cpu@0 {
+ device_type = "cpu";
+ operating-points = <
+ /* kHz uV */
+ 300000 975000
+ 600000 1150000
+ 800000 1275000
+ 1000000 1337500
+ >;
+ /* OPP tolerance in percentage */
+ voltage-tolerance = <0>;
+ reset-voltage = <1275000>;
+ };
+ };
+};
+
+&vc {
+ ti,i2c-high-speed;
+ ti,i2c-pad-load = <3>;
+ vc_mpu{
+ ti,master-channel;
+ };
+
+ vc_core{
+ ti,use-master-slave-addr;
+ };
+};
+
+&glbl_prm {
+ status = "ok";
+ sys_clk = "osc_sys_ck";
+ autoextclkmode = <2>;
+ sys_off_mode;
+ auto_off;
+ auto_retention;
+ offmodesetup_time = <3500>;
+ clksetup_time = <1000>;
+};
diff --git a/arch/arm/configs/omap3_h1_defconfig b/arch/arm/configs/omap3_h1_defconfig
new file mode 100644
index 00000000000..7f07b5d5c44
--- /dev/null
+++ b/arch/arm/configs/omap3_h1_defconfig
@@ -0,0 +1,2952 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.10.0 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+# CONFIG_BOOTINFO is not set
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+# CONFIG_AUDIT is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+
+#
+# RCU Subsystem
+#
+# CONFIG_TREE_PREEMPT_RCU is not set
+CONFIG_TINY_PREEMPT_RCU=y
+CONFIG_PREEMPT_RCU=y
+# CONFIG_RCU_STALL_COMMON is not set
+# CONFIG_TREE_RCU_TRACE is not set
+CONFIG_RCU_BOOST=y
+CONFIG_RCU_BOOST_PRIO=1
+CONFIG_RCU_BOOST_DELAY=500
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=18
+# CONFIG_CGROUPS is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_UIDGID_CONVERTED=y
+# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_HAVE_UID16=y
+CONFIG_HOTPLUG=y
+CONFIG_PANIC_TIMEOUT=1
+CONFIG_EXPERT=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OLD_SIGACTION=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_EFI_PARTITION=y
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_IOSCHED_ROW=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_ROW=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="row"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_FREEZER=y
+
+#
+# System Type
+#
+CONFIG_MMU=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP1 is not set
+
+#
+# Multiple platform selection
+#
+
+#
+# CPU Core family selection
+#
+# CONFIG_ARCH_MULTI_V6 is not set
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_MXC is not set
+
+#
+# TI OMAP Common Features
+#
+
+#
+# OMAP Feature Selections
+#
+CONFIG_POWER_AVS_OMAP=y
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX=y
+# CONFIG_OMAP_MUX_DEBUG is not set
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_32K_TIMER=y
+# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set
+CONFIG_OMAP_DM_TIMER=y
+CONFIG_OMAP_PM_NOOP=y
+# CONFIG_MACH_OMAP_GENERIC is not set
+CONFIG_ARCH_OMAP=y
+CONFIG_ARCH_OMAP2PLUS=y
+
+#
+# TI OMAP2/3/4 Specific Features
+#
+# CONFIG_ARCH_OMAP2PLUS_TYPICAL is not set
+CONFIG_SOC_HAS_OMAP2_SDRC=y
+CONFIG_ARCH_OMAP3=y
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_SOC_OMAP5 is not set
+# CONFIG_SOC_OMAP3430 is not set
+# CONFIG_SOC_TI81XX is not set
+# CONFIG_SOC_AM33XX is not set
+CONFIG_OMAP_PACKAGE_CBP=y
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP3_BEAGLE is not set
+CONFIG_MACH_OMAP3_H1=y
+# CONFIG_MACH_OMAP3_H1_EVT1 is not set
+# CONFIG_MACH_OMAP3_H1_EVT2 is not set
+# CONFIG_MACH_OMAP3_H1_DVT1 is not set
+CONFIG_MACH_OMAP3_H1_DVT2=y
+# CONFIG_MACH_DEVKIT8000 is not set
+# CONFIG_MACH_OMAP_LDP is not set
+# CONFIG_MACH_OMAP3530_LV_SOM is not set
+# CONFIG_MACH_OMAP3_TORPEDO is not set
+# CONFIG_MACH_OVERO is not set
+# CONFIG_MACH_OMAP3EVM is not set
+# CONFIG_MACH_OMAP3517EVM is not set
+# CONFIG_MACH_CRANEBOARD is not set
+# CONFIG_MACH_OMAP3_PANDORA is not set
+# CONFIG_MACH_TOUCHBOOK is not set
+# CONFIG_MACH_OMAP_3430SDP is not set
+# CONFIG_MACH_NOKIA_RM680 is not set
+# CONFIG_MACH_NOKIA_RX51 is not set
+# CONFIG_MACH_OMAP_ZOOM2 is not set
+# CONFIG_MACH_OMAP_ZOOM3 is not set
+# CONFIG_MACH_CM_T35 is not set
+# CONFIG_MACH_CM_T3517 is not set
+# CONFIG_MACH_IGEP0020 is not set
+# CONFIG_MACH_IGEP0030 is not set
+# CONFIG_MACH_SBC3530 is not set
+# CONFIG_MACH_OMAP_3630SDP is not set
+# CONFIG_MACH_MINNOW is not set
+# CONFIG_OMAP3_EMU is not set
+# CONFIG_OMAP3_SDRC_AC_TIMING is not set
+CONFIG_OMAP3_PAD_WKUP_IO=y
+CONFIG_DISABLE_OMAP_ERRATA_i583=y
+# CONFIG_ARCH_SOCFPGA is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SIRF is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_ZYNQ is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_ARM_VIRT_EXT=y
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_ARM_NR_BANKS=8
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_ARM_ERRATA_430973=y
+# CONFIG_ARM_ERRATA_720789 is not set
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_775420 is not set
+# CONFIG_FIQ_DEBUGGER is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_ARM_PSCI=y
+CONFIG_ARCH_NR_GPIO=256
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_HW_PERF_EVENTS=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_NEED_PER_CPU_KM=y
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+CONFIG_SECCOMP=y
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_XEN is not set
+# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set
+
+#
+# Boot options
+#
+CONFIG_USE_OF=y
+# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set
+CONFIG_ATAGS=y
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_ARM_APPENDED_DTB is not set
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_AUTO_ZRELADDR=y
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+# CONFIG_GENERIC_CPUFREQ_CPU0 is not set
+
+#
+# ARM CPU frequency scaling drivers
+#
+# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+CONFIG_ARM_OMAP2PLUS_CPUFREQ=y
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_COREDUMP=y
+
+#
+# Power management options
+#
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_HAS_WAKELOCK=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_SLEEP=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=100
+CONFIG_PM_WAKELOCKS_GC=y
+CONFIG_PM_RUNTIME=y
+CONFIG_PM=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_ADVANCED_DEBUG=y
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_PM_SLEEP_DEBUG=y
+# CONFIG_APM_EMULATION is not set
+CONFIG_ARCH_HAS_OPP=y
+CONFIG_PM_OPP=y
+CONFIG_PM_CLK=y
+CONFIG_CPU_PM=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_HAS_AMBIENTMODE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_ALGO=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_XFRM_IPCOMP=y
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_INET_AH is not set
+CONFIG_INET_ESP=y
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_INET6_XFRM_TUNNEL=y
+CONFIG_INET6_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=y
+# CONFIG_IPV6_GRE is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
+# CONFIG_IPV6_SUBTREES is not set
+# CONFIG_IPV6_MROUTE is not set
+CONFIG_ANDROID_PARANOID_NETWORK=y
+CONFIG_NET_ACTIVITY_STATS=y
+CONFIG_NETWORK_SECMARK=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=y
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_MARK=y
+# CONFIG_NF_CONNTRACK_SECMARK is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_GRE=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_BROADCAST=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+# CONFIG_NF_CONNTRACK_SNMP is not set
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+# CONFIG_NF_CONNTRACK_SIP is not set
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_NF_NAT_PROTO_DCCP=y
+CONFIG_NF_NAT_PROTO_UDPLITE=y
+CONFIG_NF_NAT_PROTO_SCTP=y
+CONFIG_NF_NAT_AMANDA=y
+CONFIG_NF_NAT_FTP=y
+CONFIG_NF_NAT_IRC=y
+# CONFIG_NF_NAT_SIP is not set
+CONFIG_NF_NAT_TFTP=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XTABLES=y
+
+#
+# Xtables combined modules
+#
+CONFIG_NETFILTER_XT_MARK=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+
+#
+# Xtables targets
+#
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+# CONFIG_NETFILTER_XT_TARGET_CT is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+
+#
+# Xtables matches
+#
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+CONFIG_NETFILTER_XT_MATCH_ECN=y
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_HL=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+# CONFIG_NETFILTER_XT_MATCH_OSF is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+# CONFIG_IP_SET is not set
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV4=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+# CONFIG_IP_NF_TARGET_ULOG is not set
+CONFIG_NF_NAT_IPV4=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_NF_NAT_PROTO_GRE=y
+CONFIG_NF_NAT_PPTP=y
+CONFIG_NF_NAT_H323=y
+CONFIG_IP_NF_MANGLE=y
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV6=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_NF_NAT_IPV6=y
+CONFIG_IP6_NF_TARGET_MASQUERADE=y
+# CONFIG_IP6_NF_TARGET_NPT is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+CONFIG_HAVE_NET_DSA=y
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+CONFIG_NET_SCH_HTB=y
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_CODEL is not set
+# CONFIG_NET_SCH_FQ_CODEL is not set
+# CONFIG_NET_SCH_INGRESS is not set
+# CONFIG_NET_SCH_PLUG is not set
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+CONFIG_NET_CLS_U32=y
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_CLS_U32_MARK is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+CONFIG_NET_EMATCH_U32=y
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_TEXT is not set
+CONFIG_NET_CLS_ACT=y
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_CLS_IND is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_DCB is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+CONFIG_BQL=y
+# CONFIG_BPF_JIT is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+# CONFIG_BT_BNEP_MC_FILTER is not set
+# CONFIG_BT_BNEP_PROTO_FILTER is not set
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIBTUSB is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_BCSP is not set
+# CONFIG_BT_HCIUART_ATH3K is not set
+# CONFIG_BT_HCIUART_LL is not set
+# CONFIG_BT_HCIUART_3WIRE is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_MRVL is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+CONFIG_RFKILL=y
+CONFIG_RFKILL_PM=y
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_REGULATOR is not set
+CONFIG_RFKILL_GPIO=y
+# CONFIG_RFKILL_WL18XX is not set
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_SOC_BUS=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_SPI=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGMAP_IRQ=y
+CONFIG_DMA_SHARED_BUFFER=y
+# CONFIG_CMA is not set
+
+#
+# Bus devices
+#
+# CONFIG_OMAP_OCP2SCP is not set
+CONFIG_OMAP_INTERCONNECT=y
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_OF_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PHYSMAP_OF is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_PLATRAM is not set
+# CONFIG_MTD_LATCH_ADDR is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_BCH_CONST_M=13
+CONFIG_BCH_CONST_T=8
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCH=y
+CONFIG_MTD_NAND_ECC_BCH=y
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_OMAP2=y
+CONFIG_MTD_NAND_OMAP_BCH=y
+CONFIG_MTD_NAND_OMAP_BCH8=y
+# CONFIG_MTD_NAND_OMAP_BCH4 is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_DOCG4 is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_PLATFORM=y
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_LIMIT=20
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_DTC=y
+CONFIG_OF=y
+
+#
+# Device Tree and Open Firmware support
+#
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_OF_SELFTEST is not set
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_DEVICE=y
+CONFIG_OF_I2C=y
+CONFIG_OF_NET=y
+CONFIG_OF_MTD=y
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_BLK_DEV_RBD is not set
+
+#
+# Misc devices
+#
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_ATMEL_PWM is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_VIB_GPIO is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_UID_STAT is not set
+# CONFIG_BMP085_I2C is not set
+# CONFIG_BMP085_SPI is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_SRAM is not set
+# CONFIG_C55_CTRL is not set
+# CONFIG_MMI_FACTORY is not set
+# CONFIG_MOT_UTAG is not set
+# CONFIG_BQ5105X_CTRL is not set
+# CONFIG_BQ5105X_DETECT is not set
+CONFIG_WAKEUP_SOURCE_NOTIFY=y
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+# CONFIG_TI_ST is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_ALTERA_STAPL is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_NET_CORE=y
+# CONFIG_BONDING is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_MII is not set
+CONFIG_IFB=y
+# CONFIG_NET_TEAM is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_VXLAN is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+CONFIG_TUN=y
+# CONFIG_VETH is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_ETHERNET is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_MICREL_KS8995MA is not set
+CONFIG_PPP=y
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_DEFLATE is not set
+# CONFIG_PPP_FILTER is not set
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOLAC is not set
+# CONFIG_PPPOPNS is not set
+CONFIG_PPP_ASYNC=y
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_WLAN is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+CONFIG_INPUT_MATRIXKMAP=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_KEYRESET=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_CYAPA is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_MOUSE_SYNAPTICS_USB is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ATMXT is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYCHORD is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+CONFIG_INPUT_UINPUT=y
+# CONFIG_INPUT_GPIO is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_CMA3000 is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+# CONFIG_VT is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVMEM=y
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIAL_ARC is not set
+CONFIG_TTY_PRINTK=y
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_DCC_TTY is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+
+#
+# Multiplexer I2C Chip support
+#
+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set
+# CONFIG_I2C_MUX_GPIO is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_PCA954x is not set
+CONFIG_I2C_MUX_PINCTRL=y
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_OMAP=y
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_OC_TINY is not set
+CONFIG_SPI_OMAP24XX=y
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+CONFIG_SPI_SPIDEV=y
+# CONFIG_SPI_TLE62X0 is not set
+
+#
+# Qualcomm MSM SSBI bus support
+#
+# CONFIG_SSBI is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+CONFIG_PINCTRL=y
+
+#
+# Pin controllers
+#
+CONFIG_PINMUX=y
+CONFIG_PINCONF=y
+CONFIG_GENERIC_PINCONF=y
+# CONFIG_DEBUG_PINCTRL is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_EXYNOS5440 is not set
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIOLIB=y
+CONFIG_OF_GPIO=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO drivers:
+#
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_GRGPIO is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_ADNP is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_74X164 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+CONFIG_GPIO_TPS65910=y
+
+#
+# USB GPIO expanders:
+#
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_GENERIC_ADC_BATTERY is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_SBS is not set
+CONFIG_BATTERY_BQ27x00=y
+CONFIG_BATTERY_BQ27X00_I2C=y
+# CONFIG_BATTERY_BQ27X00_PLATFORM is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_RESET_RESTART is not set
+CONFIG_POWER_AVS=y
+CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL=y
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_DW_WATCHDOG is not set
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_OMAP_WATCHDOG_AUTOPET=y
+# CONFIG_MAX63XX_WATCHDOG is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+CONFIG_MFD_CORE=y
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_M4SENSORHUB is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_LP8788 is not set
+CONFIG_MFD_OMAP_USB_HOST=y
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS6586X is not set
+CONFIG_MFD_TPS65910=y
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DEBUG=y
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+CONFIG_REGULATOR_TPS65910=y
+CONFIG_REGULATOR_TI_OMAP_PMIC=y
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_SGX_OMAP3630 is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_SIMPLE is not set
+CONFIG_OMAP2_VRFB=y
+CONFIG_OMAP2_DSS=y
+# CONFIG_OMAP2_DSS_DEBUG is not set
+# CONFIG_OMAP2_DSS_DEBUGFS is not set
+CONFIG_OMAP2_DSS_DPI=y
+# CONFIG_OMAP2_DSS_RFBI is not set
+# CONFIG_OMAP2_DSS_VENC is not set
+CONFIG_OMAP4_DSS_HDMI=y
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP2_DSS_DSI is not set
+CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0
+# CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET is not set
+# CONFIG_OMAP2_DSS_RESET is not set
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
+CONFIG_FB_OMAP2_NUM_FBS=3
+
+#
+# OMAP2/3 Display Device Drivers
+#
+# CONFIG_PANEL_GENERIC_DPI is not set
+# CONFIG_PANEL_TFP410 is not set
+CONFIG_PANEL_ILI_9342=y
+# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_PANEL_PICODLP is not set
+# CONFIG_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_ADF is not set
+# CONFIG_LOGO is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_COMPRESS_OFFLOAD=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_6FIRE is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_DMAENGINE_PCM=y
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_OMAP_SOC=y
+# CONFIG_SND_OMAP_SOC_C55 is not set
+CONFIG_SND_OMAP_SOC_MCBSP=y
+CONFIG_SND_OMAP_SOC_OMAP3_H1=y
+# CONFIG_SND_OMAP_SOC_OMAP_HDMI is not set
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_DMIC=y
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SOUND_PRIME is not set
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_UHID is not set
+CONFIG_HID_GENERIC=y
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LENOVO_TPKBD is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PS3REMOTE is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+
+#
+# USB HID support
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# I2C HID support
+#
+# CONFIG_I2C_HID is not set
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB_ARCH_HAS_XHCI is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_HCD_OMAP=y
+# CONFIG_USB_EHCI_HCD_PLATFORM is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+CONFIG_USB_MUSB_HDRC=y
+# CONFIG_USB_MUSB_TUSB6010 is not set
+CONFIG_USB_MUSB_OMAP2PLUS=y
+# CONFIG_USB_MUSB_AM35X is not set
+# CONFIG_USB_MUSB_DSPS is not set
+# CONFIG_USB_MUSB_UX500 is not set
+# CONFIG_USB_INVENTRA_DMA is not set
+CONFIG_MUSB_PIO_ONLY=y
+# CONFIG_USB_RENESAS_USBHS is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_CHIPIDEA is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+CONFIG_USB_PHY=y
+# CONFIG_USB_OTG_WAKELOCK is not set
+CONFIG_NOP_USB_XCEIV=y
+# CONFIG_OMAP_CONTROL_USB is not set
+# CONFIG_OMAP_USB2 is not set
+# CONFIG_OMAP_USB3 is not set
+# CONFIG_SAMSUNG_USBPHY is not set
+# CONFIG_SAMSUNG_USB2PHY is not set
+# CONFIG_SAMSUNG_USB3PHY is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_ISP1301 is not set
+# CONFIG_USB_RCAR_PHY is not set
+CONFIG_USB_ULPI=y
+# CONFIG_USB_TUSB is not set
+# CONFIG_CPCAP_USB is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+
+#
+# USB Peripheral Controller
+#
+# CONFIG_USB_FUSB300 is not set
+# CONFIG_USB_R8A66597 is not set
+# CONFIG_USB_PXA27X is not set
+# CONFIG_USB_MV_UDC is not set
+# CONFIG_USB_MV_U3D is not set
+CONFIG_USB_GADGET_MUSB_HDRC=y
+# CONFIG_USB_M66592 is not set
+# CONFIG_USB_NET2272 is not set
+# CONFIG_USB_DUMMY_HCD is not set
+CONFIG_USB_LIBCOMPOSITE=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_ETH_EEM is not set
+# CONFIG_USB_G_NCM is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_G_ANDROID is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_G_ACM_MS is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_DBGP is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_LM3530=y
+# CONFIG_LEDS_LM3535 is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA9633 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_RENESAS_TPU is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_BLINKM is not set
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+# CONFIG_SWITCH is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+CONFIG_RTC_DRV_TPS65910=y
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SNVS is not set
+
+#
+# HID Sensor RTC drivers
+#
+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_DMA_OMAP=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DMA_OF=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
+# CONFIG_AUXDISPLAY is not set
+CONFIG_UIO=y
+CONFIG_UIO_PDRV=y
+CONFIG_UIO_PDRV_GENIRQ=y
+# CONFIG_UIO_DMEM_GENIRQ is not set
+# CONFIG_VFIO is not set
+# CONFIG_VIRT_DRIVERS is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+CONFIG_STAGING=y
+# CONFIG_USBIP_CORE is not set
+# CONFIG_ECHO is not set
+# CONFIG_COMEDI is not set
+# CONFIG_ASUS_OLED is not set
+# CONFIG_TRANZPORT is not set
+# CONFIG_LINE6_USB is not set
+
+#
+# IIO staging drivers
+#
+
+#
+# Accelerometers
+#
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16204 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16220 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_LIS3L02DQ is not set
+# CONFIG_SCA3000 is not set
+
+#
+# Analog to digital converters
+#
+# CONFIG_AD7291 is not set
+# CONFIG_AD7606 is not set
+# CONFIG_AD799X is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7280 is not set
+
+#
+# Analog digital bi-direction converters
+#
+# CONFIG_ADT7316 is not set
+
+#
+# Capacitance to digital converters
+#
+# CONFIG_AD7150 is not set
+# CONFIG_AD7152 is not set
+# CONFIG_AD7746 is not set
+
+#
+# Direct Digital Synthesis
+#
+# CONFIG_AD5930 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_AD9850 is not set
+# CONFIG_AD9852 is not set
+# CONFIG_AD9910 is not set
+# CONFIG_AD9951 is not set
+
+#
+# Digital gyroscope sensors
+#
+# CONFIG_ADIS16060 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16260 is not set
+
+#
+# Network Analyzer, Impedance Converters
+#
+# CONFIG_AD5933 is not set
+
+#
+# Light sensors
+#
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_ISL29028 is not set
+# CONFIG_TSL2583 is not set
+# CONFIG_TSL2x7x is not set
+
+#
+# Magnetometer sensors
+#
+# CONFIG_SENSORS_HMC5843 is not set
+
+#
+# Active energy metering IC
+#
+# CONFIG_ADE7753 is not set
+# CONFIG_ADE7754 is not set
+# CONFIG_ADE7758 is not set
+# CONFIG_ADE7759 is not set
+# CONFIG_ADE7854 is not set
+
+#
+# Resolver to digital converters
+#
+# CONFIG_AD2S90 is not set
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+
+#
+# Triggers - standalone
+#
+# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set
+# CONFIG_IIO_GPIO_TRIGGER is not set
+# CONFIG_IIO_SYSFS_TRIGGER is not set
+CONFIG_INV_MPU_IIO=y
+CONFIG_OLIO_TAP_ENABLE=y
+# CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 is not set
+# CONFIG_DTS_INV_MPU_IIO is not set
+# CONFIG_IIO_SIMPLE_DUMMY is not set
+# CONFIG_ZSMALLOC is not set
+# CONFIG_BCM_WIMAX is not set
+# CONFIG_FT1000 is not set
+
+#
+# Speakup console speech
+#
+# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set
+# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set
+# CONFIG_STAGING_MEDIA is not set
+
+#
+# Android
+#
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_IPC_32BIT=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+# CONFIG_SW_SYNC_USER is not set
+CONFIG_ION=y
+# CONFIG_ION_TEST is not set
+# CONFIG_USB_WPAN_HCD is not set
+# CONFIG_WIMAX_GDM72XX is not set
+# CONFIG_CED1401 is not set
+# CONFIG_DGRP is not set
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+
+#
+# Common Clock Framework
+#
+CONFIG_COMMON_CLK_DEBUG=y
+# CONFIG_COMMON_CLK_SI5351 is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_MMIO=y
+CONFIG_ARM_ARCH_TIMER=y
+# CONFIG_MAILBOX is not set
+CONFIG_IOMMU_API=y
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_OF_IOMMU=y
+CONFIG_OMAP_IOMMU=y
+CONFIG_OMAP_IOVMM=y
+CONFIG_OMAP_IOMMU_DEBUG=y
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+CONFIG_MEMORY=y
+# CONFIG_TI_EMIF is not set
+CONFIG_IIO=y
+CONFIG_IIO_BUFFER=y
+# CONFIG_IIO_BUFFER_CB is not set
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_IIO_TRIGGER=y
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+
+#
+# Accelerometers
+#
+# CONFIG_KXSD9 is not set
+# CONFIG_IIO_ST_ACCEL_3AXIS is not set
+
+#
+# Analog to digital converters
+#
+# CONFIG_AD7266 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_EXYNOS_ADC is not set
+# CONFIG_MAX1363 is not set
+# CONFIG_TI_ADC081C is not set
+
+#
+# Amplifiers
+#
+# CONFIG_AD8366 is not set
+
+#
+# Hid Sensor IIO Common
+#
+
+#
+# Digital to analog converters
+#
+# CONFIG_AD5064 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5380 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5446 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD5686 is not set
+# CONFIG_MAX517 is not set
+# CONFIG_MCP4725 is not set
+
+#
+# Frequency Synthesizers DDS/PLL
+#
+
+#
+# Clock Generator/Distribution
+#
+# CONFIG_AD9523 is not set
+
+#
+# Phase-Locked Loop (PLL) frequency synthesizers
+#
+# CONFIG_ADF4350 is not set
+
+#
+# Digital gyroscope sensors
+#
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADXRS450 is not set
+# CONFIG_IIO_ST_GYRO_3AXIS is not set
+# CONFIG_ITG3200 is not set
+
+#
+# Light sensors
+#
+# CONFIG_ADJD_S311 is not set
+# CONFIG_CM32181 is not set
+CONFIG_CM3391=y
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_VCNL4000 is not set
+
+#
+# Magnetometer sensors
+#
+# CONFIG_AK8975 is not set
+# CONFIG_IIO_ST_MAGN_3AXIS is not set
+# CONFIG_PWM is not set
+CONFIG_IRQCHIP=y
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_FUSE_FS=y
+# CONFIG_CUSE is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_CONFIGFS_FS=y
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=1
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_TOUCHSCREEN_DEBUG is not set
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+
+#
+# RCU Debugging
+#
+# CONFIG_PROVE_RCU_DELAY is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_LKDTM is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+CONFIG_DYNAMIC_DEBUG=y
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_RODATA is not set
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_OMAP2PLUS_UART=y
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_OMAP2UART1 is not set
+# CONFIG_DEBUG_OMAP2UART2 is not set
+# CONFIG_DEBUG_OMAP2UART3 is not set
+CONFIG_DEBUG_OMAP3UART3=y
+# CONFIG_DEBUG_OMAP4UART3 is not set
+# CONFIG_DEBUG_OMAP3UART4 is not set
+# CONFIG_DEBUG_OMAP4UART4 is not set
+# CONFIG_DEBUG_TI81XXUART1 is not set
+# CONFIG_DEBUG_TI81XXUART2 is not set
+# CONFIG_DEBUG_TI81XXUART3 is not set
+# CONFIG_DEBUG_AM33XXUART1 is not set
+# CONFIG_DEBUG_ZOOM_UART is not set
+CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S"
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_EARLY_PRINTK=y
+CONFIG_EARLY_PRINTK_DIRECT=y
+# CONFIG_PID_IN_CONTEXTIDR is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA1_ARM is not set
+CONFIG_CRYPTO_SHA256=y
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_TWOFISH_COMMON=y
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ZLIB is not set
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set
+# CONFIG_CRYPTO_DEV_OMAP_AES is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_XZ_DEC=y
+CONFIG_XZ_DEC_X86=y
+CONFIG_XZ_DEC_POWERPC=y
+CONFIG_XZ_DEC_IA64=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_ARMTHUMB=y
+CONFIG_XZ_DEC_SPARC=y
+CONFIG_XZ_DEC_BCJ=y
+# CONFIG_XZ_DEC_TEST is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_BCH=y
+CONFIG_BCH_CONST_PARAMS=y
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=y
+CONFIG_TEXTSEARCH_BM=y
+CONFIG_TEXTSEARCH_FSM=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+# CONFIG_VIRTUALIZATION is not set
diff --git a/arch/arm/configs/omap3_h1_defconfig.old b/arch/arm/configs/omap3_h1_defconfig.old
new file mode 100644
index 00000000000..26083d70273
--- /dev/null
+++ b/arch/arm/configs/omap3_h1_defconfig.old
@@ -0,0 +1,2858 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.10.0 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+# CONFIG_BOOTINFO is not set
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_WATCH=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_IRQ_DOMAIN_DEBUG is not set
+CONFIG_SPARSE_IRQ=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+
+#
+# RCU Subsystem
+#
+# CONFIG_TREE_PREEMPT_RCU is not set
+CONFIG_TINY_PREEMPT_RCU=y
+CONFIG_PREEMPT_RCU=y
+# CONFIG_RCU_STALL_COMMON is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_RCU_BOOST is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+# CONFIG_CGROUP_DEVICE is not set
+# CONFIG_CPUSETS is not set
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+# CONFIG_MEMCG is not set
+CONFIG_CGROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_CFS_BANDWIDTH is not set
+CONFIG_RT_GROUP_SCHED=y
+# CONFIG_BLK_CGROUP is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_UIDGID_CONVERTED=y
+# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_HAVE_UID16=y
+CONFIG_HOTPLUG=y
+CONFIG_PANIC_TIMEOUT=1
+CONFIG_EXPERT=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+# CONFIG_PERF_EVENTS is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OLD_SIGACTION=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_EFI_PARTITION=y
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_IOSCHED_ROW=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_ROW=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="row"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_FREEZER=y
+
+#
+# System Type
+#
+CONFIG_MMU=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP1 is not set
+
+#
+# Multiple platform selection
+#
+
+#
+# CPU Core family selection
+#
+# CONFIG_ARCH_MULTI_V6 is not set
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_MXC is not set
+
+#
+# TI OMAP Common Features
+#
+
+#
+# OMAP Feature Selections
+#
+# CONFIG_POWER_AVS_OMAP is not set
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX=y
+CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_32K_TIMER=y
+# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set
+CONFIG_OMAP_DM_TIMER=y
+CONFIG_OMAP_PM_NOOP=y
+CONFIG_MACH_OMAP_GENERIC=y
+CONFIG_ARCH_OMAP=y
+CONFIG_ARCH_OMAP2PLUS=y
+
+#
+# TI OMAP2/3/4 Specific Features
+#
+# CONFIG_ARCH_OMAP2PLUS_TYPICAL is not set
+CONFIG_SOC_HAS_OMAP2_SDRC=y
+CONFIG_ARCH_OMAP3=y
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_SOC_OMAP5 is not set
+CONFIG_SOC_OMAP3430=y
+# CONFIG_SOC_TI81XX is not set
+# CONFIG_SOC_AM33XX is not set
+CONFIG_OMAP_PACKAGE_CBP=y
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP3_BEAGLE is not set
+CONFIG_MACH_OMAP3_H1=y
+# CONFIG_MACH_DEVKIT8000 is not set
+# CONFIG_MACH_OMAP_LDP is not set
+# CONFIG_MACH_OMAP3530_LV_SOM is not set
+# CONFIG_MACH_OMAP3_TORPEDO is not set
+# CONFIG_MACH_OVERO is not set
+# CONFIG_MACH_OMAP3EVM is not set
+# CONFIG_MACH_OMAP3517EVM is not set
+# CONFIG_MACH_CRANEBOARD is not set
+# CONFIG_MACH_OMAP3_PANDORA is not set
+# CONFIG_MACH_TOUCHBOOK is not set
+# CONFIG_MACH_OMAP_3430SDP is not set
+# CONFIG_MACH_NOKIA_RM680 is not set
+# CONFIG_MACH_NOKIA_RX51 is not set
+# CONFIG_MACH_OMAP_ZOOM2 is not set
+# CONFIG_MACH_OMAP_ZOOM3 is not set
+# CONFIG_MACH_CM_T35 is not set
+# CONFIG_MACH_CM_T3517 is not set
+# CONFIG_MACH_IGEP0020 is not set
+# CONFIG_MACH_IGEP0030 is not set
+# CONFIG_MACH_SBC3530 is not set
+# CONFIG_MACH_OMAP_3630SDP is not set
+# CONFIG_MACH_MINNOW is not set
+# CONFIG_OMAP3_EMU is not set
+# CONFIG_OMAP3_SDRC_AC_TIMING is not set
+# CONFIG_OMAP3_PAD_WKUP_IO is not set
+CONFIG_DISABLE_OMAP_ERRATA_i583=y
+# CONFIG_ARCH_SOCFPGA is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SIRF is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_ZYNQ is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_ARM_VIRT_EXT=y
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_ARM_NR_BANKS=8
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_ARM_ERRATA_430973=y
+# CONFIG_ARM_ERRATA_720789 is not set
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_775420 is not set
+# CONFIG_FIQ_DEBUGGER is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_HAVE_ARM_ARCH_TIMER is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_ARM_PSCI is not set
+CONFIG_ARCH_NR_GPIO=0
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_NEED_PER_CPU_KM=y
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+CONFIG_SECCOMP=y
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_XEN is not set
+# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set
+
+#
+# Boot options
+#
+CONFIG_USE_OF=y
+# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set
+CONFIG_ATAGS=y
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_ARM_APPENDED_DTB is not set
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_AUTO_ZRELADDR=y
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set
+# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_GENERIC_CPUFREQ_CPU0 is not set
+
+#
+# ARM CPU frequency scaling drivers
+#
+# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+CONFIG_ARM_OMAP2PLUS_CPUFREQ=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_COREDUMP=y
+
+#
+# Power management options
+#
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_HAS_WAKELOCK=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_SLEEP=y
+# CONFIG_PM_AUTOSLEEP is not set
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=100
+CONFIG_PM_WAKELOCKS_GC=y
+CONFIG_PM_RUNTIME=y
+CONFIG_PM=y
+CONFIG_PM_DEBUG=y
+# CONFIG_PM_ADVANCED_DEBUG is not set
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_PM_SLEEP_DEBUG=y
+# CONFIG_APM_EMULATION is not set
+CONFIG_ARCH_HAS_OPP=y
+CONFIG_PM_OPP=y
+CONFIG_PM_CLK=y
+CONFIG_CPU_PM=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_HAS_AMBIENTMODE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_ALGO=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_XFRM_IPCOMP=y
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_INET_AH is not set
+CONFIG_INET_ESP=y
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_INET6_XFRM_TUNNEL=y
+CONFIG_INET6_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=y
+# CONFIG_IPV6_GRE is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
+# CONFIG_IPV6_SUBTREES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETLABEL is not set
+CONFIG_ANDROID_PARANOID_NETWORK=y
+CONFIG_NET_ACTIVITY_STATS=y
+CONFIG_NETWORK_SECMARK=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=y
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_MARK=y
+# CONFIG_NF_CONNTRACK_SECMARK is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_GRE=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_BROADCAST=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+# CONFIG_NF_CONNTRACK_SNMP is not set
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+# CONFIG_NF_CONNTRACK_SIP is not set
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_NF_NAT_PROTO_DCCP=y
+CONFIG_NF_NAT_PROTO_UDPLITE=y
+CONFIG_NF_NAT_PROTO_SCTP=y
+CONFIG_NF_NAT_AMANDA=y
+CONFIG_NF_NAT_FTP=y
+CONFIG_NF_NAT_IRC=y
+# CONFIG_NF_NAT_SIP is not set
+CONFIG_NF_NAT_TFTP=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XTABLES=y
+
+#
+# Xtables combined modules
+#
+CONFIG_NETFILTER_XT_MARK=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+
+#
+# Xtables targets
+#
+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+# CONFIG_NETFILTER_XT_TARGET_CT is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+
+#
+# Xtables matches
+#
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+CONFIG_NETFILTER_XT_MATCH_ECN=y
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_HL=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+# CONFIG_NETFILTER_XT_MATCH_OSF is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+# CONFIG_IP_SET is not set
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV4=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+# CONFIG_IP_NF_TARGET_ULOG is not set
+CONFIG_NF_NAT_IPV4=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_NF_NAT_PROTO_GRE=y
+CONFIG_NF_NAT_PPTP=y
+CONFIG_NF_NAT_H323=y
+CONFIG_IP_NF_MANGLE=y
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+CONFIG_IP_NF_RAW=y
+# CONFIG_IP_NF_SECURITY is not set
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV6=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+# CONFIG_IP6_NF_SECURITY is not set
+CONFIG_NF_NAT_IPV6=y
+CONFIG_IP6_NF_TARGET_MASQUERADE=y
+# CONFIG_IP6_NF_TARGET_NPT is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+CONFIG_HAVE_NET_DSA=y
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+CONFIG_NET_SCH_HTB=y
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_CODEL is not set
+# CONFIG_NET_SCH_FQ_CODEL is not set
+# CONFIG_NET_SCH_INGRESS is not set
+# CONFIG_NET_SCH_PLUG is not set
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+CONFIG_NET_CLS_U32=y
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_CLS_U32_MARK is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_CGROUP is not set
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+CONFIG_NET_EMATCH_U32=y
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_TEXT is not set
+CONFIG_NET_CLS_ACT=y
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_CLS_IND is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_DCB is not set
+CONFIG_DNS_RESOLVER=y
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NETPRIO_CGROUP is not set
+CONFIG_BQL=y
+# CONFIG_BPF_JIT is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
+# CONFIG_CFG80211_REG_DEBUG is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+CONFIG_CFG80211_DEFAULT_PS=y
+# CONFIG_CFG80211_DEBUGFS is not set
+# CONFIG_CFG80211_INTERNAL_REGDB is not set
+# CONFIG_CFG80211_WEXT is not set
+# CONFIG_LIB80211 is not set
+# CONFIG_CFG80211_ALLOW_RECONNECT is not set
+CONFIG_MAC80211=y
+CONFIG_MAC80211_HAS_RC=y
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set
+CONFIG_MAC80211_RC_DEFAULT="pid"
+# CONFIG_MAC80211_MESH is not set
+# CONFIG_MAC80211_LEDS is not set
+# CONFIG_MAC80211_DEBUGFS is not set
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+CONFIG_MAC80211_DEBUG_MENU=y
+# CONFIG_MAC80211_NOINLINE is not set
+# CONFIG_MAC80211_VERBOSE_DEBUG is not set
+# CONFIG_MAC80211_MLME_DEBUG is not set
+# CONFIG_MAC80211_STA_DEBUG is not set
+# CONFIG_MAC80211_HT_DEBUG is not set
+# CONFIG_MAC80211_IBSS_DEBUG is not set
+# CONFIG_MAC80211_PS_DEBUG is not set
+# CONFIG_MAC80211_TDLS_DEBUG is not set
+# CONFIG_WIMAX is not set
+CONFIG_RFKILL=y
+CONFIG_RFKILL_PM=y
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_REGULATOR is not set
+# CONFIG_RFKILL_GPIO is not set
+CONFIG_RFKILL_WL18XX=y
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_SOC_BUS=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_SPI=y
+CONFIG_REGMAP_IRQ=y
+CONFIG_DMA_SHARED_BUFFER=y
+# CONFIG_CMA is not set
+
+#
+# Bus devices
+#
+# CONFIG_OMAP_OCP2SCP is not set
+CONFIG_OMAP_INTERCONNECT=y
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+CONFIG_MTD_OF_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PHYSMAP_OF is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_PLATRAM is not set
+# CONFIG_MTD_LATCH_ADDR is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCH=y
+CONFIG_MTD_NAND_ECC_BCH=y
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_OMAP2=y
+# CONFIG_MTD_NAND_OMAP_BCH is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_DOCG4 is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_PLATFORM=y
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_LIMIT=20
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_DTC=y
+CONFIG_OF=y
+
+#
+# Device Tree and Open Firmware support
+#
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_OF_SELFTEST is not set
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_DEVICE=y
+CONFIG_OF_I2C=y
+CONFIG_OF_NET=y
+CONFIG_OF_MTD=y
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_BLK_DEV_RBD is not set
+
+#
+# Misc devices
+#
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_ATMEL_PWM is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_VIB_GPIO is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_UID_STAT is not set
+# CONFIG_BMP085_I2C is not set
+# CONFIG_BMP085_SPI is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_SRAM is not set
+# CONFIG_C55_CTRL is not set
+# CONFIG_MMI_FACTORY is not set
+# CONFIG_MOT_UTAG is not set
+# CONFIG_BQ5105X_CTRL is not set
+# CONFIG_BQ5105X_DETECT is not set
+CONFIG_WAKEUP_SOURCE_NOTIFY=y
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+# CONFIG_TI_ST is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_ALTERA_STAPL is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_NET_CORE=y
+# CONFIG_BONDING is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_MII is not set
+CONFIG_IFB=y
+# CONFIG_NET_TEAM is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_VXLAN is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+CONFIG_TUN=y
+# CONFIG_VETH is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_ETHERNET is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_MICREL_KS8995MA is not set
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_MPPE=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOLAC is not set
+# CONFIG_PPPOPNS is not set
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+# CONFIG_WLAN is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+CONFIG_INPUT_MATRIXKMAP=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_KEYRESET=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_CYAPA is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMXT=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_KEYCHORD is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_GPIO is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_CMA3000 is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+# CONFIG_VT is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVMEM=y
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIAL_ARC is not set
+CONFIG_TTY_PRINTK=y
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_DCC_TTY is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_MUX is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_OMAP=y
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+CONFIG_I2C_DEBUG_CORE=y
+# CONFIG_I2C_DEBUG_ALGO is not set
+CONFIG_I2C_DEBUG_BUS=y
+CONFIG_SPI=y
+CONFIG_SPI_DEBUG=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_OC_TINY is not set
+CONFIG_SPI_OMAP24XX=y
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+CONFIG_SPI_SPIDEV=y
+# CONFIG_SPI_TLE62X0 is not set
+
+#
+# Qualcomm MSM SSBI bus support
+#
+# CONFIG_SSBI is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+CONFIG_PINCTRL=y
+
+#
+# Pin controllers
+#
+CONFIG_PINMUX=y
+CONFIG_PINCONF=y
+CONFIG_GENERIC_PINCONF=y
+# CONFIG_DEBUG_PINCTRL is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_EXYNOS5440 is not set
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIOLIB=y
+CONFIG_OF_GPIO=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO drivers:
+#
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_GRGPIO is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_ADNP is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_74X164 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+# CONFIG_GPIO_TPS65910 is not set
+
+#
+# USB GPIO expanders:
+#
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_GENERIC_ADC_BATTERY is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_SBS is not set
+CONFIG_BATTERY_BQ27x00=y
+CONFIG_BATTERY_BQ27X00_I2C=y
+CONFIG_BATTERY_BQ27X00_PLATFORM=y
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_RESTART is not set
+# CONFIG_POWER_RESET_MINNOW is not set
+CONFIG_POWER_AVS=y
+# CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_DW_WATCHDOG is not set
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_OMAP_WATCHDOG_AUTOPET=y
+# CONFIG_MAX63XX_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+CONFIG_MFD_CORE=y
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_M4SENSORHUB is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS6586X is not set
+CONFIG_MFD_TPS65910=y
+CONFIG_MFD_TPS65912=y
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS65912_DEBUGFS is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+CONFIG_REGULATOR_TPS65910=y
+CONFIG_REGULATOR_TI_OMAP_PMIC=y
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_SGX_OMAP3630 is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_SIMPLE is not set
+CONFIG_OMAP2_VRFB=y
+CONFIG_OMAP2_DSS=y
+CONFIG_OMAP2_DSS_DEBUG=y
+CONFIG_OMAP2_DSS_DEBUGFS=y
+# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set
+CONFIG_OMAP2_DSS_DPI=y
+# CONFIG_OMAP2_DSS_RFBI is not set
+# CONFIG_OMAP2_DSS_VENC is not set
+# CONFIG_OMAP4_DSS_HDMI is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP2_DSS_DSI is not set
+CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0
+CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET=y
+CONFIG_OMAP2_DSS_RESET=y
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
+CONFIG_FB_OMAP2_NUM_FBS=1
+
+#
+# OMAP2/3 Display Device Drivers
+#
+# CONFIG_PANEL_GENERIC_DPI is not set
+# CONFIG_PANEL_TFP410 is not set
+# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_PANEL_PICODLP is not set
+# CONFIG_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_ADF is not set
+# CONFIG_LOGO is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_COMPRESS_OFFLOAD=y
+CONFIG_SND_JACK=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+# CONFIG_SND_VERBOSE_PROCFS is not set
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_DMAENGINE_PCM=y
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_OMAP_SOC=y
+# CONFIG_SND_OMAP_SOC_C55 is not set
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SOUND_PRIME is not set
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_UHID is not set
+CONFIG_HID_GENERIC=y
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PS3REMOTE is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+
+#
+# I2C HID support
+#
+# CONFIG_I2C_HID is not set
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB_ARCH_HAS_XHCI is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_LM3530=y
+# CONFIG_LEDS_LM3535 is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA9633 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_RENESAS_TPU is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_BLINKM is not set
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+CONFIG_SWITCH=y
+# CONFIG_SWITCH_GPIO is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+CONFIG_RTC_DRV_TPS65910=y
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SNVS is not set
+
+#
+# HID Sensor RTC drivers
+#
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_DMA_OMAP=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DMA_OF=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
+# CONFIG_AUXDISPLAY is not set
+CONFIG_UIO=y
+CONFIG_UIO_PDRV=y
+CONFIG_UIO_PDRV_GENIRQ=y
+# CONFIG_UIO_DMEM_GENIRQ is not set
+# CONFIG_VIRT_DRIVERS is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+CONFIG_STAGING=y
+<<<<<<< HEAD
+# CONFIG_ECHO is not set
+# CONFIG_COMEDI is not set
+
+#
+# IIO staging drivers
+#
+=======
+CONFIG_ECHO=y
+CONFIG_IIO=y
+# CONFIG_IIO_ST_HWMON is not set
+CONFIG_IIO_BUFFER=y
+# CONFIG_IIO_SW_RING is not set
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_IIO_TRIGGER=y
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Accelerometers
+#
+<<<<<<< HEAD
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16204 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16220 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_LIS3L02DQ is not set
+# CONFIG_SCA3000 is not set
+=======
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Analog to digital converters
+#
+# CONFIG_AD7291 is not set
+# CONFIG_AD7606 is not set
+# CONFIG_AD799X is not set
+<<<<<<< HEAD
+# CONFIG_AD7780 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7280 is not set
+=======
+# CONFIG_ADT7410 is not set
+# CONFIG_MAX1363 is not set
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Analog digital bi-direction converters
+#
+# CONFIG_ADT7316 is not set
+
+#
+# Capacitance to digital converters
+#
+# CONFIG_AD7150 is not set
+# CONFIG_AD7152 is not set
+# CONFIG_AD7746 is not set
+
+#
+<<<<<<< HEAD
+# Direct Digital Synthesis
+#
+# CONFIG_AD5930 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_AD9850 is not set
+# CONFIG_AD9852 is not set
+# CONFIG_AD9910 is not set
+# CONFIG_AD9951 is not set
+=======
+# Digital to analog converters
+#
+# CONFIG_AD5380 is not set
+# CONFIG_MAX517 is not set
+
+#
+# Direct Digital Synthesis
+#
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Digital gyroscope sensors
+#
+<<<<<<< HEAD
+# CONFIG_ADIS16060 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16260 is not set
+=======
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Network Analyzer, Impedance Converters
+#
+# CONFIG_AD5933 is not set
+
+#
+<<<<<<< HEAD
+# Light sensors
+#
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_ISL29028 is not set
+# CONFIG_TSL2583 is not set
+# CONFIG_TSL2x7x is not set
+=======
+# Inertial measurement units
+#
+CONFIG_INV_MPU_IIO=m
+# CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 is not set
+# CONFIG_DTS_INV_MPU_IIO is not set
+
+#
+# Light sensors
+#
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_TSL2583 is not set
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Magnetometer sensors
+#
+# CONFIG_SENSORS_HMC5843 is not set
+
+#
+# Active energy metering IC
+#
+<<<<<<< HEAD
+# CONFIG_ADE7753 is not set
+# CONFIG_ADE7754 is not set
+# CONFIG_ADE7758 is not set
+# CONFIG_ADE7759 is not set
+=======
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+# CONFIG_ADE7854 is not set
+
+#
+# Resolver to digital converters
+#
+<<<<<<< HEAD
+# CONFIG_AD2S90 is not set
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+=======
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+
+#
+# Triggers - standalone
+#
+# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set
+# CONFIG_IIO_GPIO_TRIGGER is not set
+# CONFIG_IIO_SYSFS_TRIGGER is not set
+# CONFIG_IIO_SIMPLE_DUMMY is not set
+<<<<<<< HEAD
+# CONFIG_ZSMALLOC is not set
+=======
+# CONFIG_FB_SM7XX is not set
+# CONFIG_TIDSPBRIDGE is not set
+>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver
+# CONFIG_FT1000 is not set
+
+#
+# Speakup console speech
+#
+# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set
+# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set
+# CONFIG_STAGING_MEDIA is not set
+
+#
+# Android
+#
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_IPC_32BIT=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+# CONFIG_SW_SYNC_USER is not set
+CONFIG_ION=y
+# CONFIG_ION_TEST is not set
+# CONFIG_WIMAX_GDM72XX is not set
+# CONFIG_DGRP is not set
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+
+#
+# Common Clock Framework
+#
+# CONFIG_COMMON_CLK_DEBUG is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKSRC_MMIO=y
+# CONFIG_MAILBOX is not set
+# CONFIG_IOMMU_SUPPORT is not set
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+# CONFIG_MEMORY is not set
+CONFIG_IIO=y
+CONFIG_IIO_BUFFER=y
+# CONFIG_IIO_BUFFER_CB is not set
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_IIO_TRIGGER=y
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+
+#
+# Accelerometers
+#
+# CONFIG_KXSD9 is not set
+# CONFIG_IIO_ST_ACCEL_3AXIS is not set
+
+#
+# Analog to digital converters
+#
+# CONFIG_AD7266 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_EXYNOS_ADC is not set
+# CONFIG_MAX1363 is not set
+# CONFIG_TI_ADC081C is not set
+
+#
+# Amplifiers
+#
+# CONFIG_AD8366 is not set
+
+#
+# Hid Sensor IIO Common
+#
+
+#
+# Digital to analog converters
+#
+# CONFIG_AD5064 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5380 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5446 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD5686 is not set
+# CONFIG_MAX517 is not set
+# CONFIG_MCP4725 is not set
+
+#
+# Frequency Synthesizers DDS/PLL
+#
+
+#
+# Clock Generator/Distribution
+#
+# CONFIG_AD9523 is not set
+
+#
+# Phase-Locked Loop (PLL) frequency synthesizers
+#
+# CONFIG_ADF4350 is not set
+
+#
+# Digital gyroscope sensors
+#
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADXRS450 is not set
+# CONFIG_IIO_ST_GYRO_3AXIS is not set
+# CONFIG_ITG3200 is not set
+
+#
+# Inertial measurement units
+#
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_INV_MPU6050_IIO is not set
+
+#
+# Light sensors
+#
+# CONFIG_ADJD_S311 is not set
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_VCNL4000 is not set
+
+#
+# Magnetometer sensors
+#
+# CONFIG_AK8975 is not set
+# CONFIG_IIO_ST_MAGN_3AXIS is not set
+# CONFIG_PWM is not set
+CONFIG_IRQCHIP=y
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_FANOTIFY is not set
+CONFIG_QUOTA=y
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+CONFIG_PRINT_QUOTA_WARNING=y
+# CONFIG_QUOTA_DEBUG is not set
+CONFIG_QUOTA_TREE=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_FUSE_FS=y
+# CONFIG_CUSE is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_CONFIGFS_FS=y
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=7
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_TOUCHSCREEN_DEBUG is not set
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+
+#
+# RCU Debugging
+#
+# CONFIG_PROVE_RCU_DELAY is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_LKDTM is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACE_CLOCK=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_FUNCTION_GRAPH_TRACER=y
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_FTRACE_SYSCALLS is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+CONFIG_STACK_TRACER=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_PROBE_EVENTS is not set
+CONFIG_DYNAMIC_FTRACE=y
+# CONFIG_FUNCTION_PROFILER is not set
+CONFIG_FTRACE_MCOUNT_RECORD=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+CONFIG_DYNAMIC_DEBUG=y
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+CONFIG_OLD_MCOUNT=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_RODATA is not set
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_OMAP2PLUS_UART=y
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_OMAP2UART1 is not set
+# CONFIG_DEBUG_OMAP2UART2 is not set
+# CONFIG_DEBUG_OMAP2UART3 is not set
+CONFIG_DEBUG_OMAP3UART3=y
+# CONFIG_DEBUG_OMAP4UART3 is not set
+# CONFIG_DEBUG_OMAP3UART4 is not set
+# CONFIG_DEBUG_OMAP4UART4 is not set
+# CONFIG_DEBUG_TI81XXUART1 is not set
+# CONFIG_DEBUG_TI81XXUART2 is not set
+# CONFIG_DEBUG_TI81XXUART3 is not set
+# CONFIG_DEBUG_AM33XXUART1 is not set
+# CONFIG_DEBUG_ZOOM_UART is not set
+CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S"
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_EARLY_PRINTK=y
+CONFIG_EARLY_PRINTK_DIRECT=y
+# CONFIG_PID_IN_CONTEXTIDR is not set
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+# CONFIG_ENCRYPTED_KEYS is not set
+# CONFIG_KEYS_DEBUG_PROC_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+CONFIG_SECURITY=y
+# CONFIG_SECURITYFS is not set
+CONFIG_SECURITY_NETWORK=y
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+# CONFIG_SECURITY_PATH is not set
+CONFIG_LSM_MMAP_MIN_ADDR=32768
+CONFIG_SECURITY_SELINUX=y
+# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set
+# CONFIG_SECURITY_SELINUX_DISABLE is not set
+# CONFIG_SECURITY_SELINUX_DEVELOP is not set
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+# CONFIG_SECURITY_APPARMOR is not set
+# CONFIG_SECURITY_YAMA is not set
+# CONFIG_IMA is not set
+# CONFIG_EVM is not set
+CONFIG_DEFAULT_SECURITY_SELINUX=y
+# CONFIG_DEFAULT_SECURITY_DAC is not set
+CONFIG_DEFAULT_SECURITY="selinux"
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA1_ARM is not set
+CONFIG_CRYPTO_SHA256=y
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_TWOFISH_COMMON=y
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ZLIB is not set
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set
+# CONFIG_CRYPTO_DEV_OMAP_AES is not set
+# CONFIG_ASYMMETRIC_KEY_TYPE is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+# CONFIG_CRC8 is not set
+CONFIG_AUDIT_GENERIC=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_XZ_DEC=y
+CONFIG_XZ_DEC_X86=y
+CONFIG_XZ_DEC_POWERPC=y
+CONFIG_XZ_DEC_IA64=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_ARMTHUMB=y
+CONFIG_XZ_DEC_SPARC=y
+CONFIG_XZ_DEC_BCJ=y
+# CONFIG_XZ_DEC_TEST is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_BCH=y
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=y
+CONFIG_TEXTSEARCH_BM=y
+CONFIG_TEXTSEARCH_FSM=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+# CONFIG_VIRTUALIZATION is not set
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 0168b07ce0c..c3fdbc0f08a 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -207,6 +207,34 @@ config MACH_OMAP3_BEAGLE
depends on ARCH_OMAP3
default y
select OMAP_PACKAGE_CBB
+
+config MACH_OMAP3_H1
+ bool "OMAP3 H1 board"
+ depends on ARCH_OMAP3
+ default y
+ select OMAP_PACKAGE_CBP
+
+choice
+ prompt "Olio H1B build version"
+ default MACH_OMAP3_H1_EVT2
+
+config MACH_OMAP3_H1_EVT1
+ bool "EVT1 Build of the H1 board"
+ depends on MACH_OMAP3_H1
+
+config MACH_OMAP3_H1_EVT2
+ bool "EVT2 Build of the H1 board"
+ depends on MACH_OMAP3_H1
+
+config MACH_OMAP3_H1_DVT1
+ bool "DVT1 Build of the H1 board"
+ depends on MACH_OMAP3_H1
+
+config MACH_OMAP3_H1_DVT2
+ bool "DVT2 Build of the H1 board"
+ depends on MACH_OMAP3_H1
+
+endchoice
config MACH_DEVKIT8000
bool "DEVKIT8000 board"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index ff5c4457325..9245b2356df 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -225,6 +225,8 @@ obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o
obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o
obj-$(CONFIG_MACH_OMAP_2430SDP) += board-2430sdp.o
obj-$(CONFIG_MACH_OMAP3_BEAGLE) += board-omap3beagle.o
+obj-$(CONFIG_MACH_OMAP3_H1) += board-omap3h1.o
+obj-$(CONFIG_MACH_OMAP3_H1) += board-omap3h1-bluetooth.o
obj-$(CONFIG_MACH_DEVKIT8000) += board-devkit8000.o
obj-$(CONFIG_MACH_OMAP_LDP) += board-ldp.o
obj-$(CONFIG_MACH_OMAP3530_LV_SOM) += board-omap3logic.o
diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c
index c33adea0247..bb25fff9ee6 100644
--- a/arch/arm/mach-omap2/board-flash.c
+++ b/arch/arm/mach-omap2/board-flash.c
@@ -139,7 +139,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, u8 nr_parts, u8 cs,
board_nand_data.nr_parts = nr_parts;
board_nand_data.devsize = nand_type;
- board_nand_data.ecc_opt = OMAP_ECC_HAMMING_CODE_DEFAULT;
+ board_nand_data.ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
gpmc_nand_init(&board_nand_data, gpmc_t);
}
#endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */
diff --git a/arch/arm/mach-omap2/board-omap3h1-bluetooth.c b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c
new file mode 100644
index 00000000000..52bf419b549
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c
@@ -0,0 +1,387 @@
+/*
+ * Bluetooth Broadcomm and low power control via GPIO
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+ /*
+ * Adapted from board-tuna-bluetooth.c by Evan Wilson <evan@oliodevices.com
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/irq.h>
+#include <linux/rfkill.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <asm/mach-types.h>
+#include "serial.h"
+#include "board-omap3h1.h"
+#include <linux/regulator/driver.h>
+
+static void update_host_wake_locked(int);
+
+#define BT_REG_GPIO 180
+
+#define BT_WAKE_GPIO 93
+#define BT_HOST_WAKE_GPIO 11
+
+static struct rfkill *bt_rfkill;
+static struct regulator *clk32ksys_reg;
+static bool bt_enabled;
+static bool host_wake_uart_enabled;
+static bool wake_uart_enabled;
+static struct dentry *btdebugdent;
+
+struct bcm_bt_lpm {
+ int wake;
+ int host_wake;
+
+ struct hrtimer enter_lpm_timer;
+ ktime_t enter_lpm_delay;
+
+ struct uart_port *uport;
+
+ struct wake_lock wake_lock;
+ char wake_lock_name[100];
+} bt_lpm;
+
+static int bcm20702_bt_rfkill_set_power(void *data, bool blocked)
+{
+ // rfkill_ops callback. Turn transmitter on when blocked is false
+ if (!blocked) {
+ if (clk32ksys_reg && !bt_enabled)
+ regulator_enable(clk32ksys_reg);
+
+ gpio_set_value(BT_REG_GPIO, 1);
+
+ } else {
+ // Chip won't toggle host_wake after reset. Make sure
+ // we don't hold the wake_lock until chip wakes up again.
+ update_host_wake_locked(0);
+
+ gpio_set_value(BT_REG_GPIO, 0);
+ if (clk32ksys_reg && bt_enabled)
+ regulator_disable(clk32ksys_reg);
+ }
+
+ bt_enabled = !blocked;
+
+ return 0;
+}
+
+static const struct rfkill_ops bcm20702_bt_rfkill_ops = {
+ .set_block = bcm20702_bt_rfkill_set_power,
+};
+
+static void set_wake_locked(int wake)
+{
+ bt_lpm.wake = wake;
+
+ if (!wake)
+ wake_unlock(&bt_lpm.wake_lock);
+
+ if (!wake_uart_enabled && wake)
+ //omap_uart_enable(2);
+
+ gpio_set_value(BT_WAKE_GPIO, wake);
+
+ if (wake_uart_enabled && !wake)
+ //omap_uart_disable(2);
+
+ wake_uart_enabled = wake;
+}
+
+static enum hrtimer_restart enter_lpm(struct hrtimer *timer) {
+ unsigned long flags;
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+ set_wake_locked(0);
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) {
+ bt_lpm.uport = uport;
+
+ hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer);
+
+ set_wake_locked(1);
+
+ hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay,
+ HRTIMER_MODE_REL);
+}
+EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked);
+
+static void update_host_wake_locked(int host_wake)
+{
+ if (host_wake == bt_lpm.host_wake)
+ return;
+
+ bt_lpm.host_wake = host_wake;
+
+ if (host_wake) {
+ wake_lock(&bt_lpm.wake_lock);
+ if (!host_wake_uart_enabled) {
+ //omap_uart_enable(2);
+ }
+ } else {
+ if (host_wake_uart_enabled) {
+ //omap_uart_disable(2);
+ }
+ // Take a timed wakelock, so that upper layers can take it.
+ // The chipset deasserts the hostwake lock, when there is no
+ // more data to send.
+ wake_lock_timeout(&bt_lpm.wake_lock, HZ/2);
+ }
+
+ host_wake_uart_enabled = host_wake;
+
+}
+
+static irqreturn_t host_wake_isr(int irq, void *dev)
+{
+ int host_wake;
+ unsigned long flags;
+
+ host_wake = gpio_get_value(BT_HOST_WAKE_GPIO);
+ irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+
+ if (!bt_lpm.uport) {
+ bt_lpm.host_wake = host_wake;
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+ update_host_wake_locked(host_wake);
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm_bt_lpm_init(struct platform_device *pdev)
+{
+ int irq;
+ int ret;
+ int rc;
+
+ rc = gpio_request(BT_WAKE_GPIO, "bcm20702_wake_gpio");
+ if (unlikely(rc)) {
+ return rc;
+ }
+
+ rc = gpio_request(BT_HOST_WAKE_GPIO, "bcm20702_host_wake_gpio");
+ if (unlikely(rc)) {
+ gpio_free(BT_WAKE_GPIO);
+ return rc;
+ }
+
+ hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ bt_lpm.enter_lpm_delay = ktime_set(1, 0); /* 1 sec */
+ bt_lpm.enter_lpm_timer.function = enter_lpm;
+
+ bt_lpm.host_wake = 0;
+
+ irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH,
+ "bt host_wake", NULL);
+ if (ret) {
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ return ret;
+ }
+
+ ret = irq_set_irq_wake(irq, 1);
+ if (ret) {
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ return ret;
+ }
+
+ gpio_direction_output(BT_WAKE_GPIO, 0);
+ gpio_direction_input(BT_HOST_WAKE_GPIO);
+
+ snprintf(bt_lpm.wake_lock_name, sizeof(bt_lpm.wake_lock_name),
+ "BTLowPower");
+ wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND,
+ bt_lpm.wake_lock_name);
+ return 0;
+}
+
+static int btdebug_dump(struct seq_file *sf, void *private)
+{
+ seq_printf(sf, "en=%d bt_wake=%d lpm.w=%d w_uart_en=%d\n",
+ bt_enabled, gpio_get_value(BT_WAKE_GPIO),
+ bt_lpm.wake, wake_uart_enabled);
+ seq_printf(sf, "bt_host_wake=%d lpm.hw=%d hw_uart_en=%d\n",
+ gpio_get_value(BT_HOST_WAKE_GPIO), bt_lpm.host_wake,
+ host_wake_uart_enabled);
+ return 0;
+}
+
+static int btdebug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, btdebug_dump, NULL);
+}
+
+static const struct file_operations btdebug_fops = {
+ .open = btdebug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int bcm20702_bluetooth_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ int ret = 0;
+
+// rc = gpio_request(BT_RESET_GPIO, "bcm20702_nreset_gpip");
+// if (unlikely(rc)) {
+// return rc;
+// }
+
+ rc = gpio_request(BT_REG_GPIO, "bcm20702_nshutdown_gpio");
+ if (unlikely(rc)) {
+ //gpio_free(BT_RESET_GPIO);
+ return rc;
+ }
+
+ clk32ksys_reg = regulator_get(0, "clk32ksys");
+ if (IS_ERR(clk32ksys_reg)) {
+ pr_err("clk32ksys reg not found!\n");
+ clk32ksys_reg = NULL;
+ }
+
+ gpio_direction_output(BT_REG_GPIO, 1);
+ //gpio_direction_output(BT_RESET_GPIO, 1);
+
+ bt_rfkill = rfkill_alloc("bcm20702 Bluetooth", &pdev->dev,
+ RFKILL_TYPE_BLUETOOTH, &bcm20702_bt_rfkill_ops,
+ NULL);
+
+ if (unlikely(!bt_rfkill)) {
+ //gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ return -ENOMEM;
+ }
+
+ rfkill_set_states(bt_rfkill, true, false);
+ rc = rfkill_register(bt_rfkill);
+
+ if (unlikely(rc)) {
+ rfkill_destroy(bt_rfkill);
+ //gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ return -1;
+ }
+
+ ret = bcm_bt_lpm_init(pdev);
+ if (ret) {
+ rfkill_unregister(bt_rfkill);
+ rfkill_destroy(bt_rfkill);
+
+ //gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ }
+
+ btdebugdent = debugfs_create_file("bt", S_IRUGO, NULL, NULL,
+ &btdebug_fops);
+ if (IS_ERR_OR_NULL(btdebugdent))
+ pr_err("%s: failed to create debugfs file\n", __func__);
+
+ return ret;
+}
+
+static int bcm20702_bluetooth_remove(struct platform_device *pdev)
+{
+ rfkill_unregister(bt_rfkill);
+ rfkill_destroy(bt_rfkill);
+
+ if (!IS_ERR_OR_NULL(btdebugdent))
+ debugfs_remove(btdebugdent);
+
+ gpio_free(BT_REG_GPIO);
+ //gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ regulator_put(clk32ksys_reg);
+
+ wake_lock_destroy(&bt_lpm.wake_lock);
+ return 0;
+}
+
+int bcm4430_bluetooth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ int host_wake;
+
+ disable_irq(irq);
+ host_wake = gpio_get_value(BT_HOST_WAKE_GPIO);
+
+ if (host_wake) {
+ enable_irq(irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int bcm4430_bluetooth_resume(struct platform_device *pdev)
+{
+ int irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ enable_irq(irq);
+ return 0;
+}
+
+static struct platform_driver bcm20702_bluetooth_platform_driver = {
+ .probe = bcm20702_bluetooth_probe,
+ .remove = bcm20702_bluetooth_remove,
+ .suspend = bcm4430_bluetooth_suspend,
+ .resume = bcm4430_bluetooth_resume,
+ .driver = {
+ .name = "bcm20702_bluetooth",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bcm20702_bluetooth_init(void)
+{
+ bt_enabled = false;
+ return platform_driver_register(&bcm20702_bluetooth_platform_driver);
+}
+
+static void __exit bcm20702_bluetooth_exit(void)
+{
+ platform_driver_unregister(&bcm20702_bluetooth_platform_driver);
+}
+
+
+module_init(bcm20702_bluetooth_init);
+module_exit(bcm20702_bluetooth_exit);
+
+MODULE_ALIAS("platform:bcm20702");
+MODULE_DESCRIPTION("bcm20702_bluetooth");
+MODULE_AUTHOR("Jaikumar Ganesh <jaikumar@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-omap2/board-omap3h1.c b/arch/arm/mach-omap2/board-omap3h1.c
new file mode 100644
index 00000000000..61c01c90a8f
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap3h1.c
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (C) 2014 Olio Devices, Inc.
+ * Authors: Evan Wilson <evan@oliodevices.com>
+ * Mattis Fjallstrom <mattis@oliodevices.com>
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Modified from the original mach-omap/omap2/board-generic.c did by Paul
+ * to support the OMAP2+ device tree boards with an unique board file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/opp.h>
+#include <linux/cpu.h>
+#include <linux/mpu.h>
+#include <linux/spi/spi.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+
+#include <linux/usb/musb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/nop-usb-xceiv.h>
+
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/gpio.h>
+
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/mfd/tps65910.h>
+
+#include <linux/wakelock.h> /* used in interrupt, waking up. */
+#include <linux/debugfs.h>
+
+#include <linux/led-lm3530.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+#include <linux/platform_data/mtd-nand-omap2.h>
+#include <linux/platform_data/serial-omap.h>
+#include <linux/interrupt.h>
+
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+
+#include "common.h"
+#include "omap_device.h"
+#include "gpmc.h"
+#include "soc.h"
+#include "mux.h"
+#include "pm.h"
+#include "board-flash.h"
+#include "common-board-devices.h"
+#include "board-omap3h1.h"
+
+#include "sdram-micron-mt29c4g48.h"
+
+#define NAND_CS 0
+
+#define MPUIRQ_GPIO 31
+#define ATMEL_MXT_GPIO 105
+#define TPS_SYS_NIRQ 0
+#define USB_IRQ 124
+#define USB_ON_ETK_D7 21
+
+#define DEFAULT_RXDMA_POLLRATE 1 /* RX DMA polling rate (us) */
+#define DEFAULT_RXDMA_BUFSIZE 4096 /* RX DMA buffer size */
+#define DEFAULT_RXDMA_TIMEOUT (3 * HZ)/* RX DMA timeout (jiffies) */
+
+#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2)
+#define LCD_RESET_GPIO 94
+#else
+#define LCD_RESET_GPIO 122
+#endif
+
+static struct accelerometer_wake {
+ int awake;
+ struct wake_lock lock;
+ char lock_name[100];
+ int user_pid;
+} acc_wake;
+
+static struct mtd_partition omap3h1_nand_partitions[] = {
+ /* All the partition sizes are listed in terms of NAND block size */
+ {
+ .name = "X-Loader",
+ .offset = 0,
+ .size = 4 * NAND_BLOCK_SIZE,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */
+ .size = 15 * NAND_BLOCK_SIZE,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot Env",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */
+ .size = 1 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */
+ .size = 40 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "initramfs",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0xC80000 */
+ .size = 80 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "ramdisk",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x1180000 */
+ .size = 40 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "system",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x1680000 */
+ .size = 2000 * NAND_BLOCK_SIZE,
+ },
+ {
+ .name = "userdata",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x11180000 */
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct omap_dss_device omap3h1_lcd_device = {
+ .type = OMAP_DISPLAY_TYPE_DPI,
+ .name = "olio_h1_panel",
+ .driver_name = "ili9342_panel",
+ .phy.dpi.data_lines = 18,
+ .reset_gpio = LCD_RESET_GPIO,
+};
+
+static struct omap_dss_device *omap3h1_dss_devices[] = {
+ &omap3h1_lcd_device,
+};
+
+static struct omap_dss_board_info omap3h1_dss_data = {
+ .num_devices = 1,
+ .devices = omap3h1_dss_devices,
+ .default_device = &omap3h1_lcd_device,
+};
+
+static struct spi_board_info omap3h1_spi_board_info[] __initdata = {
+ {
+ .modalias = "ili9342-spi",
+ .bus_num = 1,
+ .chip_select = 1,
+ .max_speed_hz = 375000,
+ .platform_data = &omap3h1_lcd_device,
+ .mode = SPI_MODE_0,
+ }
+};
+
+static int __init omap3h1_spi_init(void) {
+ spi_register_board_info(omap3h1_spi_board_info,
+ ARRAY_SIZE(omap3h1_spi_board_info));
+ return 0;
+}
+
+
+/*
+ * int_config is interrupt config, INT_ENABLE register. To enable _all_
+ * interrupts, set to 0x59. I think. --mfj
+ *
+ * Level shifter seems to set power rail between two options - 1 means VDD.
+ * (0 means VLogic, which I'm not sure what it is).
+ */
+
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x00,
+ .level_shifter = 1,
+ .orientation = { 0, -1, 0,
+ -1, 0, 0,
+ 0, 0, 1 },
+};
+
+static struct lm3530_platform_data omap3h1_backlight_platform_data = {
+ .mode = LM3530_BL_MODE_MANUAL,
+ //.als_input_mode = LM3530_INPUT_ALS1,
+ .max_current = LM3530_FS_CURR_29mA,
+ //.pwm_pol_hi = true,
+ //.als_avrg_time = LM3530_ALS_AVRG_TIME_512ms,
+ .brt_ramp_law = 0,
+ .brt_ramp_fall = LM3530_RAMP_TIME_1s, /* LM3530_RAMP_TIME_1s, */
+ .brt_ramp_rise = LM3530_RAMP_TIME_1s, /* LM3530_RAMP_TIME_1s, */
+ //.als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm,
+ //.als2_resistor_sel = LM3530_ALS_IMPD_Z,
+ //.als_vmin = 730, /* mV */
+ //.als_vmax = 10a20, /* mV */
+ .brt_val = 0x64, /* initial brightness */
+};
+
+
+
+static struct mxt_platform_data mxt_data = {
+ .irqflags = IRQF_TRIGGER_FALLING,
+};
+
+/*
+static struct platform_device bcm20702_bluetooth_device = {
+ .name = "bcm20702_bluetooth",
+ .id = -1,
+ };
+*/
+
+static struct platform_device omap3h1_dmic_codec = {
+ .name = "dmic-codec",
+ .id = -1,
+};
+
+/* --------------------------------------------------------------------------- */
+/* USB settings
+ */
+
+
+
+static struct omap_musb_board_data musb_board_data = {
+ .interface_type = MUSB_INTERFACE_ULPI,
+ .mode = MUSB_OTG,
+ .power = 100,
+};
+
+static struct nop_usb_xceiv_platform_data nop_plat_data = {
+ .type = USB_PHY_TYPE_USB2,
+ .clk_rate = 60000000, /* 60 MHz */
+ .needs_vcc = 1,
+ .needs_reset = 0,
+};
+
+static struct platform_device nop_phy_device = {
+ .name = "nop_usb_xceiv",
+ .id = -1,
+ .dev = {
+ .platform_data = &nop_plat_data,
+ },
+};
+
+
+/* static struct usbhs_omap_platform_data usbhs_bdata __initdata = { */
+/* .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY, */
+/* }; */
+
+
+/* --------------------------------------------------------------------------- */
+/* REGULATOR - Fixed first
+ */
+
+static struct regulator_init_data omap3h1_vbat_data = {
+ .constraints.always_on = true,
+ .constraints.valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .constraints.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
+};
+
+static struct fixed_voltage_config omap3h1_vbat_pdata = {
+ .supply_name = "VBAT",
+ .microvolts = 3800000,
+ .init_data = &omap3h1_vbat_data,
+ .gpio = -EINVAL,
+};
+
+static struct platform_device omap3h1_vbat = {
+ .name = "reg-fixed-voltage",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap3h1_vbat_pdata,
+ },
+};
+
+
+/* --------------------------------------------------------------------------- */
+
+static struct regulator_init_data tps65910_dummy = {
+ .supply_regulator = "VBAT",
+ .constraints.always_on = false,
+ .constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+};
+
+/* --------------------------------------------------------------------------- */
+/* This one is required for the display to work.
+ */
+
+static struct regulator_consumer_supply tps65910_touch3_supply[] = {
+ REGULATOR_SUPPLY("avdd", "2-004a"),
+ REGULATOR_SUPPLY("vdd", "spi1.1"),
+};
+
+static struct regulator_init_data tps65910_touch3 = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,
+ .always_on = false,
+ .apply_uV = true,
+
+ .state_mem = {
+ .uV = 0,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_disk = {
+ .uV = 0,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_standby = {
+ .uV = 0,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ },
+
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_touch3_supply),
+ .consumer_supplies = tps65910_touch3_supply,
+};
+
+/* --------------------------------------------------------------------------- */
+/* This regulator is for the vibrator. We currently lack a way of enabling /
+ * disabling this since we lack a driver for the vibrator (we're just accessing
+ * it's registers through the i2c bus).
+ *
+ * Hence, it's always on.
+ */
+
+static struct regulator_consumer_supply tps65910_vibrator_supply[] = {
+ REGULATOR_SUPPLY("vaux2", "drv2605"),
+};
+
+static struct regulator_init_data tps65910_vibrator = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE,
+ .always_on = false,
+ .apply_uV = true,
+
+ .state_mem = {
+ .disabled = 1,
+ },
+ .state_disk = {
+ .disabled = 1,
+ },
+ .state_standby = {
+ .disabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_vibrator_supply),
+ .consumer_supplies = tps65910_vibrator_supply,
+};
+
+
+
+/* --------------------------------------------------------------------------- */
+/* Seems like this one has to be on. It doesn't have any "official" consumers
+ * ... but it's definitely needed. Bluetooth relies on it.
+ */
+
+/*
+static struct regulator_consumer_supply tps65910_bluetooth_3v3[] = {
+ REGULATOR_SUPPLY("clk32ksys", 0),
+};
+*/
+
+static struct regulator_init_data tps65910_3v3 = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE |
+ REGULATOR_CHANGE_STATUS,
+ .always_on = false,
+ .apply_uV = true,
+
+ .state_mem = {
+ .disabled = 1,
+ },
+ .state_disk = {
+ .disabled = 1,
+ },
+ .state_standby = {
+ .disabled = 1,
+ },
+ },
+
+};
+
+
+/* --------------------------------------------------------------------------- */
+/* This is the OMAP display subsystem. Part of the OMAP processor OCP stuff.
+ * USB also depends on this, so if you want to transfer files it has to be
+ * always on.
+ * NAND flash and UART3 (which we don't use) requires it
+ * as well. And the i2c bus, and lots of other stuff.
+ *
+ * Setting always_on to "false" resulted in almost no power savings, at
+ * least in the current setting.
+ */
+
+static struct regulator_consumer_supply tps65910_1v8_supply[] = {
+ REGULATOR_SUPPLY("vdds_dsi", "omapdss"),
+};
+
+static struct regulator_init_data tps65910_1v8 = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE,
+ .always_on = false,
+ .apply_uV = true,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_1v8_supply),
+ .consumer_supplies = tps65910_1v8_supply,
+};
+
+/* --------------------------------------------------------------------------- */
+/* There's no driver explicitly using this one, but it goes to VDDA_DPLL_PER
+ * on the OMAP chip. So it's used internally by the DPLL devices in PER, I think.
+ */
+
+static struct regulator_consumer_supply tps65910_vpll_supply[] = {
+
+};
+
+static struct regulator_init_data tps65910_vpll = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ .apply_uV = true,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_vpll_supply),
+ .consumer_supplies = tps65910_vpll_supply,
+};
+
+
+/* --------------------------------------------------------------------------- */
+/* Touch power supply part 2.
+ */
+
+static struct regulator_consumer_supply tps65910_touch_supply[] = {
+ REGULATOR_SUPPLY("vdd", "2-004a" ),
+};
+
+static struct regulator_init_data tps65910_touch = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
+ .always_on = false,
+ .apply_uV = true,
+
+ .state_mem = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_disk = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_standby = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_touch_supply),
+ .consumer_supplies = tps65910_touch_supply,
+};
+
+/* =========================================================================== */
+
+/* --------------------------------------------------------------------------- */
+
+/* This one only works if 'always_on' is set to true. Do I need to do something
+ * special otherwise to turn it on? Maybe the driver doesn't enable it.
+ *
+ * The accelerometer driver isn't using regulators properly, hence this needs to
+ * be always on.
+ */
+
+static struct regulator_consumer_supply tps65910_accel_supply[] = {
+ REGULATOR_SUPPLY("vdd", "mpu6515"),
+};
+
+static struct regulator_init_data tps65910_accel = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ .apply_uV = true,
+
+ .state_mem = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_disk = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_standby = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_accel_supply),
+ .consumer_supplies = tps65910_accel_supply,
+};
+
+/* --------------------------------------------------------------------------- */
+/* Supplies the MPU PD and friends.
+ */
+
+static struct regulator_consumer_supply tps65910_vdd1_supply[] = {
+ REGULATOR_SUPPLY("vcc", "cpu0"),
+};
+
+static struct regulator_init_data tps65910_vdd1 = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .name = "vdd_mpu_iva",
+ .min_uV = 900000,
+ .max_uV = 1350000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = false,
+
+ .state_mem = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_disk = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_standby = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ },
+
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_vdd1_supply),
+ .consumer_supplies = tps65910_vdd1_supply,
+};
+
+/* --------------------------------------------------------------------------- */
+/* I seem to need always on, but the TWL code gets by without it. So...
+ */
+
+static struct regulator_consumer_supply tps65910_vdd2_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", "l3_main.0"),
+};
+
+static struct regulator_init_data tps65910_vdd2 = {
+ .supply_regulator = "VBAT",
+ .constraints = {
+ .name = "vdd_core",
+ .min_uV = 900000,
+ .max_uV = 1200000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = false,
+
+ .state_mem = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_disk = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ .state_standby = {
+ .uV = 1,
+ .mode = REGULATOR_MODE_STANDBY,
+ .disabled = 1,
+ },
+ },
+
+ .num_consumer_supplies = ARRAY_SIZE(tps65910_vdd2_supply),
+ .consumer_supplies = tps65910_vdd2_supply,
+};
+
+/* --------------------------------------------------------------------------- */
+
+/* The clock has to be ON, probably because we're not enabling it properly. */
+/* If I set i2chs_keepon to 6, UBIfs fails to mount (!?). Again, probably
+ * not enabling it properly.
+ *
+ * And even with therm set, the screen acts weird (looking really dark during
+ * boot, touch not working when up).
+ */
+
+/*
+static struct tps65910_sleep_keepon_data olio_slp_keepon = {
+ .therm_keepon = 0,
+ .clkout32k_keepon = 0,
+ .i2chs_keepon = 1,
+ };*/
+
+
+
+static struct tps65910_board omap3h1_tps65910_pdata = {
+ /* .irq = 7 + OMAP_INTC_START, */ /* Getting this at runtime instead. */
+ .en_ck32k_xtal = true,
+ /* .en_dev_slp = true, */
+
+ /* .slp_keepon = &olio_slp_keepon, */
+
+ .tps65910_pmic_init_data[TPS65910_REG_VIO] = &tps65910_1v8,
+ .tps65910_pmic_init_data[TPS65910_REG_VDD1] = &tps65910_vdd1,
+ .tps65910_pmic_init_data[TPS65910_REG_VDD2] = &tps65910_vdd2,
+ .tps65910_pmic_init_data[TPS65910_REG_VDIG1] = &tps65910_accel,
+ .tps65910_pmic_init_data[TPS65910_REG_VDIG2] = &tps65910_touch,
+ .tps65910_pmic_init_data[TPS65910_REG_VPLL] = &tps65910_vpll,
+
+ .tps65910_pmic_init_data[TPS65910_REG_VMMC] = &tps65910_touch3,
+ .tps65910_pmic_init_data[TPS65910_REG_VAUX2] = &tps65910_vibrator,
+
+ /* This one is connected to BT ... maybe more? */
+
+ .tps65910_pmic_init_data[TPS65910_REG_VAUX33] = &tps65910_3v3,
+
+ /* not actually used */
+
+ .tps65910_pmic_init_data[TPS65910_REG_VRTC] = &tps65910_dummy,
+ .tps65910_pmic_init_data[TPS65910_REG_VDAC] = &tps65910_dummy,
+ .tps65910_pmic_init_data[TPS65910_REG_VAUX1] = &tps65910_dummy, /* 2v85 */
+ .tps65910_pmic_init_data[TPS65910_REG_VDD3] = &tps65910_dummy,
+
+ .pm_off = true,
+};
+
+
+/* ====================================================================== */
+/* Voltage domain settings */
+
+
+#define OMAP3_SRI2C_SLAVE_ADDR 0x12
+#define OMAP3_VDD_MPU_SR_CONTROL_REG 0x00
+#define OMAP3_VDD_CORE_SR_CONTROL_REG 0x01
+#define OMAP3_VP_CONFIG_ERROROFFSET 0x00
+#define OMAP3_VP_VSTEPMIN_VSTEPMIN 0x1
+#define OMAP3_VP_VSTEPMAX_VSTEPMAX 0x04
+#define OMAP3_VP_VLIMITTO_TIMEOUT_US 200
+
+
+static unsigned long olio_vsel_to_uv(const u8 vsel)
+{
+ return (((vsel * 125) + 6000)) * 100;
+}
+
+static u8 olio_uv_to_vsel(unsigned long uv)
+{
+ return DIV_ROUND_UP(uv - 600000, 12500);
+}
+
+
+static struct omap_voltdm_pmic omap3_mpu_pmic = {
+ .slew_rate = 12500,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP3_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 900000,
+ .vddmax = 1350000,
+ .vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR,
+ .volt_reg_addr = OMAP3_VDD_MPU_SR_CONTROL_REG,
+ .i2c_high_speed = true,
+ .vsel_to_uv = olio_vsel_to_uv,
+ .uv_to_vsel = olio_uv_to_vsel,
+};
+
+static struct omap_voltdm_pmic omap3_core_pmic = {
+ .slew_rate = 12500,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP3_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 900000,
+ .vddmax = 1200000,
+ .vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR,
+ .volt_reg_addr = OMAP3_VDD_CORE_SR_CONTROL_REG,
+ .i2c_high_speed = true,
+ .vsel_to_uv = olio_vsel_to_uv,
+ .uv_to_vsel = olio_uv_to_vsel,
+};
+
+int __init omap3_voltdm_init(void)
+{
+ struct voltagedomain *voltdm;
+
+ if (!cpu_is_omap34xx())
+ return -ENODEV;
+
+ voltdm = voltdm_lookup("mpu_iva");
+ omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic);
+
+ voltdm = voltdm_lookup("core");
+ omap_voltage_register_pmic(voltdm, &omap3_core_pmic);
+
+ return 0;
+}
+
+
+
+/* ====================================================================== */
+
+
+static struct i2c_board_info __initdata omap3h1_i2c1_board_info[] = {
+ {
+ I2C_BOARD_INFO("tps65910", 0x2d),
+ .platform_data = &omap3h1_tps65910_pdata,
+ },
+#ifdef CONFIG_MACH_OMAP3_H1_DVT2
+};
+static struct i2c_board_info __initdata omap3h1_i2c2_board_info[] = {
+#endif
+ {
+ I2C_BOARD_INFO("mpu6515", 0x68),
+ // This is needed for the interrupt wake. IH_GPIO_BASE changed in 3.10
+ // .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &mpu_data,
+ },
+ {
+ /* Backlight */
+ I2C_BOARD_INFO("lm3530-led", 0x38),
+ .platform_data = &omap3h1_backlight_platform_data,
+ },
+ {
+ I2C_BOARD_INFO("mXT224", 0x4a),
+ .platform_data = &mxt_data,
+ },
+ {
+ I2C_BOARD_INFO("cm3391", 0x10),
+ },
+#ifdef CONFIG_MACH_OMAP3_H1_EVT1
+};
+static struct i2c_board_info __initdata omap3h1_i2c3_board_info[] = {
+#endif
+ {
+ I2C_BOARD_INFO("bq274xx", 0x55),
+ },
+};
+
+#ifdef ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS
+
+/* --------------------------------------------------------------------------- */
+/* Create debugfs entry
+ *
+ * We need to have this so that our wake-up thread can tell us it's PID.
+ * That PID will then be used when waking the system up.
+ */
+
+struct dentry * pidfile;
+
+#define SIG_OLIO_WAKE 44 // we choose 44 as our signal number (real-time signals are in the range of 33 to 64)
+
+static ssize_t write_pid(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ char mybuf[10];
+ int pid = 0;
+
+ /* read the value from user space */
+
+ if(count > 10)
+ return -EINVAL;
+ if (copy_from_user(mybuf, buf, count) != 0) {
+ /* nothing read?? */
+ printk ("OLIO: write_pid: Error??\n");
+ return 0;
+ }
+
+ sscanf(mybuf, "%d", &pid);
+
+ printk("pid read = %d\n", pid);
+
+ acc_wake.user_pid = pid;
+
+ return count;
+}
+
+static ssize_t send_wake_signal (void) {
+ int ret;
+ struct siginfo info;
+ struct task_struct *t;
+
+ /* send the signal */
+
+ printk ("Now sending signal!\n");
+
+ memset(&info, 0, sizeof(struct siginfo));
+
+ /* this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
+ * and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
+ * is not delivered to the user space signal handler function.
+ */
+
+ info.si_signo = SIG_OLIO_WAKE;
+ info.si_code = SI_QUEUE;
+
+ /* real time signals may have 32 bits of data. */
+
+ info.si_int = 1234;
+
+ rcu_read_lock();
+
+ t = pid_task(find_pid_ns(acc_wake.user_pid, &init_pid_ns), PIDTYPE_PID);
+
+ if(t == NULL){
+ rcu_read_unlock();
+ printk("no such pid\n");
+ return -ENODEV;
+ }
+
+ rcu_read_unlock();
+
+ /* send the signal */
+
+ ret = send_sig_info(SIG_OLIO_WAKE, &info, t);
+
+ if (ret < 0) {
+ printk("error sending signal\n");
+ }
+ return ret;
+}
+
+static const struct file_operations h1_fops = {
+ .write = write_pid,
+};
+
+
+/***************************************************************************
+ * acc_irq_handler - handle interrupt from accelerometer
+ *
+ * This routine gets called when an interrupt from the accelerometer
+ * happens. Time out is in milliseconds.
+ *
+ * TODO: BREAK this all out, place it in it's own kernel driver together
+ * with the configuration stuff for the accelerometer (the low power sleep
+ * mode stuff).
+ */
+
+#define OLIO_DOUBLE_TAP 500
+#define OLIO_ONE_SECOND 1000
+#define OLIO_DT_WAIT 3000
+
+static unsigned long previous;
+static unsigned long previous_double;
+
+static irqreturn_t acc_irq_handler_thr(int irq, void * omap3h1_d) {
+ /* struct platform_device** omap3h1_devs = (struct platform_device **) omap3h1_d; */
+
+ int ret = 99;
+ unsigned long now;
+
+ printk (KERN_DEBUG "acc_irq_handler_thr: Entered!\n");
+
+ now = jiffies;
+
+ if (jiffies_to_msecs(now - previous) < OLIO_DOUBLE_TAP) {
+
+ /* ignore double clicks that happens too soon from the previous one */
+ if (jiffies_to_msecs (now - previous_double) < OLIO_DT_WAIT)
+ {
+ previous = 0;
+ return IRQ_HANDLED;
+ }
+
+ printk (KERN_DEBUG "Double tap detected!\n");
+
+ /* We want to wake up. Take wake lock (and send signal to user space?)
+ * BT driver only takes wake lock, then lets higher levels handle
+ * everything ... but what if there's nothing there to take the lock?
+ */
+
+ send_wake_signal();
+ wake_lock_timeout (&acc_wake.lock, msecs_to_jiffies (20 * OLIO_ONE_SECOND));
+
+ previous_double = now;
+ previous = 0;
+ } else {
+ previous = now;
+ }
+
+ printk (KERN_DEBUG "acc_irq_handler_thr: Leaving! (ret = %d)\n", ret);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t acc_irq_handler(int irq, void * omap3h1_d) {
+ printk (KERN_DEBUG "acc_irq_handler: Entered!\n");
+
+ /* nothing to do, other than wake the main thread */
+
+ printk (KERN_DEBUG "acc_irq_handler: Leaving!\n");
+ return IRQ_WAKE_THREAD;
+}
+
+static int __init omap3_acc_irq_init (int irq, void * dev_id) {
+ irq_set_irq_wake(irq, 1);
+
+ if (request_threaded_irq (irq, acc_irq_handler,
+ acc_irq_handler_thr,
+ IRQF_SHARED | IRQF_TRIGGER_RISING,
+ "acc_wake",
+ dev_id)) {
+ printk (KERN_DEBUG "Couldn't register handler for accelerometer IRQ\n");
+ }
+
+ /* Should check the return value ... but if it fails, what do we do? */
+
+ return 0;
+}
+
+static int __init omap3_wake_init (void) {
+ pidfile = debugfs_create_file("wake_signal_pid", 0200, NULL, NULL, &h1_fops);
+ acc_wake.user_pid = 0; /* No user thread yet */
+
+ snprintf(acc_wake.lock_name, sizeof(acc_wake.lock_name),
+ "Accelerometer");
+ wake_lock_init(&acc_wake.lock, WAKE_LOCK_SUSPEND,
+ acc_wake.lock_name);
+ return 0;
+}
+
+
+#endif /* ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS */
+
+
+/***************************************************************************
+ * omap_uart data
+ */
+
+/* Some notes:
+ .dma_enabled = false,
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .autosuspend_timeout = DEFAULT_AUTOSUSPEND_DELAY, ( -1 or X ms. )
+
+ .wakelock_timeout - for minnow, this is 150 (BT) or 50 (M4 debug).
+ Can be left out.
+
+ .DTR_* -> can be left out. I can't find anyone using it.
+
+ int DTR_gpio;
+ int DTR_inverted;
+ int DTR_present;
+ bool wakeup_capable;
+
+ bool open_close_pm; - Minnow uses this for c55 only
+ unsigned int rx_trig; Minnow, BT & debug only
+*/
+
+static struct omap_uart_port_info omap_uart_ports[] = {
+ {
+ .dma_enabled = false, /* To specify DMA Mode */
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .autosuspend_timeout = 5000,
+ .wakeup_capable = false,
+ },
+ {
+ .dma_enabled = false, /* To specify DMA Mode */
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .autosuspend_timeout = 5000,
+ .wakeup_capable = false,
+ },
+ {
+ .dma_enabled = false, /* To specify DMA Mode */
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .autosuspend_timeout = -1,
+ .wakelock_timeout = -1,
+ .wakeup_capable = true,
+ },
+ {
+ .dma_enabled = false, /* To specify DMA Mode */
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .autosuspend_timeout = 5000,
+ .wakeup_capable = false,
+ },
+};
+
+
+/***************************************************************************
+ * DEVICE TREE STUFF
+ *
+ * Trying to include some DT stuff with our board file. Not sure how this
+ * will work out...
+ */
+
+static struct of_device_id omap_dt_match_table[] __initdata = {
+ { .compatible = "olio,omap3-h1", },
+ { .compatible = "simple-bus", },
+ { .compatible = "ti,omap-infra", },
+ { }
+};
+
+
+/***************************************************************************
+ * omap3_h1_i2c_init - init the i2c buses
+ *
+ */
+
+static int __init omap3_h1_i2c_init(void)
+{
+ int acc_irq;
+
+ /* In Linux 3.10 we need to request an IRQ through
+ * gpio_to_irq. This means it can't be set at compile time, and
+ * must be done at runtime. In other words, here.
+ */
+
+ gpio_request_one(ATMEL_MXT_GPIO, GPIOF_IN, "atmel_mxt_ts CHG");
+ gpio_request_one(MPUIRQ_GPIO, GPIOF_IN, "mpu6515 IRQ pin");
+
+ acc_irq = gpio_to_irq(MPUIRQ_GPIO);
+
+#ifdef CONFIG_MACH_OMAP3_H1_DVT2
+ omap3h1_i2c2_board_info[2].irq = gpio_to_irq(ATMEL_MXT_GPIO);
+ omap3h1_i2c2_board_info[0].irq = acc_irq;
+
+ ((struct tps65910_board *) (omap3h1_i2c1_board_info[0].platform_data))->irq =
+ gpio_to_irq (TPS_SYS_NIRQ);
+#else
+ omap3h1_i2c1_board_info[3].irq = gpio_to_irq(ATMEL_MXT_GPIO);
+ omap3h1_i2c1_board_info[1].irq = acc_irq;
+#endif
+
+ gpio_request_one(USB_ON_ETK_D7, GPIOF_OUT_INIT_HIGH, "USB on");
+
+ /* Register buses */
+
+ omap_register_i2c_bus(1, 400, omap3h1_i2c1_board_info, ARRAY_SIZE(omap3h1_i2c1_board_info));
+#ifdef CONFIG_MACH_OMAP3_H1_DVT2
+ omap_register_i2c_bus(2, 400, omap3h1_i2c2_board_info, ARRAY_SIZE(omap3h1_i2c2_board_info));
+#else
+ omap_register_i2c_bus(2, 400, NULL, 0);
+#endif
+#ifdef CONFIG_MACH_OMAP3_H1_EVT1
+ omap_register_i2c_bus(3, 400, omap3h1_i2c3_board_info, ARRAY_SIZE(omap3h1_i2c3_board_info));
+#else
+ omap_register_i2c_bus(3, 400, NULL, 0);
+#endif
+ return 0;
+}
+
+
+static struct platform_device *omap3h1_devices[] __initdata = {
+ &omap3h1_vbat,
+ // &bcm20702_bluetooth_device,
+ &nop_phy_device,
+ &omap3h1_dmic_codec,
+};
+
+#ifdef CONFIG_OMAP_MUX
+static struct omap_board_mux board_mux[] __initdata = {
+
+#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2)
+ OMAP3_MUX(CAM_HS, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
+#else
+ OMAP3_MUX(HSUSB0_DIR, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
+#endif
+ OMAP3_MUX(DSS_PCLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_HSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_VSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA0, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA2, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA3, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA4, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA5, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA6, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA7, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA8, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA9, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA10, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA11, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA12, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA13, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA14, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA16, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA17, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+
+ OMAP3_MUX(MCSPI1_CLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(MCSPI1_SIMO, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(MCSPI1_SOMI, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP),
+ OMAP3_MUX(MCSPI1_CS1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+
+ /* TOUCH_RESET */
+
+ OMAP3_MUX(CAM_D6, OMAP_MUX_MODE4 | OMAP_PIN_INPUT_PULLUP),
+
+#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2)
+ /* USB pin settings (mUSB) */
+
+ OMAP3_MUX(HSUSB0_CLK, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA0, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA1, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA2, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA3, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA4, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA5, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA6, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DATA7, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_DIR, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_NXT, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+ OMAP3_MUX(HSUSB0_STP, (OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT)),
+#endif /* CONFIG_MACH_OMAP3_H1_DVT */
+
+ /* accelerometer */
+
+ OMAP3_MUX(JTAG_EMU1, (OMAP_MUX_MODE4 | OMAP_PIN_INPUT
+ | OMAP_PIN_OFF_WAKEUPENABLE)),
+
+ /* USB on */
+
+ OMAP3_MUX(ETK_D7, (OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT)),
+
+ OMAP3_MUX(SYS_NIRQ, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)),
+
+ { .reg_offset = OMAP_MUX_TERMINATOR },
+};
+#endif
+
+static void __init omap3_h1_init(void)
+{
+ /* Read what we can from the device tree */
+
+ of_platform_populate(NULL, omap_dt_match_table, NULL, NULL);
+
+ board_nand_init(omap3h1_nand_partitions,
+ ARRAY_SIZE(omap3h1_nand_partitions), NAND_CS,
+ NAND_BUSWIDTH_16, NULL);
+
+ omap_sdrc_init(mt29c4g48_sdrc_params, mt29c4g48_sdrc_params);
+
+ omap3_mux_init(board_mux, OMAP_PACKAGE_CBP);
+
+ omap3_h1_i2c_init();
+
+ /* Set up the voltage domains */
+ omap3_voltdm_init();
+
+ platform_add_devices(omap3h1_devices, ARRAY_SIZE(omap3h1_devices));
+
+ omap3h1_spi_init();
+
+ omap_serial_board_init(omap_uart_ports);
+
+ omap_display_init(&omap3h1_dss_data);
+
+ usb_bind_phy("musb-hdrc.0.auto", 0, "nop_usb_xceiv"); /* "tusb-usb-h1" */
+
+ usb_musb_init(&musb_board_data);
+
+ /* #ifdef ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS */
+
+ /* For handling interrupts from the accelerometer */
+
+ /* previous = jiffies; */
+
+ /* Init the code that handles wake ups */
+
+ /*
+ omap3_wake_init();
+
+ omap3_acc_irq_init(omap3h1_i2c2_board_info[0].irq, (void *) &omap3h1_devices);
+ */
+
+ /* #endif ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS*/
+
+}
+
+static const char *omap3_h1_boards_compat[] __initdata = {
+ "olio,omap3-h1",
+ NULL,
+};
+
+DT_MACHINE_START(OMAP3_H1, "Olio OMAP3 H1 (Flattened Device Tree)")
+ .atag_offset = 0x100,
+ .reserve = omap_reserve,
+ .map_io = omap3_map_io,
+ .init_early = omap3630_init_early,
+ /* .init_irq = omap3_init_irq, */
+ .init_irq = omap_intc_of_init,
+ .handle_irq = omap3_intc_handle_irq,
+ .init_machine = omap3_h1_init,
+ .init_late = omap3630_init_late,
+ .init_time = omap3_sync32k_timer_init,
+ /* .init_time = omap3_secure_sync32k_timer_init, */
+ .dt_compat = omap3_h1_boards_compat,
+ .restart = omap3xxx_restart,
+MACHINE_END
+
+#if 0 /* removing ... for now */
+MACHINE_START(OMAP3_H1, "Olio OMAP3 H1 Board")
+ .atag_offset = 0x100,
+ .reserve = omap_reserve,
+ .map_io = omap3_map_io,
+ .init_early = omap3630_init_early,
+ .init_irq = omap3_init_irq,
+ .handle_irq = omap3_intc_handle_irq,
+ .init_machine = omap3_h1_init,
+ .init_late = omap3630_init_late,
+ .init_time = omap3_sync32k_timer_init,
+ /* .init_time = omap3_secure_sync32k_timer_init, */
+ /* .dt_compat = omap3_h1_boards_compat, */
+ .restart = omap3xxx_restart,
+MACHINE_END
+#endif
diff --git a/arch/arm/mach-omap2/board-omap3h1.h b/arch/arm/mach-omap2/board-omap3h1.h
new file mode 100644
index 00000000000..343eaeaaf86
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap3h1.h
@@ -0,0 +1,30 @@
+/*
+ * Bluetooth Broadcomm and low power control via GPIO
+ *
+ * Copyright (C) 2011 Samsung, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __BOARD_OMAP3H1__H__
+#define __BOARD_OMAP3H1__H__
+
+#include <linux/serial_core.h>
+
+extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport);
+
+#endif /* __BOARD_OMAP3H1_H__ */
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 9017d5ea9f3..81a30ac94a8 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -66,7 +66,7 @@ static int __init omap3_l3_init(void)
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap3_l3_init);
@@ -100,7 +100,7 @@ static int __init omap4_l3_init(void)
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap4_l3_init);
diff --git a/arch/arm/mach-omap2/fb.c b/arch/arm/mach-omap2/fb.c
index 190ae493c6e..2ca33cc0c48 100644
--- a/arch/arm/mach-omap2/fb.c
+++ b/arch/arm/mach-omap2/fb.c
@@ -83,10 +83,7 @@ static int __init omap_init_vrfb(void)
pdev = platform_device_register_resndata(NULL, "omapvrfb", -1,
res, num_res, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
- else
- return 0;
+ return PTR_RET(pdev);
}
omap_arch_initcall(omap_init_vrfb);
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c
index d9c27195caf..aeca5507820 100644
--- a/arch/arm/mach-omap2/gpmc-nand.c
+++ b/arch/arm/mach-omap2/gpmc-nand.c
@@ -140,8 +140,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
if (gpmc_nand_data->of_node) {
gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
} else {
- s.device_nand = true;
-
/* Enable RD PIN Monitoring Reg */
if (gpmc_nand_data->dev_ready) {
s.wait_on_read = true;
@@ -149,6 +147,8 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
}
}
+ s.device_nand = true;
+
if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16)
s.device_width = GPMC_DEVWIDTH_16BIT;
else
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 6c4da1254f5..c877129142b 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -30,6 +30,7 @@
#include <linux/of_mtd.h>
#include <linux/of_device.h>
#include <linux/mtd/nand.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_data/mtd-nand-omap2.h>
@@ -148,13 +149,14 @@ struct omap3_gpmc_regs {
static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
static struct irq_chip gpmc_irq_chip;
-static unsigned gpmc_irq_start;
+static int gpmc_irq_start;
static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock);
/* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1);
+static unsigned int gpmc_cs_num = GPMC_CS_NUM;
static unsigned int gpmc_nr_waitpins;
static struct device *gpmc_dev;
static int gpmc_irq;
@@ -521,8 +523,10 @@ static int gpmc_cs_remap(int cs, u32 base)
int ret;
u32 old_base, size;
- if (cs > GPMC_CS_NUM)
+ if (cs > gpmc_cs_num) {
+ pr_err("%s: requested chip-select is disabled\n", __func__);
return -ENODEV;
+ }
gpmc_cs_get_memconf(cs, &old_base, &size);
if (base == old_base)
return 0;
@@ -545,9 +549,10 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
struct resource *res = &gpmc_cs_mem[cs];
int r = -1;
- if (cs > GPMC_CS_NUM)
+ if (cs > gpmc_cs_num) {
+ pr_err("%s: requested chip-select is disabled\n", __func__);
return -ENODEV;
-
+ }
size = gpmc_mem_align(size);
if (size > (1 << GPMC_SECTION_SHIFT))
return -ENOMEM;
@@ -582,7 +587,7 @@ EXPORT_SYMBOL(gpmc_cs_request);
void gpmc_cs_free(int cs)
{
spin_lock(&gpmc_mem_lock);
- if (cs >= GPMC_CS_NUM || cs < 0 || !gpmc_cs_reserved(cs)) {
+ if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs);
BUG();
spin_unlock(&gpmc_mem_lock);
@@ -777,7 +782,7 @@ static void gpmc_mem_exit(void)
{
int cs;
- for (cs = 0; cs < GPMC_CS_NUM; cs++) {
+ for (cs = 0; cs < gpmc_cs_num; cs++) {
if (!gpmc_cs_mem_enabled(cs))
continue;
gpmc_cs_delete_mem(cs);
@@ -798,7 +803,7 @@ static void gpmc_mem_init(void)
gpmc_mem_root.end = GPMC_MEM_END;
/* Reserve all regions that has been set up by bootloader */
- for (cs = 0; cs < GPMC_CS_NUM; cs++) {
+ for (cs = 0; cs < gpmc_cs_num; cs++) {
u32 base, size;
if (!gpmc_cs_mem_enabled(cs))
@@ -1245,7 +1250,6 @@ void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
p->sync_read = of_property_read_bool(np, "gpmc,sync-read");
p->sync_write = of_property_read_bool(np, "gpmc,sync-write");
- p->device_nand = of_property_read_bool(np, "gpmc,device-nand");
of_property_read_u32(np, "gpmc,device-width", &p->device_width);
of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data);
@@ -1337,12 +1341,11 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
#ifdef CONFIG_MTD_NAND
-static const char * const nand_ecc_opts[] = {
- [OMAP_ECC_HAMMING_CODE_DEFAULT] = "sw",
- [OMAP_ECC_HAMMING_CODE_HW] = "hw",
- [OMAP_ECC_HAMMING_CODE_HW_ROMCODE] = "hw-romcode",
- [OMAP_ECC_BCH4_CODE_HW] = "bch4",
- [OMAP_ECC_BCH8_CODE_HW] = "bch8",
+static const char * const nand_xfer_types[] = {
+ [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
+ [NAND_OMAP_POLLED] = "polled",
+ [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
+ [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int gpmc_probe_nand_child(struct platform_device *pdev,
@@ -1367,10 +1370,45 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,
gpmc_nand_data->cs = val;
gpmc_nand_data->of_node = child;
- if (!of_property_read_string(child, "ti,nand-ecc-opt", &s))
- for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++)
- if (!strcasecmp(s, nand_ecc_opts[val])) {
- gpmc_nand_data->ecc_opt = val;
+ /* Detect availability of ELM module */
+ gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+ if (gpmc_nand_data->elm_of_node == NULL)
+ gpmc_nand_data->elm_of_node =
+ of_parse_phandle(child, "elm_id", 0);
+ if (gpmc_nand_data->elm_of_node == NULL)
+ pr_warn("%s: ti,elm-id property not found\n", __func__);
+
+ /* select ecc-scheme for NAND */
+ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+ pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
+ return -ENODEV;
+ }
+ if (!strcmp(s, "ham1") || !strcmp(s, "sw") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_HAM1_CODE_HW;
+ else if (!strcmp(s, "bch4"))
+ if (gpmc_nand_data->elm_of_node)
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH4_CODE_HW;
+ else
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
+ else if (!strcmp(s, "bch8"))
+ if (gpmc_nand_data->elm_of_node)
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH8_CODE_HW;
+ else
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ else
+ pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
+
+ /* select data transfer mode for NAND controller */
+ if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
+ for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
+ if (!strcasecmp(s, nand_xfer_types[val])) {
+ gpmc_nand_data->xfer_type = val;
break;
}
@@ -1473,8 +1511,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
*/
ret = gpmc_cs_remap(cs, res.start);
if (ret < 0) {
- dev_err(&pdev->dev, "cannot remap GPMC CS %d to 0x%x\n",
- cs, res.start);
+ dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
+ cs, &res.start);
goto err;
}
@@ -1513,6 +1551,20 @@ static int gpmc_probe_dt(struct platform_device *pdev)
if (!of_id)
return 0;
+ ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs",
+ &gpmc_cs_num);
+ if (ret < 0) {
+ pr_err("%s: number of chip-selects not defined\n", __func__);
+ return ret;
+ } else if (gpmc_cs_num < 1) {
+ pr_err("%s: all chip-selects are disabled\n", __func__);
+ return -EINVAL;
+ } else if (gpmc_cs_num > GPMC_CS_NUM) {
+ pr_err("%s: number of supported chip-selects cannot be > %d\n",
+ __func__, GPMC_CS_NUM);
+ return -EINVAL;
+ }
+
ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins",
&gpmc_nr_waitpins);
if (ret < 0) {
@@ -1577,7 +1629,8 @@ static int gpmc_probe(struct platform_device *pdev)
return PTR_ERR(gpmc_l3_clk);
}
- clk_prepare_enable(gpmc_l3_clk);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
gpmc_dev = &pdev->dev;
@@ -1610,12 +1663,14 @@ static int gpmc_probe(struct platform_device *pdev)
/* Now the GPMC is initialised, unreserve the chip-selects */
gpmc_cs_map = 0;
- if (!pdev->dev.of_node)
+ if (!pdev->dev.of_node) {
+ gpmc_cs_num = GPMC_CS_NUM;
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
+ }
rc = gpmc_probe_dt(pdev);
if (rc < 0) {
- clk_disable_unprepare(gpmc_l3_clk);
+ pm_runtime_put_sync(&pdev->dev);
clk_put(gpmc_l3_clk);
dev_err(gpmc_dev, "failed to probe DT parameters\n");
return rc;
@@ -1628,10 +1683,30 @@ static int gpmc_remove(struct platform_device *pdev)
{
gpmc_free_irq();
gpmc_mem_exit();
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
gpmc_dev = NULL;
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int gpmc_suspend(struct device *dev)
+{
+ omap3_gpmc_save_context();
+ pm_runtime_put_sync(dev);
+ return 0;
+}
+
+static int gpmc_resume(struct device *dev)
+{
+ pm_runtime_get_sync(dev);
+ omap3_gpmc_restore_context();
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume);
+
static struct platform_driver gpmc_driver = {
.probe = gpmc_probe,
.remove = gpmc_remove,
@@ -1639,6 +1714,7 @@ static struct platform_driver gpmc_driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpmc_dt_ids),
+ .pm = &gpmc_pm_ops,
},
};
@@ -1678,7 +1754,7 @@ static int __init omap_gpmc_init(void)
pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0);
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap_gpmc_init);
@@ -1701,7 +1777,6 @@ static irqreturn_t gpmc_handle_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-#ifdef CONFIG_ARCH_OMAP3
static struct omap3_gpmc_regs gpmc_context;
void omap3_gpmc_save_context(void)
@@ -1715,7 +1790,7 @@ void omap3_gpmc_save_context(void)
gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
- for (i = 0; i < GPMC_CS_NUM; i++) {
+ for (i = 0; i < gpmc_cs_num; i++) {
gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
if (gpmc_context.cs_context[i].is_valid) {
gpmc_context.cs_context[i].config1 =
@@ -1747,7 +1822,7 @@ void omap3_gpmc_restore_context(void)
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
- for (i = 0; i < GPMC_CS_NUM; i++) {
+ for (i = 0; i < gpmc_cs_num; i++) {
if (gpmc_context.cs_context[i].is_valid) {
gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
gpmc_context.cs_context[i].config1);
@@ -1766,4 +1841,3 @@ void omap3_gpmc_restore_context(void)
}
}
}
-#endif /* CONFIG_ARCH_OMAP3 */
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 7c5a6b81875..f9b8a4c965a 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -3818,6 +3818,7 @@ static struct omap_hwmod_ocp_if *omap34xx_gp_hwmod_ocp_ifs[] __initdata = {
NULL
};
+
static struct omap_hwmod_ocp_if *omap36xx_gp_hwmod_ocp_ifs[] __initdata = {
&omap3xxx_l4_sec__timer12,
&omap3xxx_l4_core__sham,
@@ -3975,7 +3976,8 @@ int __init omap3xxx_hwmod_init(void)
} else if (rev == OMAP3630_REV_ES1_0 || rev == OMAP3630_REV_ES1_1 ||
rev == OMAP3630_REV_ES1_2) {
h = omap36xx_hwmod_ocp_ifs;
- h_gp = omap36xx_gp_hwmod_ocp_ifs;
+ /* OLIO: We don't want these clocks running */
+ /* h_gp = omap36xx_gp_hwmod_ocp_ifs; */
} else {
WARN(1, "OMAP3 hwmod family init: unknown chip type\n");
return -EINVAL;
diff --git a/arch/arm/mach-omap2/opp.c b/arch/arm/mach-omap2/opp.c
index 82fd8c72f75..7149f838be4 100644
--- a/arch/arm/mach-omap2/opp.c
+++ b/arch/arm/mach-omap2/opp.c
@@ -41,8 +41,10 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def,
{
int i, r;
+ /* OLIO HACK
if (of_have_populated_dt())
return -EINVAL;
+ */
if (!opp_def || !opp_def_size) {
pr_err("%s: invalid params!\n", __func__);
diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c
index fc67add7644..0f5af3377af 100644
--- a/arch/arm/mach-omap2/opp3xxx_data.c
+++ b/arch/arm/mach-omap2/opp3xxx_data.c
@@ -130,9 +130,9 @@ static struct omap_opp_def __initdata omap36xx_opp_def_list[] = {
/* MPU OPP2 - OPP100 */
OPP_INITIALIZER("mpu", true, 600000000, OMAP3630_VDD_MPU_OPP100_UV),
/* MPU OPP3 - OPP-Turbo */
- OPP_INITIALIZER("mpu", false, 800000000, OMAP3630_VDD_MPU_OPP120_UV),
+ OPP_INITIALIZER("mpu", true, 800000000, OMAP3630_VDD_MPU_OPP120_UV),
/* MPU OPP4 - OPP-SB */
- OPP_INITIALIZER("mpu", false, 1000000000, OMAP3630_VDD_MPU_OPP1G_UV),
+ OPP_INITIALIZER("mpu", true, 1000000000, OMAP3630_VDD_MPU_OPP1G_UV),
/* L3 OPP1 - OPP50 */
OPP_INITIALIZER("l3_main", true, 100000000, OMAP3630_VDD_CORE_OPP50_UV),
diff --git a/arch/arm/mach-omap2/pm-debug-regs.h b/arch/arm/mach-omap2/pm-debug-regs.h
index f70bbe5c6a8..65da3221b13 100644
--- a/arch/arm/mach-omap2/pm-debug-regs.h
+++ b/arch/arm/mach-omap2/pm-debug-regs.h
@@ -27,7 +27,7 @@ extern void pm_dbg_regs_copy(int tgt, int src);
static inline int pm_dbg_regs_init(struct dentry *d) { return 0; }
static inline void pm_dbg_regs_save(int reg_set) {};
static inline void pm_dbg_regs_dump(int reg_set) {};
-static inline void pm_dbg_regs_dump_delta(int current, int ref) {}
+static inline void pm_dbg_regs_dump_delta(int curr, int ref) {};
static inline void pm_dbg_show_wakeup_source(void) {};
static inline void pm_dbg_regs_copy(int tgt, int src) {};
#endif
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index dd7931c2d54..b2dc18a6b82 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -276,6 +276,8 @@ static inline void omap_init_cpufreq(void)
devinfo.name = "omap-cpufreq";
else
devinfo.name = "cpufreq-cpu0";
+
+ devinfo.name = "omap-cpufreq"; /* OLIO HACK */
platform_device_register_full(&devinfo);
}
diff --git a/arch/arm/mach-omap2/pmu.c b/arch/arm/mach-omap2/pmu.c
index 9ace8eae7ee..33c8846b419 100644
--- a/arch/arm/mach-omap2/pmu.c
+++ b/arch/arm/mach-omap2/pmu.c
@@ -54,10 +54,7 @@ static int __init omap2_init_pmu(unsigned oh_num, char *oh_names[])
WARN(IS_ERR(omap_pmu_dev), "Can't build omap_device for %s.\n",
dev_name);
- if (IS_ERR(omap_pmu_dev))
- return PTR_ERR(omap_pmu_dev);
-
- return 0;
+ return PTR_RET(omap_pmu_dev);
}
static int __init omap_init_pmu(void)
diff --git a/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h b/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h
new file mode 100644
index 00000000000..aa012b090f9
--- /dev/null
+++ b/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h
@@ -0,0 +1,63 @@
+/*
+ * SDRC register values for the Micron MT29C4G48MAYAPAKQ-5
+ *
+ * Copyright (C) 2015 Olio Devices
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Mattis Fjallstrom (mattis at oliodevices.com)
+ * Paul Walmsley
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT29C4G48
+#define ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT29C4G48
+
+#include "sdrc.h"
+
+/* Micron MT29C4G48MAYAPAKQ-5 */
+/* 5ns -> 200MHz */
+
+
+/* 5.4ns -> 185.185185185185 MHz */
+/* 6ns -> 166.666667 MHz */
+/* 7.5ns -> 133.33333333333 */
+
+static struct omap_sdrc_params mt29c4g48_sdrc_params[] = {
+ [0] = {
+ .rate = 200000000,
+ .actim_ctrla = 0x7AE1B4C6,
+ .actim_ctrlb = 0x00021217,
+ .rfr_ctrl = 0x0005E601,
+ .mr = 0x00000034,
+ },
+ [1] = {
+ .rate = 185185185,
+ .actim_ctrla = 0x72E1B4C6,
+ .actim_ctrlb = 0x00021215,
+ .rfr_ctrl = 0x00057201,
+ .mr = 0x00000034,
+ },
+ [2] = {
+ .rate = 166000000,
+ .actim_ctrla = 0x629DB4C6,
+ .actim_ctrlb = 0x00011113,
+ .rfr_ctrl = 0x0004E201,
+ .mr = 0x00000034,
+ },
+ [3] = {
+ .rate = 133333333,
+ .actim_ctrla = 0x5259B485,
+ .actim_ctrlb = 0x0001110F,
+ .rfr_ctrl = 0x0003DE01,
+ .mr = 0x00000034,
+ },
+ [4] = {
+ .rate = 0,
+ },
+};
+
+#endif
diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index 30b24ad84f4..8ebc41a7243 100644
--- a/arch/arm/mach-s5pv210/mach-goni.c
+++ b/arch/arm/mach-s5pv210/mach-goni.c
@@ -239,14 +239,6 @@ static void __init goni_radio_init(void)
/* TSP */
static struct mxt_platform_data qt602240_platform_data = {
- .x_line = 17,
- .y_line = 11,
- .x_size = 800,
- .y_size = 480,
- .blen = 0x21,
- .threshold = 0x28,
- .voltage = 2800000, /* 2.8V */
- .orient = MXT_DIAGONAL,
.irqflags = IRQF_TRIGGER_FALLING,
};
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index e4203b7eeba..c2fa3ad5270 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -274,6 +274,7 @@ omap2evm MACH_OMAP2EVM OMAP2EVM 1534
omap3evm MACH_OMAP3EVM OMAP3EVM 1535
dns323 MACH_DNS323 DNS323 1542
omap3_beagle MACH_OMAP3_BEAGLE OMAP3_BEAGLE 1546
+omap3_h1 MACH_OMAP3_H1 OMAP3_H1 1547
nokia_n810 MACH_NOKIA_N810 NOKIA_N810 1548
pcm038 MACH_PCM038 PCM038 1551
sg310 MACH_SG310 SG310 1564
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index ef13ad08afb..f8bc937728b 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -958,6 +958,7 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
spin_lock_irqsave(&dev->power.lock, flags);
retval = rpm_resume(dev, rpmflags);
+ BUG_ON (dev == NULL); /* OLIO: I get weird crashes here */
spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index 06146219d9d..d64de51db97 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -82,7 +82,7 @@ static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
GPIO_CFG_MASK);
}
-#ifdef CONFIG_OF
+#if 0 /* #ifdef CONFIG_OF - olio: We DO have a DT, but no tps info there. */
static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
struct tps65910 *tps65910, int chip_ngpio)
{
@@ -149,7 +149,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
tps65910_gpio->gpio_chip.set = tps65910_gpio_set;
tps65910_gpio->gpio_chip.get = tps65910_gpio_get;
tps65910_gpio->gpio_chip.dev = &pdev->dev;
-#ifdef CONFIG_OF_GPIO
+#if 0 /* #ifdef CONFIG_OF_GPIO */
tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node;
#endif
if (pdata && pdata->gpio_base)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b2f963be399..a37b074755d 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -4,7 +4,6 @@
menuconfig IIO
tristate "Industrial I/O support"
- depends on GENERIC_HARDIRQS
help
The industrial I/O subsystem provides a unified framework for
drivers for many different types of embedded sensors using a
@@ -67,7 +66,7 @@ source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
-source "drivers/iio/imu/Kconfig"
+# source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index a0e8cdd67e4..35a17226827 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -18,6 +18,6 @@ obj-y += common/
obj-y += dac/
obj-y += gyro/
obj-y += frequency/
-obj-y += imu/
+# obj-y += imu/
obj-y += light/
obj-y += magnetometer/
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu-aosp/Kconfig
index 4f40a10cb74..c332d4918b1 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu-aosp/Kconfig
@@ -38,3 +38,5 @@ config IIO_ADIS_LIB_BUFFER
family.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
+# source "drivers/iio/imu/inv_mpu6515/Kconfig"
+
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu-aosp/Makefile
index f2f56ceaed2..2fb21dd4a54 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu-aosp/Makefile
@@ -13,3 +13,4 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += inv_mpu6050/
+obj-y += inv_mpu6515/
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu-aosp/adis.c
index 911255d41c1..911255d41c1 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu-aosp/adis.c
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu-aosp/adis16400.h
index 2f8f9d63238..2f8f9d63238 100644
--- a/drivers/iio/imu/adis16400.h
+++ b/drivers/iio/imu-aosp/adis16400.h
diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu-aosp/adis16400_buffer.c
index 054c01d6e73..054c01d6e73 100644
--- a/drivers/iio/imu/adis16400_buffer.c
+++ b/drivers/iio/imu-aosp/adis16400_buffer.c
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu-aosp/adis16400_core.c
index f60591f0b92..f60591f0b92 100644
--- a/drivers/iio/imu/adis16400_core.c
+++ b/drivers/iio/imu-aosp/adis16400_core.c
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu-aosp/adis16480.c
index b7db3837629..b7db3837629 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu-aosp/adis16480.c
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu-aosp/adis_buffer.c
index 99d8e0b0dd3..99d8e0b0dd3 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu-aosp/adis_buffer.c
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu-aosp/adis_trigger.c
index e0017c22bb9..e0017c22bb9 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu-aosp/adis_trigger.c
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu-aosp/inv_mpu6050/Kconfig
index 361b2328453..361b2328453 100644
--- a/drivers/iio/imu/inv_mpu6050/Kconfig
+++ b/drivers/iio/imu-aosp/inv_mpu6050/Kconfig
diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu-aosp/inv_mpu6050/Makefile
index 3a677c778af..3a677c778af 100644
--- a/drivers/iio/imu/inv_mpu6050/Makefile
+++ b/drivers/iio/imu-aosp/inv_mpu6050/Makefile
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_core.c
index fe4c61e219f..fe4c61e219f 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_core.c
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_iio.h
index f38395529a4..f38395529a4 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_iio.h
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_ring.c
index 7da0832f187..7da0832f187 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_ring.c
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_trigger.c
index 03b9372c121..03b9372c121 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_trigger.c
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/Kconfig b/drivers/iio/imu-aosp/inv_mpu6515/Kconfig
new file mode 100755
index 00000000000..18ccddb1f4e
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/Kconfig
@@ -0,0 +1,40 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices and combos
+#
+
+config INV_MPU_IIO
+ tristate "Invensense MPU devices"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU
+ default n
+ help
+ This driver supports the Invensense MPU devices.
+ This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio.
+
+config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250
+ bool "Invensense MPU3050 slave accelerometer device for bma250"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This is slave device enable MPU3050 accelerometer slave device.
+ Right now, it is only bma250. For other acceleromter device,
+ it can be added to this menu if the proper interface is filled.
+ There are some interface function to be defined.
+
+config DTS_INV_MPU_IIO
+ bool "Invensense MPU driver using Device Tree Structure (DTS)"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This enables Invensense MPU devices to use Device Tree Structure.
+ DTS must be enabled in the system.
+
+config INV_KERNEL_3_10
+ bool "Invensense MPU driver support for 3.10 kernel"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This enables Invensense MPU devices for 3.10 kernel
+
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/Makefile b/drivers/iio/imu-aosp/inv_mpu6515/Makefile
new file mode 100755
index 00000000000..6e3c2475889
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/Makefile
@@ -0,0 +1,56 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs := inv_mpu_core.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_trigger.o
+inv-mpu-iio-objs += inv_mpu_misc.o
+inv-mpu-iio-objs += inv_mpu3050_iio.o
+inv-mpu-iio-objs += dmpDefaultMPU6050.o
+inv-mpu-iio-objs += inv_slave_compass.o
+inv-mpu-iio-objs += inv_slave_pressure.o
+
+ifeq ($(CONFIG_INV_KERNEL_3_10), y)
+CFLAGS_inv_mpu_core.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_mpu_ring.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_mpu_trigger.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_mpu_misc.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_mpu3050_iio.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_dmpDefaultMPU6050.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_slave_compass.o += -Idrivers/iio -Iinclude/linux/iio
+CFLAGS_inv_slave_pressure.o += -Idrivers/iio -Iinclude/linux/iio
+else
+CFLAGS_inv_mpu_core.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_trigger.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_misc.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu3050_iio.o += -Idrivers/staging/iio
+CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio
+CFLAGS_inv_slave_compass.o += -Idrivers/staging/iio
+CFLAGS_inv_slave_pressure.o += -Idrivers/staging/iio
+endif
+
+# the Bosch BMA250 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y)
+inv-mpu-iio-objs += inv_slave_bma250.o
+ifeq ($(CONFIG_INV_KERNEL_3_10), y)
+CFLAGS_inv_slave_bma250.o += -Idrivers/iio
+else
+CFLAGS_inv_slave_bma250.o += -Idrivers/staging/iio
+endif
+endif
+
+# compile Invensense MPU IIO driver as DTS
+ifeq ($(CONFIG_DTS_INV_MPU_IIO), y)
+inv-mpu-iio-objs += inv_mpu_dts.o
+ifeq ($(CONFIG_INV_KERNEL_3_10), y)
+CFLAGS_inv_mpu_dts.o += -Idrivers/iio
+else
+CFLAGS_inv_mpu_dts.o += -Idrivers/staging/iio
+endif
+endif
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/README b/drivers/iio/imu-aosp/inv_mpu6515/README
new file mode 100755
index 00000000000..0963954a844
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/README
@@ -0,0 +1,659 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+ > Interrupt Pin
+ > Platform Data
+- Board File Modifications for Secondary I2C Configuration
+ > MPU-6050 + AKM8963 on the secondary I2C interface
+ > MPU-6500 + AKM8963 on the secondary I2C interface
+ > MPU-6515 + AKM8975 and BMP280 on the secondary I2C interface
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Board File Modifications for Invensense Devices
+ > MPU-3050
+ > ITG-3500
+ > MPU-6050
+ > MPU-6515
+ > MPU-6500
+ > MPU-6XXX
+ > MPU-9150
+ > MPU-9250
+- IIO Subsystem
+ > Communicating with the Driver in Userspace
+ > ITG-3500
+ > MPU-6050 and MPU-6500
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Suspend and Resume
+- Wake up CPU using event interrupt
+- DMP Event
+- Motion Event
+- Streaming Data to an Userspace Application
+- Recommended Sysfs Entry Setup Sequence
+ > With DMP Firmware
+ > Without DMP Firmware
+- Test Applications
+ > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+ > Running Test Applications with MPU-3050/ITG-3500
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The Invensense driver currently supports the following sensors:
+- ITG-3500
+- MPU-6050
+- MPU-9150
+- MPU-6500
+- MPU-9250
+- MPU-3050
+- MPU-6XXX(either MPU6050 or MPU6500, driver to do auto detection)
+
+The slave address of each device is either 0x68 or 0x69, depending on the AD0
+pin value of the device. Please refer to the appropriate product specification
+document for further information regarding the AD0 pin. The driver supports both
+addresses.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_mpu_core.c
+- inv_mpu_misc.c
+- inv_mpu_trigger.c
+- inv_mpu3050_iio.c
+- inv_mpu_iio.h
+- inv_mpu_ring.c
+- inv_slave_bma250.c
+- inv_slave_compass.c
+- dmpDefaultMPU6050.c
+- dmpkey.h
+- dmpmap.h
+- mpu.h
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add mpu.h to "kernel/include/linux".
+- Add all other files to drivers/staging/iio/imu/inv_mpu
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ modify "drivers/staging/iio/imu/Kconfig" with:
+ >> source "drivers/staging/iio/imu/inv_mpu/Kconfig"
+
+ modify "drivers/staging/iio/imu/Makefile" with:
+ >> obj-y += inv_mpu/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Interrupt Pin
+-------------
+The hardcoded value of 140 corresponds to the GPIO input pin connected to the
+Invensense device's interrupt pin.
+This pin will most likely be different for your platform, and the value should
+be changed accordingly.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+MPU-6050 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6500 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6500 + AK09911 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK09911,
+ .secondary_i2c_addr = 0x0D,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6515 + AK8975 + BMP280 on the secondary I2C interface
+Note: sec_slave_XXX can only be compass or accelerometer in 3050 case.
+ aux_slave_XXX can only be pressure.
+--------------------------------------------------
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8975,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+ .aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE,
+ .aux_slave_id = PRESSURE_ID_BMP280,
+ .aux_i2c_addr = 0x77,
+};
+static struct i2c_board_info __initdata chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6515", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &mpu_data,
+ }
+};
+
+MPU-9150
+--------
+For MPU-9150, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8975,
+ .secondary_i2c_addr = 0x0C,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-9250
+--------
+For MPU-9250, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0C,
+ .secondary_orientation = { 0, 1, 0,
+ -1, 0, 0,
+ 0, 0, 1 },
+};
+
+MPU-3050 + BMA250 on the secondary I2C interface
+------------------------------------------------
+For BMA250 on the secondary I2C bus, please provide the following information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL,
+ .sec_slave_id = ACCEL_ID_BMA250,
+ .secondary_i2c_addr = 0x18,
+};
+
+
+Board File Modifications for Invensense Devices
+===============================================
+For Invensense devices, please provide the i2c init data as shown in the
+examples below.
+
+In the _i2c_init function, the device is registered in the following manner:
+
+ // arch/arm/mach-omap2/board-omap4panda.c
+ // in static int __init omap4_panda_i2c_init(void)
+ omap_register_i2c_bus(4, 400,
+ single_chip_board_info,
+ ARRAY_SIZE(single_chip_board_info));
+
+MPU-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu3050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+ITG-3500
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("itg3500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6050
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6500
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6XXX
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6xxx", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9150
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9150", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9250
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9250", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+ - iio:device0
+ - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+ITG-3500
+--------
+temperature (Read-only)
+--Read temperature data directly from the temperature register.
+
+sampling_frequency (Read/write)
+--Configure the ADC sampling rate and FIFO output rate.
+
+sampling_frequency_available(read-only)
+--show commonly used frequency
+
+clock_source (Read-only)
+--Check which clock-source is used by the chip.
+
+power_state (Read/write)
+--turn on/off the power supply
+
+self_test (read-only)
+--read this entry trigger self test. The return value is D.
+D is the success/fail.
+For different chip, the result is different for success/fail.
+1 means success 0 means fail. The LSB of D is for gyro; the bit
+next to LSB of D is for accel. The bit 2 of D is for compass result.
+
+key (read-only)
+--show the key value of this driver. Used by MPL.
+
+gyro_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+MPU-6050 and MPU-6500
+---------------------
+MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above).
+In addition, it has the files below:
+
+gyro_enable (read/write)
+--enable/disable gyro functionality. Affects raw_gyro. Turning this off this
+ will shut down gyro and save power.
+
+accl_enable (read/write)
+--enable/disable accelerometer functionality. Affects raw_accl.
+Turning this off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+--Flag indicating the whether firmware is loaded or not in the DMP engine.
+0 means no firmware loaded. 1 means firmware is already loaded . This
+flag can only be written as 0. It internally updates to 1.
+
+dmp_on(read/write)
+--This entry controls whether to run DMP or not.
+Write 1 to enable DMP and write 0 to disable dmp.
+Please note that firmware_loaded must be 1 in order to enable DMP.
+
+dmp_int_on(read/write)
+--This entry controls whether dmp interrupt is on/off.
+Please note that firmware_loaded must be 1.
+Also, we'd like to remind you that it is sometimes advantageous to
+turn interrupts off while the DMP is running.
+
+dmp_output_rate
+--control dmp output rate when dmp is on.
+
+dmp_event_int_on(read/write)
+--This entry controls whether dmp event interrupt is on/off.
+Please note that turning this on will turn off the data interrupt.
+Interrupts will be generated only when events occur.
+This is useful for saving power when the system is waiting for a special event
+to wake up.
+
+dmp_firmware (write only binary file)
+--DMP firmware code is loaded into this entry.
+If loading is successful, the firmware_loaded flag will be updated to 1.
+In order to load new firmware, the firmware_loaded flag must be first set to 0.
+
+accel_matrix
+--orientation matrix for accelerometer.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+pedometer_time
+pedometer_steps,
+--Pedometer related entries
+
+event_tap
+event_display_orientation
+event_accel_motion
+event_smd
+--Event related entries.
+Please poll these entries to read their values. Direct reads will yield
+meaningless results.
+Further details are provided in the DMP Events section of this README.
+
+tap_on
+--Controls tap function of DMP
+
+tap_time
+tap_min_count
+tap_threshold
+--Tap related entries. Controls various parameters of tap function.
+
+display_orientation_on
+--Turn on/off display orientation function of DMP.
+
+smd_enable
+enable SMD(Significant Motion Detection) detection.
+
+smd_threshold
+This set the threshold of the motion when SMD start to be triggered. The
+value is in acclerometer counts.
+
+smd_delay_threshold
+This sets the threshold of time after which SMD can be triggered.
+The value is in seconds.
+
+smd_delay_threshold2
+This sets the threshold of time during which SMD can be triggered (after the
+smd_delay_threshold timer has expired).
+The value is in seconds.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+Low power accel motion interrupt related settings.
+if motion_lpa_on is set, this will disable all engines except accel. Accel will
+enter low power mode and the whole chip will be turned on/off at specific frequency.
+-----------------------------------------------------------------------------
+motion_lpa_threshold
+--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg.
+
+motion_lpa_on
+--turn on/off motion function.
+
+motion_lpa_freq
+--motion lpa frequency. which determines power on/off frequency.
+------------------------------------------------------------------------------
+MPU-9150
+--------
+MPU-9150 has all of MPU-6050's entries. It also has two additional entries,
+described below.
+
+compass_enable (read/write)
+--Enables compass function.
+
+compass_matrix (read-only)
+--Compass orientation matrix
+
+MPU-3050 with BMA250 on secondary I2C interface
+-----------------------------------------------
+MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry.
+It also has two additional entries, shown below:
+
+accl_matrix
+
+accl_enable
+
+Suspend and Resume
+===================================================
+The suspend and resume functions are call backs registered to the system
+and executed when the system goes in suspend and resumes.
+It is enabled when CONFIG_PM is defined.
+The current behavior is simple:
+- suspend will turn off the chip
+- resume will turn on the chip
+
+However, it is possible for the driver to do more complex things;
+for example, leaving pedometers running when system is off. This can save whole
+system power while letting pedometer working. Other behaviors are possible
+too.
+
+Wake up CPU using event interrupt
+====================================================
+It is common practice in current mobile device to save power as much as
+possible. Ususally the device can sleep when not use and only be wake up when in use.
+Invensense MPU series provides an efficient way of waking up CPU by events.
+The events could be pedometer steps, tap action, SMD(significant Motion Detection),
+or any customer defined actions. CPU can sleep to save power, when event happens,
+DMP will calculate the motion data to detemine whether a pre-defined event happens
+or not. If an pre-defined happens, an interrupt is generated to wake up the CPU
+to do further processing.
+The requirements of doing event wake is the following:
+1. Add enable_irq_wake() either in the driver or in the board file.
+2. Connect the interrupt pin to the CPU wake up pin.
+3. Configure CPU to be woken up by wake up pin.
+
+DMP Event
+=========
+A DMP Event is an event that is output by the DMP unit within the Invensense
+device (MPU).
+Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP.
+
+There are four sysfs entries for DMP events:
+- event_tap
+- event_display_orientation
+- event_accel_motion
+- event_smd
+
+These events must be polled before reading.
+
+The proper method to poll sysfs is as follows:
+1. open file.
+2. dummy read.
+3. poll.
+4. once the poll passed, use fopen and fread to read the sysfs entry.
+5. interpret the data.
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+gyro/accel/compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write a 1 to power_state to turn on the chip. This is the default setting
+ after installing the driver.
+2. Write the desired output rate to fifo_rate.
+3. Write 1 to enable to turn on the event.
+4. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+5. Parse this string to obtain each gyro/accel/compass element.
+6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp.
+7. If compass is enabled, the output will contain compass data.
+
+
+Recommended Sysfs Entry Setup Senquence
+=======================================
+
+Without DMP Firmware
+--------------------
+1. Set "power_state" to 1,
+2. Set the scale and fifo rate values according to your needs.
+3. Set gyro_enable, accel_enable, and compass_enable according to your needs.
+ For example:
+ - If you only want gyro data, set accel_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want accel data, set gyro_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want compass data, set gyro_enable to 0 and accel_enable to 0.
+4. Set "enable" to 1.
+5. You will now get the output that you want.
+
+With DMP Firmware
+-----------------
+1. Set "power_state" to 1.
+2. Write "0" to firmware_loaded if it is not zero already.
+3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware
+ image.
+4. Make sure firmware_loaded is 1 after loading the DMP image.
+5. Make appropriate configurations as shown above in the "without DMP firmware"
+ case.
+6. Set dmp_on to 1.
+7. Set "enable" to 1.
+
+Please note that the enable function uses the enable entry under
+"/sys/bus/iio/devices/iio:device0/buffer"
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+---------------------------------------------------------
+To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices,
+please use the following commands:
+
+1. For tap/display orientation events:
+ mpu_iio -c 10 -l 3 -p
+
+2. In addition, to test the motion interrupt (and no_motion on MPU6050) use:
+ mpu_iio -c 10 -l 3 -p -m
+
+3. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Running Test Applications with MPU-3050/ITG-3500
+------------------------------------------------
+To run test applications with MPU-3050 or ITG-3500 devices,
+please use the following command:
+
+1. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
+
+Stress test application
+=================================
+A stress test application is located under software/simple_apps/stress_iio.
+This application simulates HAL's usage calls to the driver. It creates three
+threads. One for data read; one for event read; one for sysfs control.
+It can run without any parameters or run with some control parameters. Please
+see README in the same directories for details.
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c b/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c
new file mode 100755
index 00000000000..892e62dc14f
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include "inv_mpu_iio.h"
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_OUT_PRESS (1872)
+#define CFG_PED_ENABLE (1985)
+#define CFG_OUT_GYRO (1804)
+#define CFG_PEDSTEP_DET (2807)
+#define OUT_GYRO_DAT (1813)
+#define CFG_FIFO_INT (1983)
+#define OUT_CPASS_DAT (1847)
+#define CFG_AUTH (1099)
+#define OUT_ACCL_DAT (1779)
+#define FCFG_1 (1127)
+#define FCFG_3 (1152)
+#define FCFG_2 (1131)
+#define CFG_OUT_CPASS (1838)
+#define FCFG_7 (1138)
+#define CFG_OUT_3QUAT (1666)
+#define OUT_PRESS_DAT (1881)
+#define OUT_3QUAT_DAT (1676)
+#define CFG_7 (1349)
+#define OUT_PQUAT_DAT (1745)
+#define CFG_OUT_6QUAT (1701)
+#define CFG_PED_INT (2796)
+#define SMD_TP2 (1314)
+#define SMD_TP1 (1293)
+#define CFG_MOTION_BIAS (1351)
+#define CFG_OUT_ACCL (1770)
+#define CFG_OUT_STEPDET (1636)
+#define OUT_6QUAT_DAT (1711)
+#define CFG_OUT_PQUAT (1736)
+
+#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 (30 * 16 + 4)
+#define CPASS_BIAS_Y (30 * 16 + 8)
+#define CPASS_BIAS_Z (30 * 16 + 12)
+#define CPASS_MTX_00 (32 * 16 + 4)
+#define CPASS_MTX_01 (32 * 16 + 8)
+#define CPASS_MTX_02 (36 * 16 + 12)
+#define CPASS_MTX_10 (33 * 16)
+#define CPASS_MTX_11 (33 * 16 + 4)
+#define CPASS_MTX_12 (33 * 16 + 8)
+#define CPASS_MTX_20 (33 * 16 + 12)
+#define CPASS_MTX_21 (34 * 16 + 4)
+#define CPASS_MTX_22 (34 * 16 + 8)
+#define D_EXT_GYRO_BIAS_X (61 * 16)
+#define D_EXT_GYRO_BIAS_Y (61 * 16 + 4)
+#define D_EXT_GYRO_BIAS_Z (61 * 16 + 8)
+#define D_ACT0 (40 * 16)
+#define D_ACSX (40 * 16 + 4)
+#define D_ACSY (40 * 16 + 8)
+#define D_ACSZ (40 * 16 + 12)
+
+#define FLICK_MSG (45 * 16 + 4)
+#define FLICK_COUNTER (45 * 16 + 8)
+#define FLICK_LOWER (45 * 16 + 12)
+#define FLICK_UPPER (46 * 16 + 12)
+
+#define D_SMD_ENABLE (18 * 16)
+#define D_SMD_MOT_THLD (21 * 16 + 8)
+#define D_SMD_DELAY_THLD (21 * 16 + 4)
+#define D_SMD_DELAY2_THLD (21 * 16 + 12)
+#define D_SMD_EXE_STATE (22 * 16)
+#define D_SMD_DELAY_CNTR (21 * 16)
+
+#define D_WF_GESTURE_TIME_THRSH (25 * 16 + 8)
+#define D_WF_GESTURE_TILT_ERROR (25 * 16 + 12)
+#define D_WF_GESTURE_TILT_THRSH (26 * 16 + 8)
+#define D_WF_GESTURE_TILT_REJECT_THRSH (26 * 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_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_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_STEPCTR2 (58 * 16 + 8)
+#define D_PEDSTD_TIMECTR (964)
+#define D_PEDSTD_DECI (768 + 0xA0)
+#define D_PEDSTD_SB2 (60 * 16 + 14)
+#define D_STPDET_TIMESTAMP (28 * 16 + 8)
+#define D_PEDSTD_DRIVE_STATE (58)
+
+#define D_HOST_NO_MOT (976)
+#define D_ACCEL_BIAS (660)
+
+#define D_BM_BATCH_CNTR (27 * 16 + 4)
+#define D_BM_BATCH_THLD (27 * 16 + 12)
+#define D_BM_ENABLE (28 * 16 + 6)
+#define D_BM_NUMWORD_TOFILL (28 * 16 + 4)
+
+#define D_SO_DATA (4 * 16 + 2)
+
+#define D_P_HW_ID (22 * 16 + 10)
+#define D_P_INIT (22 * 16 + 2)
+
+#define D_DMP_RUN_CNTR (24*16)
+
+#define D_ODR_S0 (45*16+8)
+#define D_ODR_S1 (45*16+12)
+#define D_ODR_S2 (45*16+10)
+#define D_ODR_S3 (45*16+14)
+#define D_ODR_S4 (46*16+8)
+#define D_ODR_S5 (46*16+12)
+#define D_ODR_S6 (46*16+10)
+#define D_ODR_S7 (46*16+14)
+#define D_ODR_S8 (42*16+8)
+#define D_ODR_S9 (42*16+12)
+
+#define D_ODR_CNTR_S0 (45*16)
+#define D_ODR_CNTR_S1 (45*16+4)
+#define D_ODR_CNTR_S2 (45*16+2)
+#define D_ODR_CNTR_S3 (45*16+6)
+#define D_ODR_CNTR_S4 (46*16)
+#define D_ODR_CNTR_S5 (46*16+4)
+#define D_ODR_CNTR_S6 (46*16+2)
+#define D_ODR_CNTR_S7 (46*16+6)
+#define D_ODR_CNTR_S8 (42*16)
+#define D_ODR_CNTR_S9 (42*16+4)
+
+#define D_FS_LPQ0 (59*16)
+#define D_FS_LPQ1 (59*16 + 4)
+#define D_FS_LPQ2 (59*16 + 8)
+#define D_FS_LPQ3 (59*16 + 12)
+
+#define D_FS_Q0 (12*16)
+#define D_FS_Q1 (12*16 + 4)
+#define D_FS_Q2 (12*16 + 8)
+#define D_FS_Q3 (12*16 + 12)
+
+#define D_FS_9Q0 (2*16)
+#define D_FS_9Q1 (2*16 + 4)
+#define D_FS_9Q2 (2*16 + 8)
+#define D_FS_9Q3 (2*16 + 12)
+
+#define D_CPASS_STATUS_CHK (22*16 + 8)
+
+static const struct tKeyLabel dmpTConfig[] = {
+ {KEY_CFG_OUT_ACCL, CFG_OUT_ACCL},
+ {KEY_CFG_OUT_GYRO, CFG_OUT_GYRO},
+ {KEY_CFG_OUT_3QUAT, CFG_OUT_3QUAT},
+ {KEY_CFG_OUT_6QUAT, CFG_OUT_6QUAT},
+ {KEY_CFG_OUT_PQUAT, CFG_OUT_PQUAT},
+ {KEY_CFG_PED_ENABLE, CFG_PED_ENABLE},
+ {KEY_CFG_FIFO_INT, CFG_FIFO_INT},
+ {KEY_CFG_AUTH, CFG_AUTH},
+ {KEY_FCFG_1, FCFG_1},
+ {KEY_FCFG_3, FCFG_3},
+ {KEY_FCFG_2, FCFG_2},
+ {KEY_FCFG_7, FCFG_7},
+ {KEY_CFG_7, CFG_7},
+ {KEY_CFG_MOTION_BIAS, CFG_MOTION_BIAS},
+ {KEY_CFG_PEDSTEP_DET, CFG_PEDSTEP_DET},
+ {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_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_D_PEDSTD_BP_B, D_PEDSTD_BP_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_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_STEPCTR2, D_PEDSTD_STEPCTR2},
+ {KEY_D_PEDSTD_TIMECTR, D_PEDSTD_TIMECTR},
+ {KEY_D_PEDSTD_DECI, D_PEDSTD_DECI},
+ {KEY_D_PEDSTD_SB2, D_PEDSTD_SB2},
+ {KEY_D_PEDSTD_DRIVE_STATE, D_PEDSTD_DRIVE_STATE},
+ {KEY_D_STPDET_TIMESTAMP, D_STPDET_TIMESTAMP},
+ {KEY_D_HOST_NO_MOT, D_HOST_NO_MOT},
+ {KEY_D_ACCEL_BIAS, D_ACCEL_BIAS},
+ {KEY_CFG_EXT_GYRO_BIAS_X, D_EXT_GYRO_BIAS_X},
+ {KEY_CFG_EXT_GYRO_BIAS_Y, D_EXT_GYRO_BIAS_Y},
+ {KEY_CFG_EXT_GYRO_BIAS_Z, D_EXT_GYRO_BIAS_Z},
+ {KEY_CFG_PED_INT, CFG_PED_INT},
+ {KEY_SMD_ENABLE, D_SMD_ENABLE},
+ {KEY_SMD_ACCEL_THLD, D_SMD_MOT_THLD},
+ {KEY_SMD_DELAY_THLD, D_SMD_DELAY_THLD},
+ {KEY_SMD_DELAY2_THLD, D_SMD_DELAY2_THLD},
+ {KEY_SMD_ENABLE_TESTPT1, SMD_TP1},
+ {KEY_SMD_ENABLE_TESTPT2, SMD_TP2},
+ {KEY_SMD_EXE_STATE, D_SMD_EXE_STATE},
+ {KEY_SMD_DELAY_CNTR, D_SMD_DELAY_CNTR},
+ {KEY_BM_ENABLE, D_BM_ENABLE},
+ {KEY_BM_BATCH_CNTR, D_BM_BATCH_CNTR},
+ {KEY_BM_BATCH_THLD, D_BM_BATCH_THLD},
+ {KEY_BM_NUMWORD_TOFILL, D_BM_NUMWORD_TOFILL},
+ {KEY_SO_DATA, D_SO_DATA},
+ {KEY_P_INIT, D_P_INIT},
+ {KEY_CFG_OUT_CPASS, CFG_OUT_CPASS},
+ {KEY_CFG_OUT_PRESS, CFG_OUT_PRESS},
+ {KEY_CFG_OUT_STEPDET, CFG_OUT_STEPDET},
+ {KEY_CFG_3QUAT_ODR, D_ODR_S1},
+ {KEY_CFG_6QUAT_ODR, D_ODR_S2},
+ {KEY_CFG_PQUAT6_ODR, D_ODR_S3},
+ {KEY_CFG_ACCL_ODR, D_ODR_S4},
+ {KEY_CFG_GYRO_ODR, D_ODR_S5},
+ {KEY_CFG_CPASS_ODR, D_ODR_S6},
+ {KEY_CFG_PRESS_ODR, D_ODR_S7},
+ {KEY_CFG_9QUAT_ODR, D_ODR_S8},
+ {KEY_CFG_PQUAT9_ODR, D_ODR_S9},
+ {KEY_ODR_CNTR_3QUAT, D_ODR_CNTR_S1},
+ {KEY_ODR_CNTR_6QUAT, D_ODR_CNTR_S2},
+ {KEY_ODR_CNTR_PQUAT, D_ODR_CNTR_S3},
+ {KEY_ODR_CNTR_ACCL, D_ODR_CNTR_S4},
+ {KEY_ODR_CNTR_GYRO, D_ODR_CNTR_S5},
+ {KEY_ODR_CNTR_CPASS, D_ODR_CNTR_S6},
+ {KEY_ODR_CNTR_PRESS, D_ODR_CNTR_S7},
+ {KEY_ODR_CNTR_9QUAT, D_ODR_CNTR_S8},
+ {KEY_ODR_CNTR_PQUAT9, D_ODR_CNTR_S9},
+ {KEY_DMP_RUN_CNTR, D_DMP_RUN_CNTR},
+ {KEY_DMP_LPQ0, D_FS_LPQ0},
+ {KEY_DMP_LPQ1, D_FS_LPQ1},
+ {KEY_DMP_LPQ2, D_FS_LPQ2},
+ {KEY_DMP_LPQ3, D_FS_LPQ3},
+ {KEY_DMP_Q0, D_FS_Q0},
+ {KEY_DMP_Q1, D_FS_Q1},
+ {KEY_DMP_Q2, D_FS_Q2},
+ {KEY_DMP_Q3, D_FS_Q3},
+ {KEY_DMP_9Q0, D_FS_9Q0},
+ {KEY_DMP_9Q1, D_FS_9Q1},
+ {KEY_DMP_9Q2, D_FS_9Q2},
+ {KEY_DMP_9Q3, D_FS_9Q3},
+ {KEY_CPASS_STATUS_CHK, D_CPASS_STATUS_CHK},
+ {KEY_TEST_01, OUT_ACCL_DAT},
+ {KEY_TEST_02, OUT_GYRO_DAT},
+ {KEY_TEST_03, OUT_CPASS_DAT},
+ {KEY_TEST_04, OUT_PRESS_DAT},
+ {KEY_TEST_05, OUT_3QUAT_DAT},
+ {KEY_TEST_06, OUT_6QUAT_DAT},
+ {KEY_TEST_07, OUT_PQUAT_DAT}
+};
+#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) {
+ pr_err("ERROR!! key not exist=%d!\n", key);
+ return 0xffff;
+ }
+ if (0xffff == keys[key].addr)
+ pr_err("ERROR!!key not local=%d!\n", key);
+ return keys[key].addr;
+}
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h b/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h
new file mode 100755
index 00000000000..20b28478785
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h
@@ -0,0 +1,607 @@
+/*
+ * 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)
+#define KEY_CFG_GYRO_RAW_DATA (KEY_CFG_LP_QUAT + 1)
+#define KEY_CFG_EXT_GYRO_BIAS (KEY_CFG_GYRO_RAW_DATA + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_X (KEY_CFG_EXT_GYRO_BIAS + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Y (KEY_CFG_EXT_GYRO_BIAS_X + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Z (KEY_CFG_EXT_GYRO_BIAS_Y + 1)
+#define KEY_bad_compass (KEY_CFG_EXT_GYRO_BIAS_Z + 1)
+#define KEY_COMPASS_CHG_SENSITIVITY (KEY_bad_compass + 1)
+#define KEY_CCS_HEADING_THLD (KEY_COMPASS_CHG_SENSITIVITY + 1)
+#define KEY_CCS_TIME_THLD (KEY_CCS_HEADING_THLD + 1)
+#define KEY_CCS_DOTP_THLD (KEY_CCS_TIME_THLD + 1)
+#define KEY_CCS_COMP_CNTR (KEY_CCS_DOTP_THLD + 1)
+#define KEY_CFG_NM_DET (KEY_CCS_COMP_CNTR + 1)
+#define KEY_SMD_ENABLE (KEY_CFG_NM_DET + 1)
+#define KEY_SMD_ACCEL_THLD (KEY_SMD_ENABLE + 1)
+#define KEY_SMD_DELAY_THLD (KEY_SMD_ACCEL_THLD + 1)
+#define KEY_SMD_DELAY2_THLD (KEY_SMD_DELAY_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT1 (KEY_SMD_DELAY2_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT2 (KEY_SMD_ENABLE_TESTPT1 + 1)
+#define KEY_SMD_EXE_STATE (KEY_SMD_ENABLE_TESTPT2 + 1)
+#define KEY_SMD_DELAY_CNTR (KEY_SMD_EXE_STATE + 1)
+
+#define KEY_BREAK (81)
+#if KEY_SMD_DELAY_CNTR != KEY_BREAK
+#error
+#endif
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER (KEY_BREAK + 1)
+#define KEY_CFG_MOTION_BIAS (KEY_CFG_ACCEL_FILTER + 1)
+#define KEY_TEMPLABEL (KEY_CFG_MOTION_BIAS + 1)
+
+#define KEY_D_0_22 (KEY_TEMPLABEL + 1)
+#define KEY_D_0_24 (KEY_D_0_22 + 1)
+#define KEY_D_0_36 (KEY_D_0_24 + 1)
+#define KEY_D_0_52 (KEY_D_0_36 + 1)
+#define KEY_D_0_96 (KEY_D_0_52 + 1)
+#define KEY_D_0_104 (KEY_D_0_96 + 1)
+#define KEY_D_0_108 (KEY_D_0_104 + 1)
+#define KEY_D_0_163 (KEY_D_0_108 + 1)
+#define KEY_D_0_188 (KEY_D_0_163 + 1)
+#define KEY_D_0_192 (KEY_D_0_188 + 1)
+#define KEY_D_0_224 (KEY_D_0_192 + 1)
+#define KEY_D_0_228 (KEY_D_0_224 + 1)
+#define KEY_D_0_232 (KEY_D_0_228 + 1)
+#define KEY_D_0_236 (KEY_D_0_232 + 1)
+
+#define KEY_DMP_PREVPTAT (KEY_D_0_236 + 1)
+#define KEY_D_1_2 (KEY_DMP_PREVPTAT + 1)
+#define KEY_D_1_4 (KEY_D_1_2 + 1)
+#define KEY_D_1_8 (KEY_D_1_4 + 1)
+#define KEY_D_1_10 (KEY_D_1_8 + 1)
+#define KEY_D_1_24 (KEY_D_1_10 + 1)
+#define KEY_D_1_28 (KEY_D_1_24 + 1)
+#define KEY_D_1_36 (KEY_D_1_28 + 1)
+#define KEY_D_1_40 (KEY_D_1_36 + 1)
+#define KEY_D_1_44 (KEY_D_1_40 + 1)
+#define KEY_D_1_72 (KEY_D_1_44 + 1)
+#define KEY_D_1_74 (KEY_D_1_72 + 1)
+#define KEY_D_1_79 (KEY_D_1_74 + 1)
+#define KEY_D_1_88 (KEY_D_1_79 + 1)
+#define KEY_D_1_90 (KEY_D_1_88 + 1)
+#define KEY_D_1_92 (KEY_D_1_90 + 1)
+#define KEY_D_1_96 (KEY_D_1_92 + 1)
+#define KEY_D_1_98 (KEY_D_1_96 + 1)
+#define KEY_D_1_100 (KEY_D_1_98 + 1)
+#define KEY_D_1_106 (KEY_D_1_100 + 1)
+#define KEY_D_1_108 (KEY_D_1_106 + 1)
+#define KEY_D_1_112 (KEY_D_1_108 + 1)
+#define KEY_D_1_128 (KEY_D_1_112 + 1)
+#define KEY_D_1_152 (KEY_D_1_128 + 1)
+#define KEY_D_1_160 (KEY_D_1_152 + 1)
+#define KEY_D_1_168 (KEY_D_1_160 + 1)
+#define KEY_D_1_175 (KEY_D_1_168 + 1)
+#define KEY_D_1_176 (KEY_D_1_175 + 1)
+#define KEY_D_1_178 (KEY_D_1_176 + 1)
+#define KEY_D_1_179 (KEY_D_1_178 + 1)
+#define KEY_D_1_218 (KEY_D_1_179 + 1)
+#define KEY_D_1_232 (KEY_D_1_218 + 1)
+#define KEY_D_1_236 (KEY_D_1_232 + 1)
+#define KEY_D_1_240 (KEY_D_1_236 + 1)
+#define KEY_D_1_244 (KEY_D_1_240 + 1)
+#define KEY_D_1_250 (KEY_D_1_244 + 1)
+#define KEY_D_1_252 (KEY_D_1_250 + 1)
+#define KEY_D_2_12 (KEY_D_1_252 + 1)
+#define KEY_D_2_96 (KEY_D_2_12 + 1)
+#define KEY_D_2_108 (KEY_D_2_96 + 1)
+#define KEY_D_2_208 (KEY_D_2_108 + 1)
+#define KEY_FLICK_MSG (KEY_D_2_208 + 1)
+#define KEY_FLICK_COUNTER (KEY_FLICK_MSG + 1)
+#define KEY_FLICK_LOWER (KEY_FLICK_COUNTER + 1)
+#define KEY_CFG_FLICK_IN (KEY_FLICK_LOWER + 1)
+#define KEY_FLICK_UPPER (KEY_CFG_FLICK_IN + 1)
+#define KEY_CGNOTICE_INTR (KEY_FLICK_UPPER + 1)
+#define KEY_D_2_224 (KEY_CGNOTICE_INTR + 1)
+#define KEY_D_2_244 (KEY_D_2_224 + 1)
+#define KEY_D_2_248 (KEY_D_2_244 + 1)
+#define KEY_D_2_252 (KEY_D_2_248 + 1)
+
+#define KEY_D_GYRO_BIAS_X (KEY_D_2_252 + 1)
+#define KEY_D_GYRO_BIAS_Y (KEY_D_GYRO_BIAS_X + 1)
+#define KEY_D_GYRO_BIAS_Z (KEY_D_GYRO_BIAS_Y + 1)
+#define KEY_D_ACC_BIAS_X (KEY_D_GYRO_BIAS_Z + 1)
+#define KEY_D_ACC_BIAS_Y (KEY_D_ACC_BIAS_X + 1)
+#define KEY_D_ACC_BIAS_Z (KEY_D_ACC_BIAS_Y + 1)
+#define KEY_D_GYRO_ENABLE (KEY_D_ACC_BIAS_Z + 1)
+#define KEY_D_ACCEL_ENABLE (KEY_D_GYRO_ENABLE + 1)
+#define KEY_D_QUAT_ENABLE (KEY_D_ACCEL_ENABLE + 1)
+#define KEY_D_OUTPUT_ENABLE (KEY_D_QUAT_ENABLE + 1)
+#define KEY_D_ACCEL_CNTR (KEY_D_OUTPUT_ENABLE + 1)
+#define KEY_D_GYRO_CNTR (KEY_D_ACCEL_CNTR + 1)
+#define KEY_D_QUAT0_CNTR (KEY_D_GYRO_CNTR + 1)
+#define KEY_D_QUAT1_CNTR (KEY_D_QUAT0_CNTR + 1)
+#define KEY_D_QUAT2_CNTR (KEY_D_QUAT1_CNTR + 1)
+#define KEY_D_CR_TIME_G (KEY_D_QUAT2_CNTR + 1)
+#define KEY_D_CR_TIME_A (KEY_D_CR_TIME_G + 1)
+#define KEY_D_CR_TIME_Q (KEY_D_CR_TIME_A + 1)
+#define KEY_D_CS_TAX (KEY_D_CR_TIME_Q + 1)
+#define KEY_D_CS_TAY (KEY_D_CS_TAX + 1)
+#define KEY_D_CS_TAZ (KEY_D_CS_TAY + 1)
+#define KEY_D_CS_TGX (KEY_D_CS_TAZ + 1)
+#define KEY_D_CS_TGY (KEY_D_CS_TGX + 1)
+#define KEY_D_CS_TGZ (KEY_D_CS_TGY + 1)
+#define KEY_D_CS_TQ0 (KEY_D_CS_TGZ + 1)
+#define KEY_D_CS_TQ1 (KEY_D_CS_TQ0 + 1)
+#define KEY_D_CS_TQ2 (KEY_D_CS_TQ1 + 1)
+#define KEY_D_CS_TQ3 (KEY_D_CS_TQ2 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_GAIN (KEY_D_CS_TQ3 + 1)
+#define KEY_CPASS_BIAS_X (KEY_CPASS_GAIN + 1)
+#define KEY_CPASS_BIAS_Y (KEY_CPASS_BIAS_X + 1)
+#define KEY_CPASS_BIAS_Z (KEY_CPASS_BIAS_Y + 1)
+#define KEY_CPASS_MTX_00 (KEY_CPASS_BIAS_Z + 1)
+#define KEY_CPASS_MTX_01 (KEY_CPASS_MTX_00 + 1)
+#define KEY_CPASS_MTX_02 (KEY_CPASS_MTX_01 + 1)
+#define KEY_CPASS_MTX_10 (KEY_CPASS_MTX_02 + 1)
+#define KEY_CPASS_MTX_11 (KEY_CPASS_MTX_10 + 1)
+#define KEY_CPASS_MTX_12 (KEY_CPASS_MTX_11 + 1)
+#define KEY_CPASS_MTX_20 (KEY_CPASS_MTX_12 + 1)
+#define KEY_CPASS_MTX_21 (KEY_CPASS_MTX_20 + 1)
+#define KEY_CPASS_MTX_22 (KEY_CPASS_MTX_21 + 1)
+#define KEY_CPASS_STATUS_CHK (KEY_CPASS_MTX_22 + 1)
+/* Tap Keys */
+#define KEY_DMP_TAP_GATE (KEY_CPASS_STATUS_CHK + 1)
+#define KEY_DMP_TAPW_MIN (KEY_DMP_TAP_GATE + 1)
+#define KEY_DMP_TAP_THR_Z (KEY_DMP_TAPW_MIN + 1)
+#define KEY_DMP_TAP_PREV_JERK_Z (KEY_DMP_TAP_THR_Z + 1)
+#define KEY_DMP_TAP_MIN_TAPS (KEY_DMP_TAP_PREV_JERK_Z + 1)
+#define KEY_DMP_TAP_NEXT_TAP_THRES (KEY_DMP_TAP_MIN_TAPS + 1)
+#define KEY_DMP_TAP_SHAKE_REJECT (KEY_DMP_TAP_NEXT_TAP_THRES + 1)
+#define KEY_DMP_TAP_SHAKE_COUNT_MAX (KEY_DMP_TAP_SHAKE_REJECT + 1)
+#define KEY_DMP_TAP_SHAKE_TIMEOUT_MAX (KEY_DMP_TAP_SHAKE_COUNT_MAX + 1)
+#define KEY_DMP_TAP_DIRECTION (KEY_DMP_TAP_SHAKE_TIMEOUT_MAX + 1)
+#define KEY_DMP_TAP_COUNT (KEY_DMP_TAP_DIRECTION + 1)
+/* Gesture Keys */
+#define KEY_DMP_SH_TH_Y (KEY_DMP_TAP_COUNT + 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_CFG_DISPLAY_ORIENT_INT (KEY_D_ACSZ + 1)
+#define KEY_NO_ORIENT_INTERRUPT (KEY_CFG_DISPLAY_ORIENT_INT + 1)
+#define KEY_X_GRT_Y_TMP2 (KEY_NO_ORIENT_INTERRUPT + 1)
+
+/*Shake Keys */
+#define KEY_D_0_64 (KEY_X_GRT_Y_TMP2 + 1)
+#define KEY_D_2_4 (KEY_D_0_64 + 1)
+#define KEY_D_2_8 (KEY_D_2_4 + 1)
+#define KEY_D_2_48 (KEY_D_2_8 + 1)
+#define KEY_D_2_92 (KEY_D_2_48 + 1)
+#define KEY_D_2_94 (KEY_D_2_92 + 1)
+#define KEY_D_2_160 (KEY_D_2_94 + 1)
+#define KEY_D_3_180 (KEY_D_2_160 + 1)
+#define KEY_D_3_184 (KEY_D_3_180 + 1)
+#define KEY_D_3_188 (KEY_D_3_184 + 1)
+#define KEY_D_3_208 (KEY_D_3_188 + 1)
+#define KEY_D_3_240 (KEY_D_3_208 + 1)
+#define KEY_RETRACTION_1 (KEY_D_3_240 + 1)
+#define KEY_RETRACTION_2 (KEY_RETRACTION_1 + 1)
+#define KEY_RETRACTION_3 (KEY_RETRACTION_2 + 1)
+#define KEY_RETRACTION_4 (KEY_RETRACTION_3 + 1)
+#define KEY_CFG_SHAKE_INT (KEY_RETRACTION_4 + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT (KEY_CFG_SHAKE_INT + 1)
+#define KEY_D_AUTH_IN (KEY_D_AUTH_OUT + 1)
+#define KEY_D_AUTH_A (KEY_D_AUTH_IN + 1)
+#define KEY_D_AUTH_B (KEY_D_AUTH_A + 1)
+
+/* Pedometer standalone only keys */
+#define KEY_D_PEDSTD_BP_B (KEY_D_AUTH_B + 1)
+#define KEY_D_PEDSTD_HP_A (KEY_D_PEDSTD_BP_B + 1)
+#define KEY_D_PEDSTD_HP_B (KEY_D_PEDSTD_HP_A + 1)
+#define KEY_D_PEDSTD_BP_A4 (KEY_D_PEDSTD_HP_B + 1)
+#define KEY_D_PEDSTD_BP_A3 (KEY_D_PEDSTD_BP_A4 + 1)
+#define KEY_D_PEDSTD_BP_A2 (KEY_D_PEDSTD_BP_A3 + 1)
+#define KEY_D_PEDSTD_BP_A1 (KEY_D_PEDSTD_BP_A2 + 1)
+#define KEY_D_PEDSTD_INT_THRSH (KEY_D_PEDSTD_BP_A1 + 1)
+#define KEY_D_PEDSTD_CLIP (KEY_D_PEDSTD_INT_THRSH + 1)
+#define KEY_D_PEDSTD_SB (KEY_D_PEDSTD_CLIP + 1)
+#define KEY_D_PEDSTD_SB_TIME (KEY_D_PEDSTD_SB + 1)
+#define KEY_D_PEDSTD_PEAKTHRSH (KEY_D_PEDSTD_SB_TIME + 1)
+#define KEY_D_PEDSTD_TIML (KEY_D_PEDSTD_PEAKTHRSH + 1)
+#define KEY_D_PEDSTD_TIMH (KEY_D_PEDSTD_TIML + 1)
+#define KEY_D_PEDSTD_PEAK (KEY_D_PEDSTD_TIMH + 1)
+#define KEY_D_PEDSTD_TIMECTR (KEY_D_PEDSTD_PEAK + 1)
+#define KEY_D_PEDSTD_STEPCTR (KEY_D_PEDSTD_TIMECTR + 1)
+#define KEY_D_PEDSTD_STEPCTR2 (KEY_D_PEDSTD_STEPCTR + 1)
+#define KEY_D_PEDSTD_WALKTIME (KEY_D_PEDSTD_STEPCTR2 + 1)
+#define KEY_D_PEDSTD_DECI (KEY_D_PEDSTD_WALKTIME + 1)
+#define KEY_D_PEDSTD_SB2 (KEY_D_PEDSTD_DECI + 1)
+#define KEY_D_PEDSTD_DRIVE_STATE (KEY_D_PEDSTD_SB2 + 1)
+#define KEY_CFG_PED_INT (KEY_D_PEDSTD_DRIVE_STATE + 1)
+#define KEY_CFG_PED_ENABLE (KEY_CFG_PED_INT + 1)
+#define KEY_D_STPDET_TIMESTAMP (KEY_CFG_PED_ENABLE + 1)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT (KEY_D_STPDET_TIMESTAMP + 1)
+
+/*Host Based Accel Bias*/
+#define KEY_D_ACCEL_BIAS (KEY_D_HOST_NO_MOT + 1)
+
+/*Screen/Display Orientation Keys*/
+#define KEY_D_ORIENT_GAP (KEY_D_ACCEL_BIAS + 1)
+#define KEY_D_TILT0_H (KEY_D_ORIENT_GAP + 1)
+#define KEY_D_TILT0_L (KEY_D_TILT0_H + 1)
+#define KEY_D_TILT1_H (KEY_D_TILT0_L + 1)
+#define KEY_D_TILT1_L (KEY_D_TILT1_H + 1)
+#define KEY_D_TILT2_H (KEY_D_TILT1_L + 1)
+#define KEY_D_TILT2_L (KEY_D_TILT2_H + 1)
+#define KEY_D_TILT3_H (KEY_D_TILT2_L + 1)
+#define KEY_D_TILT3_L (KEY_D_TILT3_H + 1)
+
+#define KEY_STREAM_P_ACCEL_Z (KEY_D_TILT3_L + 1)
+
+/* Batch mode */
+#define KEY_BM_ENABLE (KEY_STREAM_P_ACCEL_Z + 1)
+#define KEY_BM_BATCH_THLD (KEY_BM_ENABLE + 1)
+#define KEY_BM_BATCH_CNTR (KEY_BM_BATCH_THLD + 1)
+#define KEY_BM_NUMWORD_TOFILL (KEY_BM_BATCH_CNTR + 1)
+
+/* Watermark */
+#define KEY_CFG_WATERMARK_H (KEY_BM_NUMWORD_TOFILL + 1)
+#define KEY_CFG_WATERMARK_L (KEY_CFG_WATERMARK_H + 1)
+
+/* FIFO output control */
+#define KEY_CFG_OUT_ACCL (KEY_CFG_WATERMARK_L + 1)
+#define KEY_CFG_OUT_GYRO (KEY_CFG_OUT_ACCL + 1)
+#define KEY_CFG_OUT_3QUAT (KEY_CFG_OUT_GYRO + 1)
+#define KEY_CFG_OUT_6QUAT (KEY_CFG_OUT_3QUAT + 1)
+#define KEY_CFG_OUT_9QUAT (KEY_CFG_OUT_6QUAT + 1)
+#define KEY_CFG_OUT_PQUAT (KEY_CFG_OUT_9QUAT + 1)
+#define KEY_CFG_OUT_PQUAT9 (KEY_CFG_OUT_PQUAT + 1)
+#define KEY_CFG_OUT_CPASS (KEY_CFG_OUT_PQUAT9 + 1)
+#define KEY_CFG_OUT_PRESS (KEY_CFG_OUT_CPASS + 1)
+#define KEY_CFG_OUT_STEPDET (KEY_CFG_OUT_PRESS + 1)
+#define KEY_CFG_FIFO_INT (KEY_CFG_OUT_STEPDET + 1)
+
+/* Ped Step detection */
+#define KEY_CFG_PEDSTEP_DET (KEY_CFG_FIFO_INT + 1)
+
+/* Screen Orientation data */
+#define KEY_SO_DATA (KEY_CFG_PEDSTEP_DET + 1)
+
+/* MPU for DMP Android K */
+#define KEY_P_INIT (KEY_SO_DATA + 1)
+#define KEY_P_HW_ID (KEY_P_INIT + 1)
+
+/* DMP running counter */
+#define KEY_DMP_RUN_CNTR (KEY_P_HW_ID + 1)
+
+/* Sensor's ODR */
+#define KEY_CFG_3QUAT_ODR (KEY_DMP_RUN_CNTR + 1)
+#define KEY_CFG_6QUAT_ODR (KEY_CFG_3QUAT_ODR + 1)
+#define KEY_CFG_9QUAT_ODR (KEY_CFG_6QUAT_ODR + 1)
+#define KEY_CFG_PQUAT6_ODR (KEY_CFG_9QUAT_ODR + 1)
+#define KEY_CFG_PQUAT9_ODR (KEY_CFG_PQUAT6_ODR + 1)
+#define KEY_CFG_ACCL_ODR (KEY_CFG_PQUAT9_ODR + 1)
+#define KEY_CFG_GYRO_ODR (KEY_CFG_ACCL_ODR + 1)
+#define KEY_CFG_CPASS_ODR (KEY_CFG_GYRO_ODR + 1)
+#define KEY_CFG_PRESS_ODR (KEY_CFG_CPASS_ODR + 1)
+
+#define KEY_ODR_CNTR_3QUAT (KEY_CFG_PRESS_ODR + 1)
+#define KEY_ODR_CNTR_6QUAT (KEY_ODR_CNTR_3QUAT + 1)
+#define KEY_ODR_CNTR_9QUAT (KEY_ODR_CNTR_6QUAT + 1)
+#define KEY_ODR_CNTR_PQUAT (KEY_ODR_CNTR_9QUAT + 1)
+#define KEY_ODR_CNTR_PQUAT9 (KEY_ODR_CNTR_PQUAT + 1)
+#define KEY_ODR_CNTR_ACCL (KEY_ODR_CNTR_PQUAT9 + 1)
+#define KEY_ODR_CNTR_GYRO (KEY_ODR_CNTR_ACCL + 1)
+#define KEY_ODR_CNTR_CPASS (KEY_ODR_CNTR_GYRO + 1)
+#define KEY_ODR_CNTR_PRESS (KEY_ODR_CNTR_CPASS + 1)
+
+/* DMP fusion LP-Quat */
+#define KEY_DMP_LPQ0 (KEY_ODR_CNTR_PRESS + 1)
+#define KEY_DMP_LPQ1 (KEY_DMP_LPQ0 + 1)
+#define KEY_DMP_LPQ2 (KEY_DMP_LPQ1 + 1)
+#define KEY_DMP_LPQ3 (KEY_DMP_LPQ2 + 1)
+
+/* DMP fusion 6-axis Quat */
+#define KEY_DMP_Q0 (KEY_DMP_LPQ3 + 1)
+#define KEY_DMP_Q1 (KEY_DMP_Q0 + 1)
+#define KEY_DMP_Q2 (KEY_DMP_Q1 + 1)
+#define KEY_DMP_Q3 (KEY_DMP_Q2 + 1)
+
+/* 9-axis fusion */
+#define KEY_CPASS_VALID (KEY_DMP_Q3 + 1)
+#define KEY_9AXIS_ACCURACY (KEY_CPASS_VALID + 1)
+#define KEY_DMP_9Q0 (KEY_9AXIS_ACCURACY + 1)
+#define KEY_DMP_9Q1 (KEY_DMP_9Q0 + 1)
+#define KEY_DMP_9Q2 (KEY_DMP_9Q1 + 1)
+#define KEY_DMP_9Q3 (KEY_DMP_9Q2 + 1)
+
+/* Test key */
+#define KEY_TEST_01 (KEY_DMP_9Q3 + 1)
+#define KEY_TEST_02 (KEY_TEST_01 + 1)
+#define KEY_TEST_03 (KEY_TEST_02 + 1)
+#define KEY_TEST_04 (KEY_TEST_03 + 1)
+#define KEY_TEST_05 (KEY_TEST_04 + 1)
+#define KEY_TEST_06 (KEY_TEST_05 + 1)
+#define KEY_TEST_07 (KEY_TEST_06 + 1)
+#define KEY_TEST_XX (KEY_TEST_07 + 1)
+
+#define NUM_KEYS (KEY_TEST_XX + 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/iio/imu-aosp/inv_mpu6515/dmpmap.h b/drivers/iio/imu-aosp/inv_mpu6515/dmpmap.h
new file mode 100755
index 00000000000..92936372224
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpmap.h
@@ -0,0 +1,263 @@
+/*
+* 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_TAP_DIRECTION 68
+#define DMP_TAP_COUNT 70
+#define DMP_TAP_GATE 72
+#define DMP_TAP_MIN_TAPS 74
+#define DMP_BERR2NH 76
+#define DMP_SMCINC 78
+#define DMP_TAP_SHAKE_REJECT 80
+#define DMP_ANGVBXL 82
+#define DMP_TAP_SHAKE_COUNT_MAX 84
+#define DMP_TAP_SHAKE_TIMEOUT_MAX 86
+#define DMP_TAP_THZ 88
+#define DMP_TAPW_MIN 90
+#define DMP_TAP_PREV_JERK_Z 92
+#define DMP_TAP_NEXT_TAP_THRES 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_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/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c
new file mode 100755
index 00000000000..1fc5ff98097
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c
@@ -0,0 +1,271 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define MPU3050_NACK_MIN_TIME (2 * 1000)
+#define MPU3050_NACK_MAX_TIME (3 * 1000)
+
+#define MPU3050_ONE_MPU_TIME 20
+#define MPU3050_BOGUS_ADDR 0x7F
+int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+int set_3050_bypass(struct inv_mpu_state *st, bool enable)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 b;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &b);
+ if (result)
+ return result;
+ if (((b & BIT_3050_AUX_IF_EN) == 0) && enable)
+ return 0;
+ if ((b & BIT_3050_AUX_IF_EN) && (enable == 0))
+ return 0;
+ b &= ~BIT_3050_AUX_IF_EN;
+ if (!enable) {
+ b |= BIT_3050_AUX_IF_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ return result;
+ } else {
+ /* Coming out of I2C is tricky due to several erratta. Do not
+ * modify this algorithm
+ */
+ /*
+ * 1) wait for the right time and send the command to change
+ * the aux i2c slave address to an invalid address that will
+ * get nack'ed
+ *
+ * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
+ */
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ MPU3050_BOGUS_ADDR);
+ if (result)
+ return result;
+ /*
+ * 2) wait enough time for a nack to occur, then go into
+ * bypass mode:
+ */
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ /*
+ * 3) wait for up to one MPU cycle then restore the slave
+ * address
+ */
+ msleep(MPU3050_ONE_MPU_TIME);
+
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ }
+ return 0;
+}
+
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+ reg->fifo_en = REG_3050_FIFO_EN;
+ reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV;
+ reg->lpf = REG_3050_LPF;
+ reg->fifo_count_h = REG_3050_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_3050_FIFO_R_W;
+ reg->user_ctrl = REG_3050_USER_CTRL;
+ reg->pwr_mgmt_1 = REG_3050_PWR_MGMT_1;
+ reg->raw_accel = REG_3050_AUX_XOUT_H;
+ reg->temperature = REG_3050_TEMPERATURE;
+ reg->int_enable = REG_3050_INT_ENABLE;
+ reg->int_status = REG_3050_INT_STATUS;
+}
+
+int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (en) {
+ data = INV_CLK_PLL;
+ p = (BITS_3050_POWER1 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = (BITS_3050_POWER2 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = data;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ msleep(SENSOR_UP_TIME);
+ } else {
+ p = BITS_3050_GYRO_STANDBY;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ }
+
+ return result;
+}
+
+int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en)
+{
+ int result;
+ if (NULL == st->slave_accel)
+ return -EPERM;
+ if (en)
+ result = st->slave_accel->resume(st);
+ else
+ result = st->slave_accel->suspend(st);
+
+ return result;
+}
+
+/**
+ * inv_init_config_mpu3050() - Initialize hardware, disable FIFO.
+ * @st: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ * Clock source: Gyro PLL
+ */
+int inv_init_config_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 data;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ if (st->chip_config.is_asleep)
+ return -EPERM;
+ /*reading AUX VDDIO register */
+ result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data);
+ if (result)
+ return result;
+ data &= ~BIT_3050_VDDIO;
+ if (st->plat_data.level_shifter)
+ data |= BIT_3050_VDDIO;
+ result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+ if (result)
+ return result;
+
+ reg = &st->reg;
+ /*2000dps full scale range*/
+ result = inv_i2c_single_write(st, reg->lpf,
+ (INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT)
+ | INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.fsr = INV_FSR_2000DPS;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+ st->chip_info.multi = 1;
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ/INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) &&
+ st->slave_accel) {
+ result = st->slave_accel->setup(st);
+ if (result)
+ return result;
+ result = st->slave_accel->set_fs(st, INV_FS_02G);
+ if (result)
+ return result;
+ result = st->slave_accel->set_lpf(st, INIT_FIFO_RATE);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * set_power_mpu3050() - set power of mpu3050.
+ * @st: Device driver instance.
+ * @power_on: on/off
+ */
+int set_power_mpu3050(struct inv_mpu_state *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (power_on) {
+ data = 0;
+ } else {
+ if (st->slave_accel) {
+ result = st->slave_accel->suspend(st);
+ if (result)
+ return result;
+ }
+ data = BIT_SLEEP;
+ }
+ if (st->chip_config.gyro_enable) {
+ p = (BITS_3050_POWER1 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = (BITS_3050_POWER2 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+ } else {
+ data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+ }
+ if (power_on) {
+ msleep(POWER_UP_TIME);
+ if (st->slave_accel) {
+ result = st->slave_accel->resume(st);
+ if (result)
+ return result;
+ }
+ }
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c
new file mode 100755
index 00000000000..6f5a131dac5
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c
@@ -0,0 +1,3135 @@
+/*
+* 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 <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+#ifdef CONFIG_DTS_INV_MPU_IIO
+#include "inv_mpu_dts.h"
+#endif
+
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ts = CURRENT_TIME;
+ return timespec_to_ns(&ts);
+}
+
+/* This is for compatibility for power state. Should remove once HAL
+ does not use power_state sysfs entry */
+static bool fake_asleep;
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+ {119, "ITG3500"},
+ { 63, "MPU3050"},
+ {117, "MPU6050"},
+ {118, "MPU9150"},
+ {128, "MPU6500"},
+ {128, "MPU9250"},
+ {128, "MPU9350"},
+ {128, "MPU6515"},
+};
+
+static const u8 reg_gyro_offset[] = {REG_XG_OFFS_USRH,
+ REG_XG_OFFS_USRH + 2,
+ REG_XG_OFFS_USRH + 4};
+
+const u8 reg_6050_accel_offset[] = {REG_XA_OFFS_H,
+ REG_XA_OFFS_H + 2,
+ REG_XA_OFFS_H + 4};
+
+const u8 reg_6500_accel_offset[] = {REG_6500_XA_OFFS_H,
+ REG_6500_YA_OFFS_H,
+ REG_6500_ZA_OFFS_H};
+#ifdef CONFIG_INV_TESTING
+static bool suspend_state;
+static int inv_mpu_suspend(struct device *dev);
+static int inv_mpu_resume(struct device *dev);
+struct test_data_out {
+ bool gyro;
+ bool accel;
+ bool compass;
+ bool pressure;
+ bool LPQ;
+ bool SIXQ;
+ bool PEDQ;
+};
+static struct test_data_out data_out_control;
+#endif
+
+static void inv_setup_reg(struct inv_reg_map_s *reg)
+{
+ reg->sample_rate_div = REG_SAMPLE_RATE_DIV;
+ reg->lpf = REG_CONFIG;
+ reg->bank_sel = REG_BANK_SEL;
+ reg->user_ctrl = REG_USER_CTRL;
+ reg->fifo_en = REG_FIFO_EN;
+ reg->gyro_config = REG_GYRO_CONFIG;
+ reg->accel_config = REG_ACCEL_CONFIG;
+ reg->fifo_count_h = REG_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_FIFO_R_W;
+ reg->raw_accel = REG_RAW_ACCEL;
+ reg->temperature = REG_TEMPERATURE;
+ reg->int_enable = REG_INT_ENABLE;
+ reg->int_status = REG_INT_STATUS;
+ reg->pwr_mgmt_1 = REG_PWR_MGMT_1;
+ reg->pwr_mgmt_2 = REG_PWR_MGMT_2;
+ reg->mem_start_addr = REG_MEM_START_ADDR;
+ reg->mem_r_w = REG_MEM_RW;
+ reg->prgm_strt_addrh = REG_PRGM_STRT_ADDRH;
+};
+
+/**
+ * inv_i2c_read_base() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @i2c_addr: i2c address of device.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 2);
+
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3);
+ INV_I2C_INC_MPUREAD(length);
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name,
+ i2c_addr, reg, length,
+ wr_pr_debug_begin(data, length, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+ return res;
+}
+
+/**
+ * inv_i2c_single_write_base() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @i2c_addr: I2C address of the device.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ * NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data)
+{
+ u8 tmp[2];
+ struct i2c_msg msg;
+ int res;
+ tmp[0] = reg;
+ tmp[1] = data;
+
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+ INV_I2C_INC_MPUWRITE(3);
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, mgmt_1;
+ int result;
+
+ reg = &st->reg;
+ /* Only when gyro is on, can
+ clock source be switched to gyro. Otherwise, it must be set to
+ internal clock */
+ if (BIT_PWR_GYRO_STBY == mask) {
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1);
+ if (result)
+ return result;
+
+ mgmt_1 &= ~BIT_CLK_MASK;
+ }
+
+ if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ /* turning off gyro requires switch to internal clock first.
+ Then turn off gyro engine */
+ mgmt_1 |= INV_CLK_INTERNAL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data);
+ if (result)
+ return result;
+ if (en)
+ data &= (~mask);
+ else
+ data |= mask;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+ if (result)
+ return result;
+
+ if ((BIT_PWR_GYRO_STBY == mask) && en) {
+ /* only gyro on needs sensor up time */
+ msleep(SENSOR_UP_TIME);
+ /* after gyro is on & stable, switch internal clock to PLL */
+ mgmt_1 |= INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+ if ((BIT_PWR_ACCEL_STBY == mask) && en)
+ msleep(REG_UP_TIME);
+
+ return 0;
+}
+
+/*
+ * inv_lpa_freq() - store current low power frequency setting.
+ */
+static int inv_lpa_freq(struct inv_mpu_state *st, int lpa_freq)
+{
+ unsigned long result;
+ u8 d;
+ /* 2, 4, 6, 7 corresponds to 0.98, 3.91, 15.63, 31.25 */
+ const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7};
+
+ if (lpa_freq > MAX_LPA_FREQ_PARAM)
+ return -EINVAL;
+
+ if (INV_MPU6500 == st->chip_type) {
+ d = mpu6500_lpa_mapping[lpa_freq];
+ result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d);
+ if (result)
+ return result;
+ }
+ st->chip_config.lpa_freq = lpa_freq;
+
+ return 0;
+}
+
+static int set_power_itg(struct inv_mpu_state *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data;
+ int result;
+
+ if ((!power_on) == st->chip_config.is_asleep)
+ return 0;
+ reg = &st->reg;
+ if (power_on)
+ data = 0;
+ else
+ data = BIT_SLEEP;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+
+ if (power_on)
+ msleep(REG_UP_TIME);
+
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+
+/**
+ * inv_init_config() - Initialize hardware, disable FIFO.
+ * @indio_dev: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ */
+static int inv_init_config(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result, i;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ const u8 *ch;
+ u8 d[2];
+
+ reg = &st->reg;
+
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
+ if (result)
+ return result;
+
+ st->chip_config.fsr = INV_FSR_2000DPS;
+
+ result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ / INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ if (INV_MPU6050 == st->chip_type)
+ st->self_test.samples = INIT_ST_MPU6050_SAMPLES;
+ else
+ st->self_test.samples = INIT_ST_SAMPLES;
+ st->self_test.threshold = INIT_ST_THRESHOLD;
+ st->batch.wake_fifo_on = true;
+ st->suspend_state = false;
+ if (INV_ITG3500 != st->chip_type) {
+ st->chip_config.accel_fs = INV_FS_02G;
+ result = inv_i2c_single_write(st, reg->accel_config,
+ (INV_FS_02G << ACCEL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+ st->tap.time = INIT_TAP_TIME;
+ st->tap.thresh = INIT_TAP_THRESHOLD;
+ st->tap.min_count = INIT_TAP_MIN_COUNT;
+ st->sample_divider = INIT_SAMPLE_DIVIDER;
+ st->smd.threshold = MPU_INIT_SMD_THLD;
+ st->smd.delay = MPU_INIT_SMD_DELAY_THLD;
+ st->smd.delay2 = MPU_INIT_SMD_DELAY2_THLD;
+ st->ped.int_thresh = INIT_PED_INT_THRESH;
+ st->ped.step_thresh = INIT_PED_THRESH;
+ st->sensor[SENSOR_STEP].rate = MAX_DMP_OUTPUT_RATE;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR,
+ INIT_MOT_DUR);
+ if (result)
+ return result;
+ st->mot_int.mot_dur = INIT_MOT_DUR;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR,
+ INIT_MOT_THR);
+ if (result)
+ return result;
+ st->mot_int.mot_thr = INIT_MOT_THR;
+
+ for (i = 0; i < 3; i++) {
+ result = inv_i2c_read(st, reg_gyro_offset[i], 2, d);
+ if (result)
+ return result;
+ st->rom_gyro_offset[i] =
+ (short)be16_to_cpup((__be16 *)(d));
+ st->input_gyro_offset[i] = 0;
+ st->input_gyro_dmp_bias[i] = 0;
+ }
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+ for (i = 0; i < 3; i++) {
+ result = inv_i2c_read(st, ch[i], 2, d);
+ if (result)
+ return result;
+ st->rom_accel_offset[i] =
+ (short)be16_to_cpup((__be16 *)(d));
+ st->input_accel_offset[i] = 0;
+ st->input_accel_dmp_bias[i] = 0;
+ }
+ st->ped.step = 0;
+ st->ped.time = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_write_fsr() - Configure the gyro's scale range.
+ */
+static int inv_write_fsr(struct inv_mpu_state *st, int fsr)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+
+ reg = &st->reg;
+ if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM))
+ return -EINVAL;
+ if (fsr == st->chip_config.fsr)
+ return 0;
+
+ if (INV_MPU3050 == st->chip_type)
+ result = inv_i2c_single_write(st, reg->lpf,
+ (fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf);
+ else
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ fsr << GYRO_CONFIG_FSR_SHIFT);
+
+ if (result)
+ return result;
+ st->chip_config.fsr = fsr;
+
+ return 0;
+}
+
+/*
+ * inv_write_accel_fs() - Configure the accelerometer's scale range.
+ */
+static int inv_write_accel_fs(struct inv_mpu_state *st, int fs)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ if (fs < 0 || fs > MAX_ACCEL_FS_PARAM)
+ return -EINVAL;
+ if (fs == st->chip_config.accel_fs)
+ return 0;
+ if (INV_MPU3050 == st->chip_type)
+ result = st->slave_accel->set_fs(st, fs);
+ else
+ result = inv_i2c_single_write(st, reg->accel_config,
+ (fs << ACCEL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+
+ st->chip_config.accel_fs = fs;
+
+ return 0;
+}
+
+static int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val)
+{
+ int result;
+ u8 d;
+
+ d = ((val >> 8) & 0xff);
+ result = inv_i2c_single_write(st, reg, d);
+ if (result)
+ return result;
+
+ d = (val & 0xff);
+ result = inv_i2c_single_write(st, reg + 1, d);
+
+ return result;
+}
+
+int inv_reset_offset_reg(struct inv_mpu_state *st, bool en)
+{
+ const u8 *ch;
+ int i, result;
+ s16 gyro[3], accel[3];
+
+ if (en) {
+ for (i = 0; i < 3; i++) {
+ gyro[i] = st->rom_gyro_offset[i];
+ accel[i] = st->rom_accel_offset[i];
+ }
+ } else {
+ for (i = 0; i < 3; i++) {
+ gyro[i] = st->rom_gyro_offset[i] +
+ st->input_gyro_offset[i];
+ accel[i] = st->rom_accel_offset[i] +
+ (st->input_accel_offset[i] << 1);
+ }
+ }
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+
+ for (i = 0; i < 3; i++) {
+ result = inv_set_offset_reg(st, reg_gyro_offset[i], gyro[i]);
+ if (result)
+ return result;
+ result = inv_set_offset_reg(st, ch[i], accel[i]);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+/*
+ * inv_fifo_rate_store() - Set fifo rate.
+ */
+static int inv_fifo_rate_store(struct inv_mpu_state *st, int fifo_rate)
+{
+ if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+ return -EINVAL;
+ if (fifo_rate == st->chip_config.fifo_rate)
+ return 0;
+
+ st->irq_dur_ns = NSEC_PER_SEC / fifo_rate;
+ st->chip_config.fifo_rate = fifo_rate;
+
+ return 0;
+}
+
+/*
+ * inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ii;
+ char data;
+ ssize_t bytes_printed = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (ii = 0; ii < st->hw->num_reg; ii++) {
+ /* don't read fifo r/w register */
+ if (ii == st->reg.fifo_r_w)
+ data = 0;
+ else
+ inv_i2c_read(st, ii, 1, &data);
+ bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n",
+ ii, data);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return bytes_printed;
+}
+
+int write_be32_key_to_mem(struct inv_mpu_state *st,
+ u32 data, int key)
+{
+ cpu_to_be32s(&data);
+ return mem_w_key(key, sizeof(data), (u8 *)&data);
+}
+
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data)
+{
+ u8 d[2];
+
+ if (data < 0 || data > USHRT_MAX)
+ return -EINVAL;
+
+ d[0] = (u8)((data >> 8) & 0xff);
+ d[1] = (u8)(data & 0xff);
+
+ return mem_w_key(k, ARRAY_SIZE(d), d);
+}
+
+static ssize_t _dmp_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data, tmp;
+
+ if (!st->chip_config.firmware_loaded)
+ return -EINVAL;
+
+ if (!st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ }
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto dmp_bias_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_ACCEL_X_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[0];
+ st->input_accel_dmp_bias[0] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[0] = tmp;
+ break;
+ case ATTR_DMP_ACCEL_Y_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[1];
+ st->input_accel_dmp_bias[1] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[1] = tmp;
+ break;
+ case ATTR_DMP_ACCEL_Z_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[2];
+ st->input_accel_dmp_bias[2] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[2] = tmp;
+ break;
+ case ATTR_DMP_GYRO_X_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_X);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[0] = data;
+ break;
+ case ATTR_DMP_GYRO_Y_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_Y);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[1] = data;
+ break;
+ case ATTR_DMP_GYRO_Z_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_Z);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[2] = data;
+ break;
+ default:
+ break;
+ }
+
+dmp_bias_store_fail:
+ if (!st->chip_config.enable)
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_dmp_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _dmp_bias_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t _dmp_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ if (st->chip_config.enable)
+ return -EBUSY;
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+ switch (this_attr->address) {
+ /* power of chip is not turned on */
+ case ATTR_DMP_ON:
+ st->chip_config.dmp_on = !!data;
+ break;
+ case ATTR_DMP_INT_ON:
+ st->chip_config.dmp_int_on = !!data;
+ break;
+ case ATTR_DMP_EVENT_INT_ON:
+ st->chip_config.dmp_event_int_on = !!data;
+ break;
+ case ATTR_DMP_STEP_INDICATOR_ON:
+ st->chip_config.step_indicator_on = !!data;
+ break;
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ if (data < 0 || data > INT_MAX)
+ return -EINVAL;
+ st->batch.timeout = data;
+ break;
+ case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL:
+ st->batch.wake_fifo_on = !!data;
+ st->batch.overflow_on = 0;
+ break;
+ case ATTR_DMP_SIX_Q_ON:
+ st->sensor[SENSOR_SIXQ].on = !!data;
+ break;
+ case ATTR_DMP_SIX_Q_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0)
+ return -EINVAL;
+ st->sensor[SENSOR_SIXQ].rate = data;
+ st->sensor[SENSOR_SIXQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_SIXQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_LPQ_ON:
+ st->sensor[SENSOR_LPQ].on = !!data;
+ break;
+ case ATTR_DMP_LPQ_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0)
+ return -EINVAL;
+ st->sensor[SENSOR_LPQ].rate = data;
+ st->sensor[SENSOR_LPQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_LPQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_PED_Q_ON:
+ st->sensor[SENSOR_PEDQ].on = !!data;
+ break;
+ case ATTR_DMP_PED_Q_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0)
+ return -EINVAL;
+ st->sensor[SENSOR_PEDQ].rate = data;
+ st->sensor[SENSOR_PEDQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_PEDQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ st->sensor[SENSOR_STEP].on = !!data;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+/*
+ * inv_dmp_attr_store() - calling this function will store DMP attributes
+ */
+static ssize_t inv_dmp_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _dmp_attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t _dmp_mem_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ if (st->chip_config.enable)
+ return -EBUSY;
+ if (!st->chip_config.firmware_loaded)
+ return -EINVAL;
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto dmp_mem_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_PED_INT_ON:
+ result = inv_enable_pedometer_interrupt(st, !!data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->ped.int_on = !!data;
+ break;
+ case ATTR_DMP_PED_ON:
+ {
+ result = inv_enable_pedometer(st, !!data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->ped.on = !!data;
+ break;
+ }
+ case ATTR_DMP_PED_STEP_THRESH:
+ {
+ result = inv_write_2bytes(st, KEY_D_PEDSTD_SB, data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->ped.step_thresh = data;
+ break;
+ }
+ case ATTR_DMP_PED_INT_THRESH:
+ {
+ result = inv_write_2bytes(st, KEY_D_PEDSTD_SB2, data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->ped.int_thresh = data;
+ break;
+ }
+ case ATTR_DMP_SMD_ENABLE:
+ result = inv_write_2bytes(st, KEY_SMD_ENABLE, !!data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->chip_config.smd_enable = !!data;
+ break;
+ case ATTR_DMP_SMD_THLD:
+ if (data < 0 || data > SHRT_MAX)
+ goto dmp_mem_store_fail;
+ result = write_be32_key_to_mem(st, data << 16,
+ KEY_SMD_ACCEL_THLD);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->smd.threshold = data;
+ break;
+ case ATTR_DMP_SMD_DELAY_THLD:
+ if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+ goto dmp_mem_store_fail;
+ result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+ KEY_SMD_DELAY_THLD);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->smd.delay = data;
+ break;
+ case ATTR_DMP_SMD_DELAY_THLD2:
+ if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+ goto dmp_mem_store_fail;
+ result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+ KEY_SMD_DELAY2_THLD);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->smd.delay2 = data;
+ break;
+ case ATTR_DMP_TAP_ON:
+ result = inv_enable_tap_dmp(st, !!data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->tap.on = !!data;
+ break;
+ case ATTR_DMP_TAP_THRESHOLD:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_mem_store_fail;
+ }
+ result = inv_set_tap_threshold_dmp(st, data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->tap.thresh = data;
+ break;
+ case ATTR_DMP_TAP_MIN_COUNT:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_mem_store_fail;
+ }
+ result = inv_set_min_taps_dmp(st, data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->tap.min_count = data;
+ break;
+ case ATTR_DMP_TAP_TIME:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_mem_store_fail;
+ }
+ result = inv_set_tap_time_dmp(st, data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->tap.time = data;
+ break;
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ result = inv_set_display_orient_interrupt_dmp(st, !!data);
+ if (result)
+ goto dmp_mem_store_fail;
+ st->chip_config.display_orient_on = !!data;
+ break;
+#ifdef CONFIG_INV_TESTING
+ case ATTR_DEBUG_SMD_ENABLE_TESTP1:
+ {
+ u8 d[] = {0x42};
+ result = st->set_power_state(st, true);
+ if (result)
+ goto dmp_mem_store_fail;
+ result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d);
+ if (result)
+ goto dmp_mem_store_fail;
+ }
+ break;
+ case ATTR_DEBUG_SMD_ENABLE_TESTP2:
+ {
+ u8 d[] = {0x42};
+ result = st->set_power_state(st, true);
+ if (result)
+ goto dmp_mem_store_fail;
+ result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d);
+ if (result)
+ goto dmp_mem_store_fail;
+ }
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ goto dmp_mem_store_fail;
+ }
+
+dmp_mem_store_fail:
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_dmp_mem_store() - calling this function will store DMP memory data
+ */
+static ssize_t inv_dmp_mem_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _dmp_mem_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_attr64_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result;
+ u64 tmp;
+ u32 ped;
+
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable || !st->chip_config.dmp_on) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ result = 0;
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ result = inv_get_pedometer_steps(st, &ped);
+ result |= inv_read_pedometer_counter(st);
+ tmp = st->ped.step + ped;
+ break;
+ case ATTR_DMP_PEDOMETER_TIME:
+ result = inv_get_pedometer_time(st, &ped);
+ tmp = (u64)st->ped.time + ((u64)ped) * MS_PER_PED_TICKS;
+ break;
+ case ATTR_DMP_PEDOMETER_COUNTER:
+ tmp = st->ped.last_step_time;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return -EINVAL;
+ return sprintf(buf, "%lld\n", tmp);
+}
+
+static ssize_t inv_attr64_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result;
+ u64 data;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable || (!st->chip_config.firmware_loaded)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ result = st->set_power_state(st, true);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ result = kstrtoull(buf, 10, &data);
+ if (result)
+ goto attr64_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_STEPCTR);
+ if (result)
+ goto attr64_store_fail;
+ st->ped.step = data;
+ break;
+ case ATTR_DMP_PEDOMETER_TIME:
+ result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_TIMECTR);
+ if (result)
+ goto attr64_store_fail;
+ st->ped.time = data;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+attr64_store_fail:
+ mutex_unlock(&indio_dev->mlock);
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+/*
+ * inv_attr_show() - calling this function will show current
+ * dmp parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, axis;
+ s8 *m;
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ {
+ const s16 gyro_scale[] = {250, 500, 1000, 2000};
+
+ return sprintf(buf, "%d\n", gyro_scale[st->chip_config.fsr]);
+ }
+ case ATTR_ACCEL_SCALE:
+ {
+ const s16 accel_scale[] = {2, 4, 8, 16};
+ return sprintf(buf, "%d\n",
+ accel_scale[st->chip_config.accel_fs] *
+ st->chip_info.multi);
+ }
+ case ATTR_COMPASS_SCALE:
+ st->slave_compass->get_scale(st, &result);
+
+ return sprintf(buf, "%d\n", result);
+ case ATTR_ACCEL_X_CALIBBIAS:
+ case ATTR_ACCEL_Y_CALIBBIAS:
+ case ATTR_ACCEL_Z_CALIBBIAS:
+ axis = this_attr->address - ATTR_ACCEL_X_CALIBBIAS;
+ return sprintf(buf, "%d\n", st->accel_bias[axis] *
+ st->chip_info.multi);
+ case ATTR_GYRO_X_CALIBBIAS:
+ case ATTR_GYRO_Y_CALIBBIAS:
+ case ATTR_GYRO_Z_CALIBBIAS:
+ axis = this_attr->address - ATTR_GYRO_X_CALIBBIAS;
+ return sprintf(buf, "%d\n", st->gyro_bias[axis]);
+ case ATTR_SELF_TEST_GYRO_SCALE:
+ return sprintf(buf, "%d\n", SELF_TEST_GYRO_FULL_SCALE);
+ case ATTR_SELF_TEST_ACCEL_SCALE:
+ if (INV_MPU6500 == st->chip_type)
+ return sprintf(buf, "%d\n", SELF_TEST_ACCEL_6500_SCALE);
+ else
+ return sprintf(buf, "%d\n", SELF_TEST_ACCEL_FULL_SCALE);
+ case ATTR_GYRO_X_OFFSET:
+ case ATTR_GYRO_Y_OFFSET:
+ case ATTR_GYRO_Z_OFFSET:
+ axis = this_attr->address - ATTR_GYRO_X_OFFSET;
+ return sprintf(buf, "%d\n", st->input_gyro_offset[axis]);
+ case ATTR_ACCEL_X_OFFSET:
+ case ATTR_ACCEL_Y_OFFSET:
+ case ATTR_ACCEL_Z_OFFSET:
+ axis = this_attr->address - ATTR_ACCEL_X_OFFSET;
+ return sprintf(buf, "%d\n", st->input_accel_offset[axis]);
+ case ATTR_DMP_ACCEL_X_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[0]);
+ case ATTR_DMP_ACCEL_Y_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[1]);
+ case ATTR_DMP_ACCEL_Z_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[2]);
+ case ATTR_DMP_GYRO_X_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[0]);
+ case ATTR_DMP_GYRO_Y_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[1]);
+ case ATTR_DMP_GYRO_Z_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[2]);
+ case ATTR_DMP_PED_INT_ON:
+ return sprintf(buf, "%d\n", st->ped.int_on);
+ case ATTR_DMP_PED_ON:
+ return sprintf(buf, "%d\n", st->ped.on);
+ case ATTR_DMP_PED_STEP_THRESH:
+ return sprintf(buf, "%d\n", st->ped.step_thresh);
+ case ATTR_DMP_PED_INT_THRESH:
+ return sprintf(buf, "%d\n", st->ped.int_thresh);
+ case ATTR_DMP_SMD_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.smd_enable);
+ case ATTR_DMP_SMD_THLD:
+ return sprintf(buf, "%d\n", st->smd.threshold);
+ case ATTR_DMP_SMD_DELAY_THLD:
+ return sprintf(buf, "%d\n", st->smd.delay);
+ case ATTR_DMP_SMD_DELAY_THLD2:
+ return sprintf(buf, "%d\n", st->smd.delay2);
+ case ATTR_DMP_TAP_ON:
+ return sprintf(buf, "%d\n", st->tap.on);
+ case ATTR_DMP_TAP_THRESHOLD:
+ return sprintf(buf, "%d\n", st->tap.thresh);
+ case ATTR_DMP_TAP_MIN_COUNT:
+ return sprintf(buf, "%d\n", st->tap.min_count);
+ case ATTR_DMP_TAP_TIME:
+ return sprintf(buf, "%d\n", st->tap.time);
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ return sprintf(buf, "%d\n",
+ st->chip_config.display_orient_on);
+ case ATTR_DMP_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_on);
+ case ATTR_DMP_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_int_on);
+ case ATTR_DMP_EVENT_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on);
+ case ATTR_DMP_STEP_INDICATOR_ON:
+ return sprintf(buf, "%d\n", st->chip_config.step_indicator_on);
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ return sprintf(buf, "%d\n",
+ st->batch.timeout);
+ case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL:
+ return sprintf(buf, "%d\n",
+ st->batch.wake_fifo_on);
+ case ATTR_DMP_SIX_Q_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].on);
+ case ATTR_DMP_SIX_Q_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].rate);
+ case ATTR_DMP_LPQ_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].on);
+ case ATTR_DMP_LPQ_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].rate);
+ case ATTR_DMP_PED_Q_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].on);
+ case ATTR_DMP_PED_Q_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].rate);
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_STEP].on);
+ case ATTR_MOTION_LPA_ON:
+ return sprintf(buf, "%d\n", st->mot_int.mot_on);
+ case ATTR_MOTION_LPA_FREQ:{
+ const char *f[] = {"1.25", "5", "20", "40"};
+ return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]);
+ }
+ case ATTR_MOTION_LPA_THRESHOLD:
+ return sprintf(buf, "%d\n", st->mot_int.mot_thr);
+
+ case ATTR_SELF_TEST_SAMPLES:
+ return sprintf(buf, "%d\n", st->self_test.samples);
+ case ATTR_SELF_TEST_THRESHOLD:
+ return sprintf(buf, "%d\n", st->self_test.threshold);
+ case ATTR_GYRO_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+ case ATTR_GYRO_FIFO_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].on);
+ case ATTR_GYRO_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].rate);
+ case ATTR_ACCEL_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.accel_enable);
+ case ATTR_ACCEL_FIFO_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].on);
+ case ATTR_ACCEL_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].rate);
+ case ATTR_COMPASS_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].on);
+ case ATTR_COMPASS_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].rate);
+ case ATTR_PRESSURE_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].on);
+ case ATTR_PRESSURE_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].rate);
+ case ATTR_POWER_STATE:
+ return sprintf(buf, "%d\n", !fake_asleep);
+ case ATTR_FIRMWARE_LOADED:
+ return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+ case ATTR_SAMPLING_FREQ:
+ return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+ case ATTR_SELF_TEST:
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ if (INV_MPU3050 == st->chip_type)
+ result = 1;
+ else
+ result = inv_hw_self_test(st);
+ mutex_unlock(&indio_dev->mlock);
+ return sprintf(buf, "%d\n", result);
+ case ATTR_GYRO_MATRIX:
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_ACCEL_MATRIX:
+ if (st->plat_data.sec_slave_type ==
+ SECONDARY_SLAVE_TYPE_ACCEL)
+ m =
+ st->plat_data.secondary_orientation;
+ else
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_COMPASS_MATRIX:
+ if (st->plat_data.sec_slave_type ==
+ SECONDARY_SLAVE_TYPE_COMPASS)
+ m =
+ st->plat_data.secondary_orientation;
+ else
+ return -ENODEV;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_SECONDARY_NAME:
+ {
+ const char *n[] = {"NULL", "AK8975", "AK8972", "AK8963",
+ "BMA250", "MLX90399", "AK09911"};
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ return sprintf(buf, "%s\n", n[1]);
+ case COMPASS_ID_AK8972:
+ return sprintf(buf, "%s\n", n[2]);
+ case COMPASS_ID_AK8963:
+ return sprintf(buf, "%s\n", n[3]);
+ case ACCEL_ID_BMA250:
+ return sprintf(buf, "%s\n", n[4]);
+ case COMPASS_ID_MLX90399:
+ return sprintf(buf, "%s\n", n[5]);
+ case COMPASS_ID_AK09911:
+ return sprintf(buf, "%s\n", n[6]);
+ default:
+ return sprintf(buf, "%s\n", n[0]);
+ }
+ }
+#ifdef CONFIG_INV_TESTING
+ case ATTR_REG_WRITE:
+ return sprintf(buf, "1\n");
+ case ATTR_COMPASS_SENS:
+ {
+ /* these 2 conditions should never be met, since the
+ 'compass_sens' sysfs entry should be hidden if the compass
+ is not an AKM */
+ if (st->plat_data.sec_slave_type !=
+ SECONDARY_SLAVE_TYPE_COMPASS)
+ return -ENODEV;
+ if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8972 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8963)
+ return -ENODEV;
+ m = st->chip_info.compass_sens;
+ return sprintf(buf, "%d,%d,%d\n", m[0], m[1], m[2]);
+ }
+ case ATTR_DEBUG_SMD_EXE_STATE:
+ {
+ u8 d[2];
+
+ result = st->set_power_state(st, true);
+ mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_SMD_EXE_STATE), 2, d);
+ return sprintf(buf, "%d\n", (short)be16_to_cpup((__be16 *)(d)));
+ }
+ case ATTR_DEBUG_SMD_DELAY_CNTR:
+ {
+ u8 d[4];
+
+ result = st->set_power_state(st, true);
+ mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_SMD_DELAY_CNTR), 4, d);
+ return sprintf(buf, "%d\n", (int)be32_to_cpup((__be32 *)(d)));
+ }
+#endif
+ default:
+ return -EPERM;
+ }
+}
+
+/*
+ * inv_dmp_display_orient_show() - calling this function will
+ * show orientation This event must use poll.
+ */
+static ssize_t inv_dmp_display_orient_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", st->display_orient_data);
+}
+
+/*
+ * inv_accel_motion_show() - calling this function showes motion interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_accel_motion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_smd_show() - calling this function showes smd interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_smd_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_ped_show() - calling this function showes pedometer interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_ped_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_dmp_tap_show() - calling this function will show tap
+ * This event must use poll.
+ */
+static ssize_t inv_dmp_tap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+ return sprintf(buf, "%d\n", st->tap_data);
+}
+
+/*
+ * inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result, cur_scale, cur_off;
+ short temp;
+ long scale_t;
+ u8 data[2];
+ const long scale[] = {3834792L, 3158064L, 3340827L};
+ const long offset[] = {5383314L, 2394184L, 1376256L};
+
+ reg = &st->reg;
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable)
+ result = st->set_power_state(st, true);
+ else
+ result = 0;
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ result = inv_i2c_read(st, reg->temperature, 2, data);
+ if (!st->chip_config.enable)
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result) {
+ pr_err("Could not read temperature register.\n");
+ return result;
+ }
+ temp = (signed short)(be16_to_cpup((short *)&data[0]));
+ switch (st->chip_type) {
+ case INV_MPU3050:
+ cur_scale = scale[0];
+ cur_off = offset[0];
+ break;
+ case INV_MPU6050:
+ cur_scale = scale[1];
+ cur_off = offset[1];
+ break;
+ case INV_MPU6500:
+ cur_scale = scale[2];
+ cur_off = offset[2];
+ break;
+ default:
+ return -EINVAL;
+ };
+ scale_t = cur_off +
+ inv_q30_mult((int)temp << MPU_TEMP_SHIFT, cur_scale);
+
+ INV_I2C_INC_TEMPREAD(1);
+
+ return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns());
+}
+
+static ssize_t inv_flush_batch_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+ bool has_data;
+
+ mutex_lock(&indio_dev->mlock);
+ result = inv_flush_batch_data(indio_dev, &has_data);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return sprintf(buf, "%d\n", result);
+ else
+ return sprintf(buf, "%d\n", has_data);
+}
+
+/*
+ * inv_firmware_loaded() - calling this function will change
+ * firmware load
+ */
+static int inv_firmware_loaded(struct inv_mpu_state *st, int data)
+{
+ if (data)
+ return -EINVAL;
+ st->chip_config.firmware_loaded = 0;
+
+ return 0;
+}
+
+static int inv_switch_gyro_engine(struct inv_mpu_state *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY);
+}
+
+static int inv_switch_accel_engine(struct inv_mpu_state *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_ACCEL_STBY);
+}
+
+static ssize_t _attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ u8 d, axis;
+ int result;
+
+ result = 0;
+ if (st->chip_config.enable)
+ return -EBUSY;
+ if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) {
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ }
+
+ /* check the input and validate it's format */
+ switch (this_attr->address) {
+#ifdef CONFIG_INV_TESTING
+ /* these inputs are strings */
+ case ATTR_COMPASS_MATRIX:
+ case ATTR_COMPASS_SENS:
+ break;
+#endif
+ /* these inputs are integers */
+ default:
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ break;
+ }
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_X_OFFSET:
+ case ATTR_GYRO_Y_OFFSET:
+ case ATTR_GYRO_Z_OFFSET:
+ if ((data > MPU_MAX_G_OFFSET_VALUE) ||
+ (data < MPU_MIN_G_OFFSET_VALUE))
+ return -EINVAL;
+ axis = this_attr->address - ATTR_GYRO_X_OFFSET;
+ result = inv_set_offset_reg(st,
+ reg_gyro_offset[axis],
+ st->rom_gyro_offset[axis] + data);
+
+ if (result)
+ goto attr_store_fail;
+ st->input_gyro_offset[axis] = data;
+ break;
+ case ATTR_ACCEL_X_OFFSET:
+ case ATTR_ACCEL_Y_OFFSET:
+ case ATTR_ACCEL_Z_OFFSET:
+ {
+ const u8 *ch;
+
+ if ((data > MPU_MAX_A_OFFSET_VALUE) ||
+ (data < MPU_MIN_A_OFFSET_VALUE))
+ return -EINVAL;
+
+ axis = this_attr->address - ATTR_ACCEL_X_OFFSET;
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+
+ result = inv_set_offset_reg(st, ch[axis],
+ st->rom_accel_offset[axis] + (data << 1));
+ if (result)
+ goto attr_store_fail;
+ st->input_accel_offset[axis] = data;
+ break;
+ }
+ case ATTR_GYRO_SCALE:
+ result = inv_write_fsr(st, data);
+ break;
+ case ATTR_ACCEL_SCALE:
+ result = inv_write_accel_fs(st, data);
+ break;
+ case ATTR_COMPASS_SCALE:
+ result = st->slave_compass->set_scale(st, data);
+ break;
+ case ATTR_MOTION_LPA_ON:
+ if (INV_MPU6500 == st->chip_type) {
+ if (data)
+ /* enable and put in MPU6500 mode */
+ d = BIT_ACCEL_INTEL_ENABLE
+ | BIT_ACCEL_INTEL_MODE;
+ else
+ d = 0;
+ result = inv_i2c_single_write(st,
+ REG_6500_ACCEL_INTEL_CTRL, d);
+ if (result)
+ goto attr_store_fail;
+ }
+ st->mot_int.mot_on = !!data;
+ st->chip_config.lpa_mode = !!data;
+ break;
+ case ATTR_MOTION_LPA_FREQ:
+ result = inv_lpa_freq(st, data);
+ break;
+ case ATTR_MOTION_LPA_THRESHOLD:
+ if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if (INV_MPU6500 == st->chip_type) {
+ d = (u8)(data >> MPU6500_MOTION_THRESH_SHIFT);
+ data = (d << MPU6500_MOTION_THRESH_SHIFT);
+ } else {
+ d = (u8)(data >> MPU6050_MOTION_THRESH_SHIFT);
+ data = (d << MPU6050_MOTION_THRESH_SHIFT);
+ }
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d);
+ if (result)
+ goto attr_store_fail;
+ st->mot_int.mot_thr = data;
+ break;
+ /* from now on, power is not turned on */
+ case ATTR_SELF_TEST_SAMPLES:
+ if (data > ST_MAX_SAMPLES || data < 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ st->self_test.samples = data;
+ break;
+ case ATTR_SELF_TEST_THRESHOLD:
+ if (data > ST_MAX_THRESHOLD || data < 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ st->self_test.threshold = data;
+ case ATTR_GYRO_ENABLE:
+ st->chip_config.gyro_enable = !!data;
+ break;
+ case ATTR_GYRO_FIFO_ENABLE:
+ st->sensor[SENSOR_GYRO].on = !!data;
+ break;
+ case ATTR_GYRO_RATE:
+ st->sensor[SENSOR_GYRO].rate = data;
+ st->sensor[SENSOR_GYRO].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_GYRO].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_ACCEL_ENABLE:
+ st->chip_config.accel_enable = !!data;
+ break;
+ case ATTR_ACCEL_FIFO_ENABLE:
+ st->sensor[SENSOR_ACCEL].on = !!data;
+ break;
+ case ATTR_ACCEL_RATE:
+ st->sensor[SENSOR_ACCEL].rate = data;
+ st->sensor[SENSOR_ACCEL].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_ACCEL].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_COMPASS_ENABLE:
+ st->sensor[SENSOR_COMPASS].on = !!data;
+ break;
+ case ATTR_COMPASS_RATE:
+ if (data <= 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if ((MSEC_PER_SEC / st->slave_compass->rate_scale) < data)
+ data = MSEC_PER_SEC / st->slave_compass->rate_scale;
+
+ st->sensor[SENSOR_COMPASS].rate = data;
+ st->sensor[SENSOR_COMPASS].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_COMPASS].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_PRESSURE_ENABLE:
+ st->sensor[SENSOR_PRESSURE].on = !!data;
+ break;
+ case ATTR_PRESSURE_RATE:
+ if (data <= 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if ((MSEC_PER_SEC / st->slave_pressure->rate_scale) < data)
+ data = MSEC_PER_SEC / st->slave_pressure->rate_scale;
+
+ st->sensor[SENSOR_PRESSURE].rate = data;
+ st->sensor[SENSOR_PRESSURE].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_PRESSURE].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_POWER_STATE:
+ fake_asleep = !data;
+ break;
+ case ATTR_FIRMWARE_LOADED:
+ result = inv_firmware_loaded(st, data);
+ break;
+ case ATTR_SAMPLING_FREQ:
+ result = inv_fifo_rate_store(st, data);
+ break;
+#ifdef CONFIG_INV_TESTING
+ case ATTR_COMPASS_MATRIX:
+ {
+ char *str;
+ __s8 m[9];
+ d = 0;
+ if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE)
+ return -ENODEV;
+ while ((str = strsep((char **)&buf, ","))) {
+ if (d >= 9) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ result = kstrtoint(str, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ if (data < -1 || data > 1) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ m[d] = data;
+ d++;
+ }
+ if (d < 9) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ memcpy(st->plat_data.secondary_orientation, m, sizeof(m));
+ pr_debug(KERN_INFO
+ "compass_matrix: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ break;
+ }
+ case ATTR_COMPASS_SENS:
+ {
+ char *str;
+ __s8 s[3];
+ d = 0;
+ /* these 2 conditions should never be met, since the
+ 'compass_sens' sysfs entry should be hidden if the compass
+ is not an AKM */
+ if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE)
+ return -ENODEV;
+ if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8972 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8963)
+ return -ENODEV;
+ /* read the input data, expecting 3 comma separated values */
+ while ((str = strsep((char **)&buf, ","))) {
+ if (d >= 3) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ result = kstrtoint(str, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ if (data < 0 || data > 255) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ s[d] = data;
+ d++;
+ }
+ if (d < 3) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ /* store the new compass sensitivity adjustment */
+ memcpy(st->chip_info.compass_sens, s, sizeof(s));
+ pr_debug(KERN_INFO
+ "compass_sens: %d,%d,%d\n", s[0], s[1], s[2]);
+ break;
+ }
+#endif
+ default:
+ result = -EINVAL;
+ goto attr_store_fail;
+ };
+
+attr_store_fail:
+ if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD)
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_attr_store() - calling this function will store current
+ * non-dmp parameter settings
+ */
+static ssize_t inv_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_master_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int data;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable == (!!data)) {
+ result = count;
+ goto end_enable;
+ }
+ if (!!data) {
+ result = st->set_power_state(st, true);
+ if (result)
+ goto end_enable;
+ }
+ result = set_inv_enable(indio_dev, !!data);
+ if (result)
+ goto end_enable;
+ if (!data) {
+ result = st->set_power_state(st, false);
+ if (result)
+ goto end_enable;
+ }
+ result = count;
+
+end_enable:
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_master_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", st->chip_config.enable);
+}
+
+#ifdef CONFIG_INV_TESTING
+static ssize_t inv_test_suspend_resume_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int data;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ if (data)
+ inv_mpu_suspend(dev);
+ else
+ inv_mpu_resume(dev);
+ suspend_state = !!data;
+
+ return count;
+}
+
+static ssize_t inv_test_suspend_resume_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ return sprintf(buf, "%d\n", suspend_state);
+}
+
+/*
+ * inv_reg_write_store() - register write command for testing.
+ * Format: WSRRDD, where RR is the register in hex,
+ * and DD is the data in hex.
+ */
+static ssize_t inv_reg_write_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ u32 result;
+ u8 wreg, wval;
+ int temp;
+ char local_buf[10];
+
+ if ((buf[0] != 'W' && buf[0] != 'w') ||
+ (buf[1] != 'S' && buf[1] != 's'))
+ return -EINVAL;
+ if (strlen(buf) < 6)
+ return -EINVAL;
+
+ strncpy(local_buf, buf, 7);
+ local_buf[6] = 0;
+ result = sscanf(&local_buf[4], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wval = temp;
+ local_buf[4] = 0;
+ sscanf(&local_buf[2], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wreg = temp;
+
+ result = inv_i2c_single_write(st, wreg, wval);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ u8 *m;
+ int result;
+
+ if (st->chip_config.enable)
+ return -EBUSY;
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ switch (this_attr->address) {
+ case ATTR_DEBUG_ACCEL_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x80, 0xc0, 0xc8, 0xc2};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_01, ARRAY_SIZE(D), m);
+ data_out_control.accel = !!data;
+ break;
+ }
+ case ATTR_DEBUG_GYRO_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x80, 0xc4, 0xcc, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_02, ARRAY_SIZE(D), m);
+ data_out_control.gyro = !!data;
+ break;
+ }
+ case ATTR_DEBUG_COMPASS_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x81, 0xc0, 0xc8, 0xc2};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_03, ARRAY_SIZE(D), m);
+ data_out_control.compass = !!data;
+ break;
+ }
+ case ATTR_DEBUG_PRESSURE_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x81, 0xc4, 0xcc, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_04, ARRAY_SIZE(D), m);
+ data_out_control.pressure = !!data;
+ break;
+ }
+ case ATTR_DEBUG_LPQ_COUNTER:
+ {
+ u8 D[6] = {0xf1, 0xb1, 0x83, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_05, ARRAY_SIZE(D), m);
+ data_out_control.LPQ = !!data;
+ break;
+ }
+ case ATTR_DEBUG_SIXQ_COUNTER:
+ {
+ u8 D[6] = {0xf1, 0xb1, 0x89, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_06, ARRAY_SIZE(D), m);
+ data_out_control.SIXQ = !!data;
+ break;
+ }
+ case ATTR_DEBUG_PEDQ_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xf2, 0x88, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_07, ARRAY_SIZE(D), m);
+ data_out_control.PEDQ = !!data;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t inv_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ switch (this_attr->address) {
+ case ATTR_DEBUG_ACCEL_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.accel);
+ case ATTR_DEBUG_GYRO_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.gyro);
+ case ATTR_DEBUG_COMPASS_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.compass);
+ case ATTR_DEBUG_PRESSURE_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.pressure);
+ case ATTR_DEBUG_LPQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.LPQ);
+ case ATTR_DEBUG_SIXQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.SIXQ);
+ case ATTR_DEBUG_PEDQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.PEDQ);
+ default:
+ return -EINVAL;
+ }
+}
+
+#endif /* CONFIG_INV_TESTING */
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+};
+
+/*constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+
+/* special sysfs */
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+
+/* event based sysfs, needs poll to read */
+static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL);
+static DEVICE_ATTR(event_display_orientation, S_IRUGO,
+ inv_dmp_display_orient_show, NULL);
+static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL);
+static DEVICE_ATTR(event_smd, S_IRUGO, inv_smd_show, NULL);
+static DEVICE_ATTR(event_pedometer, S_IRUGO, inv_ped_show, NULL);
+
+/* master enable method */
+static DEVICE_ATTR(master_enable, S_IRUGO | S_IWUSR, inv_master_enable_show,
+ inv_master_enable_store);
+
+/* special run time sysfs entry, read only */
+static DEVICE_ATTR(flush_batch, S_IRUGO, inv_flush_batch_show, NULL);
+
+/* DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(in_accel_x_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_X_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_accel_y_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Y_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_accel_z_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Z_DMP_BIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_X_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Y_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Z_DMP_BIAS);
+
+static IIO_DEVICE_ATTR(pedometer_int_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_INT_ON);
+static IIO_DEVICE_ATTR(pedometer_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_ON);
+static IIO_DEVICE_ATTR(pedometer_step_thresh, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_STEP_THRESH);
+static IIO_DEVICE_ATTR(pedometer_int_thresh, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_INT_THRESH);
+
+static IIO_DEVICE_ATTR(smd_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_ENABLE);
+static IIO_DEVICE_ATTR(smd_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_DELAY_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold2, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_DELAY_THLD2);
+
+static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr64_show,
+ inv_attr64_store, ATTR_DMP_PEDOMETER_STEPS);
+static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr64_show,
+ inv_attr64_store, ATTR_DMP_PEDOMETER_TIME);
+static IIO_DEVICE_ATTR(pedometer_counter, S_IRUGO | S_IWUSR, inv_attr64_show,
+ NULL, ATTR_DMP_PEDOMETER_COUNTER);
+
+static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_ON);
+static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_THRESHOLD);
+static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_MIN_COUNT);
+static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_TIME);
+static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_mem_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+
+/* DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_ON);
+static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_INT_ON);
+static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON);
+static IIO_DEVICE_ATTR(step_indicator_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_STEP_INDICATOR_ON);
+static IIO_DEVICE_ATTR(batchmode_timeout, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_TIMEOUT);
+static IIO_DEVICE_ATTR(batchmode_wake_fifo_full_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL);
+
+static IIO_DEVICE_ATTR(six_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_SIX_Q_ON);
+static IIO_DEVICE_ATTR(six_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_SIX_Q_RATE);
+
+static IIO_DEVICE_ATTR(three_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_LPQ_ON);
+static IIO_DEVICE_ATTR(three_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_LPQ_RATE);
+
+static IIO_DEVICE_ATTR(ped_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PED_Q_ON);
+static IIO_DEVICE_ATTR(ped_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PED_Q_RATE);
+
+static IIO_DEVICE_ATTR(step_detector_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_STEP_DETECTOR_ON);
+
+/* non DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_ON);
+static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_FREQ);
+static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_THRESHOLD);
+
+static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_SCALE);
+static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_SCALE);
+static IIO_DEVICE_ATTR(in_magn_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_SCALE);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_X_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_Z_OFFSET);
+
+static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_X_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_Z_OFFSET);
+
+/* non DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(self_test_samples, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SELF_TEST_SAMPLES);
+static IIO_DEVICE_ATTR(self_test_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SELF_TEST_THRESHOLD);
+
+static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_ENABLE);
+static IIO_DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_FIFO_ENABLE);
+static IIO_DEVICE_ATTR(gyro_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_RATE);
+
+static IIO_DEVICE_ATTR(accel_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_ENABLE);
+static IIO_DEVICE_ATTR(accel_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_FIFO_ENABLE);
+static IIO_DEVICE_ATTR(accel_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_RATE);
+
+static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_ENABLE);
+static IIO_DEVICE_ATTR(compass_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_RATE);
+
+static IIO_DEVICE_ATTR(pressure_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_PRESSURE_ENABLE);
+static IIO_DEVICE_ATTR(pressure_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_PRESSURE_RATE);
+
+static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_POWER_STATE);
+static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_FIRMWARE_LOADED);
+static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SAMPLING_FREQ);
+
+/* show method only sysfs but with power on/off */
+static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL,
+ ATTR_SELF_TEST);
+
+/* show method only sysfs */
+static IIO_DEVICE_ATTR(in_accel_x_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_X_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_y_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_Y_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_z_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_Z_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_X_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_Y_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_Z_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_self_test_scale, S_IRUGO,
+ inv_attr_show, NULL, ATTR_SELF_TEST_GYRO_SCALE);
+static IIO_DEVICE_ATTR(in_accel_self_test_scale, S_IRUGO,
+ inv_attr_show, NULL, ATTR_SELF_TEST_ACCEL_SCALE);
+
+static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(accel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCEL_MATRIX);
+#ifdef CONFIG_INV_TESTING /* read/write in test mode */
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_MATRIX);
+static IIO_DEVICE_ATTR(compass_sens, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_SENS);
+#else
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_COMPASS_MATRIX);
+#endif
+static IIO_DEVICE_ATTR(secondary_name, S_IRUGO, inv_attr_show, NULL,
+ ATTR_SECONDARY_NAME);
+
+#ifdef CONFIG_INV_TESTING
+static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_reg_write_store, ATTR_REG_WRITE);
+/* smd debug related sysfs */
+static IIO_DEVICE_ATTR(debug_smd_enable_testp1, S_IWUSR, NULL,
+ inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP1);
+static IIO_DEVICE_ATTR(debug_smd_enable_testp2, S_IWUSR, NULL,
+ inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP2);
+static IIO_DEVICE_ATTR(debug_smd_exe_state, S_IRUGO, inv_attr_show,
+ NULL, ATTR_DEBUG_SMD_EXE_STATE);
+static IIO_DEVICE_ATTR(debug_smd_delay_cntr, S_IRUGO, inv_attr_show,
+ NULL, ATTR_DEBUG_SMD_DELAY_CNTR);
+static DEVICE_ATTR(test_suspend_resume, S_IRUGO | S_IWUSR,
+ inv_test_suspend_resume_show, inv_test_suspend_resume_store);
+
+static IIO_DEVICE_ATTR(test_gyro_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_GYRO_COUNTER);
+static IIO_DEVICE_ATTR(test_accel_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_ACCEL_COUNTER);
+static IIO_DEVICE_ATTR(test_compass_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_COMPASS_COUNTER);
+static IIO_DEVICE_ATTR(test_pressure_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_PRESSURE_COUNTER);
+static IIO_DEVICE_ATTR(test_LPQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_LPQ_COUNTER);
+static IIO_DEVICE_ATTR(test_SIXQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_SIXQ_COUNTER);
+static IIO_DEVICE_ATTR(test_PEDQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_PEDQ_COUNTER);
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_reg_dump.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_master_enable.attr,
+ &iio_dev_attr_in_anglvel_scale.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_self_test_scale.dev_attr.attr,
+ &iio_dev_attr_self_test_samples.dev_attr.attr,
+ &iio_dev_attr_self_test_threshold.dev_attr.attr,
+ &iio_dev_attr_gyro_enable.dev_attr.attr,
+ &iio_dev_attr_gyro_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_gyro_rate.dev_attr.attr,
+ &iio_dev_attr_power_state.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_self_test.dev_attr.attr,
+ &iio_dev_attr_gyro_matrix.dev_attr.attr,
+ &iio_dev_attr_secondary_name.dev_attr.attr,
+#ifdef CONFIG_INV_TESTING
+ &iio_dev_attr_reg_write.dev_attr.attr,
+ &iio_dev_attr_debug_smd_enable_testp1.dev_attr.attr,
+ &iio_dev_attr_debug_smd_enable_testp2.dev_attr.attr,
+ &iio_dev_attr_debug_smd_exe_state.dev_attr.attr,
+ &iio_dev_attr_debug_smd_delay_cntr.dev_attr.attr,
+ &dev_attr_test_suspend_resume.attr,
+ &iio_dev_attr_test_gyro_counter.dev_attr.attr,
+ &iio_dev_attr_test_accel_counter.dev_attr.attr,
+ &iio_dev_attr_test_compass_counter.dev_attr.attr,
+ &iio_dev_attr_test_pressure_counter.dev_attr.attr,
+ &iio_dev_attr_test_LPQ_counter.dev_attr.attr,
+ &iio_dev_attr_test_SIXQ_counter.dev_attr.attr,
+ &iio_dev_attr_test_PEDQ_counter.dev_attr.attr,
+#endif
+};
+
+static const struct attribute *inv_mpu6xxx_attributes[] = {
+ &dev_attr_event_accel_motion.attr,
+ &dev_attr_event_smd.attr,
+ &dev_attr_event_pedometer.attr,
+ &dev_attr_flush_batch.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_self_test_scale.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_pedometer_int_on.dev_attr.attr,
+ &iio_dev_attr_pedometer_on.dev_attr.attr,
+ &iio_dev_attr_pedometer_steps.dev_attr.attr,
+ &iio_dev_attr_pedometer_time.dev_attr.attr,
+ &iio_dev_attr_pedometer_counter.dev_attr.attr,
+ &iio_dev_attr_pedometer_step_thresh.dev_attr.attr,
+ &iio_dev_attr_pedometer_int_thresh.dev_attr.attr,
+ &iio_dev_attr_smd_enable.dev_attr.attr,
+ &iio_dev_attr_smd_threshold.dev_attr.attr,
+ &iio_dev_attr_smd_delay_threshold.dev_attr.attr,
+ &iio_dev_attr_smd_delay_threshold2.dev_attr.attr,
+ &iio_dev_attr_dmp_on.dev_attr.attr,
+ &iio_dev_attr_dmp_int_on.dev_attr.attr,
+ &iio_dev_attr_dmp_event_int_on.dev_attr.attr,
+ &iio_dev_attr_step_indicator_on.dev_attr.attr,
+ &iio_dev_attr_batchmode_timeout.dev_attr.attr,
+ &iio_dev_attr_batchmode_wake_fifo_full_on.dev_attr.attr,
+ &iio_dev_attr_six_axes_q_on.dev_attr.attr,
+ &iio_dev_attr_six_axes_q_rate.dev_attr.attr,
+ &iio_dev_attr_three_axes_q_on.dev_attr.attr,
+ &iio_dev_attr_three_axes_q_rate.dev_attr.attr,
+ &iio_dev_attr_ped_q_on.dev_attr.attr,
+ &iio_dev_attr_ped_q_rate.dev_attr.attr,
+ &iio_dev_attr_step_detector_on.dev_attr.attr,
+ &iio_dev_attr_accel_enable.dev_attr.attr,
+ &iio_dev_attr_accel_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_accel_rate.dev_attr.attr,
+ &iio_dev_attr_firmware_loaded.dev_attr.attr,
+ &iio_dev_attr_accel_matrix.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu6500_attributes[] = {
+ &iio_dev_attr_motion_lpa_on.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_freq.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_threshold.dev_attr.attr,
+};
+
+static const struct attribute *inv_tap_attributes[] = {
+ &dev_attr_event_tap.attr,
+ &iio_dev_attr_tap_on.dev_attr.attr,
+ &iio_dev_attr_tap_threshold.dev_attr.attr,
+ &iio_dev_attr_tap_min_count.dev_attr.attr,
+ &iio_dev_attr_tap_time.dev_attr.attr,
+};
+
+static const struct attribute *inv_display_orient_attributes[] = {
+ &dev_attr_event_display_orientation.attr,
+ &iio_dev_attr_display_orientation_on.dev_attr.attr,
+};
+
+static const struct attribute *inv_compass_attributes[] = {
+ &iio_dev_attr_in_magn_scale.dev_attr.attr,
+ &iio_dev_attr_compass_enable.dev_attr.attr,
+ &iio_dev_attr_compass_rate.dev_attr.attr,
+ &iio_dev_attr_compass_matrix.dev_attr.attr,
+};
+
+static const struct attribute *inv_akxxxx_attributes[] = {
+#ifdef CONFIG_INV_TESTING
+ &iio_dev_attr_compass_sens.dev_attr.attr,
+#endif
+};
+
+static const struct attribute *inv_pressure_attributes[] = {
+ &iio_dev_attr_pressure_enable.dev_attr.attr,
+ &iio_dev_attr_pressure_rate.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu3050_attributes[] = {
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_accel_enable.dev_attr.attr,
+ &iio_dev_attr_accel_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_accel_matrix.dev_attr.attr,
+};
+
+static struct attribute *inv_attributes[
+ ARRAY_SIZE(inv_gyro_attributes) +
+ ARRAY_SIZE(inv_mpu6xxx_attributes) +
+ ARRAY_SIZE(inv_mpu6500_attributes) +
+ ARRAY_SIZE(inv_compass_attributes) +
+ ARRAY_SIZE(inv_akxxxx_attributes) +
+ ARRAY_SIZE(inv_pressure_attributes) +
+ ARRAY_SIZE(inv_tap_attributes) +
+ ARRAY_SIZE(inv_display_orient_attributes) +
+ 1
+];
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "mpu",
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &inv_attribute_group,
+};
+
+static void inv_setup_func_ptr(struct inv_mpu_state *st)
+{
+ if (st->chip_type == INV_MPU3050) {
+ st->set_power_state = set_power_mpu3050;
+ st->switch_gyro_engine = inv_switch_3050_gyro_engine;
+ st->switch_accel_engine = inv_switch_3050_accel_engine;
+ st->init_config = inv_init_config_mpu3050;
+ st->setup_reg = inv_setup_reg_mpu3050;
+ } else {
+ st->set_power_state = set_power_itg;
+ st->switch_gyro_engine = inv_switch_gyro_engine;
+ st->switch_accel_engine = inv_switch_accel_engine;
+ st->init_config = inv_init_config;
+ st->setup_reg = inv_setup_reg;
+ }
+}
+
+static int inv_detect_6xxx(struct inv_mpu_state *st)
+{
+ int result;
+ u8 d;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &d);
+ if (result)
+ return result;
+ if ((d == MPU6500_ID) || (d == MPU6515_ID)) {
+ st->chip_type = INV_MPU6500;
+ strcpy(st->name, "mpu6500");
+ } else {
+ strcpy(st->name, "mpu6050");
+ }
+
+ return 0;
+}
+
+static int inv_setup_vddio(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[1];
+
+ if (INV_MPU6050 == st->chip_type) {
+ result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data);
+ if (result)
+ return result;
+ data[0] &= ~BIT_I2C_MST_VDDIO;
+ if (st->plat_data.level_shifter)
+ data[0] |= BIT_I2C_MST_VDDIO;
+ /*set up VDDIO register */
+ result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_check_chip_type() - check and setup chip type.
+ */
+static int inv_check_chip_type(struct inv_mpu_state *st,
+ const struct i2c_device_id *id, bool reset_needed)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ int t_ind;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+ if (!strcmp(id->name, "itg3500")) {
+ st->chip_type = INV_ITG3500;
+ } else if (!strcmp(id->name, "mpu3050")) {
+ st->chip_type = INV_MPU3050;
+ } else if (!strcmp(id->name, "mpu6050")) {
+ st->chip_type = INV_MPU6050;
+ } else if (!strcmp(id->name, "mpu9150")) {
+ st->chip_type = INV_MPU6050;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_AK8975;
+ } else if (!strcmp(id->name, "mpu6500")) {
+ st->chip_type = INV_MPU6500;
+ } else if (!strcmp(id->name, "mpu9250")) {
+ st->chip_type = INV_MPU6500;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_AK8963;
+ } else if (!strcmp(id->name, "mpu6xxx")) {
+ st->chip_type = INV_MPU6050;
+ } else if (!strcmp(id->name, "mpu9350")) {
+ st->chip_type = INV_MPU6500;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_MLX90399;
+ /* block use of MPU9350 in this build
+ since it's not production ready */
+ pr_err("MPU9350 support is not officially available yet.\n");
+ return -EPERM;
+ } else if (!strcmp(id->name, "mpu6515")) {
+ st->chip_type = INV_MPU6500;
+ } else {
+ return -EPERM;
+ }
+ inv_setup_func_ptr(st);
+ st->hw = &hw_info[st->chip_type];
+ reg = &st->reg;
+ st->setup_reg(reg);
+
+ if (reset_needed) {
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ return result;
+ msleep(POWER_UP_TIME);
+ }
+ /* toggle power state */
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ if (!strcmp(id->name, "mpu6xxx")) {
+ /* for MPU6500, reading register need more time */
+ msleep(POWER_UP_TIME);
+ result = inv_detect_6xxx(st);
+ if (result)
+ return result;
+ }
+
+ if ((plat->sec_slave_type != SECONDARY_SLAVE_TYPE_NONE) ||
+ (plat->aux_slave_type != SECONDARY_SLAVE_TYPE_NONE)) {
+ result = inv_setup_vddio(st);
+ if (result)
+ return result;
+ }
+
+ switch (st->chip_type) {
+ case INV_ITG3500:
+ break;
+ case INV_MPU6050:
+ case INV_MPU6500:
+ if (SECONDARY_SLAVE_TYPE_COMPASS == plat->sec_slave_type)
+ conf->has_compass = 1;
+ else
+ conf->has_compass = 0;
+ if (SECONDARY_SLAVE_TYPE_PRESSURE == plat->aux_slave_type)
+ conf->has_pressure = 1;
+ else
+ conf->has_pressure = 0;
+
+ break;
+ case INV_MPU3050:
+ if (SECONDARY_SLAVE_TYPE_ACCEL == plat->sec_slave_type) {
+ if (ACCEL_ID_BMA250 == plat->sec_slave_id)
+ inv_register_mpu3050_slave(st);
+ }
+ break;
+ default:
+ result = st->set_power_state(st, false);
+ return -ENODEV;
+ }
+ if (conf->has_compass && conf->has_pressure &&
+ (COMPASS_ID_MLX90399 == plat->sec_slave_id)) {
+ pr_err("MLX90399 can't share slaves with others\n");
+ return -EINVAL;
+ }
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ result = inv_get_silicon_rev_mpu6050(st);
+ break;
+ case INV_MPU6500:
+ result = inv_get_silicon_rev_mpu6500(st);
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ if (result) {
+ pr_err("read silicon rev error\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+
+ /* turn off the gyro engine after OTP reading */
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, false);
+ if (result)
+ return result;
+
+ if (conf->has_compass) {
+ result = inv_mpu_setup_compass_slave(st);
+ if (result) {
+ pr_err("compass setup failed\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+ }
+ if (conf->has_pressure) {
+ result = inv_mpu_setup_pressure_slave(st);
+ if (result) {
+ pr_err("pressure setup failed\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+ }
+
+ t_ind = 0;
+ memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+ sizeof(inv_gyro_attributes));
+ t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+ if (INV_MPU3050 == st->chip_type && st->slave_accel != NULL) {
+ memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes,
+ sizeof(inv_mpu3050_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu3050_attributes);
+ inv_attributes[t_ind] = NULL;
+ return 0;
+ }
+
+ /* all MPU6xxx based parts */
+ if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) {
+ memcpy(&inv_attributes[t_ind], inv_mpu6xxx_attributes,
+ sizeof(inv_mpu6xxx_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu6xxx_attributes);
+ }
+
+ /* MPU6500 only */
+ if (INV_MPU6500 == st->chip_type) {
+ memcpy(&inv_attributes[t_ind], inv_mpu6500_attributes,
+ sizeof(inv_mpu6500_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu6500_attributes);
+ }
+
+ if (conf->has_compass) {
+ memcpy(&inv_attributes[t_ind], inv_compass_attributes,
+ sizeof(inv_compass_attributes));
+ t_ind += ARRAY_SIZE(inv_compass_attributes);
+
+ /* AKM only */
+ if (st->plat_data.sec_slave_id == COMPASS_ID_AK8975 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK8972 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK09911 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK8963) {
+ memcpy(&inv_attributes[t_ind], inv_akxxxx_attributes,
+ sizeof(inv_akxxxx_attributes));
+ t_ind += ARRAY_SIZE(inv_akxxxx_attributes);
+ }
+ }
+
+ if (conf->has_pressure) {
+ memcpy(&inv_attributes[t_ind], inv_pressure_attributes,
+ sizeof(inv_pressure_attributes));
+ t_ind += ARRAY_SIZE(inv_pressure_attributes);
+ }
+
+ inv_attributes[t_ind] = NULL;
+
+ return 0;
+}
+
+/*
+ * inv_create_dmp_sysfs() - create binary sysfs dmp entry.
+ */
+static const struct bin_attribute dmp_firmware = {
+ .attr = {
+ .name = "dmp_firmware",
+ .mode = S_IRUGO | S_IWUSR
+ },
+ .size = DMP_IMAGE_SIZE + 32,
+ .read = inv_dmp_firmware_read,
+ .write = inv_dmp_firmware_write,
+};
+
+static const struct bin_attribute six_q_value = {
+ .attr = {
+ .name = "six_axes_q_value",
+ .mode = S_IWUSR
+ },
+ .size = QUATERNION_BYTES,
+ .read = NULL,
+ .write = inv_six_q_write,
+};
+
+static int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+ int result;
+
+ result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware);
+ if (result)
+ return result;
+ result = sysfs_create_bin_file(&ind->dev.kobj, &six_q_value);
+
+ return result;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ /*
+ * If we're not coming from a power-off condition, we need to
+ * reset the chip as we may have gotten here via a watchdog
+ * reboot, in which case the status of the chip is unknown
+ * (i.e. chip is not reset by hardware on a watchdog reboot).
+ */
+ bool reset_needed = true;
+
+#ifdef CONFIG_DTS_INV_MPU_IIO
+ enable_irq_wake(client->irq);
+#endif
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENOSYS;
+ pr_err("I2c function error\n");
+ goto out_no_free;
+ }
+#ifdef CONFIG_INV_KERNEL_3_10
+ indio_dev = iio_device_alloc(sizeof(*st));
+#else
+ indio_dev = iio_allocate_device(sizeof(*st));
+#endif
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->sl_handle = client->adapter;
+ st->i2c_addr = client->addr;
+#ifdef CONFIG_DTS_INV_MPU_IIO
+ result = invensense_mpu_parse_dt(&client->dev, &st->plat_data);
+ if (result)
+ goto out_free;
+
+ /*Power on device.*/
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(&client->dev,
+ "power_on failed: %d\n", result);
+ return result;
+ }
+ msleep(POWER_UP_TIME);
+
+ /*
+ * We don't need subsequent reset of chip as it's coming
+ * from a power-off condition
+ */
+ reset_needed = false;
+ }
+#else
+ /* power is turned on inside check chip type */
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+#endif
+ result = inv_check_chip_type(st, id, reset_needed);
+ if (result)
+ goto out_free;
+
+ result = st->init_config(indio_dev);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Could not initialize device.\n");
+ goto out_free;
+ }
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ if (!strcmp(id->name, "mpu6xxx"))
+ indio_dev->name = st->name;
+ else
+ indio_dev->name = id->name;
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+ st->irq = client->irq;
+ result = inv_mpu_probe_trigger(indio_dev);
+ if (result) {
+ pr_err("trigger probe fail\n");
+ goto out_remove_ring;
+ }
+
+ /* Tell the i2c counter, we have an IRQ */
+ INV_I2C_SETIRQ(IRQ_MPU, client->irq);
+
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_trigger;
+ }
+
+ if (INV_MPU6050 == st->chip_type ||
+ INV_MPU6500 == st->chip_type) {
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ }
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ mutex_init(&st->suspend_resume_lock);
+ result = st->set_power_state(st, false);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "%s could not be turned off.\n", st->hw->name);
+ goto out_unreg_iio;
+ }
+ inv_init_sensor_struct(st);
+ dev_info(&client->dev, "%s is ready to go!\n",
+ indio_dev->name);
+
+ return 0;
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_device_free(indio_dev);
+#else
+ iio_free_device(indio_dev);
+#endif
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result;
+
+ reg = &st->reg;
+ mutex_lock(&indio_dev->mlock);
+ dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = st->set_power_state(st, false);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ kfifo_free(&st->timestamps);
+ iio_device_unregister(indio_dev);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_mpu_unconfigure_ring(indio_dev);
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_device_free(indio_dev);
+#else
+ iio_free_device(indio_dev);
+#endif
+ dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+static int inv_setup_suspend_batchmode(struct iio_dev *indio_dev, bool suspend)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+ int counter;
+
+ if (st->chip_config.dmp_on &&
+ st->chip_config.enable &&
+ (!st->chip_config.dmp_event_int_on)) {
+ /* turn off data interrupt in suspend mode;turn on resume */
+ result = inv_set_interrupt_on_gesture_event(st, suspend);
+ if (result)
+ return result;
+ if (suspend)
+ counter = INT_MAX;
+ else
+ counter = st->batch.counter;
+ result = write_be32_key_to_mem(st, counter, KEY_BM_BATCH_THLD);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void inv_disable_nonwake_sensors(struct inv_mpu_state *st)
+{
+ int err = 0;
+ if (st->chip_config.gyro_enable) {
+ err = inv_switch_gyro_engine(st, false);
+ if (err)
+ pr_err("%s: ERROR %d disabling gyro\n", __func__, err);
+ }
+
+ /* don't disable accel if pedometer or significant motion is enabled */
+ if (!st->ped.on && !st->chip_config.smd_enable &&
+ st->chip_config.accel_enable) {
+ err = inv_switch_accel_engine(st, false);
+ if (err)
+ pr_err("%s: ERROR %d disabling accelerometer\n",
+ __func__, err);
+ }
+
+ if (st->sensor[SENSOR_COMPASS].on) {
+ err = st->slave_compass->suspend(st);
+ if (err)
+ pr_err("%s: ERROR %d disabling compass\n",
+ __func__, err);
+ }
+}
+
+static void inv_enable_nonwake_sensors(struct inv_mpu_state *st)
+{
+ int err = 0;
+ if (st->chip_config.gyro_enable) {
+ err = inv_switch_gyro_engine(st, true);
+ if (err)
+ pr_err("%s: ERROR %d restoring gyro state\n",
+ __func__, err);
+ }
+
+ if (!st->ped.on && !st->chip_config.smd_enable &&
+ st->chip_config.accel_enable) {
+ err = inv_switch_accel_engine(st, true);
+ if (err)
+ pr_err("%s: ERROR %d restoring accelerometer state\n",
+ __func__, err);
+ }
+
+ if (st->sensor[SENSOR_COMPASS].on) {
+ err = st->slave_compass->resume(st);
+ if (err)
+ pr_err("%s: ERROR %d restoring compass state\n",
+ __func__, err);
+ }
+}
+
+/*
+ * inv_mpu_resume(): resume method for this driver.
+ * This method can be modified according to the request of different
+ * customers. It basically undo everything suspend_noirq is doing
+ * and recover the chip to what it was before suspend.
+ */
+static int inv_mpu_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ /* add code according to different request Start */
+ pr_debug("%s inv_mpu_resume\n", st->hw->name);
+ mutex_lock(&indio_dev->mlock);
+ st->suspend_state = false;
+
+ result = 0;
+ if (st->chip_config.dmp_on && st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ enable_irq(st->client->irq);
+ result |= inv_read_time_and_ticks(st, true);
+ if (st->ped.int_on)
+ result |= inv_enable_pedometer_interrupt(st, true);
+ if (st->chip_config.display_orient_on)
+ result |= inv_set_display_orient_interrupt_dmp(st,
+ true);
+ result |= inv_setup_suspend_batchmode(indio_dev, false);
+
+ /* restore enable state all non-wakeup sensors */
+ inv_enable_nonwake_sensors(st);
+
+ } else if (st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ enable_irq(st->client->irq);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ /* add code according to different request End */
+
+ return result;
+}
+
+/*
+ * inv_mpu_suspend(): suspend method for this driver.
+ * This method can be modified according to the request of different
+ * customers. If customer want some events, such as SMD to wake up the CPU,
+ * then data interrupt should be disabled in this interrupt to avoid
+ * unnecessary interrupts. If customer want pedometer running while CPU is
+ * asleep, then pedometer should be turned on while pedometer interrupt
+ * should be turned off.
+ */
+static int inv_mpu_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ /* add code according to different request Start */
+ pr_debug("%s inv_mpu_suspend\n", st->hw->name);
+
+ result = 0;
+ if (st->chip_config.dmp_on && st->chip_config.enable) {
+ /* turn off pedometer interrupt during suspend */
+ if (st->ped.int_on)
+ result |= inv_enable_pedometer_interrupt(st, false);
+ /* turn off orientation interrupt during suspend */
+ if (st->chip_config.display_orient_on)
+ result |= inv_set_display_orient_interrupt_dmp(st,
+ false);
+ /* setup batch mode related during suspend */
+ result = inv_setup_suspend_batchmode(indio_dev, true);
+
+ /* disable all non-wakeup sensors */
+ inv_disable_nonwake_sensors(st);
+
+ /* disable irq's to assure inv_read_fifo() runs if pending */
+ disable_irq(st->client->irq);
+
+ /* only in DMP non-batch data mode, turn off the power */
+ if ((!st->batch.on) && (!st->chip_config.smd_enable) &&
+ (!st->ped.on))
+ result |= st->set_power_state(st, false);
+ } else if (st->chip_config.enable) {
+ /* disable irq's to assure inv_read_fifo() runs if pending */
+ disable_irq(st->client->irq);
+
+ /* in non DMP case, just turn off the power */
+ result |= st->set_power_state(st, false);
+ }
+ st->suspend_state = true;
+ /* add code according to different request End */
+
+ return 0;
+}
+
+static const struct dev_pm_ops inv_mpu_pmops = {
+ .suspend = inv_mpu_suspend,
+ .resume = inv_mpu_resume,
+};
+#define INV_MPU_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU_PMOPS NULL
+#endif /* CONFIG_PM */
+
+static const u16 normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+ {"itg3500", INV_ITG3500},
+ {"mpu3050", INV_MPU3050},
+ {"mpu6050", INV_MPU6050},
+ {"mpu9150", INV_MPU9150},
+ {"mpu6500", INV_MPU6500},
+ {"mpu9250", INV_MPU9250},
+ {"mpu6xxx", INV_MPU6XXX},
+ {"mpu9350", INV_MPU9350},
+ {"mpu6515", INV_MPU6515},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio",
+ .pm = INV_MPU_PMOPS,
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_mpu_init(void)
+{
+ int result = i2c_add_driver(&inv_mpu_driver);
+ if (result) {
+ pr_err("failed\n");
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_mpu_exit(void)
+{
+ i2c_del_driver(&inv_mpu_driver);
+}
+
+module_init(inv_mpu_init);
+module_exit(inv_mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-mpu-iio");
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c
new file mode 100755
index 00000000000..276d5bcfea7
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c
@@ -0,0 +1,270 @@
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+#include "inv_mpu_dts.h"
+
+int inv_mpu_power_on(struct mpu_platform_data *pdata)
+{
+ int err ;
+
+ err = regulator_enable(pdata->vdd_ana);
+ err = regulator_enable(pdata->vdd_i2c);
+ pr_debug(KERN_INFO "inv_mpu_power_on call");
+
+ return err ;
+
+}
+
+int inv_mpu_power_off(struct mpu_platform_data *pdata)
+{
+ int err ;
+
+ err = regulator_disable(pdata->vdd_ana);
+ err = regulator_disable(pdata->vdd_i2c);
+ pr_debug(KERN_INFO "inv_mpu_power_off call");
+
+ return err;
+}
+
+int inv_parse_orientation_matrix(struct device *dev, s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,secondary_type", &name)) {
+ dev_err(dev, "Missing secondary type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "compass")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ } else if (!strcmp(name, "none")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,secondary_name", &name)) {
+ dev_err(dev, "Missing secondary name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "ak8963"))
+ pdata->sec_slave_id = COMPASS_ID_AK8963;
+ else if (!strcmp(name, "ak8975"))
+ pdata->sec_slave_id = COMPASS_ID_AK8975;
+ else if (!strcmp(name, "ak8972"))
+ pdata->sec_slave_id = COMPASS_ID_AK8972;
+ else
+ return -EINVAL;
+ rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary register\n");
+ return rc;
+ }
+ pdata->secondary_i2c_addr = temp_val;
+ rc = inv_parse_secondary_orientation_matrix(dev,
+ pdata->secondary_orientation);
+
+ return rc;
+}
+
+int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,aux_type", &name)) {
+ dev_err(dev, "Missing aux type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "pressure")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE;
+ } else if (!strcmp(name, "none")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,aux_name", &name)) {
+ dev_err(dev, "Missing aux name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "bmp280"))
+ pdata->aux_slave_id = PRESSURE_ID_BMP280;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,aux_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read aux register\n");
+ return rc;
+ }
+ pdata->aux_i2c_addr = temp_val;
+
+ return 0;
+}
+
+int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ pr_debug("Invensense MPU parse_dt started.\n");
+
+ rc = inv_parse_orientation_matrix(dev, pdata->orientation);
+ if (rc)
+ return rc;
+
+ rc = inv_parse_secondary(dev, pdata);
+ if (rc)
+ return rc;
+
+ inv_parse_aux(dev, pdata);
+
+ pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana");
+ if (IS_ERR(pdata->vdd_ana)) {
+ rc = PTR_ERR(pdata->vdd_ana);
+ dev_err(dev, "Regulator get failed vdd_ana-supply rc=%d\n", rc);
+ return rc;
+ }
+ pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c");
+ if (IS_ERR(pdata->vdd_i2c)) {
+ rc = PTR_ERR(pdata->vdd_i2c);
+ dev_err(dev, "Regulator get failed vcc-i2c-supply rc=%d\n", rc);
+ return rc;
+ }
+ pdata->power_on = inv_mpu_power_on;
+ pdata->power_off = inv_mpu_power_off;
+ pr_debug("Invensense MPU parse_dt complete.\n");
+ return rc;
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h
new file mode 100755
index 00000000000..151ac74ea05
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h
@@ -0,0 +1,30 @@
+/*
+* 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_DTS_H_
+#define _INV_MPU_DTS_H_
+
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+
+int inv_mpu_power_on(struct mpu_platform_data *pdata);
+int inv_mpu_power_off(struct mpu_platform_data *pdata);
+int inv_parse_orientation_matrix(struct device *dev, s8 *orient);
+int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient);
+int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata);
+int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata);
+int invensense_mpu_parse_dt(struct device *dev,
+ struct mpu_platform_data *pdata);
+
+#endif /* #ifndef _INV_MPU_DTS_H_ */
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h
new file mode 100644
index 00000000000..701d4db78ed
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h
@@ -0,0 +1,1078 @@
+/*
+* 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/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+
+#include "dmpKey.h"
+
+/*register and associated bit definition*/
+#define REG_3050_FIFO_EN 0x12
+#define BITS_3050_ACCEL_OUT 0x0E
+
+#define REG_3050_AUX_VDDIO 0x13
+#define BIT_3050_VDDIO 0x04
+
+#define REG_3050_SLAVE_ADDR 0x14
+#define REG_3050_SAMPLE_RATE_DIV 0x15
+#define REG_3050_LPF 0x16
+#define REG_3050_INT_ENABLE 0x17
+#define REG_3050_AUX_BST_ADDR 0x18
+#define REG_3050_INT_STATUS 0x1A
+#define REG_3050_TEMPERATURE 0x1B
+#define REG_3050_RAW_GYRO 0x1D
+#define REG_3050_AUX_XOUT_H 0x23
+#define REG_3050_FIFO_COUNT_H 0x3A
+#define REG_3050_FIFO_R_W 0x3C
+
+#define REG_3050_USER_CTRL 0x3D
+#define BIT_3050_AUX_IF_EN 0x20
+#define BIT_3050_FIFO_RST 0x02
+
+#define REG_3050_PWR_MGMT_1 0x3E
+#define BITS_3050_POWER1 0x30
+#define BITS_3050_POWER2 0x10
+#define BITS_3050_GYRO_STANDBY 0x38
+
+#define REG_3500_OTP 0x0
+
+#define REG_YGOFFS_TC 0x1
+#define BIT_I2C_MST_VDDIO 0x80
+
+#define REG_XA_OFFS_H 0x6
+#define REG_XA_OFFS_L_TC 0x7
+#define REG_PRODUCT_ID 0xC
+#define REG_ST_GCT_X 0xD
+#define REG_XG_OFFS_USRH 0x13
+#define REG_SAMPLE_RATE_DIV 0x19
+#define REG_CONFIG 0x1A
+
+#define REG_GYRO_CONFIG 0x1B
+#define BITS_SELF_TEST_EN 0xE0
+
+#define REG_ACCEL_CONFIG 0x1C
+#define REG_ACCEL_MOT_THR 0x1F
+#define REG_ACCEL_MOT_DUR 0x20
+
+#define REG_FIFO_EN 0x23
+#define BIT_ACCEL_OUT 0x08
+#define BITS_GYRO_OUT 0x70
+
+#define REG_I2C_MST_CTRL 0x24
+#define BIT_WAIT_FOR_ES 0x40
+
+#define REG_I2C_SLV0_ADDR 0x25
+#define REG_I2C_SLV0_REG 0x26
+#define REG_I2C_SLV0_CTRL 0x27
+#define REG_I2C_SLV1_ADDR 0x28
+#define REG_I2C_SLV1_REG 0x29
+#define REG_I2C_SLV1_CTRL 0x2A
+#define REG_I2C_SLV2_ADDR 0x2B
+#define REG_I2C_SLV2_REG 0x2C
+#define REG_I2C_SLV2_CTRL 0x2D
+#define REG_I2C_SLV3_ADDR 0x2E
+#define REG_I2C_SLV3_REG 0x2F
+#define REG_I2C_SLV3_CTRL 0x30
+#define REG_I2C_SLV4_CTRL 0x34
+
+#define INV_MPU_BIT_SLV_EN 0x80
+#define INV_MPU_BIT_BYTE_SW 0x40
+#define INV_MPU_BIT_REG_DIS 0x20
+#define INV_MPU_BIT_GRP 0x10
+#define INV_MPU_BIT_I2C_READ 0x80
+
+#define REG_INT_PIN_CFG 0x37
+#define BIT_BYPASS_EN 0x2
+
+#define REG_INT_ENABLE 0x38
+#define BIT_DATA_RDY_EN 0x01
+#define BIT_DMP_INT_EN 0x02
+#define BIT_ZMOT_EN 0x20
+#define BIT_MOT_EN 0x40
+#define BIT_6500_WOM_EN 0x40
+
+#define REG_DMP_INT_STATUS 0x39
+#define BIT_DMP_INT_CI 0x01
+
+#define REG_INT_STATUS 0x3A
+#define BIT_MOT_INT 0x40
+#define BIT_ZMOT_INT 0x20
+
+#define REG_RAW_ACCEL 0x3B
+#define REG_TEMPERATURE 0x41
+#define REG_EXT_SENS_DATA_00 0x49
+#define REG_EXT_SENS_DATA_08 0x51
+#define REG_EXT_SENS_DATA_09 0x52
+
+#define REG_ACCEL_INTEL_STATUS 0x61
+
+#define INV_MPU_REG_I2C_SLV0_DO 0x63
+#define INV_MPU_REG_I2C_SLV1_DO 0x64
+#define INV_MPU_REG_I2C_SLV2_DO 0x65
+#define INV_MPU_REG_I2C_SLV3_DO 0x66
+
+#define REG_I2C_MST_DELAY_CTRL 0x67
+#define BIT_SLV0_DLY_EN 0x01
+#define BIT_SLV1_DLY_EN 0x02
+#define BIT_SLV2_DLY_EN 0x04
+#define BIT_SLV3_DLY_EN 0x08
+
+#define REG_USER_CTRL 0x6A
+#define BIT_FIFO_RST 0x04
+#define BIT_DMP_RST 0x08
+#define BIT_I2C_MST_EN 0x20
+#define BIT_FIFO_EN 0x40
+#define BIT_DMP_EN 0x80
+
+#define REG_PWR_MGMT_1 0x6B
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_CYCLE 0x20
+#define BIT_CLK_MASK 0x7
+
+#define REG_PWR_MGMT_2 0x6C
+#define BIT_PWR_ACCEL_STBY 0x38
+#define BIT_PWR_GYRO_STBY 0x07
+#define BIT_LPA_FREQ 0xC0
+
+#define REG_BANK_SEL 0x6D
+#define REG_MEM_START_ADDR 0x6E
+#define REG_MEM_RW 0x6F
+#define REG_PRGM_STRT_ADDRH 0x70
+#define REG_FIFO_COUNT_H 0x72
+#define REG_FIFO_R_W 0x74
+#define REG_WHOAMI 0x75
+
+#define REG_6500_XG_ST_DATA 0x0
+#define REG_6500_XA_ST_DATA 0xD
+#define REG_6500_XA_OFFS_H 0x77
+#define REG_6500_YA_OFFS_H 0x7A
+#define REG_6500_ZA_OFFS_H 0x7D
+#define REG_6500_ACCEL_CONFIG2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+#define BIT_FIFO_SIZE_1K 0x40
+
+#define REG_6500_LP_ACCEL_ODR 0x1E
+#define REG_6500_ACCEL_WOM_THR 0x1F
+
+#define REG_6500_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_ENABLE 0x80
+#define BIT_ACCEL_INTEL_MODE 0x40
+
+/* data definitions */
+#define DMP_START_ADDR 0x400
+#define DMP_MASK_TAP 0x3f
+#define DMP_MASK_DIS_ORIEN 0xC0
+#define DMP_DIS_ORIEN_SHIFT 6
+/* this is derived from 1000 divided by 50, which is the pedometer
+ running frequency */
+#define MS_PER_PED_TICKS 20
+
+
+#define BYTES_FOR_DMP 8
+#define BYTES_FOR_EVENTS 4
+#define QUATERNION_BYTES 16
+#define BYTES_PER_SENSOR 6
+#define MPU3050_FOOTER_SIZE 2
+#define FIFO_COUNT_BYTE 2
+#define FIFO_THRESHOLD 800
+#define FIFO_SIZE 992
+#define HARDWARE_FIFO_SIZE 1024
+#define MAX_READ_SIZE 64
+#define POWER_UP_TIME 100
+#define SENSOR_UP_TIME 30
+#define REG_UP_TIME 2
+#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
+#define MPU_MEM_BANK_SIZE 256
+#define SELF_TEST_GYRO_FULL_SCALE 250
+#define SELF_TEST_ACCEL_FULL_SCALE 8
+#define SELF_TEST_ACCEL_6500_SCALE 2
+
+/* data header defines */
+#define PRESSURE_HDR 0x8000
+#define ACCEL_HDR 0x4000
+#define GYRO_HDR 0x2000
+#define COMPASS_HDR 0x1000
+#define COMPASS_HDR_2 0x1800
+#define LPQUAT_HDR 0x0800
+#define SIXQUAT_HDR 0x0400
+#define PEDQUAT_HDR 0x0200
+#define STEP_DETECTOR_HDR 0x0100
+#define STEP_INDICATOR_MASK 0xf
+
+#define MAX_BYTES_PER_SAMPLE 80
+#define MAX_HW_FIFO_BYTES (BYTES_PER_SENSOR * 2)
+#define IIO_BUFFER_BYTES 8
+#define HEADERED_NORMAL_BYTES 8
+#define HEADERED_Q_BYTES 16
+
+#define MPU6XXX_MAX_MOTION_THRESH (255*4)
+#define MPU6050_MOTION_THRESH_SHIFT 5
+#define MPU6500_MOTION_THRESH_SHIFT 2
+#define MPU6050_MOTION_DUR_DEFAULT 1
+#define MPU6050_ID 0x68
+#define MPU6050_MAX_MOTION_DUR 255
+#define MPU_TEMP_SHIFT 16
+#define LPA_FREQ_SHIFT 6
+#define COMPASS_RATE_SCALE 10
+#define MAX_GYRO_FS_PARAM 3
+#define MAX_ACCEL_FS_PARAM 3
+#define MAX_LPA_FREQ_PARAM 3
+#define MPU_MAX_A_OFFSET_VALUE 16383
+#define MPU_MIN_A_OFFSET_VALUE -16384
+#define MPU_MAX_G_OFFSET_VALUE 32767
+#define MPU_MIN_G_OFFSET_VALUE -32767
+#define MPU6XXX_MAX_MPU_MEM (256 * 12)
+
+#define INIT_MOT_DUR 128
+#define INIT_MOT_THR 128
+#define INIT_ZMOT_DUR 128
+#define INIT_ZMOT_THR 128
+#define INIT_ST_SAMPLES 200
+#define INIT_ST_MPU6050_SAMPLES 600
+#define INIT_ST_THRESHOLD 50
+#define INIT_PED_INT_THRESH 2
+#define INIT_PED_THRESH 9
+#define ST_THRESHOLD_MULTIPLIER 10
+#define ST_MAX_SAMPLES 500
+#define ST_MAX_THRESHOLD 100
+#define DMP_INTERVAL_INIT (5 * NSEC_PER_MSEC)
+#define DMP_INTERVAL_MIN_ADJ (50 * NSEC_PER_USEC)
+
+/*---- MPU6500 ----*/
+#define MPU6500_ID 0x70 /* unique WHOAMI */
+#define MPU6515_ID 0x74 /* unique WHOAMI */
+#define MPU6500_PRODUCT_REVISION 1
+#define MPU6500_MEM_REV_ADDR 0x16
+#define INV_MPU_REV_MASK 0x0F
+#define MPU6500_REV 2
+#define MPU_DMP_LOAD_START 0x20
+
+/*---- MPU9250 ----*/
+#define MPU9250_ID 0x71 /* unique WHOAMI */
+
+/* ----MPU9350 ----*/
+#define MPU9350_ID 0x72
+
+#define THREE_AXIS 3
+#define GYRO_CONFIG_FSR_SHIFT 3
+#define ACCEL_CONFIG_FSR_SHIFT 3
+#define GYRO_DPS_SCALE 250
+#define MEM_ADDR_PROD_REV 0x6
+#define SOFT_PROD_VER_BYTES 5
+#define CRC_FIRMWARE_SEED 0
+#define SELF_TEST_SUCCESS 1
+#define MS_PER_DMP_TICK 20
+#define DMP_IMAGE_SIZE 2943
+
+/* init parameters */
+#define INIT_FIFO_RATE 50
+#define INIT_DMP_OUTPUT_RATE 25
+#define INIT_DUR_TIME (NSEC_PER_SEC / INIT_FIFO_RATE)
+#define INIT_TAP_THRESHOLD 100
+#define INIT_TAP_TIME 100
+#define INIT_TAP_MIN_COUNT 2
+#define INIT_SAMPLE_DIVIDER 4
+#define MPU_INIT_SMD_DELAY_THLD 3
+#define MPU_INIT_SMD_DELAY2_THLD 1
+#define MPU_INIT_SMD_THLD 1500
+#define MPU_DEFAULT_DMP_FREQ 200
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */
+
+#define BIT_PRFTCH_EN 0x40
+#define BIT_CFG_USER_BANK 0x20
+#define BITS_MEM_SEL 0x1f
+
+#define TIME_STAMP_TOR 5
+#define MAX_CATCH_UP 5
+#define DMP_TICK_DUR 5
+#define DEFAULT_ACCEL_TRIM 16384
+#define DEFAULT_GYRO_TRIM 131
+#define MAX_FIFO_RATE 1000
+#define MAX_DMP_OUTPUT_RATE 200
+#define MIN_FIFO_RATE 4
+#define ONE_K_HZ 1000
+#define NS_PER_MS_SHIFT 20
+#define END_MARKER 0x0010
+#define EMPTY_MARKER 0x0020
+
+/*tap related defines */
+#define INV_TAP 0x08
+#define INV_NUM_TAP_AXES 3
+
+#define INV_TAP_AXIS_X_POS 0x20
+#define INV_TAP_AXIS_X_NEG 0x10
+#define INV_TAP_AXIS_Y_POS 0x08
+#define INV_TAP_AXIS_Y_NEG 0x04
+#define INV_TAP_AXIS_Z_POS 0x02
+#define INV_TAP_AXIS_Z_NEG 0x01
+#define INV_TAP_ALL_DIRECTIONS 0x3f
+
+#define INV_TAP_AXIS_X 0x1
+#define INV_TAP_AXIS_Y 0x2
+#define INV_TAP_AXIS_Z 0x4
+
+#define INV_TAP_AXIS_ALL \
+ (INV_TAP_AXIS_X | \
+ INV_TAP_AXIS_Y | \
+ INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP 0x01
+#define INT_SRC_DISPLAY_ORIENT 0x08
+#define INT_SRC_SHAKE 0x10
+
+#define INV_X_AXIS_INDEX 0x00
+#define INV_Y_AXIS_INDEX 0x01
+#define INV_Z_AXIS_INDEX 0x02
+
+#define INV_ELEMENT_1 0x0001
+#define INV_ELEMENT_2 0x0002
+#define INV_ELEMENT_3 0x0004
+#define INV_ELEMENT_4 0x0008
+#define INV_ELEMENT_5 0x0010
+#define INV_ELEMENT_6 0x0020
+#define INV_ELEMENT_7 0x0040
+#define INV_ELEMENT_8 0x0080
+#define INV_ALL 0xFFFF
+#define INV_ELEMENT_MASK 0x00FF
+#define INV_GYRO_ACC_MASK 0x007E
+#define INV_ACCEL_MASK 0x70
+#define INV_GYRO_MASK 0xE
+
+struct inv_mpu_state;
+
+/**
+ * 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.
+ * @accel_config: accel config register
+ * @fifo_count_h: Upper byte of FIFO count.
+ * @fifo_r_w: FIFO register.
+ * @raw_accel Address of first accel register.
+ * @temperature temperature register
+ * @int_enable: Interrupt enable register.
+ * @int_status: Interrupt flags.
+ * @pwr_mgmt_1: Controls chip's power state and clock source.
+ * @pwr_mgmt_2: Controls power state of individual sensors.
+ * @mem_start_addr: Address of first memory read.
+ * @mem_r_w: Access to memory.
+ * @prgm_strt_addrh firmware program start address register
+ */
+struct inv_reg_map_s {
+ u8 sample_rate_div;
+ u8 lpf;
+ u8 bank_sel;
+ u8 user_ctrl;
+ u8 fifo_en;
+ u8 gyro_config;
+ u8 accel_config;
+ u8 fifo_count_h;
+ u8 fifo_r_w;
+ u8 raw_accel;
+ u8 temperature;
+ u8 int_enable;
+ u8 int_status;
+ u8 pwr_mgmt_1;
+ u8 pwr_mgmt_2;
+ u8 mem_start_addr;
+ u8 mem_r_w;
+ u8 prgm_strt_addrh;
+};
+
+/* device enum */
+enum inv_devices {
+ INV_ITG3500,
+ INV_MPU3050,
+ INV_MPU6050,
+ INV_MPU9150,
+ INV_MPU6500,
+ INV_MPU9250,
+ INV_MPU6XXX,
+ INV_MPU9350,
+ INV_MPU6515,
+ INV_NUM_PARTS
+};
+
+/**
+ * struct inv_hw_s - Other important hardware information.
+ * @num_reg: Number of registers on device.
+ * @name: name of the chip
+ */
+struct inv_hw_s {
+ u8 num_reg;
+ u8 *name;
+};
+
+/* enum for sensor */
+enum INV_SENSORS {
+ SENSOR_GYRO = 0,
+ SENSOR_ACCEL,
+ SENSOR_COMPASS,
+ SENSOR_PRESSURE,
+ SENSOR_STEP,
+ SENSOR_PEDQ,
+ SENSOR_SIXQ,
+ SENSOR_LPQ,
+ SENSOR_NUM_MAX,
+ SENSOR_INVALID,
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @ts: this sensors timestamp.
+ * @dur: duration between samples in ns.
+ * @rate: sensor data rate.
+ * @counter: dmp tick counter corresponding to rate.
+ * @on: sensor on/off.
+ * @sample_size: number of bytes for the sensor.
+ * @send_data: function pointer to send data or not.
+ * @set_rate: funcition pointer to set data rate.
+ */
+struct inv_sensor {
+ u64 ts;
+ int dur;
+ int rate;
+ int counter;
+ bool on;
+ u8 sample_size;
+ int (*send_data)(struct inv_mpu_state *st, bool on);
+ int (*set_rate)(struct inv_mpu_state *st);
+};
+
+/**
+ * struct inv_batch - information batchmode.
+ * @on: derived variable for batch mode.
+ * @overflow_on: overflow mode for batchmode.
+ * @wake_fifo_on: overflow for suspend mode.
+ * @counter: counter for batch mode.
+ * @timeout: nominal timeout value for batchmode in milliseconds.
+ * @min_rate: minimum sensor rate that is turned on.
+ */
+struct inv_batch {
+ bool on;
+ bool overflow_on;
+ bool wake_fifo_on;
+ u32 counter;
+ u32 timeout;
+ u32 min_rate;
+};
+
+/**
+ * struct inv_chip_config_s - Cached chip configuration data.
+ * @fsr: Full scale range.
+ * @lpf: Digital low pass filter frequency.
+ * @accel_fs: accel full scale range.
+ * @has_footer: MPU3050 specific work around.
+ * @has_compass: has compass or not.
+ * @has_pressure: has pressure sensor or not.
+ * @enable: master enable to enable output
+ * @accel_enable: enable accel functionality
+ * @gyro_enable: enable gyro functionality
+ * @is_asleep: 1 if chip is powered down.
+ * @dmp_on: dmp is on/off.
+ * @dmp_int_on: dmp interrupt on/off.
+ * @step_indicator_on: step indicate bit added to the sensor or not.
+ * @dmp_event_int_on: dmp event interrupt on/off.
+ * @firmware_loaded: flag indicate firmware loaded or not.
+ * @lpa_mod: low power mode.
+ * @display_orient_on: display orientation on/off.
+ * @normal_compass_measure: discard first compass data after reset.
+ * @normal_pressure_measure: discard first pressure data after reset.
+ * @smd_enable: disable/enable SMD function.
+ * @adjust_time: flag to indicate whether adjust chip clock or not.
+ * @smd_triggered: smd is triggered.
+ * @lpa_freq: low power frequency
+ * @prog_start_addr: firmware program start address.
+ * @fifo_rate: current FIFO update rate.
+ * @bytes_per_datum: number of bytes for 1 sample data.
+ */
+struct inv_chip_config_s {
+ u32 fsr:2;
+ u32 lpf:3;
+ u32 accel_fs:2;
+ u32 has_footer:1;
+ u32 has_compass:1;
+ u32 has_pressure:1;
+ u32 enable:1;
+ u32 accel_enable:1;
+ u32 gyro_enable:1;
+ u32 is_asleep:1;
+ u32 dmp_on:1;
+ u32 dmp_int_on:1;
+ u32 dmp_event_int_on:1;
+ u32 step_indicator_on:1;
+ u32 firmware_loaded:1;
+ u32 lpa_mode:1;
+ u32 display_orient_on:1;
+ u32 normal_compass_measure:1;
+ u32 normal_pressure_measure:1;
+ u32 smd_enable:1;
+ u32 adjust_time:1;
+ u32 smd_triggered:1;
+ u16 lpa_freq;
+ u16 prog_start_addr;
+ u16 fifo_rate;
+ u16 bytes_per_datum;
+};
+
+/**
+ * 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.
+ * @accel_sens_trim: accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+ u8 product_id;
+ u8 product_revision;
+ u8 silicon_revision;
+ u8 software_revision;
+ u8 multi;
+ u8 compass_sens[3];
+ u32 gyro_sens_trim;
+ u32 accel_sens_trim;
+};
+
+/**
+ * struct inv_tap structure to store tap data.
+ * @min_count: minimum taps counted.
+ * @thresh: tap threshold.
+ * @time: tap time.
+ * @on: tap on/off.
+ */
+struct inv_tap {
+ u16 min_count;
+ u16 thresh;
+ u16 time;
+ bool on;
+};
+
+/**
+ * struct accel_mot_int_s structure to store motion interrupt data
+ * @mot_thr: motion threshold.
+ * @mot_dur: motion duration.
+ * @mot_on: flag to indicate motion detection on;
+ */
+struct accel_mot_int {
+ u16 mot_thr;
+ u32 mot_dur;
+ u8 mot_on:1;
+};
+
+/**
+ * struct self_test_setting - self test settables from sysfs
+ * samples: number of samples used in self test.
+ * threshold: threshold fail/pass criterion in self test.
+ * This value is in the percentage multiplied by 100.
+ * So 14% would be 14.
+ */
+struct self_test_setting {
+ u16 samples;
+ u16 threshold;
+};
+
+/**
+ * struct inv_smd significant motion detection structure.
+ * @threshold: accel threshold for motion detection.
+ * @delay: delay time to confirm 2nd motion.
+ * @delay2: delay window parameter.
+ */
+struct inv_smd {
+ u32 threshold;
+ u32 delay;
+ u32 delay2;
+};
+
+/**
+ * struct inv_ped pedometer related data structure.
+ * @step: steps taken.
+ * @time: time taken during the period.
+ * @last_step_time: last time the step is taken.
+ * @step_thresh: step threshold to show steps.
+ * @int_thresh: step threshold to generate interrupt.
+ * @int_on: pedometer interrupt enable/disable.
+ * @on: pedometer on/off.
+ */
+struct inv_ped {
+ u64 step;
+ u64 time;
+ u64 last_step_time;
+ u16 step_thresh;
+ u16 int_thresh;
+ bool int_on;
+ bool on;
+};
+
+struct inv_mpu_slave;
+/**
+ * struct inv_mpu_state - Driver state variables.
+ * @chip_config: Cached attribute information.
+ * @chip_info: Chip information from read-only registers.
+ * @trig; iio trigger.
+ * @tap: tap data structure.
+ * @smd: SMD data structure.
+ * @ped: pedometer data structure.
+ * @batch: batchmode data structure.
+ * @reg: Map of important registers.
+ * @self_test: self test settings.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @time_stamp_lock: spin lock to time stamp.
+ * @suspend_resume_lock: mutex lock for suspend/resume.
+ * @client: i2c client handle.
+ * @plat_data: platform data.
+ * @slave_accel: mpu slave handle for accelerometer(MPU3050 only).
+ * @slave_compass: mpu slave handle for magnetometer.
+ * @slave_pressure: mpu slave handle for pressure sensor.
+ * (*set_power_state)(struct inv_mpu_state *, int on): function ptr
+ * (*switch_gyro_engine)(struct inv_mpu_state *, int on): function ptr
+ * (*switch_accel_engine)(struct inv_mpu_state *, int on): function ptr
+ * (*init_config)(struct iio_dev *indio_dev): function ptr
+ * (*setup_reg)(struct inv_reg_map_s *reg): function ptr
+ * @timestamps: kfifo queue to store time stamp.
+ * @irq: irq number store.
+ * @accel_bias: accel bias store.
+ * @gyro_bias: gyro bias store.
+ * @input_gyro_offset[3]: gyro offset from sysfs.
+ * @input_accel_offset[3]: accel offset from sysfs.
+ * @input_accel_dmp_bias[3]: accel bias for dmp.
+ * @input_gyro_dmp_bias[3];: gyro bias for dmp.
+ * @rom_gyro_bias[3]: gyro bias from sysfs.
+ * @rom_accel_bias[3]: accel bias from sysfs.
+ * @fifo_data[6]: fifo data storage.
+ * @i2c_addr: i2c address.
+ * @sample_divider: sample divider for dmp.
+ * @fifo_divider: fifo divider for dmp.
+ * @display_orient_data:display orient data.
+ * @tap_data: tap data.
+ * @bytes_per_sec: bytes per seconds when in DMP mode.
+ * @left_over[HEADERED_Q_BYTES]: left over bytes storage.
+ * @left_over_size: left over size.
+ * @sensor{SENSOR_NUM_MAX]: sensor individual properties.
+ * @fifo_count: current fifo_count;
+ * @dmp_counter: dmp_counter;
+ * @dmp_ticks: DMP ticks past since the previous read.
+ * @sl_handle: Handle to I2C port.
+ * @irq_dur_ns: duration between each irq.
+ * @ts_counter: time stamp counter.
+ * @suspend_state: state indicator suspend.
+ * @dmp_interval: dmp interval. nomial value is 5 ms.
+ * @dmp_interval_accum: dmp interval accumlater.
+ * @diff_accumulater: accumlator for the difference of nominal and actual.
+ * @last_ts: last time stamp.
+ * @step_detector_base_ts: base time stamp for step detector calculation.
+ * @prev_ts: previous time stamp.
+ * @pedometer_step: pedometer steps stored in driver.
+ * @pedometer_time: pedometer time stored in driver.
+ * @last_run_time: last time the post ISR runs.
+ * @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX.
+ * @secondary_name: name for the slave device in the secondary I2C.
+ */
+struct inv_mpu_state {
+#define TIMESTAMP_FIFO_SIZE 64
+ struct inv_chip_config_s chip_config;
+ struct inv_chip_info_s chip_info;
+ struct iio_trigger *trig;
+ struct inv_tap tap;
+ struct inv_smd smd;
+ struct inv_ped ped;
+ struct inv_batch batch;
+ struct inv_reg_map_s reg;
+ struct self_test_setting self_test;
+ const struct inv_hw_s *hw;
+ enum inv_devices chip_type;
+ spinlock_t time_stamp_lock;
+ struct mutex suspend_resume_lock;
+ struct i2c_client *client;
+ struct mpu_platform_data plat_data;
+ struct inv_mpu_slave *slave_accel;
+ struct inv_mpu_slave *slave_compass;
+ struct inv_mpu_slave *slave_pressure;
+ struct accel_mot_int mot_int;
+ int (*set_power_state)(struct inv_mpu_state *, bool on);
+ int (*switch_gyro_engine)(struct inv_mpu_state *, bool on);
+ int (*switch_accel_engine)(struct inv_mpu_state *, bool on);
+ int (*init_config)(struct iio_dev *indio_dev);
+ void (*setup_reg)(struct inv_reg_map_s *reg);
+ DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE);
+ short irq;
+ int accel_bias[3];
+ int gyro_bias[3];
+ s16 input_gyro_offset[3];
+ s16 input_accel_offset[3];
+ int input_accel_dmp_bias[3];
+ int input_gyro_dmp_bias[3];
+ s16 rom_gyro_offset[3];
+ s16 rom_accel_offset[3];
+ u8 fifo_data[6];
+ u8 i2c_addr;
+ u8 sample_divider;
+ u8 fifo_divider;
+ u8 display_orient_data;
+ u8 tap_data;
+ u16 bytes_per_sec;
+ u8 left_over[HEADERED_Q_BYTES];
+ u32 left_over_size;
+ struct inv_sensor sensor[SENSOR_NUM_MAX];
+ u32 fifo_count;
+ u32 dmp_counter;
+ u32 dmp_ticks;
+ void *sl_handle;
+ u32 irq_dur_ns;
+ u32 ts_counter;
+ bool suspend_state;
+ u32 dmp_interval;
+ s32 dmp_interval_accum;
+ s64 diff_accumulater;
+ u64 last_ts;
+ u64 step_detector_base_ts;
+ u64 prev_ts;
+ u64 last_run_time;
+ u8 name[20];
+ u8 secondary_name[20];
+};
+
+/* produces an unique identifier for each device based on the
+ combination of product version and product revision */
+struct prod_rev_map_t {
+ u16 mpl_product_key;
+ u8 silicon_rev;
+ u16 gyro_trim;
+ u16 accel_trim;
+};
+
+/**
+ * struct inv_mpu_slave - MPU slave structure.
+ * @st_upper: compass self test upper limit.
+ * @st_lower: compass self test lower limit.
+ * @scale: compass scale.
+ * @rate_scale: decide how fast a compass can read.
+ * @min_read_time: minimum time between each reading.
+ * @self_test: self test method of the slave.
+ * @set_scale: set scale of slave
+ * @get_scale: read scale back of the slave.
+ * @suspend: suspend operation.
+ * @resume: resume operation.
+ * @setup: setup chip. initialization.
+ * @combine_data: combine raw data into meaningful data.
+ * @read_data: read external sensor and output
+ * @get_mode: get current chip mode.
+ * @set_lpf: set low pass filter.
+ * @set_fs: set full scale
+ * @prev_ts: last time it is read.
+ */
+struct inv_mpu_slave {
+ const short *st_upper;
+ const short *st_lower;
+ int scale;
+ int rate_scale;
+ int min_read_time;
+ int (*self_test)(struct inv_mpu_state *);
+ int (*set_scale)(struct inv_mpu_state *, int scale);
+ int (*get_scale)(struct inv_mpu_state *, int *val);
+ int (*suspend)(struct inv_mpu_state *);
+ int (*resume)(struct inv_mpu_state *);
+ int (*setup)(struct inv_mpu_state *);
+ int (*combine_data)(u8 *in, short *out);
+ int (*read_data)(struct inv_mpu_state *, short *out);
+ int (*get_mode)(void);
+ int (*set_lpf)(struct inv_mpu_state *, int rate);
+ int (*set_fs)(struct inv_mpu_state *, int fs);
+ u64 prev_ts;
+};
+
+/* 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_ACCEL_X,
+ INV_MPU_SCAN_ACCEL_Y,
+ INV_MPU_SCAN_ACCEL_Z,
+ INV_MPU_SCAN_GYRO_X,
+ INV_MPU_SCAN_GYRO_Y,
+ INV_MPU_SCAN_GYRO_Z,
+ INV_MPU_SCAN_MAGN_X,
+ INV_MPU_SCAN_MAGN_Y,
+ INV_MPU_SCAN_MAGN_Z,
+ INV_MPU_SCAN_TIMESTAMP,
+};
+
+enum inv_filter_e {
+ INV_FILTER_256HZ_NOLPF2 = 0,
+ INV_FILTER_188HZ,
+ INV_FILTER_98HZ,
+ INV_FILTER_42HZ,
+ INV_FILTER_20HZ,
+ INV_FILTER_10HZ,
+ INV_FILTER_5HZ,
+ INV_FILTER_2100HZ_NOLPF,
+ NUM_FILTER
+};
+
+enum inv_slave_mode {
+ INV_MODE_SUSPEND,
+ INV_MODE_NORMAL,
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+ MEM_RAM_BANK_0 = 0,
+ MEM_RAM_BANK_1,
+ MEM_RAM_BANK_2,
+ MEM_RAM_BANK_3,
+ MEM_RAM_BANK_4,
+ MEM_RAM_BANK_5,
+ MEM_RAM_BANK_6,
+ MEM_RAM_BANK_7,
+ MEM_RAM_BANK_8,
+ MEM_RAM_BANK_9,
+ MEM_RAM_BANK_10,
+ MEM_RAM_BANK_11,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = 16
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+ ATTR_DMP_GYRO_X_DMP_BIAS,
+ ATTR_DMP_GYRO_Y_DMP_BIAS,
+ ATTR_DMP_GYRO_Z_DMP_BIAS,
+ ATTR_DMP_ACCEL_X_DMP_BIAS,
+ ATTR_DMP_ACCEL_Y_DMP_BIAS,
+ ATTR_DMP_ACCEL_Z_DMP_BIAS,
+ ATTR_DMP_PED_INT_ON,
+ ATTR_DMP_PED_STEP_THRESH,
+ ATTR_DMP_PED_INT_THRESH,
+ ATTR_DMP_PED_ON,
+ ATTR_DMP_SMD_ENABLE,
+ ATTR_DMP_SMD_THLD,
+ ATTR_DMP_SMD_DELAY_THLD,
+ ATTR_DMP_SMD_DELAY_THLD2,
+ ATTR_DMP_PEDOMETER_STEPS,
+ ATTR_DMP_PEDOMETER_TIME,
+ ATTR_DMP_PEDOMETER_COUNTER,
+ ATTR_DMP_TAP_ON,
+ ATTR_DMP_TAP_THRESHOLD,
+ ATTR_DMP_TAP_MIN_COUNT,
+ ATTR_DMP_TAP_TIME,
+ ATTR_DMP_DISPLAY_ORIENTATION_ON,
+/* *****above this line, are DMP features, power needs on/off */
+/* *****below this line, are DMP features, no power needed */
+ ATTR_DMP_ON,
+ ATTR_DMP_INT_ON,
+ ATTR_DMP_EVENT_INT_ON,
+ ATTR_DMP_STEP_INDICATOR_ON,
+ ATTR_DMP_BATCHMODE_TIMEOUT,
+ ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL,
+ ATTR_DMP_SIX_Q_ON,
+ ATTR_DMP_SIX_Q_RATE,
+ ATTR_DMP_LPQ_ON,
+ ATTR_DMP_LPQ_RATE,
+ ATTR_DMP_PED_Q_ON,
+ ATTR_DMP_PED_Q_RATE,
+ ATTR_DMP_STEP_DETECTOR_ON,
+ ATTR_DMP_STEP_DETECTOR_RATE,
+/* *****above this line, it is all DMP related features */
+/* *****below this line, it is all non-DMP related features */
+ ATTR_GYRO_SCALE,
+ ATTR_ACCEL_SCALE,
+ ATTR_COMPASS_SCALE,
+ ATTR_GYRO_X_OFFSET,
+ ATTR_GYRO_Y_OFFSET,
+ ATTR_GYRO_Z_OFFSET,
+ ATTR_ACCEL_X_OFFSET,
+ ATTR_ACCEL_Y_OFFSET,
+ ATTR_ACCEL_Z_OFFSET,
+ ATTR_MOTION_LPA_ON,
+ ATTR_MOTION_LPA_FREQ,
+ ATTR_MOTION_LPA_THRESHOLD,
+/* *****above this line, it is non-DMP, power needs on/off */
+/* *****below this line, it is non-DMP, no needs to on/off power */
+ ATTR_SELF_TEST_SAMPLES,
+ ATTR_SELF_TEST_THRESHOLD,
+ ATTR_GYRO_ENABLE,
+ ATTR_GYRO_FIFO_ENABLE,
+ ATTR_GYRO_RATE,
+ ATTR_ACCEL_ENABLE,
+ ATTR_ACCEL_FIFO_ENABLE,
+ ATTR_ACCEL_RATE,
+ ATTR_COMPASS_ENABLE,
+ ATTR_COMPASS_RATE,
+ ATTR_PRESSURE_ENABLE,
+ ATTR_PRESSURE_RATE,
+ ATTR_POWER_STATE, /* this is fake sysfs for compatibility */
+ ATTR_FIRMWARE_LOADED,
+ ATTR_SAMPLING_FREQ,
+/* *****below this line, it is attributes only has show methods */
+ ATTR_SELF_TEST, /* this has show-only methods needs power on/off */
+ ATTR_GYRO_X_CALIBBIAS,
+ ATTR_GYRO_Y_CALIBBIAS,
+ ATTR_GYRO_Z_CALIBBIAS,
+ ATTR_ACCEL_X_CALIBBIAS,
+ ATTR_ACCEL_Y_CALIBBIAS,
+ ATTR_ACCEL_Z_CALIBBIAS,
+ ATTR_SELF_TEST_GYRO_SCALE,
+ ATTR_SELF_TEST_ACCEL_SCALE,
+ ATTR_GYRO_MATRIX,
+ ATTR_ACCEL_MATRIX,
+ ATTR_COMPASS_MATRIX,
+ ATTR_SECONDARY_NAME,
+#ifdef CONFIG_INV_TESTING
+ ATTR_COMPASS_SENS,
+ ATTR_I2C_COUNTERS,
+ ATTR_REG_WRITE,
+ ATTR_DEBUG_SMD_ENABLE_TESTP1,
+ ATTR_DEBUG_SMD_ENABLE_TESTP2,
+ ATTR_DEBUG_SMD_EXE_STATE,
+ ATTR_DEBUG_SMD_DELAY_CNTR,
+ ATTR_DEBUG_GYRO_COUNTER,
+ ATTR_DEBUG_ACCEL_COUNTER,
+ ATTR_DEBUG_COMPASS_COUNTER,
+ ATTR_DEBUG_PRESSURE_COUNTER,
+ ATTR_DEBUG_LPQ_COUNTER,
+ ATTR_DEBUG_SIXQ_COUNTER,
+ ATTR_DEBUG_PEDQ_COUNTER,
+#endif
+};
+
+enum inv_accel_fs_e {
+ INV_FS_02G = 0,
+ INV_FS_04G,
+ INV_FS_08G,
+ INV_FS_16G,
+ NUM_ACCEL_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);
+ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos, size_t size);
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+int inv_init_config_mpu3050(struct iio_dev *indio_dev);
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st);
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st);
+int set_3050_bypass(struct inv_mpu_state *st, bool enable);
+int inv_register_mpu3050_slave(struct inv_mpu_state *st);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en);
+int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en);
+int set_power_mpu3050(struct inv_mpu_state *st, bool power_on);
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on);
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on);
+u16 inv_dmp_get_address(u16 key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold);
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data);
+int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps);
+int inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time);
+int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on);
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data);
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data);
+int inv_hw_self_test(struct inv_mpu_state *st);
+s64 get_time_ns(void);
+int write_be32_key_to_mem(struct inv_mpu_state *st,
+ u32 data, int key);
+int inv_set_accel_bias_dmp(struct inv_mpu_state *st);
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st);
+int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data);
+int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data);
+int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len,
+ u8 const *d);
+int inv_quaternion_on(struct inv_mpu_state *st,
+ struct iio_buffer *ring, bool en);
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en);
+int inv_read_pedometer_counter(struct inv_mpu_state *st);
+int inv_enable_pedometer(struct inv_mpu_state *st, bool en);
+int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps);
+int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time);
+int inv_reset_fifo(struct iio_dev *indio_dev);
+int inv_reset_offset_reg(struct inv_mpu_state *st, bool en);
+int inv_batchmode_setup(struct inv_mpu_state *st);
+void inv_init_sensor_struct(struct inv_mpu_state *st);
+int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume);
+int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data);
+int set_inv_enable(struct iio_dev *indio_dev, bool enable);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+#define mem_w(a, b, c) \
+ mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c)
+#define inv_i2c_read(st, reg, len, data) \
+ inv_i2c_read_base(st, st->i2c_addr, reg, len, data)
+#define inv_i2c_single_write(st, reg, data) \
+ inv_i2c_single_write_base(st, st->i2c_addr, reg, data)
+#define inv_secondary_read(reg, len, data) \
+ inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data)
+#define inv_secondary_write(reg, data) \
+ inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \
+ reg, data)
+#define inv_aux_read(reg, len, data) \
+ inv_i2c_read_base(st, st->plat_data.aux_i2c_addr, reg, len, data)
+#define inv_aux_write(reg, data) \
+ inv_i2c_single_write_base(st, st->plat_data.aux_i2c_addr, \
+ reg, data)
+
+#endif /* #ifndef _INV_MPU_IIO_H_ */
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c
new file mode 100644
index 00000000000..73b883679c9
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c
@@ -0,0 +1,2041 @@
+/*
+* 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 <linux/crc32.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_test/inv_counters.h"
+
+/* DMP defines */
+#define DMP_ORIENTATION_TIME 500
+#define DMP_ORIENTATION_ANGLE 60
+#define DMP_DEFAULT_FIFO_RATE 200
+#define DMP_TAP_SCALE (767603923 / 5)
+#define DMP_MULTI_SHIFT 30
+#define DMP_MULTI_TAP_TIME 500
+#define DMP_SHAKE_REJECT_THRESH 100
+#define DMP_SHAKE_REJECT_TIME 10
+#define DMP_SHAKE_REJECT_TIMEOUT 10
+#define DMP_ANGLE_SCALE 15
+#define DMP_PRECISION 1000
+#define DMP_MAX_DIVIDER 4
+#define DMP_MAX_MIN_TAPS 4
+#define DMP_IMAGE_CRC_VALUE 0xa7e2110d
+
+/*--- Test parameters defaults --- */
+#define DEF_OLDEST_SUPP_PROD_REV 8
+#define DEF_OLDEST_SUPP_SW_REV 2
+
+/* sample rate */
+#define DEF_SELFTEST_SAMPLE_RATE 0
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FS (0 << 3)
+#define DEF_SELFTEST_ACCEL_FS (2 << 3)
+#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME 10
+#define DEF_ST_STABLE_TIME 20
+#define DEF_ST_6500_STABLE_TIME 20
+#define DEF_GYRO_SCALE 131
+#define DEF_ST_PRECISION 1000
+#define DEF_ST_ACCEL_FS_MG 8000UL
+#define DEF_ST_SCALE (1L << 15)
+#define DEF_ST_TRY_TIMES 2
+#define DEF_ST_COMPASS_RESULT_SHIFT 2
+#define DEF_ST_ACCEL_RESULT_SHIFT 1
+#define DEF_ST_OTP0_THRESH 60
+#define DEF_ST_ABS_THRESH 20
+#define DEF_ST_TOR 2
+
+#define X 0
+#define Y 1
+#define Z 2
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5 105
+#define MPU_PRODUCT_KEY_B2_F1 431
+/* accelerometer Hw self test min and max bias shift (mg) */
+#define DEF_ACCEL_ST_SHIFT_MIN 300
+#define DEF_ACCEL_ST_SHIFT_MAX 950
+
+#define DEF_ACCEL_ST_SHIFT_DELTA 500
+#define DEF_GYRO_CT_SHIFT_DELTA 500
+/* gyroscope Coriolis self test min and max bias shift (dps) */
+#define DEF_GYRO_CT_SHIFT_MIN 10
+#define DEF_GYRO_CT_SHIFT_MAX 105
+
+/*---- MPU6500 Self Test Pass/Fail Criteria ----*/
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX 20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL 60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN 225
+#define DEF_ACCEL_ST_AL_MAX 675
+#define DEF_6500_ACCEL_ST_SHIFT_DELTA 500
+#define DEF_6500_GYRO_CT_SHIFT_DELTA 500
+#define DEF_ST_MPU6500_ACCEL_LPF 2
+#define DEF_ST_6500_ACCEL_FS_MG 2000UL
+#define DEF_SELFTEST_6500_ACCEL_FS (0 << 3)
+
+/* Note: The ST_AL values are only used when ST_OTP = 0,
+ * i.e no factory self test values for reference
+ */
+
+/* NOTE: product entries are in chronological order */
+static const struct prod_rev_map_t prod_rev_map[] = {
+ /* prod_ver = 0 */
+ {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 2 */
+ {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 3 */
+ {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 4 */
+ {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192},
+ /* prod_ver = 5 */
+ {MPL_PROD_KEY(5, 3), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 6 */
+ {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 7 */
+ {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 8 */
+ {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 9 */
+ {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 10 */
+ {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}
+};
+
+/*
+* List of product software revisions
+*
+* NOTE :
+* software revision 0 falls back to the old detection method
+* based off the product version and product revision per the
+* table above
+*/
+static const struct prod_rev_map_t sw_rev_map[] = {
+ {0, 0, 0, 0},
+ {1, MPU_SILICON_REV_B1, 131, 8192}, /* rev C */
+ {2, MPU_SILICON_REV_B1, 131, 16384} /* rev D */
+};
+
+static const u16 mpu_6500_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+static const int accel_st_tb[31] = {
+ 340, 351, 363, 375, 388, 401, 414, 428,
+ 443, 458, 473, 489, 506, 523, 541, 559,
+ 578, 597, 617, 638, 660, 682, 705, 729,
+ 753, 779, 805, 832, 860, 889, 919
+};
+
+static const int gyro_6050_st_tb[31] = {
+ 3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486,
+ 4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429,
+ 6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213,
+ 9637, 10080, 10544, 11029, 11537, 12067, 12622
+};
+
+static const int gyro_3500_st_tb[255] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string)
+{
+ int ii;
+ string = kmalloc(len * 2 + 1, GFP_KERNEL);
+ for (ii = 0; ii < len; ii++)
+ sprintf(&string[ii * 2], "%02X", data[ii]);
+ string[len * 2] = 0;
+ return string;
+}
+
+char *wr_pr_debug_end(char *string)
+{
+ kfree(string);
+ return "";
+}
+
+int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = REG_MEM_RW;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (u8 *)buf;
+ msgs[2].len = len + 1;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *write = 0;
+ pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+ mpu_addr, bank[1], addr[1],
+ wr_pr_debug_begin(data, len, write),
+ wr_pr_debug_end(write),
+ len);
+ }
+#endif
+
+ res = i2c_transfer(st->sl_handle, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = REG_MEM_RW;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(st->sl_handle, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + 3);
+ INV_I2C_INC_MPUREAD(len);
+#if CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+ mpu_addr, bank[1], addr[1], len,
+ wr_pr_debug_begin(data, len, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+
+ return res;
+}
+
+int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len,
+ u8 const *d)
+{
+ u32 addr;
+ int start, end;
+ int len1, len2;
+ int result = 0;
+
+ if (len > MPU_MEM_BANK_SIZE)
+ return -EINVAL;
+ addr = inv_dmp_get_address(key);
+ if (addr > MPU6XXX_MAX_MPU_MEM)
+ return -EINVAL;
+
+ start = (addr >> 8);
+ end = ((addr + len - 1) >> 8);
+ if (start == end) {
+ result = mpu_memory_write(st, st->i2c_addr, addr, len, d);
+ } else {
+ end <<= 8;
+ len1 = end - addr;
+ len2 = len - len1;
+ result = mpu_memory_write(st, st->i2c_addr, addr, len1, d);
+ result |= mpu_memory_write(st, st->i2c_addr, end, len2,
+ d + len1);
+ }
+
+ return result;
+}
+
+/**
+ * index_of_key()- Inverse lookup of the index of an MPL product key .
+ * @key: the MPL product indentifier also referred to as 'key'.
+ */
+static short index_of_key(u16 key)
+{
+ int i;
+ for (i = 0; i < NUM_OF_PROD_REVS; i++)
+ if (prod_rev_map[i].mpl_product_key == key)
+ return (short)i;
+ return -EINVAL;
+}
+
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st)
+{
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ int result;
+ u8 whoami, sw_rev;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami);
+ if (result)
+ return result;
+ if (whoami != MPU6500_ID && whoami != MPU9250_ID &&
+ whoami != MPU9350_ID && whoami != MPU6515_ID)
+ return -EINVAL;
+
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr,
+ MPU6500_MEM_REV_ADDR, 1, &sw_rev);
+ sw_rev &= INV_MPU_REV_MASK;
+ if (result)
+ return result;
+ if (sw_rev != 0)
+ return -EINVAL;
+ /* these values are place holders and not real values */
+ chip_info->product_id = MPU6500_PRODUCT_REVISION;
+ chip_info->product_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->silicon_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM;
+ chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM;
+ chip_info->multi = 1;
+
+ return 0;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+ u8 prod_ver = 0x00, prod_rev = 0x00;
+ struct prod_rev_map_t *p_rev;
+ u8 bank =
+ (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+ u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV);
+ u16 key;
+ u8 regs[5];
+ u16 sw_rev;
+ short index;
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ reg = &st->reg;
+
+ result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver);
+ if (result)
+ return result;
+ prod_ver &= 0xf;
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr, mem_addr, 1, &prod_rev);
+ if (result)
+ return result;
+ prod_rev >>= 2;
+ /* clean the prefetch and cfg user bank bits */
+ result = inv_i2c_single_write(st, reg->bank_sel, 0);
+ if (result)
+ return result;
+ /* get the software-product version, read from XA_OFFS_L */
+ result = inv_i2c_read(st, REG_XA_OFFS_L_TC,
+ SOFT_PROD_VER_BYTES, regs);
+ if (result)
+ return result;
+
+ sw_rev = (regs[4] & 0x01) << 2 | /* 0x0b, bit 0 */
+ (regs[2] & 0x01) << 1 | /* 0x09, bit 0 */
+ (regs[0] & 0x01); /* 0x07, bit 0 */
+ /* if 0, use the product key to determine the type of part */
+ if (sw_rev == 0) {
+ key = MPL_PROD_KEY(prod_ver, prod_rev);
+ if (key == 0)
+ return -EINVAL;
+ index = index_of_key(key);
+ if (index < 0 || index >= NUM_OF_PROD_REVS)
+ return -EINVAL;
+ /* check MPL is compiled for this device */
+ if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+ return -EINVAL;
+ p_rev = (struct prod_rev_map_t *)&prod_rev_map[index];
+ /* if valid, use the software product key */
+ } else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+ p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev];
+ } else {
+ return -EINVAL;
+ }
+ chip_info->product_id = prod_ver;
+ chip_info->product_revision = prod_rev;
+ chip_info->silicon_revision = p_rev->silicon_rev;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = p_rev->gyro_trim;
+ chip_info->accel_sens_trim = p_rev->accel_trim;
+ if (chip_info->accel_sens_trim == 0)
+ chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM;
+ chip_info->multi = DEFAULT_ACCEL_TRIM / chip_info->accel_sens_trim;
+ if (chip_info->multi != 1)
+ pr_info("multi is %d\n", chip_info->multi);
+ return result;
+}
+
+/**
+ * read_accel_hw_self_test_prod_shift()- read the accelerometer hardware
+ * self-test bias shift calculated
+ * during final production test and
+ * stored in chip non-volatile memory.
+ * @st: main data structure.
+ * @st_prod: A pointer to an array of 3 elements to hold the values
+ * for production hardware self-test bias shifts returned to the
+ * user.
+ * @accel_sens: accel sensitivity.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_mpu_state *st,
+ int *st_prod, int *accel_sens)
+{
+ u8 regs[4];
+ u8 shift_code[3];
+ int result, i;
+
+ for (i = 0; i < 3; i++)
+ st_prod[i] = 0;
+
+ result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+ if ((0 == regs[0]) && (0 == regs[1]) &&
+ (0 == regs[2]) && (0 == regs[3]))
+ return -EINVAL;
+ shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4);
+ shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2);
+ shift_code[Z] = ((regs[2] & 0xE0) >> 3) | (regs[3] & 0x03);
+ for (i = 0; i < 3; i++)
+ if (shift_code[i] != 0)
+ st_prod[i] = accel_sens[i] *
+ accel_st_tb[shift_code[i] - 1];
+
+ return 0;
+}
+
+/**
+* inv_check_accel_self_test()- check accel self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int gravity, j, ret_val;
+ int tmp;
+ int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS];
+ int st_shift_ratio[THREE_AXIS];
+ int accel_sens[THREE_AXIS];
+
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+ ret_val = 0;
+ tmp = DEF_ST_SCALE * DEF_ST_PRECISION / DEF_ST_ACCEL_FS_MG;
+ for (j = 0; j < 3; j++)
+ accel_sens[j] = tmp;
+
+ if (MPL_PROD_KEY(st->chip_info.product_id,
+ st->chip_info.product_revision) ==
+ MPU_PRODUCT_KEY_B1_E1_5) {
+ /* half sensitivity Z accelerometer parts */
+ accel_sens[Z] /= 2;
+ } else {
+ /* half sensitivity X, Y, Z accelerometer parts */
+ accel_sens[X] /= st->chip_info.multi;
+ accel_sens[Y] /= st->chip_info.multi;
+ accel_sens[Z] /= st->chip_info.multi;
+ }
+ gravity = accel_sens[Z];
+ ret_val = read_accel_hw_self_test_prod_shift(st, st_shift_prod,
+ accel_sens);
+ if (ret_val)
+ return ret_val;
+
+ for (j = 0; j < 3; j++) {
+ st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+ if (st_shift_prod[j]) {
+ tmp = st_shift_prod[j] / DEF_ST_PRECISION;
+ st_shift_ratio[j] = abs(st_shift_cust[j] / tmp
+ - DEF_ST_PRECISION);
+ if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ } else {
+ if (st_shift_cust[j] <
+ DEF_ACCEL_ST_SHIFT_MIN * gravity)
+ ret_val = 1;
+ if (st_shift_cust[j] >
+ DEF_ACCEL_ST_SHIFT_MAX * gravity)
+ ret_val = 1;
+ }
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_3500_gyro_self_test() check gyro self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+
+static int inv_check_3500_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int gst[3], ret_val;
+ int gst_otp[3], i;
+ u8 st_code[THREE_AXIS];
+ ret_val = 0;
+
+ for (i = 0; i < 3; i++)
+ gst[i] = st_avg[i] - reg_avg[i];
+ result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code);
+ if (result)
+ return result;
+ gst_otp[0] = 0;
+ gst_otp[1] = 0;
+ gst_otp[2] = 0;
+ for (i = 0; i < 3; i++) {
+ if (st_code[i] != 0)
+ gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1];
+ }
+ /* check self test value passing criterion. Using the DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (gst_otp[i] == 0) {
+ if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH *
+ DEF_ST_PRECISION *
+ DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ } else {
+ if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) >
+ DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val |= (1 << i);
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6050_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int ret_val;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs);
+ if (result)
+ return result;
+ regs[X] &= 0x1f;
+ regs[Y] &= 0x1f;
+ regs[Z] &= 0x1f;
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0)
+ st_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+ else
+ st_shift_prod[i] = 0;
+ }
+ st_shift_prod[1] = -st_shift_prod[1];
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (st_shift_prod[i]) {
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val = 1;
+ } else {
+ if (st_shift_cust[i] < DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MIN * DEF_SELFTEST_GYRO_SENS)
+ ret_val = 1;
+ if (st_shift_cust[i] > DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MAX * DEF_SELFTEST_GYRO_SENS)
+ ret_val = 1;
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val = 1;
+
+ return ret_val;
+}
+
+/**
+* inv_check_6500_gyro_self_test() - check 6500 gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6500_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ u8 regs[3];
+ int ret_val, result;
+ int otp_value_zero = 0;
+ int st_shift_prod[3], st_shift_cust[3], i;
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_6500_XG_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ if (st_shift_cust[i] < DEF_6500_GYRO_CT_SHIFT_DELTA
+ * st_shift_prod[i])
+ ret_val = 1;
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ if (st_shift_cust[i] < DEF_GYRO_ST_AL *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ if (ret_val == 0) {
+ /* Self Test Pass/Fail Criteria C */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_6500_accel_self_test() - check 6500 accel self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6500_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ int ret_val, result;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+ int otp_value_zero = 0;
+
+#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
+ / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
+ / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_6500_XA_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_6500_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ }
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
+ if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
+ st_shift_cust[i] > ACCEL_ST_AL_MAX)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ return ret_val;
+}
+
+/*
+ * inv_do_test() - do the actual test of self testing
+ */
+static int inv_do_test(struct inv_mpu_state *st, int self_test_flag,
+ int *gyro_result, int *accel_result)
+{
+ struct inv_reg_map_s *reg;
+ int result, i, j, packet_size;
+ u8 data[BYTES_PER_SENSOR * 2], d;
+ bool has_accel;
+ int fifo_count, packet_count, ind, s;
+
+ reg = &st->reg;
+ has_accel = (st->chip_type != INV_ITG3500);
+ if (has_accel)
+ packet_size = BYTES_PER_SENSOR * 2;
+ else
+ packet_size = BYTES_PER_SENSOR;
+
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* setup parameters */
+ result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_98HZ);
+ if (result)
+ return result;
+
+ if (INV_MPU6500 == st->chip_type) {
+ /* config accel LPF register for MPU6500 */
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2,
+ DEF_ST_MPU6500_ACCEL_LPF |
+ BIT_FIFO_SIZE_1K);
+ if (result)
+ return result;
+ }
+
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ DEF_SELFTEST_SAMPLE_RATE);
+ if (result)
+ return result;
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ self_test_flag | DEF_SELFTEST_GYRO_FS);
+ if (result)
+ return result;
+ if (has_accel) {
+ if (INV_MPU6500 == st->chip_type)
+ d = DEF_SELFTEST_6500_ACCEL_FS;
+ else
+ d = DEF_SELFTEST_ACCEL_FS;
+ d |= self_test_flag;
+ result = inv_i2c_single_write(st, reg->accel_config, d);
+ if (result)
+ return result;
+ }
+ /* wait for the output to get stable */
+ if (self_test_flag) {
+ if (INV_MPU6500 == st->chip_type)
+ msleep(DEF_ST_6500_STABLE_TIME);
+ else
+ msleep(DEF_ST_STABLE_TIME);
+ }
+
+ /* enable FIFO reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO */
+ if (has_accel)
+ d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
+ else
+ d = BITS_GYRO_OUT;
+ for (i = 0; i < THREE_AXIS; i++) {
+ gyro_result[i] = 0;
+ accel_result[i] = 0;
+ }
+ s = 0;
+ while (s < st->self_test.samples) {
+ result = inv_i2c_single_write(st, reg->fifo_en, d);
+ if (result)
+ return result;
+ mdelay(DEF_GYRO_WAIT_TIME);
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ pr_debug("%s self_test fifo_count - %d\n",
+ st->hw->name, fifo_count);
+ packet_count = fifo_count / packet_size;
+ i = 0;
+ while ((i < packet_count) && (s < st->self_test.samples)) {
+ short vals[3];
+ result = inv_i2c_read(st, reg->fifo_r_w,
+ packet_size, data);
+ if (result)
+ return result;
+ ind = 0;
+ if (has_accel) {
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ accel_result[j] += vals[j];
+ }
+ ind += BYTES_PER_SENSOR;
+ pr_debug(
+ "%s self_test accel data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+ }
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ gyro_result[j] += vals[j];
+ }
+ pr_debug("%s self_test gyro data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ s++;
+ i++;
+ }
+ }
+
+ if (has_accel) {
+ for (j = 0; j < THREE_AXIS; j++) {
+ accel_result[j] = accel_result[j] / s;
+ accel_result[j] *= DEF_ST_PRECISION;
+ }
+ }
+ for (j = 0; j < THREE_AXIS; j++) {
+ gyro_result[j] = gyro_result[j] / s;
+ gyro_result[j] *= DEF_ST_PRECISION;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_recover_setting() recover the old settings after everything is done
+ */
+static void inv_recover_setting(struct inv_mpu_state *st)
+{
+ struct inv_reg_map_s *reg;
+ int data;
+
+ reg = &st->reg;
+ inv_i2c_single_write(st, reg->gyro_config,
+ st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT);
+ inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+ data = ONE_K_HZ/st->chip_config.fifo_rate - 1;
+ inv_i2c_single_write(st, reg->sample_rate_div, data);
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ if (INV_ITG3500 != st->chip_type) {
+ inv_i2c_single_write(st, reg->accel_config,
+ (st->chip_config.accel_fs <<
+ ACCEL_CONFIG_FSR_SHIFT));
+ }
+ inv_reset_offset_reg(st, false);
+ st->switch_gyro_engine(st, false);
+ st->switch_accel_engine(st, false);
+ st->set_power_state(st, false);
+}
+
+
+static int inv_power_up_self_test(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, true);
+ if (result)
+ return result;
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/*
+ * inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+ int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS];
+ int test_times, i;
+ char compass_result, accel_result, gyro_result;
+
+ result = inv_power_up_self_test(st);
+ if (result)
+ return result;
+ result = inv_reset_offset_reg(st, true);
+ if (result)
+ return result;
+ compass_result = 0;
+ accel_result = 0;
+ gyro_result = 0;
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular,
+ accel_bias_regular);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular[0],
+ accel_bias_regular[1], accel_bias_regular[2]);
+ pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1],
+ gyro_bias_regular[2]);
+
+ for (i = 0; i < 3; i++) {
+ st->gyro_bias[i] = gyro_bias_regular[i];
+ st->accel_bias[i] = accel_bias_regular[i];
+ }
+
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+ accel_bias_st);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ break;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_st - %+d %+d %+d\n",
+ st->hw->name, accel_bias_st[0], accel_bias_st[1],
+ accel_bias_st[2]);
+ pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_st[0], gyro_bias_st[1],
+ gyro_bias_st[2]);
+
+ if (st->chip_type == INV_ITG3500) {
+ gyro_result = !inv_check_3500_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ } else {
+ if (st->chip_config.has_compass)
+ compass_result = !st->slave_compass->self_test(st);
+
+ if (INV_MPU6050 == st->chip_type) {
+ accel_result = !inv_check_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_6050_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ } else if (INV_MPU6500 == st->chip_type) {
+ accel_result = !inv_check_6500_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_6500_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ }
+ }
+
+test_fail:
+ inv_recover_setting(st);
+
+ return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) |
+ (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
+
+static int inv_load_firmware(struct inv_mpu_state *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+
+ /* first bank start at MPU_DMP_LOAD_START */
+ write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START;
+ memaddr = MPU_DMP_LOAD_START;
+ result = mem_w(memaddr, write_size, data);
+ if (result)
+ return result;
+ size -= write_size;
+ data += write_size;
+
+ /* Write and verify memory */
+ for (bank = 1; size > 0; bank++, size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+
+ result = mem_w(memaddr, write_size, data);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+static int inv_verify_firmware(struct inv_mpu_state *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+ u8 firmware[MPU_MEM_BANK_SIZE];
+
+ /* Write and verify memory */
+ write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START;
+ size -= write_size;
+ data += write_size;
+ for (bank = 1; size > 0; bank++,
+ size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, firmware);
+ if (result)
+ return result;
+ if (0 != memcmp(firmware, data, write_size))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int inv_set_step_buffer_time(struct inv_mpu_state *st)
+{
+ /* Pedometer executes at 50Hz so 1.5 seconds is 20ms * 75 */
+ return inv_write_2bytes(st, KEY_D_PEDSTD_SB_TIME, 75);
+}
+
+static int inv_set_step_threshold(struct inv_mpu_state *st)
+{
+ return inv_write_2bytes(st, KEY_D_PEDSTD_SB, st->ped.step_thresh);
+}
+
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
+{
+ u8 reg[3];
+
+ if (en) {
+ reg[0] = 0xf4;
+ reg[1] = 0x44;
+ reg[2] = 0xf1;
+
+ } else {
+ reg[0] = 0xf1;
+ reg[1] = 0xf1;
+ reg[2] = 0xf1;
+ }
+
+ return mem_w_key(KEY_CFG_PED_INT, ARRAY_SIZE(reg), reg);
+}
+
+int inv_read_pedometer_counter(struct inv_mpu_state *st)
+{
+ int result;
+ u8 d[4];
+ u32 last_step_counter, curr_counter;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_STPDET_TIMESTAMP), 4, d);
+ if (result)
+ return result;
+ last_step_counter = (u32)be32_to_cpup((__be32 *)(d));
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, d);
+ if (result)
+ return result;
+ curr_counter = (u32)be32_to_cpup((__be32 *)(d));
+ if (0 != last_step_counter)
+ st->ped.last_step_time = get_time_ns() -
+ ((u64)(curr_counter - last_step_counter)) *
+ DMP_INTERVAL_INIT;
+
+ return 0;
+}
+
+int inv_enable_pedometer(struct inv_mpu_state *st, bool en)
+{
+ u8 d[1];
+
+ if (en) {
+ inv_set_step_buffer_time(st);
+ inv_set_step_threshold(st);
+ d[0] = 0xf1;
+ } else {
+ d[0] = 0xff;
+ }
+ return mem_w_key(KEY_CFG_PED_ENABLE, ARRAY_SIZE(d), d);
+}
+
+int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps)
+{
+ u8 d[4];
+ int result;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d);
+ *steps = (u32)be32_to_cpup((__be32 *)(d));
+
+ return result;
+}
+
+int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time)
+{
+ u8 d[4];
+ int result;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d);
+ *time = (u32)be32_to_cpup((__be32 *)(d));
+
+ return result;
+}
+
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on)
+{
+ int r;
+ u8 rn[] = {0xf4, 0x41};
+ u8 rf[] = {0xd8, 0xd8};
+
+ if (on)
+ r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rn), rn);
+ else
+ r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rf), rf);
+
+ return r;
+}
+
+static int inv_set_tap_interrupt_dmp(struct inv_mpu_state *st, u8 on)
+{
+ int result;
+ u16 d;
+
+ if (on)
+ d = 192;
+ else
+ d = 128;
+
+ result = inv_write_2bytes(st, KEY_DMP_TAP_GATE, d);
+
+ return result;
+}
+
+/*
+ * inv_set_tap_threshold_dmp():
+ * Sets the tap threshold in the dmp
+ * Simultaneously sets secondary tap threshold to help correct the tap
+ * direction for soft taps.
+ */
+int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold)
+{
+ int result;
+ int sampleDivider;
+ int scaledThreshold;
+ u32 dmpThreshold;
+ u8 sample_div;
+ const u32 accel_sens = (0x20000000 / 0x00010000);
+
+ if (threshold > (1 << 15))
+ return -EINVAL;
+ sample_div = st->sample_divider;
+
+ sampleDivider = (1 + sample_div);
+ /* Scale factor corresponds linearly using
+ * 0 : 0
+ * 25 : 0.0250 g/ms
+ * 50 : 0.0500 g/ms
+ * 100: 1.0000 g/ms
+ * 200: 2.0000 g/ms
+ * 400: 4.0000 g/ms
+ * 800: 8.0000 g/ms
+ */
+ /*multiply by 1000 to avoid floating point 1000/1000*/
+ scaledThreshold = threshold;
+ /* Convert to per sample */
+ scaledThreshold *= sampleDivider;
+
+ /* Scale to DMP 16 bit value */
+ if (accel_sens != 0)
+ dmpThreshold = (u32)(scaledThreshold * accel_sens);
+ else
+ return -EINVAL;
+ dmpThreshold = dmpThreshold / DMP_PRECISION;
+ result = inv_write_2bytes(st, KEY_DMP_TAP_THR_Z, dmpThreshold);
+ if (result)
+ return result;
+ result = inv_write_2bytes(st, KEY_DMP_TAP_PREV_JERK_Z,
+ dmpThreshold * 3 / 4);
+
+ return result;
+}
+
+
+/*
+ * inv_set_min_taps_dmp():
+ * Indicates the minimum number of consecutive taps required
+ * before the DMP will generate an interrupt.
+ */
+int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps)
+{
+ u8 result;
+
+ /* check if any spurious bit other the ones expected are set */
+ if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1))
+ return -EINVAL;
+
+ /* DMP tap count is zero-based. So single-tap is 0.
+ Furthermore, DMP code checks for tap_count > min_taps.
+ So we have to do minus 2 here.
+ For example, if the user expects any single tap will generate an
+ interrupt, (s)he will call inv_set_min_taps_dmp(1).
+ When DMP gets a single tap, tap_count = 0. To get
+ tap_count > min_taps, we have to decrement min_taps by 2 to -1. */
+ result = inv_write_2bytes(st, KEY_DMP_TAP_MIN_TAPS, (u16)(min_taps-2));
+
+ return result;
+}
+
+/*
+ * inv_set_tap_time_dmp():
+ * Determines how long after a tap the DMP requires before
+ * another tap can be registered.
+ */
+int inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAPW_MIN, dmpTime);
+
+ return result;
+}
+
+/*
+ * inv_set_multiple_tap_time_dmp():
+ * Determines how close together consecutive taps must occur
+ * to be considered double/triple taps.
+ */
+static int inv_set_multiple_tap_time_dmp(struct inv_mpu_state *st, u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_NEXT_TAP_THRES, dmpTime);
+
+ return result;
+}
+
+int inv_q30_mult(int a, int b)
+{
+ u64 temp;
+ int result;
+
+ temp = (u64)a * b;
+ result = (int)(temp >> DMP_MULTI_SHIFT);
+
+ return result;
+}
+
+static u16 inv_row_2_scale(const s8 *row)
+{
+ u16 b;
+
+ if (row[0] > 0)
+ b = 0;
+ else if (row[0] < 0)
+ b = 4;
+ else if (row[1] > 0)
+ b = 1;
+ else if (row[1] < 0)
+ b = 5;
+ else if (row[2] > 0)
+ b = 2;
+ else if (row[2] < 0)
+ b = 6;
+ else
+ b = 7;
+
+ return b;
+}
+
+/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar
+* representation.
+* @param[in] mtx Orientation matrix to convert to a scalar.
+* @return Description of orientation matrix. The lowest 2 bits (0 and 1)
+* represent the column the one is on for the
+* first row, with the bit number 2 being the sign. The next 2 bits
+* (3 and 4) represent
+* the column the one is on for the second row with bit number 5 being
+* the sign.
+* The next 2 bits (6 and 7) represent the column the one is on for the
+* third row with
+* bit number 8 being the sign. In binary the identity matrix would therefor
+* be: 010_001_000 or 0x88 in hex.
+*/
+static u16 inv_orientation_matrix_to_scaler(const signed char *mtx)
+{
+
+ u16 scalar;
+ scalar = inv_row_2_scale(mtx);
+ scalar |= inv_row_2_scale(mtx + 3) << 3;
+ scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+ return scalar;
+}
+
+static int inv_gyro_dmp_cal(struct inv_mpu_state *st)
+{
+ int inv_gyro_orient;
+ u8 regs[3];
+ int result;
+
+ u8 tmpD = DINA4C;
+ u8 tmpE = DINACD;
+ u8 tmpF = DINA6C;
+
+ inv_gyro_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ if ((inv_gyro_orient & 3) == 0)
+ regs[0] = tmpD;
+ else if ((inv_gyro_orient & 3) == 1)
+ regs[0] = tmpE;
+ else if ((inv_gyro_orient & 3) == 2)
+ regs[0] = tmpF;
+ if ((inv_gyro_orient & 0x18) == 0)
+ regs[1] = tmpD;
+ else if ((inv_gyro_orient & 0x18) == 0x8)
+ regs[1] = tmpE;
+ else if ((inv_gyro_orient & 0x18) == 0x10)
+ regs[1] = tmpF;
+ if ((inv_gyro_orient & 0xc0) == 0)
+ regs[2] = tmpD;
+ else if ((inv_gyro_orient & 0xc0) == 0x40)
+ regs[2] = tmpE;
+ else if ((inv_gyro_orient & 0xc0) == 0x80)
+ regs[2] = tmpF;
+
+ result = mem_w_key(KEY_FCFG_1, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ if (inv_gyro_orient & 4)
+ regs[0] = DINA36 | 1;
+ else
+ regs[0] = DINA36;
+ if (inv_gyro_orient & 0x20)
+ regs[1] = DINA56 | 1;
+ else
+ regs[1] = DINA56;
+ if (inv_gyro_orient & 0x100)
+ regs[2] = DINA76 | 1;
+ else
+ regs[2] = DINA76;
+ result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+static int inv_accel_dmp_cal(struct inv_mpu_state *st)
+{
+ int inv_accel_orient;
+ int result;
+ u8 regs[3];
+ const u8 tmp[3] = { DINA0C, DINAC9, DINA2C };
+ inv_accel_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ regs[0] = tmp[inv_accel_orient & 3];
+ regs[1] = tmp[(inv_accel_orient >> 3) & 3];
+ regs[2] = tmp[(inv_accel_orient >> 6) & 3];
+ result = mem_w_key(KEY_FCFG_2, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ regs[0] = DINA26;
+ regs[1] = DINA46;
+ regs[2] = DINA66;
+ if (inv_accel_orient & 4)
+ regs[0] |= 1;
+ if (inv_accel_orient & 0x20)
+ regs[1] |= 1;
+ if (inv_accel_orient & 0x100)
+ regs[2] |= 1;
+ result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+int inv_set_accel_bias_dmp(struct inv_mpu_state *st)
+{
+ int inv_accel_orient, result, i, accel_bias_body[3], out[3];
+ int tmp[] = {1, 1, 1};
+ int mask[] = {4, 0x20, 0x100};
+ int accel_sf = 0x20000000;/* 536870912 */
+ u8 *regs;
+
+ inv_accel_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ for (i = 0; i < 3; i++)
+ if (inv_accel_orient & mask[i])
+ tmp[i] = -1;
+
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] =
+ st->input_accel_dmp_bias[(inv_accel_orient >>
+ (i * 3)) & 3] * tmp[i];
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] = inv_q30_mult(accel_sf,
+ accel_bias_body[i]);
+ for (i = 0; i < 3; i++)
+ out[i] = cpu_to_be32p(&accel_bias_body[i]);
+ regs = (u8 *)out;
+ result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs);
+
+ return result;
+}
+
+/*
+ * inv_set_gyro_sf_dmp():
+ * The gyro threshold, in dps, above which taps will be rejected.
+ */
+static int inv_set_gyro_sf_dmp(struct inv_mpu_state *st)
+{
+ int result;
+ u8 sampleDivider;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens,
+ (int)(DMP_TAP_SCALE * (sampleDivider + 1)));
+ result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_thresh_dmp():
+ * The gyro threshold, in dps, above which taps will be rejected.
+ */
+static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_state *st,
+ int thresh)
+{
+ int result;
+ u8 sampleDivider;
+ int thresh_scaled;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE *
+ (sampleDivider + 1)));
+ /* We're in units of DPS, convert it back to chip units*/
+ /*split the operation to aviod overflow of integer*/
+ thresh_scaled = gyro_sens / (1L << 16);
+ thresh_scaled = thresh_scaled / thresh;
+ thresh_scaled = gyro_sf / thresh_scaled;
+ result = write_be32_key_to_mem(st, thresh_scaled,
+ KEY_DMP_TAP_SHAKE_REJECT);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_time_dmp():
+ * How long a gyro axis must remain above its threshold
+ * before taps are rejected.
+ */
+static int inv_set_shake_reject_time_dmp(struct inv_mpu_state *st,
+ u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_COUNT_MAX, dmpTime);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_timeout_dmp():
+ * How long the gyros must remain below their threshold,
+ * after taps have been rejected, before taps can be detected again.
+ */
+static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_state *st,
+ u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_TIMEOUT_MAX, dmpTime);
+
+ return result;
+}
+
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on)
+{
+ u8 r;
+ const u8 rn[] = {0xA3};
+ const u8 rf[] = {0xFE};
+
+ if (on)
+ r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rn), rn);
+ else
+ r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rf), rf);
+
+ return r;
+}
+
+/*
+ * inv_enable_tap_dmp() - calling this function will enable/disable tap
+ * function.
+ */
+int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on)
+{
+ int result;
+
+ result = inv_set_tap_interrupt_dmp(st, on);
+ if (result)
+ return result;
+ result = inv_set_tap_threshold_dmp(st, st->tap.thresh);
+ if (result)
+ return result;
+
+ result = inv_set_min_taps_dmp(st, st->tap.min_count);
+ if (result)
+ return result;
+
+ result = inv_set_tap_time_dmp(st, st->tap.time);
+ if (result)
+ return result;
+
+ result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_gyro_sf_dmp(st);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_timeout_dmp(st,
+ DMP_SHAKE_REJECT_TIMEOUT);
+ return result;
+}
+
+static int inv_dry_run_dmp(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_DMP_EN);
+ if (result)
+ return result;
+ msleep(400);
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ return result;
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+static void inv_test_reset(struct inv_mpu_state *st)
+{
+ int result, ii;
+ u8 d[0x80];
+
+ if (INV_MPU6500 != st->chip_type)
+ return;
+
+ for (ii = 3; ii < 0x80; ii++) {
+ /* don't read fifo r/w register */
+ if (ii != st->reg.fifo_r_w)
+ inv_i2c_read(st, ii, 1, &d[ii]);
+ }
+ result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ return;
+ msleep(POWER_UP_TIME);
+
+ for (ii = 3; ii < 0x80; ii++) {
+ /* don't write certain registers */
+ if ((ii != st->reg.fifo_r_w) &&
+ (ii != st->reg.mem_r_w) &&
+ (ii != st->reg.mem_start_addr) &&
+ (ii != st->reg.fifo_count_h) &&
+ ii != (st->reg.fifo_count_h + 1))
+ result = inv_i2c_single_write(st, ii, d[ii]);
+ }
+}
+
+/*
+ * inv_dmp_firmware_write() - calling this function will load the firmware.
+ * This is the write function of file "dmp_firmware".
+ */
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t pos, size_t size)
+{
+ u8 *firmware;
+ int result;
+ struct inv_reg_map_s *reg;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ if (st->chip_config.firmware_loaded)
+ return -EINVAL;
+ if (st->chip_config.enable)
+ return -EBUSY;
+
+ reg = &st->reg;
+ if (DMP_IMAGE_SIZE != size) {
+ pr_err("wrong DMP image size - expected %d, actual %d\n",
+ DMP_IMAGE_SIZE, size);
+ return -EINVAL;
+ }
+
+ firmware = kmalloc(size, GFP_KERNEL);
+ if (!firmware)
+ return -ENOMEM;
+
+ mutex_lock(&indio_dev->mlock);
+
+ memcpy(firmware, buf, size);
+ result = crc32(CRC_FIRMWARE_SEED, firmware, size);
+ if (DMP_IMAGE_CRC_VALUE != result) {
+ pr_err("firmware CRC error - 0x%08x vs 0x%08x\n",
+ result, DMP_IMAGE_CRC_VALUE);
+ result = -EINVAL;
+ goto firmware_write_fail;
+ }
+
+ result = st->set_power_state(st, true);
+ if (result)
+ goto firmware_write_fail;
+ inv_test_reset(st);
+
+ result = inv_load_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_verify_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh,
+ st->chip_config.prog_start_addr >> 8);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1,
+ st->chip_config.prog_start_addr & 0xff);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_gyro_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_accel_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_dry_run_dmp(st);
+ if (result)
+ goto firmware_write_fail;
+
+ st->chip_config.firmware_loaded = 1;
+
+firmware_write_fail:
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ kfree(firmware);
+ if (result)
+ return result;
+
+ return size;
+}
+
+ssize_t inv_dmp_firmware_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int bank, write_size, size, data, result;
+ u16 memaddr;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+
+ size = count;
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ data = 0;
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ }
+ for (bank = 0; size > 0; bank++, size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = (bank << 8);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, &buf[data]);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ }
+ if (!st->chip_config.enable)
+ result = st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return count;
+}
+
+ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos, size_t size)
+{
+ u8 q[QUATERNION_BYTES];
+ struct inv_reg_map_s *reg;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+ int result;
+
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (!st->chip_config.firmware_loaded) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ if (st->chip_config.enable) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ reg = &st->reg;
+ if (QUATERNION_BYTES != size) {
+ pr_err("wrong quaternion size=%d, should=%d\n", size,
+ QUATERNION_BYTES);
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+
+ memcpy(q, buf, size);
+ result = st->set_power_state(st, true);
+ if (result)
+ goto firmware_write_fail;
+ result = mem_w_key(KEY_DMP_Q0, QUATERNION_BYTES, q);
+
+firmware_write_fail:
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return size;
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c
new file mode 100644
index 00000000000..9c3d0912df4
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c
@@ -0,0 +1,1899 @@
+/*
+* 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 "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_mpu_iio.h"
+
+static u8 fifo_data[HARDWARE_FIFO_SIZE + HEADERED_Q_BYTES];
+static int inv_process_batchmode(struct inv_mpu_state *st);
+
+static int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+
+ memcpy(buf, &hdr, sizeof(hdr));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+ return 0;
+}
+
+static int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr,
+ u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ for (i = 0; i < 3; i++)
+ memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i]));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+ memcpy(buf, &t, sizeof(t));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+ return 0;
+}
+
+static int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t,
+ int *q)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+ for (i = 0; i < 2; i++)
+ memcpy(buf + 4 * i, &q[i + 1], sizeof(q[i]));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+ memcpy(buf, &t, sizeof(t));
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_push_to_buffers(indio_dev, buf);
+#else
+ iio_push_to_buffer(indio_dev->buffer, buf, 0);
+#endif
+
+ return 0;
+}
+
+static int inv_send_pressure_data(struct inv_mpu_state *st)
+{
+ short sen[3];
+ struct inv_chip_config_s *conf;
+ struct inv_mpu_slave *slave;
+ u64 curr_ts;
+ int result;
+
+ conf = &st->chip_config;
+ slave = st->slave_pressure;
+ if (!st->sensor[SENSOR_PRESSURE].on)
+ return 0;
+ if (conf->dmp_on && conf->dmp_event_int_on)
+ return 0;
+ if (!conf->normal_pressure_measure) {
+ conf->normal_pressure_measure = 1;
+ return 0;
+ }
+ curr_ts = get_time_ns();
+ if (curr_ts - slave->prev_ts > slave->min_read_time) {
+ result = slave->read_data(st, sen);
+ if (!result)
+ inv_push_8bytes_buffer(st, PRESSURE_HDR,
+ st->last_ts, sen);
+ slave->prev_ts = curr_ts;
+ }
+
+ return 0;
+}
+
+static int inv_send_compass_data(struct inv_mpu_state *st)
+{
+ short sen[3];
+ struct inv_chip_config_s *conf;
+ struct inv_mpu_slave *slave;
+ u64 curr_ts;
+ int result;
+
+ conf = &st->chip_config;
+ slave = st->slave_compass;
+ if (!st->sensor[SENSOR_COMPASS].on)
+ return 0;
+ if (conf->dmp_on && conf->dmp_event_int_on)
+ return 0;
+ if (!conf->normal_compass_measure) {
+ conf->normal_compass_measure = 1;
+ return 0;
+ }
+ curr_ts = get_time_ns();
+ if (curr_ts - slave->prev_ts > slave->min_read_time) {
+ result = slave->read_data(st, sen);
+ if (result)
+ inv_push_marker_to_buffer(st, COMPASS_HDR_2);
+ else
+ inv_push_8bytes_buffer(st, COMPASS_HDR,
+ st->last_ts, sen);
+ slave->prev_ts = curr_ts;
+ }
+
+ return 0;
+}
+
+static int inv_batchmode_calc(struct inv_mpu_state *st)
+{
+ int b, timeout;
+ int rate_dur;
+
+ rate_dur = MSEC_PER_SEC / st->batch.min_rate;
+ if (st->batch.timeout < rate_dur)
+ st->batch.timeout = rate_dur;
+ b = st->batch.timeout * st->bytes_per_sec;
+ if ((b > (FIFO_SIZE * ONE_K_HZ)) && (!st->batch.overflow_on))
+ timeout = FIFO_SIZE * ONE_K_HZ / st->bytes_per_sec;
+ else
+ timeout = st->batch.timeout;
+
+ st->batch.counter = timeout / 5;
+ if (timeout)
+ st->batch.on = true;
+
+ return 0;
+}
+
+int inv_batchmode_setup(struct inv_mpu_state *st)
+{
+ int r;
+
+ r = inv_write_2bytes(st, KEY_BM_NUMWORD_TOFILL, 0);
+ if (r)
+ return r;
+ r = write_be32_key_to_mem(st, 0, KEY_BM_BATCH_CNTR);
+ if (r)
+ return r;
+
+ if (st->chip_config.dmp_on && (st->batch.timeout > 0) &&
+ (st->chip_config.dmp_event_int_on == 0)) {
+ r = inv_batchmode_calc(st);
+ if (r)
+ return r;
+ }
+
+ if (st->batch.on) {
+ r = write_be32_key_to_mem(st, st->batch.counter,
+ KEY_BM_BATCH_THLD);
+ if (r)
+ return r;
+ }
+ r = inv_write_2bytes(st, KEY_BM_ENABLE, st->batch.on);
+
+ return r;
+}
+
+/**
+ * reset_fifo_mpu3050() - Reset FIFO related registers
+ * @indio_dev: Device driver instance.
+ */
+static int reset_fifo_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 val, user_ctrl;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ reg = &st->reg;
+
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ user_ctrl &= ~BIT_FIFO_EN;
+ st->chip_config.has_footer = 0;
+ /* reset fifo */
+ val = (BIT_3050_FIFO_RST | user_ctrl);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ if (st->chip_config.dmp_on) {
+ /* enable interrupt when DMP is done */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DMP_INT_EN);
+ if (result)
+ return result;
+
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN|user_ctrl);
+ if (result)
+ return result;
+ } else {
+ /* enable interrupt */
+ if (st->sensor[SENSOR_ACCEL].on ||
+ st->sensor[SENSOR_GYRO].on) {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DATA_RDY_EN);
+ if (result)
+ return result;
+ }
+ /* enable FIFO reading and I2C master interface*/
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN | user_ctrl);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO and FIFO footer*/
+ val = 1;
+ if (st->sensor[SENSOR_ACCEL].on)
+ val |= BITS_3050_ACCEL_OUT;
+ if (st->sensor[SENSOR_GYRO].on)
+ val |= BITS_GYRO_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ return result;
+ }
+
+ return 0;
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/*
+ * inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+ INV_FILTER_42HZ, INV_FILTER_20HZ,
+ INV_FILTER_10HZ, INV_FILTER_5HZ};
+ int i, h, data, result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ if (INV_MPU3050 == st->chip_type) {
+ if (st->slave_accel != NULL) {
+ result = st->slave_accel->set_lpf(st, rate);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->lpf, data |
+ (st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT));
+ } else {
+ result = inv_i2c_single_write(st, reg->lpf, data);
+ }
+ if (result)
+ return result;
+ st->chip_config.lpf = data;
+
+ return 0;
+}
+
+/*
+ * set_fifo_rate_reg() - Set fifo rate in hardware register
+ */
+static int set_fifo_rate_reg(struct inv_mpu_state *st)
+{
+ u8 data;
+ u16 fifo_rate;
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ fifo_rate = st->chip_config.fifo_rate;
+ data = ONE_K_HZ / fifo_rate - 1;
+ result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+ if (result)
+ return result;
+ result = inv_set_lpf(st, fifo_rate);
+
+ return result;
+}
+
+/*
+ * inv_lpa_mode() - store current low power mode settings
+ */
+static int inv_lpa_mode(struct inv_mpu_state *st, int lpa_mode)
+{
+ unsigned long result;
+ u8 d;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+ if (result)
+ return result;
+ if (lpa_mode)
+ d |= BIT_CYCLE;
+ else
+ d &= ~BIT_CYCLE;
+
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+ if (result)
+ return result;
+ if (INV_MPU6500 == st->chip_type) {
+ d = BIT_FIFO_SIZE_1K;
+ if (lpa_mode)
+ d |= BIT_ACCEL_FCHOCIE_B;
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, d);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+static int inv_saturate_secondary_counter(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+#define COUNT_SATURATE_TIME_MS 32
+ reg = &st->reg;
+ /* set sampling to 1KHz */
+ result = inv_i2c_single_write(st, reg->sample_rate_div, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL,
+ BIT_SLV0_DLY_EN);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_I2C_MST_EN);
+ if (result)
+ return result;
+ msleep(COUNT_SATURATE_TIME_MS);
+
+ return 0;
+}
+static int inv_set_master_delay(struct inv_mpu_state *st)
+{
+ int d, result, rate;
+ u8 delay;
+
+ if ((!st->sensor[SENSOR_COMPASS].on) &&
+ (!st->sensor[SENSOR_PRESSURE].on))
+ return 0;
+
+ delay = 0;
+ d = 0;
+ if (st->sensor[SENSOR_COMPASS].on) {
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ case COMPASS_ID_AK8972:
+ case COMPASS_ID_AK8963:
+ case COMPASS_ID_AK09911:
+ delay = (BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN);
+ break;
+ case COMPASS_ID_MLX90399:
+ delay = (BIT_SLV0_DLY_EN |
+ BIT_SLV1_DLY_EN |
+ BIT_SLV2_DLY_EN |
+ BIT_SLV3_DLY_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ d = max(d, st->slave_compass->rate_scale);
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ /* read fake data when compass is disabled for DMP read */
+ if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on)
+ delay |= BIT_SLV0_DLY_EN;
+ switch (st->plat_data.aux_slave_id) {
+ case PRESSURE_ID_BMP280:
+ delay |= (BIT_SLV2_DLY_EN | BIT_SLV3_DLY_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ d = max(d, st->slave_pressure->rate_scale);
+ }
+
+ d = d * st->chip_config.fifo_rate / ONE_K_HZ;
+ if (st->chip_config.dmp_on) {
+ rate = 0;
+ if (st->sensor[SENSOR_PRESSURE].on)
+ rate = max(rate, st->sensor[SENSOR_PRESSURE].rate);
+ if (st->sensor[SENSOR_COMPASS].on)
+ rate = max(rate, st->sensor[SENSOR_COMPASS].rate);
+ if (rate == 0)
+ return -EINVAL;
+ d = max(d, st->chip_config.fifo_rate / rate);
+ }
+
+ if (d > 0)
+ d -= 1;
+ if (d > 0x1F)
+ d = 0x1F;
+
+ /* I2C_MST_DLY is set to slow down secondary I2C */
+ if (0 == d)
+ delay = 0;
+ if (delay) {
+ result = inv_saturate_secondary_counter(st);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, delay);
+ if (result)
+ return result;
+
+ return inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, d);
+}
+
+/*
+ * reset_fifo_itg() - Reset FIFO related registers.
+ */
+static int reset_fifo_itg(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result, i;
+ u8 val, int_word;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ reg = &st->reg;
+ if (st->chip_config.lpa_mode) {
+ result = inv_lpa_mode(st, 0);
+ if (result) {
+ pr_err("reset lpa mode failed\n");
+ return result;
+ }
+ }
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result) {
+ pr_err("int_enable write failed\n");
+ return result;
+ }
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ goto reset_fifo_fail;
+ int_word = 0;
+
+ /* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */
+ if (st->mot_int.mot_on)
+ int_word |= BIT_MOT_EN;
+
+ if (st->chip_config.dmp_on) {
+ val = (BIT_FIFO_RST | BIT_DMP_RST);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ if (st->chip_config.dmp_int_on) {
+ int_word |= BIT_DMP_INT_EN;
+ result = inv_i2c_single_write(st, reg->int_enable,
+ int_word);
+ if (result)
+ return result;
+ }
+ val = (BIT_DMP_EN | BIT_FIFO_EN);
+ if ((st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on) &&
+ (!st->chip_config.dmp_event_int_on))
+ val |= BIT_I2C_MST_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ } 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;
+ /* enable interrupt */
+ if (st->sensor[SENSOR_ACCEL].on ||
+ st->sensor[SENSOR_GYRO].on ||
+ st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on)
+ int_word |= BIT_DATA_RDY_EN;
+
+ result = inv_i2c_single_write(st, reg->int_enable, int_word);
+ if (result)
+ return result;
+ /* enable FIFO reading and I2C master interface*/
+ val = BIT_FIFO_EN;
+ if (st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on)
+ 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->sensor[SENSOR_GYRO].on)
+ val |= BITS_GYRO_OUT;
+ if (st->sensor[SENSOR_ACCEL].on)
+ val |= BIT_ACCEL_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ goto reset_fifo_fail;
+ }
+ st->last_ts = get_time_ns();
+ st->prev_ts = st->last_ts;
+ st->last_run_time = st->last_ts;
+ if (st->sensor[SENSOR_COMPASS].on)
+ st->slave_compass->prev_ts = st->last_ts;
+ if (st->sensor[SENSOR_PRESSURE].on)
+ st->slave_pressure->prev_ts = st->last_ts;
+
+ st->dmp_interval = DMP_INTERVAL_INIT;
+ st->ts_counter = 0;
+ st->diff_accumulater = 0;
+ st->dmp_interval_accum = 0;
+ st->step_detector_base_ts = st->last_ts;
+ st->chip_config.normal_compass_measure = 0;
+ st->chip_config.normal_pressure_measure = 0;
+ st->left_over_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = st->last_ts;
+
+ result = inv_lpa_mode(st, st->chip_config.lpa_mode);
+ if (result)
+ goto reset_fifo_fail;
+
+ return 0;
+
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable, val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/**
+ * inv_clear_kfifo() - clear time stamp fifo
+ * @st: Device driver instance.
+ */
+static void inv_clear_kfifo(struct inv_mpu_state *st)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/*
+ * inv_reset_fifo() - Reset FIFO related registers.
+ */
+int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ inv_clear_kfifo(st);
+ if (INV_MPU3050 == st->chip_type)
+ return reset_fifo_mpu3050(indio_dev);
+ else
+ return reset_fifo_itg(indio_dev);
+}
+
+static int inv_send_gyro_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_GYRO, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_accel_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_ACCL, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_three_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x13};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_3QUAT, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_six_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x13};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_6QUAT, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_ped_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_PQUAT, ARRAY_SIZE(rn), r);
+
+ return result;
+}
+
+static int inv_add_step_indicator(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xf3, 0xf3};
+ u8 rf[] = {0xf4, 0x03};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_PEDSTEP_DET, ARRAY_SIZE(rn), r);
+
+ return result;
+}
+
+static int inv_send_compass_dmp_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_CPASS, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_pressure_dmp_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_PRESS, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_step_detector(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x0e};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_STEPDET, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_set_rate(struct inv_mpu_state *st, int k, int k_ct, int rate)
+{
+ int v, result;
+
+ v = MPU_DEFAULT_DMP_FREQ / rate - 1;
+ result = inv_write_2bytes(st, k, v);
+ if (result)
+ return result;
+ result = inv_write_2bytes(st, k_ct, 0);
+
+ return result;
+}
+
+static int inv_set_gyro_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_GYRO_ODR, KEY_ODR_CNTR_GYRO,
+ st->sensor[SENSOR_GYRO].rate);
+
+ return result;
+}
+
+static int inv_set_accel_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_ACCL_ODR, KEY_ODR_CNTR_ACCL,
+ st->sensor[SENSOR_ACCEL].rate);
+
+ return result;
+}
+
+static int inv_set_compass_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_CPASS_ODR, KEY_ODR_CNTR_CPASS,
+ st->sensor[SENSOR_COMPASS].rate);
+
+ return result;
+}
+
+static int inv_set_pressure_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_PRESS_ODR, KEY_ODR_CNTR_PRESS,
+ st->sensor[SENSOR_PRESSURE].rate);
+
+ return result;
+}
+
+static int inv_set_step_detector(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+
+static int inv_set_lpq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_3QUAT_ODR, KEY_ODR_CNTR_3QUAT,
+ st->sensor[SENSOR_LPQ].rate);
+
+ return result;
+}
+
+static int inv_set_sixq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_6QUAT_ODR, KEY_ODR_CNTR_6QUAT,
+ st->sensor[SENSOR_SIXQ].rate);
+
+ return result;
+}
+
+static int inv_set_pedq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_PQUAT6_ODR, KEY_ODR_CNTR_PQUAT,
+ st->sensor[SENSOR_PEDQ].rate);
+
+ return result;
+}
+
+
+static int inv_set_dmp_sysfs(struct inv_mpu_state *st)
+{
+ int result, i, s;
+ u8 d[] = {0, 0, 0, 0};
+
+ result = inv_set_interrupt_on_gesture_event(st,
+ st->chip_config.dmp_event_int_on);
+ if (result)
+ return result;
+
+ if (st->chip_config.dmp_event_int_on) {
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ result = st->sensor[i].send_data(st, false);
+ if (result)
+ return result;
+ }
+ } else {
+ s = 0;
+ st->batch.min_rate = MAX_DMP_OUTPUT_RATE;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ result = st->sensor[i].send_data(st, st->sensor[i].on);
+ if (result)
+ return result;
+ if (st->sensor[i].on) {
+ if (0 == st->sensor[i].rate)
+ return -EINVAL;
+ if (st->sensor[i].rate < st->batch.min_rate)
+ st->batch.min_rate = st->sensor[i].rate;
+ s += st->sensor[i].rate *
+ st->sensor[i].sample_size;
+
+ result = st->sensor[i].set_rate(st);
+ if (result)
+ return result;
+ st->sensor[i].counter = MPU_DEFAULT_DMP_FREQ /
+ st->sensor[i].rate;
+ }
+ }
+ st->bytes_per_sec = s;
+ if (st->sensor[SENSOR_STEP].on)
+ result = inv_add_step_indicator(st, true);
+ else
+ result = inv_add_step_indicator(st,
+ st->chip_config.step_indicator_on);
+ if (result)
+ return result;
+ }
+ result = inv_batchmode_setup(st);
+ if (result)
+ return result;
+
+ st->dmp_counter = 0;
+ result = mem_w_key(KEY_DMP_RUN_CNTR, ARRAY_SIZE(d), d);
+ if (result)
+ return result;
+ result = mem_w_key(KEY_D_STPDET_TIMESTAMP, ARRAY_SIZE(d), d);
+
+ return result;
+}
+
+static void inv_get_data_count(struct inv_mpu_state *st)
+{
+ struct inv_chip_config_s *c;
+ int b, i;
+
+ c = &st->chip_config;
+ b = 0;
+ if (st->chip_config.dmp_on) {
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ b += st->sensor[i].sample_size;
+ }
+ } else {
+ if (st->sensor[SENSOR_ACCEL].on)
+ b += BYTES_PER_SENSOR;
+ if (st->sensor[SENSOR_GYRO].on)
+ b += BYTES_PER_SENSOR;
+ }
+ c->bytes_per_datum = b;
+
+ return;
+}
+/*
+ * set_inv_enable() - main enable/disable function.
+ */
+int set_inv_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ u8 data[2];
+ int result;
+
+ reg = &st->reg;
+ if (enable) {
+ if (st->chip_config.dmp_on &&
+ (!st->chip_config.firmware_loaded))
+ return -EINVAL;
+ st->batch.on = false;
+
+ inv_get_data_count(st);
+ result = inv_set_master_delay(st);
+ if (result)
+ return result;
+
+ result = set_fifo_rate_reg(st);
+ if (result)
+ return result;
+ if (st->chip_config.dmp_on) {
+ result = inv_set_dmp_sysfs(st);
+ if (result)
+ return result;
+ }
+
+ if (st->chip_config.gyro_enable) {
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+ }
+ if (st->chip_config.accel_enable) {
+ result = st->switch_accel_engine(st, true);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_COMPASS].on) {
+ result = st->slave_compass->resume(st);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ result = st->slave_pressure->resume(st);
+ if (result)
+ return result;
+ }
+ result = inv_reset_fifo(indio_dev);
+ if (result)
+ return result;
+ } else {
+ if ((INV_MPU3050 != st->chip_type)
+ && st->chip_config.lpa_mode) {
+ /* if the chip is in low power mode,
+ register write/read could fail */
+ result = inv_lpa_mode(st, 0);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ if (st->chip_config.dmp_on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ return result;
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ st->fifo_count = be16_to_cpup((__be16 *)(data));
+ if (st->fifo_count) {
+ result = inv_process_batchmode(st);
+ if (result)
+ return result;
+ }
+ }
+ inv_push_marker_to_buffer(st, END_MARKER);
+ /* disable fifo reading */
+ if (INV_MPU3050 != st->chip_type) {
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ } else {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ }
+ if (result)
+ return result;
+ /* turn off the gyro/accel engine during disable phase */
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, false);
+ if (result)
+ return result;
+ if (st->sensor[SENSOR_COMPASS].on) {
+ result = st->slave_compass->suspend(st);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ result = st->slave_pressure->suspend(st);
+ if (result)
+ return result;
+ }
+ }
+ st->chip_config.enable = enable;
+
+ return 0;
+}
+
+/*
+ * inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+ u64 ts;
+
+ if (!st->chip_config.dmp_on) {
+ ts = get_time_ns();
+ kfifo_in_spinlocked(&st->timestamps, &ts, 1,
+ &st->time_stamp_lock);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t,
+ int has_footer, u8 *data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int ind, i;
+ short s[THREE_AXIS];
+
+ ind = 0;
+ if (has_footer)
+ ind += 2;
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+
+ inv_push_8bytes_buffer(st, GYRO_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+ if (st->sensor[SENSOR_ACCEL].on) {
+ st->slave_accel->combine_data(&data[ind], s);
+ inv_push_8bytes_buffer(st, ACCEL_HDR, t, s);
+ }
+}
+
+/*
+ * inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for
+ * mpu3050.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int bytes_per_datum;
+ u8 data[64];
+ int result;
+ short fifo_count, byte_read;
+ s64 timestamp;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ mutex_lock(&st->suspend_resume_lock);
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.dmp_on)
+ bytes_per_datum = HEADERED_NORMAL_BYTES;
+ else
+ bytes_per_datum = (st->sensor[SENSOR_ACCEL].on +
+ st->sensor[SENSOR_GYRO].on) * BYTES_PER_SENSOR;
+ if (st->chip_config.has_footer)
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ else
+ byte_read = bytes_per_datum;
+
+ fifo_count = 0;
+ if (byte_read != 0) {
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ goto end_session;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ if (fifo_count < byte_read)
+ goto end_session;
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ /* Timestamp mismatch. */
+ if (kfifo_len(&st->timestamps) <
+ fifo_count / byte_read)
+ goto flush_fifo;
+ if (kfifo_len(&st->timestamps) >
+ fifo_count / byte_read + TIME_STAMP_TOR) {
+ if (st->chip_config.dmp_on) {
+ result = kfifo_out(&st->timestamps,
+ &timestamp, 1);
+ if (result != 1)
+ goto flush_fifo;
+ } else {
+ goto flush_fifo;
+ }
+ }
+ }
+ while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) {
+ result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data);
+ if (result)
+ goto flush_fifo;
+
+ result = kfifo_out(&st->timestamps, &timestamp, 1);
+ if (result != 1)
+ goto flush_fifo;
+ inv_report_data_3050(indio_dev, timestamp,
+ st->chip_config.has_footer, data);
+ fifo_count -= byte_read;
+ if (st->chip_config.has_footer == 0) {
+ st->chip_config.has_footer = 1;
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ }
+ }
+
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int inv_report_gyro_accel(struct iio_dev *indio_dev,
+ u8 *data, s64 t)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ short s[THREE_AXIS];
+ int ind;
+ int i;
+
+ ind = 0;
+ if (st->sensor[SENSOR_ACCEL].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ inv_push_8bytes_buffer(st, ACCEL_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ inv_push_8bytes_buffer(st, GYRO_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+
+ return 0;
+}
+
+static void inv_process_motion(struct inv_mpu_state *st)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result;
+ u8 data[1];
+
+ /* motion interrupt */
+ result = inv_i2c_read(st, REG_INT_STATUS, 1, data);
+ if (result)
+ return;
+
+ if (data[0] & BIT_MOT_INT)
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_accel_motion");
+}
+
+static int inv_get_timestamp(struct inv_mpu_state *st, int count)
+{
+ u32 *dur;
+ u32 thresh;
+ s32 diff, result, counter;
+ u64 ts;
+
+ /* goal of algorithm is to estimate the true frequency of the chip */
+ if (st->chip_config.dmp_on && st->chip_config.dmp_event_int_on)
+ return 0;
+ dur = &st->irq_dur_ns;
+ counter = 1;
+ thresh = min((u32)((*dur) >> 2), (u32)(10 * NSEC_PER_MSEC));
+ while (kfifo_len(&st->timestamps) >= count) {
+ result = kfifo_out(&st->timestamps, &ts, 1);
+ if (result != 1)
+ return -EINVAL;
+ /* first time since reset fifo, just take it */
+ if (!st->ts_counter) {
+ st->last_ts = ts;
+ st->prev_ts = ts;
+ st->ts_counter++;
+ return 0;
+ }
+ diff = (s32)(ts - st->prev_ts);
+ st->prev_ts = ts;
+ if (abs(diff - (*dur)) < thresh) {
+ st->diff_accumulater >>= 1;
+ if (*dur > diff)
+ st->diff_accumulater -= (((*dur) - diff) >> 7);
+ else
+ st->diff_accumulater += ((diff - (*dur)) >> 7);
+ *dur += st->diff_accumulater;
+ }
+ }
+ ts = *dur;
+ ts *= counter;
+ st->last_ts += ts;
+
+ return 0;
+}
+
+static int inv_process_dmp_interrupt(struct inv_mpu_state *st)
+{
+ int r;
+ u8 d[1];
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+
+#define DMP_INT_SMD 0x04
+#define DMP_INT_PED 0x08
+
+ if ((!st->chip_config.smd_enable) &&
+ (!st->ped.int_on) &&
+ (!st->tap.on))
+ return 0;
+
+ r = inv_i2c_read(st, REG_DMP_INT_STATUS, 1, d);
+ if (r)
+ return r;
+ if (d[0] & DMP_INT_SMD) {
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_smd");
+ st->chip_config.smd_enable = false;
+ st->chip_config.smd_triggered = true;
+ }
+ if (d[0] & DMP_INT_PED)
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_pedometer");
+
+ return 0;
+}
+
+static int inv_get_shift2(int count)
+{
+ int i;
+
+ if (1 == count)
+ return 13;
+ if (count > 2000)
+ return 2;
+ i = 13;
+ while (count > 0) {
+ count >>= 1;
+ i--;
+ }
+
+ return i;
+}
+
+static void inv_adjust_sensor_ts(struct inv_mpu_state *st, int sensor_ind)
+{
+ s64 diff;
+ int i, rate_adj, s3, delta, total_count;
+
+ if (!st->chip_config.adjust_time)
+ return;
+#define MAX_DIFF 0x7fffffff
+ total_count = st->dmp_ticks;
+ if (0 == total_count)
+ total_count = 1;
+
+ diff = (st->last_ts - st->prev_ts) - (u64)(st->dmp_interval) *
+ total_count;
+ if (diff > MAX_DIFF)
+ diff = MAX_DIFF;
+ if (diff < -MAX_DIFF)
+ diff = -MAX_DIFF;
+ s3 = 4;
+ rate_adj = (int)diff;
+ rate_adj /= total_count;
+ delta = min(abs(rate_adj) >> inv_get_shift2(total_count),
+ DMP_INTERVAL_MIN_ADJ);
+ if (rate_adj < 0)
+ delta = -delta;
+ st->dmp_interval_accum >>= 1;
+ st->dmp_interval_accum += delta;
+ st->dmp_interval += st->dmp_interval_accum;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ if (st->sensor[i].on)
+ st->sensor[i].dur = st->dmp_interval *
+ st->sensor[i].counter;
+
+ st->prev_ts = st->last_ts;
+}
+
+static void inv_reset_ts(struct inv_mpu_state *st, u64 curr_ts)
+{
+ u32 dur, i;
+
+ dur = USEC_PER_SEC / st->bytes_per_sec;
+ dur *= 1024;
+ curr_ts -= ((u64)dur * NSEC_PER_USEC);
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = curr_ts;
+}
+
+static void inv_push_step_indicator(struct inv_mpu_state *st, int sensor_ind,
+ int steps)
+{
+ int dur, i;
+ s16 sen[3];
+ u64 base;
+#define STEP_INDICATOR_HEADER 0x0001
+
+ dur = st->sensor[sensor_ind].dur / steps;
+ base = st->sensor[sensor_ind].ts;
+
+ for (i = 1; i < steps; i++)
+ inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER,
+ base + i * dur, sen);
+}
+
+static int inv_parse_header(u16 hdr)
+{
+ switch (hdr) {
+ case ACCEL_HDR:
+ return SENSOR_ACCEL;
+ case GYRO_HDR:
+ return SENSOR_GYRO;
+ case PEDQUAT_HDR:
+ return SENSOR_PEDQ;
+ case LPQUAT_HDR:
+ return SENSOR_LPQ;
+ case SIXQUAT_HDR:
+ return SENSOR_SIXQ;
+ case COMPASS_HDR:
+ case COMPASS_HDR_2:
+ return SENSOR_COMPASS;
+ case PRESSURE_HDR:
+ return SENSOR_PRESSURE;
+ case STEP_DETECTOR_HDR:
+ return SENSOR_STEP;
+ default:
+ return SENSOR_INVALID;
+ }
+}
+#define FEATURE_IKR_PANIC 1
+
+static int inv_process_batchmode(struct inv_mpu_state *st)
+{
+ int i, target_bytes, tmp, res, counter;
+ int sensor_ind, q[3];
+ u8 *dptr, *d;
+ u16 hdr, steps;
+ s16 sen[3];
+ u64 t;
+ bool done_flag;
+
+#if FEATURE_IKR_PANIC
+ if (1024 <= st->fifo_count) {
+ if (1024 < st->fifo_count) {
+ pr_err("fifo_count over spec\n");
+ return 0;
+ }
+ inv_reset_ts(st, st->last_ts);
+ st->left_over_size = 0;
+ }
+#else
+ if (1024 == st->fifo_count) {
+ inv_reset_ts(st, st->last_ts);
+ st->left_over_size = 0;
+ }
+#endif
+
+ d = fifo_data;
+ if (st->left_over_size > 0) {
+#if FEATURE_IKR_PANIC
+ if (st->left_over_size > HEADERED_Q_BYTES) {
+ pr_err("left_over_size overflow 1\n");
+ st->left_over_size = HEADERED_Q_BYTES;
+ }
+#endif
+ dptr = d + st->left_over_size;
+ memcpy(d, st->left_over, st->left_over_size);
+ } else {
+ dptr = d;
+ }
+ target_bytes = st->fifo_count;
+ while (target_bytes > 0) {
+ if (target_bytes < MAX_READ_SIZE)
+ tmp = target_bytes;
+ else
+ tmp = MAX_READ_SIZE;
+ res = inv_i2c_read(st, st->reg.fifo_r_w, tmp, dptr);
+ if (res < 0)
+ return res;
+ dptr += tmp;
+ target_bytes -= tmp;
+ }
+ dptr = d;
+ done_flag = false;
+ target_bytes = st->fifo_count + st->left_over_size;
+ counter = 0;
+ while ((dptr - d <= target_bytes - HEADERED_NORMAL_BYTES) &&
+ (!done_flag)) {
+ hdr = (u16)be16_to_cpup((__be16 *)(dptr));
+ steps = (hdr & STEP_INDICATOR_MASK);
+ hdr &= (~STEP_INDICATOR_MASK);
+ sensor_ind = inv_parse_header(hdr);
+ /* error packet */
+ if ((sensor_ind == SENSOR_INVALID) ||
+ (!st->sensor[sensor_ind].on)) {
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ /* incomplete packet */
+ if (target_bytes - (dptr - d) <
+ st->sensor[sensor_ind].sample_size) {
+ done_flag = true;
+ continue;
+ }
+ if (sensor_ind == SENSOR_STEP) {
+ tmp = (int)be32_to_cpup((__be32 *)(dptr + 4));
+ t = st->step_detector_base_ts +
+ (u64)tmp * 5 * NSEC_PER_MSEC;
+ inv_push_8bytes_buffer(st, hdr, t, sen);
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (steps > 1)
+ inv_push_step_indicator(st, sensor_ind, steps);
+ st->sensor[sensor_ind].ts += (u64)st->sensor[sensor_ind].dur;
+ t = st->sensor[sensor_ind].ts;
+ if (sensor_ind == SENSOR_COMPASS) {
+ if (!st->chip_config.normal_compass_measure) {
+ st->chip_config.normal_compass_measure = 1;
+ } else if (COMPASS_HDR == hdr) {
+ for (i = 0; i < 6; i++)
+ st->fifo_data[i] = dptr[i + 2];
+ res = st->slave_compass->read_data(st, sen);
+ if (!res)
+ inv_push_8bytes_buffer(st, hdr |
+ (!!steps), t, sen);
+ } else if (COMPASS_HDR_2 == hdr) {
+ inv_push_marker_to_buffer(st, hdr);
+ }
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (sensor_ind == SENSOR_PRESSURE) {
+ if (!st->chip_config.normal_pressure_measure) {
+ st->chip_config.normal_pressure_measure = 1;
+ } else {
+ for (i = 0; i < 6; i++)
+ st->fifo_data[i] = dptr[i + 2];
+ res = st->slave_pressure->read_data(st, sen);
+ if (!res)
+ inv_push_8bytes_buffer(st, hdr |
+ (!!steps), t, sen);
+ }
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (st->sensor[sensor_ind].sample_size == HEADERED_Q_BYTES) {
+ for (i = 0; i < 3; i++)
+ q[i] = (int)be32_to_cpup((__be32 *)(dptr + 4
+ + i * 4));
+ inv_push_16bytes_buffer(st, hdr | (!!steps), t, q);
+ } else {
+ for (i = 0; i < 3; i++)
+ sen[i] = (short)be16_to_cpup((__be16 *)(dptr +
+ 2 + i * 2));
+ inv_push_8bytes_buffer(st, hdr | (!!steps), t, sen);
+ }
+ dptr += st->sensor[sensor_ind].sample_size;
+ }
+ inv_adjust_sensor_ts(st, sensor_ind);
+ st->left_over_size = target_bytes - (dptr - d);
+
+ if (st->left_over_size) {
+#if FEATURE_IKR_PANIC
+ if (st->left_over_size > HEADERED_Q_BYTES) {
+ pr_err("left_over_size overflow 2\n");
+ st->left_over_size = HEADERED_Q_BYTES;
+ }
+#endif
+ memcpy(st->left_over, dptr, st->left_over_size);
+ }
+
+ return 0;
+}
+
+int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume)
+{
+ int result;
+ u32 counter;
+ u8 data[4];
+
+#define MIN_TICK_READING_TIME NSEC_PER_SEC
+ st->last_ts = get_time_ns();
+ if ((st->last_ts - st->prev_ts < MIN_TICK_READING_TIME) &&
+ (!resume)) {
+ st->chip_config.adjust_time = false;
+ return 0;
+ }
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, data);
+ if (result)
+ return result;
+
+ counter = be32_to_cpup((__be32 *)(data));
+ if (resume) {
+ st->dmp_counter = counter;
+ st->prev_ts = st->last_ts;
+
+ return 0;
+ }
+ if (counter > st->dmp_counter)
+ st->dmp_ticks = counter - st->dmp_counter;
+ else
+ st->dmp_ticks = 0xffffffff - st->dmp_counter + counter + 1;
+ st->dmp_counter = counter;
+ st->chip_config.adjust_time = true;
+
+ 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_state *st = (struct inv_mpu_state *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result, bpm;
+ u8 data[MAX_HW_FIFO_BYTES];
+ u16 fifo_count;
+ struct inv_reg_map_s *reg;
+ u64 pts1;
+
+#define DMP_MIN_RUN_TIME (37 * NSEC_PER_MSEC)
+ if (st->suspend_state)
+ return IRQ_HANDLED;
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.dmp_on) {
+ pts1 = get_time_ns();
+ result = inv_process_dmp_interrupt(st);
+ if (result || st->chip_config.dmp_event_int_on)
+ goto end_session;
+ if (!st->chip_config.smd_triggered) {
+ if (pts1 - st->last_run_time < DMP_MIN_RUN_TIME)
+ goto end_session;
+ else
+ st->last_run_time = pts1;
+ } else {
+ st->chip_config.smd_triggered = false;
+ }
+ }
+
+ if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable))
+ goto end_session;
+
+ reg = &st->reg;
+ if (!(st->sensor[SENSOR_ACCEL].on |
+ st->sensor[SENSOR_GYRO].on |
+ st->sensor[SENSOR_COMPASS].on |
+ st->sensor[SENSOR_PRESSURE].on |
+ st->chip_config.dmp_on |
+ st->mot_int.mot_on))
+ goto end_session;
+ if (st->chip_config.lpa_mode) {
+ result = inv_i2c_read(st, reg->raw_accel,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto end_session;
+ inv_report_gyro_accel(indio_dev, data, get_time_ns());
+ if (st->mot_int.mot_on)
+ inv_process_motion(st);
+
+ goto end_session;
+ }
+
+ if (st->chip_config.dmp_on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ goto end_session;
+ }
+ bpm = st->chip_config.bytes_per_datum;
+ fifo_count = 0;
+ if (bpm) {
+ 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));
+ /* fifo count can't be odd number */
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count == 0)
+ goto end_session;
+ st->fifo_count = fifo_count;
+ }
+
+ if (st->chip_config.dmp_on) {
+ result = inv_process_batchmode(st);
+ } else {
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ if (bpm) {
+ while (fifo_count >= bpm) {
+ result = inv_i2c_read(st, reg->fifo_r_w, bpm,
+ data);
+ if (result)
+ goto flush_fifo;
+ result = inv_get_timestamp(st,
+ fifo_count / bpm);
+ if (result)
+ goto flush_fifo;
+ inv_report_gyro_accel(indio_dev, data,
+ st->last_ts);
+ fifo_count -= bpm;
+ }
+ } else {
+ result = inv_get_timestamp(st, 1);
+ if (result)
+ goto flush_fifo;
+ }
+ inv_send_compass_data(st);
+ inv_send_pressure_data(st);
+ }
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+
+ return IRQ_HANDLED;
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ mutex_unlock(&indio_dev->mlock);
+
+ return IRQ_HANDLED;
+}
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ free_irq(st->client->irq, st);
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ if (st->chip_config.enable) {
+ result = set_inv_enable(indio_dev, false);
+ if (result)
+ return result;
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+static int inv_check_conflict_sysfs(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ if (st->chip_config.lpa_mode) {
+ /* dmp cannot run with low power mode on */
+ st->chip_config.dmp_on = 0;
+ st->chip_config.gyro_enable = false;
+ st->sensor[SENSOR_GYRO].on = false;
+ st->sensor[SENSOR_COMPASS].on = false;
+ }
+ if (st->sensor[SENSOR_GYRO].on &&
+ (!st->chip_config.gyro_enable)) {
+ st->chip_config.gyro_enable = true;
+ }
+ if (st->sensor[SENSOR_ACCEL].on &&
+ (!st->chip_config.accel_enable)) {
+ st->chip_config.accel_enable = true;
+ }
+
+ return 0;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+ int result;
+
+ result = inv_check_conflict_sysfs(indio_dev);
+ if (result)
+ return result;
+ result = iio_sw_buffer_preenable(indio_dev);
+
+ return result;
+}
+
+void inv_init_sensor_struct(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (i < SENSOR_SIXQ)
+ st->sensor[i].sample_size =
+ HEADERED_NORMAL_BYTES;
+ else
+ st->sensor[i].sample_size = HEADERED_Q_BYTES;
+ if (i == SENSOR_STEP) {
+ st->sensor[i].rate = 1;
+ st->sensor[i].dur = NSEC_PER_SEC;
+ } else {
+ st->sensor[i].rate = INIT_DMP_OUTPUT_RATE;
+ st->sensor[i].dur = NSEC_PER_SEC /
+ INIT_DMP_OUTPUT_RATE;
+ }
+ }
+
+ st->sensor[SENSOR_ACCEL].send_data = inv_send_accel_data;
+ st->sensor[SENSOR_GYRO].send_data = inv_send_gyro_data;
+ st->sensor[SENSOR_COMPASS].send_data = inv_send_compass_dmp_data;
+ st->sensor[SENSOR_PRESSURE].send_data = inv_send_pressure_dmp_data;
+ st->sensor[SENSOR_STEP].send_data = inv_send_step_detector;
+ st->sensor[SENSOR_PEDQ].send_data = inv_send_ped_q_data;
+ st->sensor[SENSOR_SIXQ].send_data = inv_send_six_q_data;
+ st->sensor[SENSOR_LPQ].send_data = inv_send_three_q_data;
+
+ st->sensor[SENSOR_ACCEL].set_rate = inv_set_accel_rate;
+ st->sensor[SENSOR_GYRO].set_rate = inv_set_gyro_rate;
+ st->sensor[SENSOR_COMPASS].set_rate = inv_set_compass_rate;
+ st->sensor[SENSOR_PRESSURE].set_rate = inv_set_pressure_rate;
+ st->sensor[SENSOR_STEP].set_rate = inv_set_step_detector;
+ st->sensor[SENSOR_PEDQ].set_rate = inv_set_pedq_rate;
+ st->sensor[SENSOR_SIXQ].set_rate = inv_set_sixq_rate;
+ st->sensor[SENSOR_LPQ].set_rate = inv_set_lpq_rate;
+}
+
+int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ u8 data[FIFO_COUNT_BYTE];
+ int result;
+
+ reg = &st->reg;
+ if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable))
+ return -EINVAL;
+
+ if (st->batch.on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ return result;
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ st->fifo_count = be16_to_cpup((__be16 *)(data));
+ if (st->fifo_count) {
+ result = inv_process_batchmode(st);
+ if (result)
+ return result;
+ *has_data = !!st->fifo_count;
+ inv_push_marker_to_buffer(st, END_MARKER);
+ result = write_be32_key_to_mem(st, 0,
+ KEY_BM_BATCH_CNTR);
+ return result;
+ }
+ }
+ inv_push_marker_to_buffer(st, EMPTY_MARKER);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+ .preenable = &inv_preenable,
+ .predisable = &inv_predisable,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *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*/
+ if (INV_MPU3050 == st->chip_type)
+ ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+ inv_read_fifo_mpu3050,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ else
+ ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ if (ret)
+ goto error_iio_sw_rb_free;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ return 0;
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+
+ return ret;
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c
new file mode 100755
index 00000000000..ad67c089cc3
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c
@@ -0,0 +1,94 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+
+#include "inv_mpu_iio.h"
+
+/*
+ * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state
+ */
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ return 0;
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifdef CONFIG_INV_KERNEL_3_10
+ st->trig = iio_trigger_alloc("%s-dev%d",
+#else
+ st->trig = iio_allocate_trigger("%s-dev%d",
+#endif
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL)
+ return -ENOMEM;
+ st->trig->dev.parent = &st->client->dev;
+#ifndef CONFIG_INV_KERNEL_3_10
+ st->trig->private_data = indio_dev;
+#endif
+ st->trig->ops = &inv_mpu_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ if (ret) {
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_trigger_free(st->trig);
+#else
+ iio_free_trigger(st->trig);
+#endif
+ return -EPERM;
+ }
+ indio_dev->trig = st->trig;
+
+ return 0;
+}
+
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+#ifdef CONFIG_INV_KERNEL_3_10
+ iio_trigger_free(st->trig);
+#else
+ iio_free_trigger(st->trig);
+#endif
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c
new file mode 100644
index 00000000000..99ae702e67a
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c
@@ -0,0 +1,315 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define BMA250_CHIP_ID 3
+#define BMA250_RANGE_SET 0
+#define BMA250_BW_SET 4
+
+/* range and bandwidth */
+#define BMA250_RANGE_2G 3
+#define BMA250_RANGE_4G 5
+#define BMA250_RANGE_8G 8
+#define BMA250_RANGE_16G 12
+#define BMA250_RANGE_MAX 4
+#define BMA250_RANGE_MASK 0xF0
+
+#define BMA250_BW_7_81HZ 0x08
+#define BMA250_BW_15_63HZ 0x09
+#define BMA250_BW_31_25HZ 0x0A
+#define BMA250_BW_62_50HZ 0x0B
+#define BMA250_BW_125HZ 0x0C
+#define BMA250_BW_250HZ 0x0D
+#define BMA250_BW_500HZ 0x0E
+#define BMA250_BW_1000HZ 0x0F
+#define BMA250_MAX_BW_SIZE 8
+#define BMA250_BW_REG_MASK 0xE0
+
+/* register definitions */
+#define BMA250_X_AXIS_LSB_REG 0x02
+#define BMA250_RANGE_SEL_REG 0x0F
+#define BMA250_BW_SEL_REG 0x10
+#define BMA250_MODE_CTRL_REG 0x11
+
+/* mode settings */
+#define BMA250_MODE_NORMAL 0
+#define BMA250_MODE_LOWPOWER 1
+#define BMA250_MODE_SUSPEND 2
+#define BMA250_MODE_MAX 3
+#define BMA250_MODE_MASK 0x3F
+#define BMA250_BIT_SUSPEND 0x80
+#define BMA250_BIT_LP 0x40
+
+struct bma_property {
+ int range;
+ int bandwidth;
+ int mode;
+};
+
+static struct bma_property bma_static_property = {
+ .range = BMA250_RANGE_SET,
+ .bandwidth = BMA250_BW_SET,
+ .mode = BMA250_MODE_SUSPEND
+};
+
+static int bma250_set_bandwidth(struct inv_mpu_state *st, u8 bw)
+{
+ int res;
+ u8 data;
+ int bandwidth;
+ switch (bw) {
+ case 0:
+ bandwidth = BMA250_BW_7_81HZ;
+ break;
+ case 1:
+ bandwidth = BMA250_BW_15_63HZ;
+ break;
+ case 2:
+ bandwidth = BMA250_BW_31_25HZ;
+ break;
+ case 3:
+ bandwidth = BMA250_BW_62_50HZ;
+ break;
+ case 4:
+ bandwidth = BMA250_BW_125HZ;
+ break;
+ case 5:
+ bandwidth = BMA250_BW_250HZ;
+ break;
+ case 6:
+ bandwidth = BMA250_BW_500HZ;
+ break;
+ case 7:
+ bandwidth = BMA250_BW_1000HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_BW_REG_MASK;
+ data |= bandwidth;
+ res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+ return res;
+}
+
+static int bma250_set_range(struct inv_mpu_state *st, u8 range)
+{
+ int res;
+ u8 orig, data;
+ switch (range) {
+ case 0:
+ data = BMA250_RANGE_2G;
+ break;
+ case 1:
+ data = BMA250_RANGE_4G;
+ break;
+ case 2:
+ data = BMA250_RANGE_8G;
+ break;
+ case 3:
+ data = BMA250_RANGE_16G;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+ if (res)
+ return res;
+ orig &= BMA250_RANGE_MASK;
+ data |= orig;
+ res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.range = range;
+
+ return 0;
+}
+
+static int setup_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[2];
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ /*read secondary i2c ID register */
+ result = inv_secondary_read(0, 1, data);
+ if (result)
+ return result;
+ if (BMA250_CHIP_ID != data[0])
+ return -EINVAL;
+ result = set_3050_bypass(st, false);
+ if (result)
+ return result;
+ /*AUX(accel), slave address is set inside set_3050_bypass*/
+ /* bma250 x axis LSB register address is 2 */
+ result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR,
+ BMA250_X_AXIS_LSB_REG);
+
+ return result;
+}
+
+static int bma250_set_mode(struct inv_mpu_state *st, u8 mode)
+{
+ int res;
+ u8 data;
+
+ res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_MODE_MASK;
+ switch (mode) {
+ case BMA250_MODE_NORMAL:
+ break;
+ case BMA250_MODE_LOWPOWER:
+ data |= BMA250_BIT_LP;
+ break;
+ case BMA250_MODE_SUSPEND:
+ data |= BMA250_BIT_SUSPEND;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.mode = mode;
+
+ return 0;
+}
+
+static int suspend_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ bma250_set_mode(st, BMA250_MODE_SUSPEND);
+ /* no need to recover to non-bypass mode because we need it now */
+
+ return 0;
+}
+
+static int resume_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_NORMAL)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_mode(st, BMA250_MODE_NORMAL);
+ /* recover bypass mode */
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static int combine_data_slave_bma250(u8 *in, short *out)
+{
+ out[0] = le16_to_cpup((__le16 *)(&in[0]));
+ out[1] = le16_to_cpup((__le16 *)(&in[2]));
+ out[2] = le16_to_cpup((__le16 *)(&in[4]));
+
+ return 0;
+}
+
+static int get_mode_slave_bma250(void)
+{
+ switch (bma_static_property.mode) {
+ case BMA250_MODE_SUSPEND:
+ return INV_MODE_SUSPEND;
+ case BMA250_MODE_NORMAL:
+ return INV_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7};
+ const int d[] = {7, 6, 5, 4, 3, 2, 1, 0};
+ int i, h, data, result;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1))
+ i++;
+ data = d[i];
+
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_bandwidth(st, (u8) data);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+/**
+ * set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_mpu_state *st, int fs)
+{
+ int result;
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_range(st, (u8) fs);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static struct inv_mpu_slave slave_bma250 = {
+ .suspend = suspend_slave_bma250,
+ .resume = resume_slave_bma250,
+ .setup = setup_slave_bma250,
+ .combine_data = combine_data_slave_bma250,
+ .get_mode = get_mode_slave_bma250,
+ .set_lpf = set_lpf_bma250,
+ .set_fs = set_fs_bma250
+};
+
+int inv_register_mpu3050_slave(struct inv_mpu_state *st)
+{
+ st->slave_accel = &slave_bma250;
+
+ return 0;
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c
new file mode 100755
index 00000000000..68b76d3a8e0
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c
@@ -0,0 +1,854 @@
+/*
+* 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 "inv_mpu_iio.h"
+/* AKM definitions */
+#define REG_AKM_ID 0x00
+#define REG_AKM_INFO 0x01
+#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
+
+/* AK09911 register definition */
+#define REG_AK09911_DMP_READ 0x3
+#define REG_AK09911_STATUS1 0x10
+#define REG_AK09911_CNTL2 0x31
+#define REG_AK09911_SENSITIVITY 0x60
+
+#define DATA_AKM_ID 0x48
+#define DATA_AKM_MODE_PD 0x00
+#define DATA_AKM_MODE_SM 0x01
+#define DATA_AKM_MODE_ST 0x08
+#define DATA_AKM_MODE_FR 0x0F
+#define DATA_AK09911_MODE_FR 0x1F
+#define DATA_AKM_SELF_TEST 0x40
+#define DATA_AKM_DRDY 0x01
+#define DATA_AKM8963_BIT 0x10
+#define DATA_AKM_STAT_MASK 0x0C
+
+#define DATA_AKM8975_SCALE (9830 * (1L << 15))
+#define DATA_AKM8972_SCALE (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE0 (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE1 (4915 * (1L << 15))
+#define DATA_AK09911_SCALE (19661 * (1L << 15))
+#define DATA_MLX_SCALE (4915 * (1L << 15))
+#define DATA_MLX_SCALE_EMPIRICAL (26214 * (1L << 15))
+
+#define DATA_AKM8963_SCALE_SHIFT 4
+#define DATA_AKM_99_BYTES_DMP 10
+#define DATA_AKM_89_BYTES_DMP 9
+#define DATA_AKM_MIN_READ_TIME (9 * NSEC_PER_MSEC)
+
+#define DEF_ST_COMPASS_WAIT_MIN (10 * 1000)
+#define DEF_ST_COMPASS_WAIT_MAX (15 * 1000)
+#define DEF_ST_COMPASS_TRY_TIMES 10
+#define DEF_ST_COMPASS_8963_SHIFT 2
+#define X 0
+#define Y 1
+#define Z 2
+
+/* milliseconds between each access */
+#define AKM_RATE_SCALE 10
+#define MLX_RATE_SCALE 50
+
+/* MLX90399 compass definition */
+#define DATA_MLX_CMD_READ_MEASURE 0x4F
+#define DATA_MLX_CMD_SINGLE_MEASURE 0x3F
+#define DATA_MLX_READ_DATA_BYTES 9
+#define DATA_MLX_STATUS_DATA 3
+#define DATA_MLX_MIN_READ_TIME (95 * NSEC_PER_MSEC)
+
+static const short AKM8975_ST_Lower[3] = {-100, -100, -1000};
+static const short AKM8975_ST_Upper[3] = {100, 100, -300};
+
+static const short AKM8972_ST_Lower[3] = {-50, -50, -500};
+static const short AKM8972_ST_Upper[3] = {50, 50, -100};
+
+static const short AKM8963_ST_Lower[3] = {-200, -200, -3200};
+static const short AKM8963_ST_Upper[3] = {200, 200, -800};
+
+/*
+ * inv_setup_compass_akm() - Configure akm series compass.
+ */
+static int inv_setup_compass_akm(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[4];
+ u8 sens, mode, cmd;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+ /* read secondary i2c ID register */
+ result = inv_secondary_read(REG_AKM_ID, 1, data);
+ if (result)
+ return result;
+ if (data[0] != DATA_AKM_ID)
+ return -ENXIO;
+ /* set AKM to Fuse ROM access mode */
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ mode = REG_AK09911_CNTL2;
+ sens = REG_AK09911_SENSITIVITY;
+ cmd = DATA_AK09911_MODE_FR;
+ } else {
+ mode = REG_AKM_MODE;
+ sens = REG_AKM_SENSITIVITY;
+ cmd = DATA_AKM_MODE_FR;
+ }
+
+ result = inv_secondary_write(mode, cmd);
+ if (result)
+ return result;
+ result = inv_secondary_read(sens, THREE_AXIS,
+ st->chip_info.compass_sens);
+ if (result)
+ return result;
+ /* revert to power down mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ if (result)
+ return result;
+ pr_debug("%s senx=%d, seny=%d, senz=%d\n",
+ st->hw->name,
+ st->chip_info.compass_sens[0],
+ st->chip_info.compass_sens[1],
+ st->chip_info.compass_sens[2]);
+ /* restore to non-bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+
+ /* setup master mode and master clock and ES bit */
+ result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (result)
+ return result;
+ /* slave 1 is used for AKM mode change only */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM mode register address */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, mode);
+ if (result)
+ return result;
+ /* output data for slave 1 is fixed, single measure mode */
+ st->slave_compass->scale = 1;
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8975_ST_Upper;
+ st->slave_compass->st_lower = AKM8975_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8972_ST_Upper;
+ st->slave_compass->st_lower = AKM8972_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8963_ST_Upper;
+ st->slave_compass->st_lower = AKM8963_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM |
+ (st->slave_compass->scale << DATA_AKM8963_SCALE_SHIFT);
+ } else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8963_ST_Upper;
+ st->slave_compass->st_lower = AKM8963_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else {
+ return -EINVAL;
+ }
+
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, data[0]);
+
+ return result;
+}
+
+static int inv_akm_read_data(struct inv_mpu_state *st, short *o)
+{
+ int result, shift;
+ int i;
+ u8 d[DATA_AKM_99_BYTES_DMP - 1];
+ u8 *sens;
+
+ sens = st->chip_info.compass_sens;
+ result = 0;
+ if (st->chip_config.dmp_on) {
+ for (i = 0; i < 6; i++)
+ d[1 + i] = st->fifo_data[i];
+ } else {
+ result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+ DATA_AKM_99_BYTES_DMP - 1, d);
+ if ((DATA_AKM_DRDY != d[0]) || (d[7] & 0x8) || result)
+ result = -EINVAL;
+ }
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ shift = 7;
+ else
+ shift = 8;
+ for (i = 0; i < 3; i++) {
+ o[i] = (short)((d[i * 2 + 1] << 8) | d[i * 2 + 2]);
+ o[i] = (short)(((int)o[i] * (sens[i] + 128)) >> shift);
+ }
+
+ return result;
+}
+
+static int inv_mlx_read_data(struct inv_mpu_state *st, short *o)
+{
+ int result;
+ int i, z;
+ u8 d[DATA_MLX_READ_DATA_BYTES];
+
+ result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+ DATA_MLX_READ_DATA_BYTES, d);
+ if ((!(d[0] & ~DATA_MLX_STATUS_DATA)) && (!result)) {
+ for (i = 0; i < 3; i++)
+ o[i] = (short)((d[i * 2 + 3] << 8) + d[i * 2 + 4]);
+ } else {
+ for (i = 0; i < 3; i++)
+ o[i] = 0;
+ }
+ z = o[2];
+ /* axis sensitivity conversion. Z axis has different sensitiviy from
+ x and y */
+ z *= 26;
+ z /= 15;
+ o[2] = z;
+
+ return 0;
+}
+
+static int inv_check_akm_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[6], mode;
+ u8 counter, cntl;
+ short x, y, z;
+ u8 *sens;
+ sens = st->chip_info.compass_sens;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result) {
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+ }
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ mode = REG_AK09911_CNTL2;
+ else
+ mode = REG_AKM_MODE;
+ /* set to power down mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ if (result)
+ goto AKM_fail;
+
+ /* write 1 to ASTC register */
+ result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST);
+ if (result)
+ goto AKM_fail;
+ /* set self test mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_ST);
+ if (result)
+ goto AKM_fail;
+ counter = DEF_ST_COMPASS_TRY_TIMES;
+ while (counter > 0) {
+ usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX);
+ result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+ if (result)
+ goto AKM_fail;
+ if ((data[0] & DATA_AKM_DRDY) == 0)
+ counter--;
+ else
+ counter = 0;
+ }
+ if ((data[0] & DATA_AKM_DRDY) == 0) {
+ result = -EINVAL;
+ goto AKM_fail;
+ }
+ result = inv_secondary_read(REG_AKM_MEASURE_DATA,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto AKM_fail;
+
+ x = le16_to_cpup((__le16 *)(&data[0]));
+ y = le16_to_cpup((__le16 *)(&data[2]));
+ z = le16_to_cpup((__le16 *)(&data[4]));
+ x = ((x * (sens[0] + 128)) >> 8);
+ y = ((y * (sens[1] + 128)) >> 8);
+ z = ((z * (sens[2] + 128)) >> 8);
+ if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+ if (result)
+ goto AKM_fail;
+ if (0 == (cntl & DATA_AKM8963_BIT)) {
+ x <<= DEF_ST_COMPASS_8963_SHIFT;
+ y <<= DEF_ST_COMPASS_8963_SHIFT;
+ z <<= DEF_ST_COMPASS_8963_SHIFT;
+ }
+ }
+ result = -EINVAL;
+ if (x > st->slave_compass->st_upper[X] ||
+ x < st->slave_compass->st_lower[X])
+ goto AKM_fail;
+ if (y > st->slave_compass->st_upper[Y] ||
+ y < st->slave_compass->st_lower[Y])
+ goto AKM_fail;
+ if (z > st->slave_compass->st_upper[Z] ||
+ z < st->slave_compass->st_lower[Z])
+ goto AKM_fail;
+ result = 0;
+AKM_fail:
+ /*write 0 to ASTC register */
+ result |= inv_secondary_write(REG_AKM_ST_CTRL, 0);
+ /*set to power down mode */
+ result |= inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ /*restore to non-bypass mode */
+ result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+}
+
+/*
+ * inv_write_akm_scale() - Configure the akm scale range.
+ */
+static int inv_write_akm_scale(struct inv_mpu_state *st, int data)
+{
+ char d, en;
+ int result;
+
+ if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+ return 0;
+ en = !!data;
+ if (st->slave_compass->scale == en)
+ return 0;
+ d = (DATA_AKM_MODE_SM | (en << DATA_AKM8963_SCALE_SHIFT));
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, d);
+ if (result)
+ return result;
+ st->slave_compass->scale = en;
+
+ return 0;
+}
+
+/*
+ * inv_read_akm_scale() - show AKM scale.
+ */
+static int inv_read_akm_scale(struct inv_mpu_state *st, int *scale)
+{
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8975_SCALE;
+ else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8972_SCALE;
+ else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+ if (st->slave_compass->scale)
+ *scale = DATA_AKM8963_SCALE1;
+ else
+ *scale = DATA_AKM8963_SCALE0;
+ else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ *scale = DATA_AK09911_SCALE;
+ else
+ return -EINVAL;
+
+ return IIO_VAL_INT;
+}
+
+static int inv_suspend_akm(struct inv_mpu_state *st)
+{
+ int result;
+
+ /* slave 0 is disabled */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (result)
+ return result;
+ /* slave 1 is disabled */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0);
+
+ return result;
+}
+
+static int inv_resume_akm(struct inv_mpu_state *st)
+{
+ int result;
+ u8 reg_addr, bytes;
+
+ /* slave 0 is used to read data from compass */
+ /*read mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM status register address is 1 */
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ if (st->chip_config.dmp_on) {
+ reg_addr = REG_AK09911_DMP_READ;
+ bytes = DATA_AKM_99_BYTES_DMP;
+ } else {
+ reg_addr = REG_AK09911_STATUS1;
+ bytes = DATA_AKM_99_BYTES_DMP - 1;
+ }
+ } else {
+ if (st->chip_config.dmp_on) {
+ reg_addr = REG_AKM_INFO;
+ bytes = DATA_AKM_89_BYTES_DMP;
+ } else {
+ reg_addr = REG_AKM_STATUS;
+ bytes = DATA_AKM_89_BYTES_DMP - 1;
+ }
+ }
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, reg_addr);
+ if (result)
+ return result;
+
+ /* slave 0 is enabled, read 10 or 8 bytes from here, swap bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_GRP |
+ INV_MPU_BIT_BYTE_SW |
+ INV_MPU_BIT_SLV_EN |
+ bytes);
+ if (result)
+ return result;
+ /* slave 1 is enabled, write byte length is 1 */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ return result;
+}
+
+/*
+ * inv_write_mlx_scale() - Configure the mlx90399 scale range.
+ */
+static int inv_write_mlx_scale(struct inv_mpu_state *st, int data)
+{
+ st->slave_compass->scale = data;
+ return 0;
+}
+
+/*
+ * inv_read_mlx_scale() - show mlx90399 scale.
+ */
+static int inv_read_mlx_scale(struct inv_mpu_state *st, int *scale)
+{
+ *scale = st->slave_compass->scale;
+ return IIO_VAL_INT;
+}
+
+static int inv_i2c_read_mlx(struct inv_mpu_state *st, u16 i2c_addr,
+ u16 length, u8 *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = I2C_M_RD;
+ msgs[0].buf = data;
+ msgs[0].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 1);
+
+ if (res < 1) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ return res;
+}
+
+static int inv_i2c_write_mlx(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 data)
+{
+ u8 tmp[1];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = data;
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 1;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_read_reg_mlx(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u16 *val)
+{
+ u8 tmp[10];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = 0x50;
+ tmp[1] = (reg << 2);
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ }
+ res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp);
+ if (res)
+ return res;
+ *val = ((tmp[1] << 8) | tmp[2]);
+
+ return res;
+}
+
+static int inv_i2c_write_mlx_reg(struct inv_mpu_state *st,
+ u16 i2c_addr, int reg, u16 d)
+{
+ u8 tmp[10];
+ struct i2c_msg msg;
+ int res;
+
+ /* write register command, writing volatile memory */
+ tmp[0] = 0x60;
+ tmp[1] = ((d >> 8) & 0xff);
+ tmp[2] = (d & 0xff);
+ tmp[3] = (reg << 2);
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 4;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ }
+ /* read status */
+ res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp);
+
+ return res;
+}
+
+static int inv_write_mlx_cmd(struct inv_mpu_state *st, u8 cmd)
+{
+ int result;
+ u8 d[10];
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+ result = inv_i2c_write_mlx(st, addr, cmd);
+ if (result)
+ return result;
+ /* read back status byte */
+ result = inv_i2c_read_mlx(st, addr, 10, d);
+
+ return result;
+}
+
+static int inv_read_mlx_z_axis(struct inv_mpu_state *st, s16 *z)
+{
+ int result;
+ u8 d[10];
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* measure z axis */
+ result = inv_write_mlx_cmd(st, 0x39);
+ if (result)
+ return result;
+ msleep(100);
+ /* read z axis */
+ result = inv_i2c_write_mlx(st, addr, 0x49);
+ if (result)
+ return result;
+ /* read back status byte */
+ result = inv_i2c_read_mlx(st, addr, 10, d);
+ if (result)
+ return result;
+ if ((d[0] & 0x3) == 1)
+ *z = (short)((d[3] << 8) + d[4]);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int inv_write_mlx_reg(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+ u16 r_val;
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* write register 0.
+ set GAIN_SEL as 7;
+ set HALL_CONF as 0xC. */
+ result = inv_i2c_write_mlx_reg(st, addr, 0, 0x7c);
+ if (result)
+ return result;
+ /* write register 2.
+ set resolution is zero for all axes;
+ set DIGI filter as 6.
+ set OSR as 0.
+ set OSR2 as 0. */
+ result = inv_i2c_write_mlx_reg(st, addr, 2, 0x18);
+ if (result)
+ return result;
+ /* read register 1 */
+ result = inv_i2c_read_reg_mlx(st, addr, 1, &r_val);
+ if (result)
+ return result;
+ /* enable temp comp */
+ r_val |= 0x400;
+ result = inv_i2c_write_mlx_reg(st, addr, 1, r_val);
+ /* the value should be kept in the volatile memory */
+
+ return result;
+}
+
+static int inv_check_mlx_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+ s16 meas_ref, meas_coil;
+ u16 diff, r_val;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result) {
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+ }
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* fake read to flush the previous data */
+ result = inv_read_mlx_z_axis(st, &meas_ref);
+
+ result = inv_read_mlx_z_axis(st, &meas_ref);
+ if (result)
+ return result;
+
+ /* read register 1 */
+ result = inv_i2c_read_reg_mlx(st, addr, 0, &r_val);
+ if (result)
+ return result;
+ /* enable self test */
+ r_val |= 0x100;
+ result = inv_i2c_write_mlx_reg(st, addr, 0, r_val);
+ if (result)
+ return result;
+ msleep(200);
+ result = inv_read_mlx_z_axis(st, &meas_coil);
+ if (result)
+ return result;
+ result = inv_write_mlx_cmd(st, 0xD0);
+ if (result)
+ return result;
+ result = inv_write_mlx_reg(st);
+ if (result)
+ return result;
+ diff = abs(meas_ref - meas_coil);
+ if (diff < 25 || diff > 300)
+ result = 1;
+
+ /*restore to non-bypass mode */
+ result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+
+ return result;
+}
+
+/*
+ * inv_setup_compass_mlx() - Configure akm series compass.
+ */
+static int inv_setup_compass_mlx(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+ result = inv_write_mlx_reg(st);
+ if (result)
+ return result;
+
+ /*restore to non-bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+
+ /*setup master mode and master clock and ES bit*/
+ result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (result)
+ return result;
+
+ /* slave 0 used to write read measurement command, write mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, addr);
+ if (result)
+ return result;
+ /* ignore the register address, send out data only */
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV0_DO,
+ DATA_MLX_CMD_READ_MEASURE);
+ if (result)
+ return result;
+
+ /* slave 1 used to read status bytes and data of read measurement */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+ INV_MPU_BIT_I2C_READ | addr);
+ if (result)
+ return result;
+ /* slave 2 used to write single measurement command, write mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, addr);
+ if (result)
+ return result;
+ /* ignore the register address, send out data only */
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV2_DO,
+ DATA_MLX_CMD_SINGLE_MEASURE);
+ if (result)
+ return result;
+ /* slave 3 used to read status bytes and data of read measurement */
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR,
+ INV_MPU_BIT_I2C_READ | addr);
+
+ st->slave_compass->scale = DATA_MLX_SCALE;
+
+ return result;
+}
+
+static int inv_suspend_mlx(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0);
+
+ return result;
+}
+
+static int inv_resume_mlx(struct inv_mpu_state *st)
+{
+ int result;
+
+ /* enable, ignore register, write 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+ if (result)
+ return result;
+
+ /* enable, ignore register, read 9 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ DATA_MLX_READ_DATA_BYTES);
+ if (result)
+ return result;
+ /* enable, ignore register, write 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+ if (result)
+ return result;
+
+ /* enable, ignore register, read 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+
+ return result;
+}
+
+static struct inv_mpu_slave slave_akm = {
+ .suspend = inv_suspend_akm,
+ .resume = inv_resume_akm,
+ .get_scale = inv_read_akm_scale,
+ .set_scale = inv_write_akm_scale,
+ .self_test = inv_check_akm_self_test,
+ .setup = inv_setup_compass_akm,
+ .read_data = inv_akm_read_data,
+ .rate_scale = AKM_RATE_SCALE,
+ .min_read_time = DATA_AKM_MIN_READ_TIME,
+};
+
+static struct inv_mpu_slave slave_mlx90399 = {
+ .suspend = inv_suspend_mlx,
+ .resume = inv_resume_mlx,
+ .get_scale = inv_read_mlx_scale,
+ .set_scale = inv_write_mlx_scale,
+ .self_test = inv_check_mlx_self_test,
+ .setup = inv_setup_compass_mlx,
+ .read_data = inv_mlx_read_data,
+ .rate_scale = MLX_RATE_SCALE,
+ .min_read_time = DATA_MLX_MIN_READ_TIME,
+};
+
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st)
+{
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ case COMPASS_ID_AK8972:
+ case COMPASS_ID_AK8963:
+ case COMPASS_ID_AK09911:
+ st->slave_compass = &slave_akm;
+ break;
+ case COMPASS_ID_MLX90399:
+ st->slave_compass = &slave_mlx90399;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return st->slave_compass->setup(st);
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c
new file mode 100755
index 00000000000..24d80810449
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c
@@ -0,0 +1,522 @@
+/*
+* 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 "inv_mpu_iio.h"
+
+/* Constants */
+#define SHIFT_RIGHT_4_POSITION 4
+#define SHIFT_LEFT_2_POSITION 2
+#define SHIFT_LEFT_4_POSITION 4
+#define SHIFT_LEFT_5_POSITION 5
+#define SHIFT_LEFT_8_POSITION 8
+#define SHIFT_LEFT_12_POSITION 12
+#define SHIFT_LEFT_16_POSITION 16
+
+/* Sensor Specific constants */
+#define BMP280_SLEEP_MODE 0x00
+#define BMP280_FORCED_MODE 0x01
+#define BMP280_NORMAL_MODE 0x03
+#define BMP280_SOFT_RESET 0xB6
+
+#define BMP280_DELAYTIME_MS_NONE 0
+#define BMP280_DELAYTIME_MS_5 5
+#define BMP280_DELAYTIME_MS_6 6
+#define BMP280_DELAYTIME_MS_8 8
+#define BMP280_DELAYTIME_MS_12 12
+#define BMP280_DELAYTIME_MS_22 22
+#define BMP280_DELAYTIME_MS_38 38
+
+#define BMP280_OVERSAMPLING_SKIPPED 0x00
+#define BMP280_OVERSAMPLING_1X 0x01
+#define BMP280_OVERSAMPLING_2X 0x02
+#define BMP280_OVERSAMPLING_4X 0x03
+#define BMP280_OVERSAMPLING_8X 0x04
+#define BMP280_OVERSAMPLING_16X 0x05
+
+#define BMP280_ULTRALOWPOWER_MODE 0x00
+#define BMP280_LOWPOWER_MODE 0x01
+#define BMP280_STANDARDRESOLUTION_MODE 0x02
+#define BMP280_HIGHRESOLUTION_MODE 0x03
+#define BMP280_ULTRAHIGHRESOLUTION_MODE 0x04
+
+#define BMP280_ULTRALOWPOWER_OSRS_P BMP280_OVERSAMPLING_1X
+#define BMP280_ULTRALOWPOWER_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_LOWPOWER_OSRS_P BMP280_OVERSAMPLING_2X
+#define BMP280_LOWPOWER_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_STANDARDRESOLUTION_OSRS_P BMP280_OVERSAMPLING_4X
+#define BMP280_STANDARDRESOLUTION_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_HIGHRESOLUTION_OSRS_P BMP280_OVERSAMPLING_8X
+#define BMP280_HIGHRESOLUTION_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_ULTRAHIGHRESOLUTION_OSRS_P BMP280_OVERSAMPLING_16X
+#define BMP280_ULTRAHIGHRESOLUTION_OSRS_T BMP280_OVERSAMPLING_2X
+
+#define BMP280_FILTERCOEFF_OFF 0x00
+#define BMP280_FILTERCOEFF_2 0x01
+#define BMP280_FILTERCOEFF_4 0x02
+#define BMP280_FILTERCOEFF_8 0x03
+#define BMP280_FILTERCOEFF_16 0x04
+
+/*calibration parameters */
+#define BMP280_DIG_T1_LSB_REG 0x88
+#define BMP280_DIG_T1_MSB_REG 0x89
+#define BMP280_DIG_T2_LSB_REG 0x8A
+#define BMP280_DIG_T2_MSB_REG 0x8B
+#define BMP280_DIG_T3_LSB_REG 0x8C
+#define BMP280_DIG_T3_MSB_REG 0x8D
+#define BMP280_DIG_P1_LSB_REG 0x8E
+#define BMP280_DIG_P1_MSB_REG 0x8F
+#define BMP280_DIG_P2_LSB_REG 0x90
+#define BMP280_DIG_P2_MSB_REG 0x91
+#define BMP280_DIG_P3_LSB_REG 0x92
+#define BMP280_DIG_P3_MSB_REG 0x93
+#define BMP280_DIG_P4_LSB_REG 0x94
+#define BMP280_DIG_P4_MSB_REG 0x95
+#define BMP280_DIG_P5_LSB_REG 0x96
+#define BMP280_DIG_P5_MSB_REG 0x97
+#define BMP280_DIG_P6_LSB_REG 0x98
+#define BMP280_DIG_P6_MSB_REG 0x99
+#define BMP280_DIG_P7_LSB_REG 0x9A
+#define BMP280_DIG_P7_MSB_REG 0x9B
+#define BMP280_DIG_P8_LSB_REG 0x9C
+#define BMP280_DIG_P8_MSB_REG 0x9D
+#define BMP280_DIG_P9_LSB_REG 0x9E
+#define BMP280_DIG_P9_MSB_REG 0x9F
+
+#define BMP280_CHIPID_REG 0xD0 /*Chip ID Register */
+#define BMP280_RESET_REG 0xE0 /*Softreset Register */
+#define BMP280_STATUS_REG 0xF3 /*Status Register */
+#define BMP280_CTRLMEAS_REG 0xF4 /*Ctrl Measure Register */
+#define BMP280_CONFIG_REG 0xF5 /*Configuration Register */
+#define BMP280_PRESSURE_MSB_REG 0xF7 /*Pressure MSB Register */
+#define BMP280_PRESSURE_LSB_REG 0xF8 /*Pressure LSB Register */
+#define BMP280_PRESSURE_XLSB_REG 0xF9 /*Pressure XLSB Register */
+#define BMP280_TEMPERATURE_MSB_REG 0xFA /*Temperature MSB Reg */
+#define BMP280_TEMPERATURE_LSB_REG 0xFB /*Temperature LSB Reg */
+#define BMP280_TEMPERATURE_XLSB_REG 0xFC /*Temperature XLSB Reg */
+
+/* Status Register */
+#define BMP280_STATUS_REG_MEASURING__POS 3
+#define BMP280_STATUS_REG_MEASURING__MSK 0x08
+#define BMP280_STATUS_REG_MEASURING__LEN 1
+#define BMP280_STATUS_REG_MEASURING__REG BMP280_STATUS_REG
+
+#define BMP280_STATUS_REG_IMUPDATE__POS 0
+#define BMP280_STATUS_REG_IMUPDATE__MSK 0x01
+#define BMP280_STATUS_REG_IMUPDATE__LEN 1
+#define BMP280_STATUS_REG_IMUPDATE__REG BMP280_STATUS_REG
+
+/* Control Measurement Register */
+#define BMP280_CTRLMEAS_REG_OSRST__POS 5
+#define BMP280_CTRLMEAS_REG_OSRST__MSK 0xE0
+#define BMP280_CTRLMEAS_REG_OSRST__LEN 3
+#define BMP280_CTRLMEAS_REG_OSRST__REG BMP280_CTRLMEAS_REG
+
+#define BMP280_CTRLMEAS_REG_OSRSP__POS 2
+#define BMP280_CTRLMEAS_REG_OSRSP__MSK 0x1C
+#define BMP280_CTRLMEAS_REG_OSRSP__LEN 3
+#define BMP280_CTRLMEAS_REG_OSRSP__REG BMP280_CTRLMEAS_REG
+
+#define BMP280_CTRLMEAS_REG_MODE__POS 0
+#define BMP280_CTRLMEAS_REG_MODE__MSK 0x03
+#define BMP280_CTRLMEAS_REG_MODE__LEN 2
+#define BMP280_CTRLMEAS_REG_MODE__REG BMP280_CTRLMEAS_REG
+
+/* Configuation Register */
+#define BMP280_CONFIG_REG_TSB__POS 5
+#define BMP280_CONFIG_REG_TSB__MSK 0xE0
+#define BMP280_CONFIG_REG_TSB__LEN 3
+#define BMP280_CONFIG_REG_TSB__REG BMP280_CONFIG_REG
+
+#define BMP280_CONFIG_REG_FILTER__POS 2
+#define BMP280_CONFIG_REG_FILTER__MSK 0x1C
+#define BMP280_CONFIG_REG_FILTER__LEN 3
+#define BMP280_CONFIG_REG_FILTER__REG BMP280_CONFIG_REG
+
+#define BMP280_CONFIG_REG_SPI3WEN__POS 0
+#define BMP280_CONFIG_REG_SPI3WEN__MSK 0x01
+#define BMP280_CONFIG_REG_SPI3WEN__LEN 1
+#define BMP280_CONFIG_REG_SPI3WEN__REG BMP280_CONFIG_REG
+
+/* Data Register */
+#define BMP280_PRESSURE_XLSB_REG_DATA__POS 4
+#define BMP280_PRESSURE_XLSB_REG_DATA__MSK 0xF0
+#define BMP280_PRESSURE_XLSB_REG_DATA__LEN 4
+#define BMP280_PRESSURE_XLSB_REG_DATA__REG BMP280_PRESSURE_XLSB_REG
+
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__POS 4
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__MSK 0xF0
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__LEN 4
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__REG BMP280_TEMPERATURE_XLSB_REG
+
+#define BMP280_RATE_SCALE 35
+#define DATA_BMP280_MIN_READ_TIME (32 * NSEC_PER_MSEC)
+#define BMP280_DATA_BYTES_9911 6
+#define FAKE_DATA_NUM_BYTES 10
+
+/** this structure holds all device specific calibration parameters */
+struct bmp280_calibration_param_t {
+ u32 dig_T1;
+ s32 dig_T2;
+ s32 dig_T3;
+ u32 dig_P1;
+ s32 dig_P2;
+ s32 dig_P3;
+ s32 dig_P4;
+ s32 dig_P5;
+ s32 dig_P6;
+ s32 dig_P7;
+ s32 dig_P8;
+ s32 dig_P9;
+
+ s32 t_fine;
+};
+/** BMP280 image registers data structure */
+struct bmp280_t {
+ struct bmp280_calibration_param_t cal_param;
+
+ u8 chip_id;
+ u8 dev_addr;
+
+ u8 waittime;
+
+ u8 osrs_t;
+ u8 osrs_p;
+};
+static struct bmp280_t bmp280;
+
+static int bmp280_get_calib_param(struct inv_mpu_state *st)
+{
+ u8 d[24];
+ int r;
+
+ r = inv_aux_read(BMP280_DIG_T1_LSB_REG, 24, d);
+ if (r)
+ return r;
+
+ bmp280.cal_param.dig_T1 = (u16)((((u16)((u8)d[1])) <<
+ SHIFT_LEFT_8_POSITION) | d[0]);
+ bmp280.cal_param.dig_T2 = (s16)((((s16)((s8)d[3])) <<
+ SHIFT_LEFT_8_POSITION) | d[2]);
+ bmp280.cal_param.dig_T3 = (s16)((((s16)((s8)d[5])) <<
+ SHIFT_LEFT_8_POSITION) | d[4]);
+ bmp280.cal_param.dig_P1 = (u16)((((u16)((u8)d[7])) <<
+ SHIFT_LEFT_8_POSITION) | d[6]);
+ bmp280.cal_param.dig_P2 = (s16)((((s16)((s8)d[9])) <<
+ SHIFT_LEFT_8_POSITION) | d[8]);
+ bmp280.cal_param.dig_P3 = (s16)((((s16)((s8)d[11])) <<
+ SHIFT_LEFT_8_POSITION) | d[10]);
+ bmp280.cal_param.dig_P4 = (s16)((((s16)((s8)d[13])) <<
+ SHIFT_LEFT_8_POSITION) | d[12]);
+ bmp280.cal_param.dig_P5 = (s16)((((s16)((s8)d[15])) <<
+ SHIFT_LEFT_8_POSITION) | d[14]);
+ bmp280.cal_param.dig_P6 = (s16)((((s16)((s8)d[17])) <<
+ SHIFT_LEFT_8_POSITION) | d[16]);
+ bmp280.cal_param.dig_P7 = (s16)((((s16)((s8)d[19])) <<
+ SHIFT_LEFT_8_POSITION) | d[18]);
+ bmp280.cal_param.dig_P8 = (s16)((((s16)((s8)d[21])) <<
+ SHIFT_LEFT_8_POSITION) | d[20]);
+ bmp280.cal_param.dig_P9 = (s16)((((s16)((s8)d[23])) <<
+ SHIFT_LEFT_8_POSITION) | d[22]);
+
+ return 0;
+}
+
+static int inv_setup_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+ u8 d[10];
+
+ /* set to bypass mode */
+ r = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (r)
+ return r;
+ /* issue soft reset */
+ r = inv_aux_write(BMP280_RESET_REG, BMP280_SOFT_RESET);
+ if (r)
+ return r;
+ msleep(100);
+ r = inv_aux_read(BMP280_CHIPID_REG, 1, d);
+ if (r)
+ return r;
+ /* set pressure as ultra high resolution */
+ bmp280.osrs_t = BMP280_ULTRAHIGHRESOLUTION_OSRS_T;
+ bmp280.osrs_p = BMP280_ULTRAHIGHRESOLUTION_OSRS_P;
+
+ /* set IIR filter as 4 */
+ r = inv_aux_write(BMP280_CONFIG_REG_FILTER__REG,
+ BMP280_FILTERCOEFF_16 << SHIFT_LEFT_2_POSITION);
+ if (r)
+ return r;
+ r = bmp280_get_calib_param(st);
+ if (r)
+ return r;
+
+ /*restore to non-bypass mode */
+ r = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (r)
+ return r;
+
+ /* setup master mode and master clock and ES bit */
+ r = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (r)
+ return r;
+ /*slave 3 is used for pressure mode change only*/
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR,
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* pressure sensor mode register address */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_REG, BMP280_CTRLMEAS_REG);
+ if (r)
+ return r;
+ d[0] = (bmp280.osrs_t << SHIFT_LEFT_5_POSITION) +
+ (bmp280.osrs_p << SHIFT_LEFT_2_POSITION) +
+ BMP280_FORCED_MODE;
+ r = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV3_DO, d[0]);
+
+ return r;
+}
+
+static int inv_check_bmp280_self_test(struct inv_mpu_state *st)
+{
+ return 0;
+}
+static int inv_write_bmp280_scale(struct inv_mpu_state *st, int data)
+{
+ return 0;
+}
+static int inv_read_bmp280_scale(struct inv_mpu_state *st, int *scale)
+{
+ return 0;
+}
+
+static int inv_resume_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+ u8 bytes, start;
+
+ bytes = BMP280_DATA_BYTES_9911;
+ start = BMP280_PRESSURE_MSB_REG;
+ if (st->chip_config.dmp_on) {
+ if (st->sensor[SENSOR_COMPASS].on) {
+ if (COMPASS_ID_AK09911 != st->plat_data.sec_slave_id) {
+ bytes++;
+ start -= 1;
+ }
+ } else {
+ /* if compass is disabled, read fake data for DMP */
+ /*read mode */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* read calibration data as the fake data */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_REG,
+ BMP280_DIG_T1_LSB_REG);
+ if (r)
+ return r;
+ /* slave 0 is enabled, read 10 bytes from here */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ FAKE_DATA_NUM_BYTES);
+ }
+ }
+ /* slave 2 is used to read data from pressure sensor */
+ /*read mode */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* start from pressure sensor */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_REG, start);
+ if (r)
+ return r;
+
+ /* slave 2 is enabled, read 6 or 7 bytes from here */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL,
+ INV_MPU_BIT_SLV_EN | bytes);
+ if (r)
+ return r;
+ /* slave 3 is enabled, write byte length is 1 */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ return r;
+}
+
+static int inv_suspend_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+
+ if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) {
+ /* slave 0 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (r)
+ return r;
+ }
+
+ /* slave 2 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0);
+ if (r)
+ return r;
+ /* slave 3 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0);
+
+ return r;
+}
+
+static s32 bmp280_compensate_T_int32(s32 adc_t)
+{
+ s32 v_x1_u32r = 0;
+ s32 v_x2_u32r = 0;
+ s32 temperature = 0;
+
+ v_x1_u32r = ((((adc_t >> 3) - ((s32)
+ bmp280.cal_param.dig_T1 << 1))) *
+ ((s32)bmp280.cal_param.dig_T2)) >> 11;
+ v_x2_u32r = (((((adc_t >> 4) -
+ ((s32)bmp280.cal_param.dig_T1)) * ((adc_t >> 4) -
+ ((s32)bmp280.cal_param.dig_T1))) >> 12) *
+ ((s32)bmp280.cal_param.dig_T3)) >> 14;
+ bmp280.cal_param.t_fine = v_x1_u32r + v_x2_u32r;
+ temperature = (bmp280.cal_param.t_fine * 5 + 128) >> 8;
+
+ return temperature;
+}
+
+
+static u32 bmp280_compensate_P_int32(s32 adc_p)
+{
+ s32 v_x1_u32r = 0;
+ s32 v_x2_u32r = 0;
+ u32 pressure = 0;
+
+ v_x1_u32r = (((s32)bmp280.cal_param.t_fine) >> 1) -
+ (s32)64000;
+ v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) *
+ ((s32)bmp280.cal_param.dig_P6);
+ v_x2_u32r = v_x2_u32r + ((v_x1_u32r *
+ ((s32)bmp280.cal_param.dig_P5)) << 1);
+ v_x2_u32r = (v_x2_u32r >> 2) +
+ (((s32)bmp280.cal_param.dig_P4) << 16);
+ v_x1_u32r = (((bmp280.cal_param.dig_P3 * (((v_x1_u32r >> 2) *
+ (v_x1_u32r >> 2)) >> 13)) >> 3) +
+ ((((s32)bmp280.cal_param.dig_P2) *
+ v_x1_u32r) >> 1)) >> 18;
+ v_x1_u32r = ((((32768+v_x1_u32r)) *
+ ((s32)bmp280.cal_param.dig_P1)) >> 15);
+ /* Avoid exception caused by division by zero */
+ if (v_x1_u32r == 0)
+ return 0;
+ pressure = (((u32)(((s32)1048576) - adc_p) -
+ (v_x2_u32r >> 12))) * 3125;
+ if (pressure < 0x80000000)
+ pressure = (pressure << 1) / ((u32)v_x1_u32r);
+ else
+ pressure = (pressure / (u32)v_x1_u32r) * 2;
+ v_x1_u32r = (((s32)bmp280.cal_param.dig_P9) *
+ ((s32)(((pressure >> 3) * (pressure >> 3)) >> 13)))
+ >> 12;
+ v_x2_u32r = (((s32)(pressure >> 2)) *
+ ((s32)bmp280.cal_param.dig_P8)) >> 13;
+ pressure = (u32)((s32)pressure +
+ ((v_x1_u32r + v_x2_u32r + bmp280.cal_param.dig_P7) >> 4));
+
+ return pressure;
+}
+
+static int inv_bmp280_read_data(struct inv_mpu_state *st, short *o)
+{
+ int r, i;
+ u8 d[BMP280_DATA_BYTES_9911], reg_addr;
+ s32 upressure, utemperature;
+
+ if (st->chip_config.dmp_on) {
+ for (i = 0; i < 6; i++)
+ d[i] = st->fifo_data[i];
+ } else {
+ if (st->sensor[SENSOR_COMPASS].on) {
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ reg_addr = REG_EXT_SENS_DATA_09;
+ else
+ reg_addr = REG_EXT_SENS_DATA_08;
+ } else {
+ reg_addr = REG_EXT_SENS_DATA_00;
+ }
+ r = inv_i2c_read(st, reg_addr, BMP280_DATA_BYTES_9911, d);
+ if (r)
+ return r;
+ }
+ /* pressure */
+ upressure = (s32)((((s32)(d[0]))
+ << SHIFT_LEFT_12_POSITION) | (((u32)(d[1]))
+ << SHIFT_LEFT_4_POSITION) | ((u32)d[2] >>
+ SHIFT_RIGHT_4_POSITION));
+
+ /* Temperature */
+ utemperature = (s32)(((
+ (s32) (d[3])) << SHIFT_LEFT_12_POSITION) |
+ (((u32)(d[4])) << SHIFT_LEFT_4_POSITION)
+ | ((u32)d[5] >> SHIFT_RIGHT_4_POSITION));
+
+ bmp280_compensate_T_int32(utemperature);
+ r = bmp280_compensate_P_int32(upressure);
+ o[0] = 0;
+ o[1] = (r >> 16);
+ o[2] = (r & 0xffff);
+
+ return 0;
+}
+
+static struct inv_mpu_slave slave_bmp280 = {
+ .suspend = inv_suspend_bmp280,
+ .resume = inv_resume_bmp280,
+ .get_scale = inv_read_bmp280_scale,
+ .set_scale = inv_write_bmp280_scale,
+ .self_test = inv_check_bmp280_self_test,
+ .setup = inv_setup_bmp280,
+ .read_data = inv_bmp280_read_data,
+ .rate_scale = BMP280_RATE_SCALE,
+ .min_read_time = DATA_BMP280_MIN_READ_TIME,
+};
+
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st)
+{
+ switch (st->plat_data.aux_slave_id) {
+ case PRESSURE_ID_BMP280:
+ st->slave_pressure = &slave_bmp280;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return st->slave_pressure->setup(st);
+}
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig
new file mode 100755
index 00000000000..86c30bd8a63
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig
@@ -0,0 +1,13 @@
+#
+# Kconfig for Invensense IIO testing hooks
+#
+
+config INV_TESTING
+ boolean "Invensense IIO testing hooks"
+ depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
+ default n
+ help
+ This flag enables display of additional testing information from the
+ Invensense IIO drivers.
+ It also enables the I2C counters facility to perform IO profiling.
+ Some additional sysfs entries will appear when this flag is enabled.
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile
new file mode 100755
index 00000000000..4f0edd3de90
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense IIO testing hooks.
+#
+
+obj-$(CONFIG_INV_TESTING) += inv_counters.o
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c
new file mode 100755
index 00000000000..3b26ca97284
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c
@@ -0,0 +1,154 @@
+/*
+ * @file inv_counters.c
+ * @brief Exports i2c read write counts through sysfs
+ *
+ * @version 0.1
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+ uint32_t i2c_tempreads;
+ uint32_t i2c_mpureads;
+ uint32_t i2c_mpuwrites;
+ uint32_t i2c_accelreads;
+ uint32_t i2c_accelwrites;
+ uint32_t i2c_compassreads;
+ uint32_t i2c_compasswrites;
+ uint32_t i2c_compassirq;
+ uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+static ssize_t i2c_counters_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+ jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+ mpu_irq ? kstat_irqs(mpu_irq) : 0,
+ Counters.i2c_tempreads,
+ Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+ accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+ Counters.i2c_accelreads, Counters.i2c_accelwrites,
+ compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+ Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+ switch (type) {
+ case IRQ_MPU:
+ mpu_irq = irq;
+ break;
+ case IRQ_ACCEL:
+ accel_irq = irq;
+ break;
+ case IRQ_COMPASS:
+ compass_irq = irq;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
+
+void inv_iio_counters_tempread(int count)
+{
+ Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+ Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+ Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+ Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+ Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+ Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+ Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+ Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+ Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+ __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+ __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+ .name = "inv_counters",
+ .owner = THIS_MODULE,
+ .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+ memset(&Counters, 0, sizeof(Counters));
+
+ return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+ class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h
new file mode 100755
index 00000000000..d60dac9d97b
--- /dev/null
+++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h
@@ -0,0 +1,72 @@
+/*
+ * @file inv_counters.h
+ * @brief Debug file to keep track of various counters for the InvenSense
+ * sensor drivers.
+ *
+ * @version 0.1
+ */
+
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_INV_TESTING
+
+enum irqtype {
+ IRQ_MPU,
+ IRQ_ACCEL,
+ IRQ_COMPASS
+};
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 13515f40700..e145931ef1b 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -66,11 +66,6 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_ALTVOLTAGE] = "altvoltage",
[IIO_CCT] = "cct",
[IIO_PRESSURE] = "pressure",
- [IIO_HEARTRATE] = "heartrate",
- [IIO_PEDOMETER] = "pedometer",
- [IIO_PASSIVE] = "passive",
- [IIO_GESTURE] = "gesture",
- [IIO_FUSION] = "fusion",
};
static const char * const iio_modifier_names[] = {
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 5ef1a396e0c..e86ccb49d9d 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -32,6 +32,31 @@ config SENSORS_LM3533
changes. The ALS-control output values can be set per zone for the
three current output channels.
+config CM32181
+ tristate "CM32181 driver"
+ depends on I2C
+ help
+ Say Y here if you use cm32181.
+
+ This option enables ambient light sensor using
+ Capella cm32181 device driver.
+
+ To compile this driver as a module, choose M here:
+ the module will be called cm32181.
+
+config CM3391
+ tristate "CM3391 driver"
+ depends on I2C
+ help
+ Say Y here if you use cm3391.
+
+ This option enables ambient light sensor using
+ Capella cm3391 device driver.
+
+ To compile this driver as a module, choose M here:
+ the module will be called cm3391.
+
+
config SENSORS_TSL2563
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 040d9c75f8e..fdf3b87e1f1 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
+obj-$(CONFIG_CM32181) += cm32181.o
+obj-$(CONFIG_CM3391) += cm3391.o \ No newline at end of file
diff --git a/drivers/iio/light/cm3391.c b/drivers/iio/light/cm3391.c
new file mode 100644
index 00000000000..ed2d84a61fe
--- /dev/null
+++ b/drivers/iio/light/cm3391.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2014-2015 Capella Microsystems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2, as published
+ * by the Free Software Foundation.
+ *
+ * Special thanks Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ * help to add ACPI support.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+#include <linux/init.h>
+
+#ifdef CONFIG_ACPI
+#include <linux/acpi.h>
+#endif /* CONFIG_ACPI */
+
+#define OLDAPI
+
+/* Registers Address */
+#define CM3391_REG_ADDR_CMD 0x00
+#define CM3391_REG_ADDR_TEST 0x01
+#define CM3391_REG_ADDR_WH 0x02
+#define CM3391_REG_ADDR_WL 0x03
+#define CM3391_REG_ADDR_RED 0x08
+#define CM3391_REG_ADDR_ALS 0x09
+#define CM3391_REG_ADDR_BLUE 0x0A
+#define CM3391_REG_ADDR_CLEAR 0x0B
+#define CM3391_REG_ADDR_ID 0x0C
+#define CM3391_REG_ADDR_STATUS 0x0D
+
+/* Number of Configurable Registers */
+#define CM3391_CONF_REG_NUM 16
+
+/* CMD register */
+#define CM3391_CMD_CS_DISABLE BIT(0)
+#define CM3391_CMD_CS_INT_EN BIT(8)
+
+#define CM3391_CMD_CS_PERS_SHIFT 10
+#define CM3391_CMD_CS_PERS_MASK (0x03 << CM3391_CMD_CS_PERS_SHIFT)
+#define CM3391_CMD_CS_PERS_DEFAULT (0x01 << CM3391_CMD_CS_PERS_SHIFT)
+
+#define CM3391_CMD_CS_IT_SHIFT 4
+#define CM3391_CMD_CS_IT_MASK (0x07 << CM3391_CMD_CS_IT_SHIFT)
+#define CM3391_CMD_CS_IT_DEFAULT (0x01 << CM3391_CMD_CS_IT_SHIFT)
+
+#define CM3391_CMD_CS_HS_SHIFT 3
+#define CM3391_CMD_CS_HS_MASK (0x01 << CM3391_CMD_CS_HS_SHIFT)
+#define CM3391_CMD_CS_HS_DEFAULT (0x00 << CM3391_CMD_CS_HS_SHIFT)
+
+#define CM3391_CMD_DEFAULT (\
+ CM3391_CMD_CS_PERS_DEFAULT |\
+ CM3391_CMD_CS_IT_DEFAULT |\
+ CM3391_CMD_CS_HS_DEFAULT)
+
+#define CM3391_WH_DEFAULT 0xFFFF
+#define CM3391_WL_DEFAULT 0x0000
+
+#define CM3391_CALIBSCALE_DEFAULT 100000
+#define CM3391_CALIBSCALE_RESOLUTION 100000
+#define CM3391_MLUX_PER_LUX 1000
+#define CM3391_THRESHOLD_PERCENT 10 /* 10 percent */
+
+#define CM3391_MLUX_PER_BIT_DEFAULT 550
+#define CM3391_MLUX_PER_BIT_BASE_IT 100000
+static const int CM3391_cs_it_bits[] = { 0, 1, 2, 3, 4};
+static const int CM3391_cs_it_values[] = {
+ 50000, 100000, 200000, 400000, 800000};
+
+struct cm3391_cs_info {
+ u32 id;
+ int regs_bmp;
+ int calibscale;
+ int mlux_per_bit;
+ int mlux_per_bit_base_it;
+ const int *cs_it_bits;
+ const int *cs_it_values;
+ const int num_cs_it;
+ int als_raw;
+};
+
+static struct cm3391_cs_info cm3391_cs_info_default = {
+ .id = 3391,
+ .regs_bmp = 0x0F,
+ .calibscale = CM3391_CALIBSCALE_DEFAULT,
+ .mlux_per_bit = CM3391_MLUX_PER_BIT_DEFAULT,
+ .mlux_per_bit_base_it = CM3391_MLUX_PER_BIT_BASE_IT,
+ .cs_it_bits = CM3391_cs_it_bits,
+ .cs_it_values = CM3391_cs_it_values,
+ .num_cs_it = ARRAY_SIZE(CM3391_cs_it_bits),
+};
+
+struct cm3391_chip {
+ struct i2c_client *client;
+ struct mutex lock;
+ u16 conf_regs[CM3391_CONF_REG_NUM];
+ struct cm3391_cs_info *cs_info;
+};
+
+static int cm3391_get_lux(struct cm3391_chip *chip);
+static int cm3391_threshold_update(struct cm3391_chip *chip, int percent);
+static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2);
+
+/**
+ * cm3391_interrupt_config() - Enable/Disable CM3391 interrupt
+ * @chip: pointer of struct cm3391.
+ * @enable: 0 to disable; otherwise to enable
+ *
+ * Config CM3391 interrupt control bit.
+ *
+ * Return: 0 for success; otherwise for error code.
+ */
+static int cm3391_interrupt_config(struct cm3391_chip *chip, int enable)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ int status;
+
+ if (!cs_info)
+ return -ENODEV;
+
+ /* Force to clean interrupt */
+ status = i2c_smbus_read_word_data(client,
+ CM3391_REG_ADDR_STATUS);
+ if (status < 0)
+ return -ENODEV;
+
+ if (enable)
+ chip->conf_regs[CM3391_REG_ADDR_CMD] |=
+ CM3391_CMD_CS_INT_EN;
+ else
+ chip->conf_regs[CM3391_REG_ADDR_CMD] &=
+ ~CM3391_CMD_CS_INT_EN;
+
+ status = i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD,
+ chip->conf_regs[CM3391_REG_ADDR_CMD]);
+
+ if (status < 0)
+ return -ENODEV;
+
+ return status;
+}
+
+#ifdef CONFIG_ACPI
+/**
+ * cm3391_acpi_get_cpm_info() - Get CPM object from ACPI
+ * @client pointer of struct i2c_client.
+ * @obj_name pointer of ACPI object name.
+ * @count maximum size of return array.
+ * @vals pointer of array for return elements.
+ *
+ * Convert ACPI CPM table to array. Special thanks Srinivas Pandruvada's
+ * help to implement this routine.
+ *
+ * Return: -ENODEV for fail. Otherwise is number of elements.
+ */
+static int cm3391_acpi_get_cpm_info(struct i2c_client *client, char *obj_name,
+ int count, u64 *vals)
+{
+ acpi_handle handle;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ int i;
+ acpi_status status;
+ union acpi_object *cpm;
+
+ handle = ACPI_HANDLE(&client->dev);
+ if (!handle)
+ return -ENODEV;
+
+ status = acpi_evaluate_object(handle, obj_name, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&client->dev, "object %s not found\n", obj_name);
+ return -ENODEV;
+ }
+
+ cpm = buffer.pointer;
+ for (i = 0; i < cpm->package.count && i < count; ++i) {
+ union acpi_object *elem;
+ elem = &(cpm->package.elements[i]);
+ vals[i] = elem->integer.value;
+ }
+
+ kfree(buffer.pointer);
+
+ return cpm->package.count;
+}
+#endif /* CONFIG_ACPI */
+
+/**
+ * cm3391_reg_init() - Initialize CM3391 registers
+ * @chip: pointer of struct cm3391.
+ *
+ * Initialize CM3391 color sensor register to default values.
+ *
+ Return: 0 for success; otherwise for error code.
+ */
+static int cm3391_reg_init(struct cm3391_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int i;
+ s32 ret;
+ struct cm3391_cs_info *cs_info;
+#ifdef CONFIG_ACPI
+ int cpm_elem_count;
+ u64 cpm_elems[20];
+#endif /* CONFIG_ACPI */
+
+
+ /* Default device */
+ cs_info = chip->cs_info = &cm3391_cs_info_default;
+ chip->conf_regs[CM3391_REG_ADDR_CMD] = CM3391_CMD_DEFAULT;
+ chip->conf_regs[CM3391_REG_ADDR_WH] = CM3391_WH_DEFAULT;
+ chip->conf_regs[CM3391_REG_ADDR_WL] = CM3391_WL_DEFAULT;
+
+ /* Disable interrupt */
+ cm3391_interrupt_config(chip, 0);
+
+ /* Disable Test Mode */
+ i2c_smbus_write_word_data(client, CM3391_REG_ADDR_TEST, 0x0000);
+
+ /* Disable device */
+ i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD,
+ CM3391_CMD_CS_DISABLE);
+
+ /* Identify device */
+ ret = i2c_smbus_read_word_data(client, CM3391_REG_ADDR_ID);
+ if (ret < 0)
+ return ret;
+
+ if ((ret & 0xFF) != 0x91)
+ return -ENODEV;
+
+#ifdef CONFIG_ACPI
+#error ACPI is enabled, strange! OLIO MFJ
+
+ if (ACPI_HANDLE(&client->dev)) {
+ /* Load from ACPI */
+ cpm_elem_count = cm3391_acpi_get_cpm_info(client, "CPM0",
+ ARRAY_SIZE(cpm_elems),
+ cpm_elems);
+ if (cpm_elem_count > 0) {
+ int header_num = 3;
+ int reg_num = cpm_elem_count - header_num;
+
+ cs_info->id = cpm_elems[0];
+ cs_info->regs_bmp = cpm_elems[2];
+ for (i = 0; i < reg_num; i++)
+ if (cs_info->regs_bmp & (1<<i))
+ chip->conf_regs[i] =
+ cpm_elems[header_num+i];
+ }
+
+ cpm_elem_count = cm3391_acpi_get_cpm_info(client, "CPM1",
+ ARRAY_SIZE(cpm_elems),
+ cpm_elems);
+ if (cpm_elem_count > 0) {
+ cs_info->mlux_per_bit = (int)cpm_elems[0] / 100;
+ cs_info->calibscale = (int)cpm_elems[1];
+ }
+ }
+#endif /* CONFIG_ACPI */
+
+ /* Force to disable interrupt */
+ chip->conf_regs[CM3391_REG_ADDR_CMD] &= ~CM3391_CMD_CS_INT_EN;
+
+ /* Initialize registers */
+ for (i = 0; i < CM3391_CONF_REG_NUM; i++) {
+ if (cs_info->regs_bmp & (1<<i)) {
+ ret = i2c_smbus_write_word_data(client, i,
+ chip->conf_regs[i]);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * cm3391_read_cs_it() - Get sensor integration time (ms)
+ * @chip: pointer of struct cm3391
+ * @val2: pointer of int to load the cs_it value.
+ *
+ * Report the current integration time in milliseconds.
+ *
+ * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
+ */
+static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2)
+{
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ u16 cs_it;
+ int i;
+
+ cs_it = chip->conf_regs[CM3391_REG_ADDR_CMD];
+ cs_it &= CM3391_CMD_CS_IT_MASK;
+ cs_it >>= CM3391_CMD_CS_IT_SHIFT;
+ for (i = 0; i < cs_info->num_cs_it; i++) {
+ if (cs_it == cs_info->cs_it_bits[i]) {
+ *val2 = cs_info->cs_it_values[i];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ }
+
+ return -EINVAL;
+}
+
+#ifndef OLDAPI
+/**
+ * cm3391_write_cs_it() - Write sensor integration time
+ * @chip: pointer of struct cm3391.
+ * @val: integration time in milliseconds.
+ *
+ * Convert integration time (ms) to sensor value.
+ *
+ * Return: i2c_smbus_write_word_data command return value.
+ */
+static int cm3391_write_cs_it(struct cm3391_chip *chip, int val)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ u16 cs_it;
+ int ret, i;
+
+ for (i = 0; i < cs_info->num_cs_it; i++)
+ if (val <= cs_info->cs_it_values[i])
+ break;
+ if (i >= cs_info->num_cs_it)
+ i = cs_info->num_cs_it - 1;
+
+ cs_it = cs_info->cs_it_bits[i];
+ cs_it <<= CM3391_CMD_CS_IT_SHIFT;
+
+ mutex_lock(&chip->lock);
+ chip->conf_regs[CM3391_REG_ADDR_CMD] &=
+ ~CM3391_CMD_CS_IT_MASK;
+ chip->conf_regs[CM3391_REG_ADDR_CMD] |=
+ cs_it;
+ ret = i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD,
+ chip->conf_regs[CM3391_REG_ADDR_CMD]);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+#endif /* !OLDAPI */
+
+/**
+ * cm3391_get_lux() - report current lux value
+ * @chip: pointer of struct cm3391.
+ *
+ * Convert sensor raw data to lux. It depends on integration
+ * time and calibscale variable.
+ *
+ * Return: Positive value is lux, otherwise is error code.
+ */
+static int cm3391_get_lux(struct cm3391_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ int ret;
+ int cs_it;
+ u64 tmp;
+
+ /* Calculate mlux per bit based on cs_it */
+ ret = cm3391_read_cs_it(chip, &cs_it);
+ if (ret < 0)
+ return -EINVAL;
+ tmp = (__force u64)cs_info->mlux_per_bit;
+ tmp *= cs_info->mlux_per_bit_base_it;
+ tmp = div_u64(tmp, cs_it);
+
+ /* Get als_raw */
+ if (!(chip->conf_regs[CM3391_REG_ADDR_CMD] & CM3391_CMD_CS_INT_EN))
+ cs_info->als_raw = i2c_smbus_read_word_data(
+ client,
+ CM3391_REG_ADDR_ALS);
+ if (cs_info->als_raw < 0)
+ return cs_info->als_raw;
+
+ tmp *= cs_info->als_raw;
+ tmp *= cs_info->calibscale;
+ tmp = div_u64(tmp, CM3391_CALIBSCALE_RESOLUTION);
+ tmp = div_u64(tmp, CM3391_MLUX_PER_LUX);
+
+ if (tmp > 0xFFFF)
+ tmp = 0xFFFF;
+
+ return (int)tmp;
+}
+
+static int cm3391_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cm3391_chip *chip = iio_priv(indio_dev);
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_word_data(chip->client, chan->address);
+ if (ret < 0) {
+ return ret;
+ }
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = cm3391_get_lux(chip);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = cs_info->calibscale;
+ return IIO_VAL_INT;
+#ifndef OLDAPI
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ ret = cm3391_read_cs_it(chip, val2);
+ return ret;
+#endif /* !OLDAPI */
+ }
+
+ return -EINVAL;
+}
+
+static int cm3391_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct cm3391_chip *chip = iio_priv(indio_dev);
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+#ifndef OLDAPI
+ long ms;
+#endif /* !OLDAPI */
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ cs_info->calibscale = val;
+ return val;
+#ifndef OLDAPI
+ case IIO_CHAN_INFO_INT_TIME:
+ ms = val * 1000000 + val2;
+ return cm3391_write_cs_it(chip, (int)ms);
+#endif /* !OLDAPI */
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * cm3391_get_it_available() - Get available IT value
+ * @dev: pointer of struct device.
+ * @attr: pointer of struct device_attribute.
+ * @buf: pointer of return string buffer.
+ *
+ * Display the available integration time in milliseconds.
+ *
+ * Return: string length.
+ */
+static ssize_t cm3391_get_it_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cm3391_chip *chip = iio_priv(dev_to_iio_dev(dev));
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ int i, len;
+
+ for (i = 0, len = 0; i < cs_info->num_cs_it; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
+ cs_info->cs_it_values[i]/1000000,
+ cs_info->cs_it_values[i]%1000000);
+ return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
+}
+
+/**
+ * cm3391_threshold_update() - Update the threshold registers.
+ * @chip: pointer of struct cm3391_chip.
+ * @percent: +/- percent.
+ *
+ * Based on the current ALS value, update the hi and low threshold registers.
+ *
+ * Return: 0 for success; otherwise for error code.
+ */
+static int cm3391_threshold_update(struct cm3391_chip *chip, int percent)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3391_cs_info *cs_info = chip->cs_info;
+ int ret;
+ int wh, wl;
+
+ ret = cs_info->als_raw = i2c_smbus_read_word_data(client,
+ CM3391_REG_ADDR_ALS);
+ if (ret < 0)
+ return ret;
+
+ wh = wl = ret;
+ ret *= percent;
+ ret /= 100;
+ if (ret < 1)
+ ret = 1;
+ wh += ret;
+ wl -= ret;
+ if (wh > 65535)
+ wh = 65535;
+ if (wl < 0)
+ wl = 0;
+
+ chip->conf_regs[CM3391_REG_ADDR_WH] = wh;
+ ret = i2c_smbus_write_word_data(
+ client,
+ CM3391_REG_ADDR_WH,
+ chip->conf_regs[CM3391_REG_ADDR_WH]);
+ if (ret < 0)
+ return ret;
+
+ chip->conf_regs[CM3391_REG_ADDR_WL] = wl;
+ ret = i2c_smbus_write_word_data(
+ client,
+ CM3391_REG_ADDR_WL,
+ chip->conf_regs[CM3391_REG_ADDR_WL]);
+
+ return ret;
+}
+
+/**
+ * cm3391_event_handler() - Interrupt handling routine.
+ * @irq: irq number.
+ * @private: pointer of void.
+ *
+ * Clean interrupt and reset threshold registers.
+ *
+ * Return: IRQ_HANDLED.
+ */
+static irqreturn_t cm3391_event_handler(int irq, void *private)
+{
+ struct iio_dev *dev_info = private;
+ struct cm3391_chip *chip = iio_priv(dev_info);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ /* Disable interrupt */
+ ret = cm3391_interrupt_config(chip, 0);
+ if (ret < 0)
+ goto error_handler_unlock;
+
+ /* Update Hi/Lo windows */
+ ret = cm3391_threshold_update(chip, CM3391_THRESHOLD_PERCENT);
+ if (ret < 0)
+ goto error_handler_unlock;
+
+ /* Enable interrupt */
+ cm3391_interrupt_config(chip, 1);
+
+error_handler_unlock:
+ mutex_unlock(&chip->lock);
+ return IRQ_HANDLED;
+}
+
+#ifdef OLDAPI
+#define CM3391_COLOR_CHANNEL(_color, _addr) { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .channel2 = IIO_MOD_LIGHT_##_color, \
+ .address = _addr, \
+}
+
+static const struct iio_chan_spec cm3391_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE)
+ },
+ CM3391_COLOR_CHANNEL(RED, CM3391_REG_ADDR_RED),
+ CM3391_COLOR_CHANNEL(GREEN, CM3391_REG_ADDR_ALS),
+ CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE),
+ CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR),
+};
+
+#else
+#define CM3391_COLOR_CHANNEL(_color, _addr) { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .channel2 = IIO_MOD_LIGHT_##_color, \
+ .address = _addr, \
+}
+
+static const struct iio_chan_spec cm3391_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ CM3391_COLOR_CHANNEL(RED, CM3391_REG_ADDR_RED),
+ CM3391_COLOR_CHANNEL(GREEN, CM3391_REG_ADDR_ALS),
+ CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE),
+ CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR),
+};
+#endif /* !OLDAPI */
+
+static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
+ S_IRUGO, cm3391_get_it_available, NULL, 0);
+
+static struct attribute *cm3391_attributes[] = {
+ &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group cm3391_attribute_group = {
+ .attrs = cm3391_attributes
+};
+
+static const struct iio_info cm3391_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cm3391_read_raw,
+ .write_raw = &cm3391_write_raw,
+ .attrs = &cm3391_attribute_group,
+};
+
+static int cm3391_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cm3391_chip *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+#ifdef OLDAPI
+ indio_dev = iio_device_alloc(sizeof(*chip));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio_device_alloc fails\n");
+ return -ENOMEM;
+ }
+#else
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev) {
+ dev_err(&client->dev, "devm_iio_device_alloc failed\n");
+ return -ENOMEM;
+ }
+#endif /* OLDAPI */
+
+ chip = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ chip->client = client;
+
+ mutex_init(&chip->lock);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = cm3391_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cm3391_channels);
+ indio_dev->info = &cm3391_info;
+ if (id && id->name)
+ indio_dev->name = id->name;
+ else
+ indio_dev->name = (char *)dev_name(&client->dev);
+ indio_dev->channels = cm3391_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cm3391_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = cm3391_reg_init(chip);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s: register init failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (client->irq) {
+ ret = request_threaded_irq(client->irq,
+ NULL,
+ cm3391_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "cm3391_event",
+ indio_dev);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "irq request error %d\n",
+ -ret);
+ goto error_disable_int;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: regist device failed\n",
+ __func__);
+ goto error_free_irq;
+ }
+
+ if (client->irq) {
+ ret = cm3391_threshold_update(chip, CM3391_THRESHOLD_PERCENT);
+ if (ret < 0)
+ goto error_free_irq;
+
+ ret = cm3391_interrupt_config(chip, 1);
+ if (ret < 0)
+ goto error_free_irq;
+ }
+
+ return 0;
+
+error_free_irq:
+ if (client->irq)
+ free_irq(client->irq, indio_dev);
+#ifdef OLDAPI
+ iio_device_free(indio_dev);
+#endif /* OLDAPI */
+error_disable_int:
+ cm3391_interrupt_config(chip, 0);
+
+ return ret;
+}
+
+static int cm3391_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct cm3391_chip *chip = iio_priv(indio_dev);
+
+ cm3391_interrupt_config(chip, 0);
+ if (client->irq)
+ free_irq(client->irq, indio_dev);
+ iio_device_unregister(indio_dev);
+#ifdef OLDAPI
+ iio_device_free(indio_dev);
+#endif /* OLDAPI */
+ return 0;
+}
+
+static const struct i2c_device_id cm3391_id[] = {
+ { "cm3391", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, cm3391_id);
+
+static const struct of_device_id cm3391_of_match[] = {
+ { .compatible = "capella,cm3391" },
+ { }
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cm3391_acpi_match[] = {
+ { "CPLM3391", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, cm3391_acpi_match);
+#endif /* CONFIG_ACPI */
+
+static struct i2c_driver cm3391_driver = {
+ .driver = {
+ .name = "cm3391",
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(cm3391_acpi_match),
+#endif /* CONFIG_ACPI */
+ .of_match_table = of_match_ptr(cm3391_of_match),
+ .owner = THIS_MODULE,
+ },
+ .id_table = cm3391_id,
+ .probe = cm3391_probe,
+ .remove = cm3391_remove,
+};
+
+module_i2c_driver(cm3391_driver);
+
+MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
+MODULE_DESCRIPTION("CM3391 color sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 7f328e37fba..f9ea48f27d0 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -11,3 +11,5 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
+
+obj-y += inv_compass/
diff --git a/drivers/iio/magnetometer/inv_compass/Kconfig b/drivers/iio/magnetometer/inv_compass/Kconfig
new file mode 100755
index 00000000000..aa0ffde1869
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/Kconfig
@@ -0,0 +1,47 @@
+#
+# Kconfig for Invensense IIO compass drivers of 3rd party compass devices.
+#
+
+# Yamaha YAS530/YAS532/YAS533
+config INV_YAS53X_IIO
+ tristate "Invensense IIO driver for Yamaha YAS530/YAS532/YAS533 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Yamaha YAS530/YAS532/YAS533. It is the Invensense
+ implementation of YAS53x series compass devices.
+ This driver can be built as a module. The module will be called
+ inv_yas53x_iio.
+
+# Aichi AMI306
+config INV_AMI306_IIO
+ tristate "Invensense IIO driver for Aichi AMI306 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Aichi AMI306 compass. It is the Invensense
+ IIO implementation for the AMI306 compass device.
+ This driver can be built as a module. The module will be called
+ inv-ami306-iio.
+
+# Asahi Kasei AK8975/AK8972/AK8963
+config INV_AK89XX_IIO
+ tristate "Invensense IIO driver for Asahi Kasei AK8975/AK8972/AK8963 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Asahi Kasei AK8975/AK8972/AK8963 compasses. It is the Invensense
+ IIO implementation of AK89xx series compass devices.
+ This driver can be built as a module. The module will be called
+ inv-ak89xx-iio.
+
+# Asahi Kasei AK09911
+config INV_AK09911_IIO
+ tristate "Invensense IIO driver for Asahi Kasei AK09911 compass"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF
+ default n
+ help
+ This driver supports the Asahi Kasei AK09911 compasses. It is the Invensense
+ IIO implementation of AK09911 compass devices.
+ This driver can be built as a module. The module will be called
+ inv-ak09911-iio.
diff --git a/drivers/iio/magnetometer/inv_compass/Makefile b/drivers/iio/magnetometer/inv_compass/Makefile
new file mode 100755
index 00000000000..568e5e2bf71
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/Makefile
@@ -0,0 +1,48 @@
+#
+# Makefile for Invensense IIO compass drivers of 3rd party compass devices.
+#
+
+# Yamaha YAS530/YAS532/YAS533
+obj-$(CONFIG_INV_YAS53X_IIO) += inv_yas53x.o
+
+inv_yas53x-objs := inv_yas53x_core.o
+inv_yas53x-objs += inv_yas53x_ring.o
+inv_yas53x-objs += inv_yas53x_trigger.o
+
+CFLAGS_inv_yas53x_core.o += -Idrivers/staging/iio
+CFLAGS_inv_yas53x_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_yas53x_trigger.o += -Idrivers/staging/iio
+
+# Aichi AMI306
+obj-$(CONFIG_INV_AMI306_IIO) += inv-ami306-iio.o
+
+inv-ami306-iio-objs := inv_ami306_core.o
+inv-ami306-iio-objs += inv_ami306_ring.o
+inv-ami306-iio-objs += inv_ami306_trigger.o
+
+CFLAGS_inv_ami306_core.o += -Idrivers/staging/iio
+CFLAGS_inv_ami306_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_ami306_trigger.o += -Idrivers/staging/iio
+
+# Asahi Kasei AK8975/AK8972/AK8963
+obj-$(CONFIG_INV_AK89XX_IIO) += inv-ak89xx-iio.o
+
+inv-ak89xx-iio-objs := inv_ak89xx_core.o
+inv-ak89xx-iio-objs += inv_ak89xx_ring.o
+inv-ak89xx-iio-objs += inv_ak89xx_trigger.o
+
+CFLAGS_inv_ak89xx_core.o += -Idrivers/staging/iio
+CFLAGS_inv_ak89xx_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_ak89xx_trigger.o += -Idrivers/staging/iio
+
+# Asahi Kasei AK09911
+obj-$(CONFIG_INV_AK09911_IIO) += inv-ak09911-iio.o
+
+inv-ak09911-iio-objs := inv_ak09911_core.o
+inv-ak09911-iio-objs += inv_ak09911_ring.o
+inv-ak09911-iio-objs += inv_ak09911_trigger.o
+
+CFLAGS_inv_ak09911_core.o += -Idrivers/staging/iio
+CFLAGS_inv_ak09911_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_ak09911_trigger.o += -Idrivers/staging/iio
+
diff --git a/drivers/iio/magnetometer/inv_compass/README b/drivers/iio/magnetometer/inv_compass/README
new file mode 100755
index 00000000000..54f2bb8ded2
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/README
@@ -0,0 +1,176 @@
+Kernel driver
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+ > Platform Data
+- Board File Modifications for compass
+ > AMI306
+ > YAS530/532/533
+- IIO Subsystem
+ > Communicating with the Driver in Userspace
+- Streaming Data to an Userspace Application
+- Test Applications
+ > Running Test Applications with AMI306 or YAS53x
+
+Description
+===========
+This document describes how to install the Invensense device driver for AMI306
+and YAS53x series compass chip into a Linux kernel. The Invensense driver
+currently supports the following sensors:
+- AMI306
+- YAS530
+- YAS532
+- YAS533
+
+Please refer to the appropriate product specification
+document for further information regarding the slave address.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_ami306_core.c
+- inv_ami306_ring.c
+- inv_ami306_trigger.c
+- inv_ami306_iio.h
+- inv_yas53x_core.c
+- inv_yas53x_ring.c
+- inv_yas53x_trigger.c
+- inv_yas53x_iio.h
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add all above files to drivers/staging/iio/magnetometer/inv_compass
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ modify "drivers/staging/iio/magnetometer/Kconfig" with:
+ >> source "drivers/staging/iio/magnetometer/inv_compass/Kconfig"
+
+ modify "drivers/staging/iio/magnetometer/Makefile" with:
+ >> obj-y += inv_compass/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+AMI306
+-------------------------------------------------
+static struct mpu_platform_data compass_data = {
+ .orientation = { 0, 0, 1,
+ 0, 1, 0,
+ 1, 0, 0 },
+};
+
+static struct i2c_board_info __initdata chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("ami306", 0x0E),
+ .platform_data = &compass_data,
+ },
+};
+
+YAS53x(Use YAS532 as an example)
+-------------------------------------------------
+static struct mpu_platform_data compass_data = {
+ .orientation = { 0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1 },
+};
+
+static struct i2c_board_info __initdata compass_board_info[] = {
+ {
+ I2C_BOARD_INFO("yas532", 0x2E),
+ .platform_data = &compass_data,
+ },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+ - iio:device0
+ - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+AMI306
+--------
+compass_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+sampling_frequency(read and write)
+--show and change the sampling rate of the sensor.
+
+YAS53x
+---------------------
+YAS53x has all the attributes AMI306 has. It has one more additional attribute:
+
+overunderflow(read-only)
+--value 1 shows an overflow or underflow happens. Need to write into it to make
+ it zero.
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write the desired output rate to sampling_frequency.
+2. Write 1 to enable to turn on the event.
+3. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+4. Parse this string to obtain each compass element.
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications
+---------------------------------------------------------
+To run test applications with AMI306 or YAS53x devices,
+please use the following commands:
+
+1. for ami306:
+ mpu_iio -n ami306 -c 10 -l 3
+
+2. for yas532:
+ mpu_iio -n yas532 -c 10 -l 3
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c
new file mode 100755
index 00000000000..a52fb403207
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c
@@ -0,0 +1,512 @@
+/*
+* Copyright (C) 2013 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_ak09911_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+static s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+/**
+ * inv_serial_read() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE: The slave register will not increment when reading from the FIFO.
+ */
+int inv_serial_read(struct inv_ak09911_state_s *st, u8 reg, u16 length, u8 *data)
+{
+ int result;
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ result = i2c_smbus_read_i2c_block_data(st->i2c, reg, length, data);
+ if (result != length) {
+ if (result < 0)
+ return result;
+ else
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * inv_serial_single_write() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ */
+int inv_serial_single_write(struct inv_ak09911_state_s *st, u8 reg, u8 data)
+{
+ u8 d[1];
+ d[0] = data;
+ INV_I2C_INC_COMPASSWRITE(3);
+
+ return i2c_smbus_write_i2c_block_data(st->i2c, reg, 1, d);
+}
+
+static int ak09911_init(struct inv_ak09911_state_s *st)
+{
+ int result = 0;
+ unsigned char serial_data[3];
+
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+ /* Wait at least 100us */
+ udelay(100);
+
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_FUSE_ACCESS);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ /* Wait at least 200us */
+ udelay(200);
+
+ result = inv_serial_read(st, AK09911_FUSE_ASAX, 3, serial_data);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ st->asa[0] = serial_data[0];
+ st->asa[1] = serial_data[1];
+ st->asa[2] = serial_data[2];
+
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+ udelay(100);
+
+ return result;
+}
+
+int ak09911_read(struct inv_ak09911_state_s *st, short rawfixed[3])
+{
+ unsigned char regs[8];
+ unsigned char *stat = &regs[0];
+ unsigned char *stat2 = &regs[8];
+ int result = 0;
+ int status = 0;
+
+ result = inv_serial_read(st, AK09911_REG_ST1, 9, regs);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ rawfixed[0] = (short)((regs[2]<<8) | regs[1]);
+ rawfixed[1] = (short)((regs[4]<<8) | regs[3]);
+ rawfixed[2] = (short)((regs[6]<<8) | regs[5]);
+
+ /*
+ * ST : data ready -
+ * Measurement has been completed and data is ready to be read.
+ */
+ if (*stat & 0x01)
+ status = 0;
+ /*
+ * ST2 : overflow -
+ * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
+ * This is likely to happen in presence of an external magnetic
+ * disturbance; it indicates, the sensor data is incorrect and should
+ * be ignored.
+ * An error is returned.
+ * HOFL bit clears when a new measurement starts.
+ */
+ if (*stat2 & 0x08)
+ status = 0x08;
+ /*
+ * ST : overrun -
+ * the previous sample was not fetched and lost.
+ * Valid in continuous measurement mode only.
+ * In single measurement mode this error should not occour and we
+ * don't consider this condition an error.
+ * DOR bit is self-clearing when ST2 or any meas. data register is
+ * read.
+ */
+ if (*stat & 0x02) {
+ /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
+ status = 0;
+ }
+
+ /*
+ * trigger next measurement if:
+ * - stat is non zero;
+ * - if stat is zero and stat2 is non zero.
+ * Won't trigger if data is not ready and there was no error.
+ */
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_SNG_MEASURE);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ if (status)
+ pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status);
+
+ return status;
+}
+
+/**
+ * ak09911_read_raw() - read raw method.
+ */
+static int ak09911_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ int scale = 0;
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ scale = 19661;
+ scale *= (1L << 15);
+ *val = scale;
+ return IIO_VAL_INT;
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ak09911_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ short c[3];
+
+ mutex_lock(&indio_dev->mlock);
+ c[0] = st->compass_data[0];
+ c[1] = st->compass_data[1];
+ c[2] = st->compass_data[2];
+ mutex_unlock(&indio_dev->mlock);
+ return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]);
+}
+
+static ssize_t ak09911_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", (1000 / st->delay));
+}
+
+/**
+ * ak09911_matrix_show() - show orientation matrix
+ */
+static ssize_t ak09911_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+void set_ak09911_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ int result = 0;
+
+ if (enable) {
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_SNG_MEASURE);
+ if (result)
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ schedule_delayed_work(&st->work,
+ msecs_to_jiffies(st->delay));
+ } else {
+ cancel_delayed_work_sync(&st->work);
+ result = inv_serial_single_write(st, AK09911_REG_CNTL,
+ AK09911_CNTL_MODE_POWER_DOWN);
+ if (result)
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ mdelay(1); /* wait at least 100us */
+ }
+}
+
+static ssize_t ak09911_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ /* transform rate to delay in ms */
+ data = 1000 / data;
+ if (data > AK09911_MAX_DELAY)
+ data = AK09911_MAX_DELAY;
+ if (data < AK09911_MIN_DELAY)
+ data = AK09911_MIN_DELAY;
+ st->delay = (unsigned int) data;
+ return count;
+}
+
+static void ak09911_work_func(struct work_struct *work)
+{
+ struct inv_ak09911_state_s *st =
+ container_of((struct delayed_work *)work,
+ struct inv_ak09911_state_s, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ unsigned long delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ st->timestamp = get_time_ns();
+ schedule_delayed_work(&st->work, delay);
+ inv_read_ak09911_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK09911_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK09911_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK09911_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_AK09911_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(value, S_IRUGO, ak09911_value_show, NULL);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ak09911_rate_show,
+ ak09911_rate_store);
+static DEVICE_ATTR(compass_matrix, S_IRUGO, ak09911_matrix_show, NULL);
+
+static struct attribute *inv_ak09911_attributes[] = {
+ &dev_attr_value.attr,
+ &dev_attr_sampling_frequency.attr,
+ &dev_attr_compass_matrix.attr,
+ NULL,
+};
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "ak09911",
+ .attrs = inv_ak09911_attributes
+};
+
+static const struct iio_info ak09911_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ak09911_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_ak09911_probe() - probe function.
+ */
+static int inv_ak09911_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_ak09911_state_s *st;
+ struct iio_dev *indio_dev;
+ int result;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->i2c = client;
+ st->sl_handle = client->adapter;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->i2c_addr = client->addr;
+ st->delay = AK09911_DEFAULT_DELAY;
+ st->compass_id = id->driver_data;
+
+ i2c_set_clientdata(client, indio_dev);
+ result = ak09911_init(st);
+ if (result)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &ak09911_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_ak09911_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_ak09911_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, ak09911_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_ak09911_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_ak09911_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_ak09911_remove() - remove function.
+ */
+static int inv_ak09911_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_ak09911_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_ak09911_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-ak09911-iio module removed.\n");
+ return 0;
+}
+
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_ak09911_id[] = {
+ {"akm9911", COMPASS_ID_AK09911},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_ak09911_id);
+
+static struct i2c_driver inv_ak09911_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_ak09911_probe,
+ .remove = inv_ak09911_remove,
+ .id_table = inv_ak09911_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-ak09911-iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_ak09911_init(void)
+{
+ int result = i2c_add_driver(&inv_ak09911_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_ak09911_exit(void)
+{
+ i2c_del_driver(&inv_ak09911_driver);
+}
+
+module_init(inv_ak09911_init);
+module_exit(inv_ak09911_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-ak09911-iio");
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h
new file mode 100755
index 00000000000..3c96a43d1d6
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h
@@ -0,0 +1,115 @@
+/*
+* 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_AK09911_IIO_H_
+#define _INV_AK09911_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+/**
+ * struct inv_ak09911_state_s - Driver state variables.
+ * @plat_data: board file platform data.
+ * @i2c: i2c client handle.
+ * @trig: not used. for compatibility.
+ * @work: work data structure.
+ * @delay: delay between each scheduled work.
+ * @compass_id: id of compass.
+ * @compass_data: compass data.
+ * @asa: fuse data to adjust final data.
+ * @timestamp: timestamp of each data.
+ * @i2c_addr: i2c address
+ * @sl_handle: Handle to I2C port.
+ */
+struct inv_ak09911_state_s {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *i2c;
+ struct iio_trigger *trig;
+ struct delayed_work work;
+ int delay; /* msec */
+ unsigned char compass_id;
+ short compass_data[3];
+ u8 asa[3]; /* axis sensitivity adjustment */
+ s64 timestamp;
+ short i2c_addr;
+ void *sl_handle;
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_AK09911_SCAN_MAGN_X,
+ INV_AK09911_SCAN_MAGN_Y,
+ INV_AK09911_SCAN_MAGN_Z,
+ INV_AK09911_SCAN_TIMESTAMP,
+};
+
+/*! \name ak09911 constant definition
+ \anchor ak09911_Def
+ Constant definitions of the ak09911.*/
+#define AK09911_MEASUREMENT_TIME_US 10000
+
+/*! \name ak09911 operation mode
+ \anchor AK09911_Mode
+ Defines an operation mode of the ak09911.*/
+/*! @{*/
+#define AK09911_CNTL_MODE_SNG_MEASURE 0x01
+#define AK09911_CNTL_MODE_SELF_TEST 0x08
+#define AK09911_CNTL_MODE_FUSE_ACCESS 0x1F
+#define AK09911_CNTL_MODE_POWER_DOWN 0x00
+/*! @}*/
+
+/*! \name ak09911 register address
+\anchor AK09911_REG
+Defines a register address of the ak09911.*/
+/*! @{*/
+#define AK09911_REG_WIA 0x00
+#define AK09911_REG_INFO 0x01
+#define AK09911_REG_ST1 0x10
+#define AK09911_REG_HXL 0x11
+#define AK09911_REG_ST2 0x18
+#define AK09911_REG_CNTL 0x31
+/*! @}*/
+
+/*! \name ak09911 fuse-rom address
+\anchor AK09911_FUSE
+Defines a read-only address of the fuse ROM of the ak09911.*/
+/*! @{*/
+#define AK09911_FUSE_ASAX 0x60
+/*! @}*/
+
+#define AK09911_MAX_DELAY (200)
+#define AK09911_MIN_DELAY (10)
+#define AK09911_DEFAULT_DELAY (100)
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+int inv_ak09911_configure_ring(struct iio_dev *indio_dev);
+void inv_ak09911_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_ak09911_probe_trigger(struct iio_dev *indio_dev);
+void inv_ak09911_remove_trigger(struct iio_dev *indio_dev);
+void set_ak09911_enable(struct iio_dev *indio_dev, bool enable);
+int ak09911_read_raw_data(struct inv_ak09911_state_s *st,
+ short dat[3]);
+void inv_read_ak09911_fifo(struct iio_dev *indio_dev);
+int ak09911_read(struct inv_ak09911_state_s *st, short rawfixed[3]);
+
+#endif
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c
new file mode 100755
index 00000000000..ae3119bb5b0
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) 2013 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 <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 "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_ak09911_iio.h"
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+
+ d_ind = 0;
+ 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;
+}
+
+/**
+ * inv_read_ak09911_fifo() - Transfer data from FIFO to ring buffer.
+ */
+void inv_read_ak09911_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int d_ind, i;
+ s8 *tmp;
+ s64 tmp_buf[2];
+
+ if (!ak09911_read(st, st->compass_data)) {
+ for (i = 0; i < 3; i++) {
+ st->compass_data[i] = (short)(((int)st->compass_data[i]
+ * (st->asa[i] + 128)) >> 7);
+ }
+ tmp = (u8 *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_AK09911_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7)/8] = st->timestamp;
+ ring->access->store_to(indio_dev->buffer, tmp, st->timestamp);
+ }
+}
+
+void inv_ak09911_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_ak09911_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_Z)))
+ return 0;
+
+ set_ak09911_enable(indio_dev, true);
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_ak09911_predisable(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_AK09911_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_AK09911_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_AK09911_SCAN_MAGN_Z, ring->scan_mask);
+ set_ak09911_enable(indio_dev, false);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_ak09911_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_ak09911_postenable,
+ .predisable = &inv_ak09911_predisable,
+};
+
+int inv_ak09911_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_ak09911_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c
new file mode 100755
index 00000000000..7fc6096ecb0
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c
@@ -0,0 +1,75 @@
+/*
+* Copyright (C) 2013 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+#include "inv_ak09911_iio.h"
+
+static const struct iio_trigger_ops inv_ak09911_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_ak09911_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->i2c->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_ak09911_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_ak09911_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_ak09911_state_s *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c
new file mode 100755
index 00000000000..a781fd39822
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c
@@ -0,0 +1,590 @@
+/*
+* Copyright (C) 2013 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_ak89xx_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+static s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+/**
+ * inv_serial_read() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE: The slave register will not increment when reading from the FIFO.
+ */
+int inv_serial_read(struct inv_ak89xx_state_s *st, u8 reg, u16 length, u8 *data)
+{
+ int result;
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ result = i2c_smbus_read_i2c_block_data(st->i2c, reg, length, data);
+ if (result != length) {
+ if (result < 0)
+ return result;
+ else
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * inv_serial_single_write() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ */
+int inv_serial_single_write(struct inv_ak89xx_state_s *st, u8 reg, u8 data)
+{
+ u8 d[1];
+ d[0] = data;
+ INV_I2C_INC_COMPASSWRITE(3);
+
+ return i2c_smbus_write_i2c_block_data(st->i2c, reg, 1, d);
+}
+
+static int ak89xx_init(struct inv_ak89xx_state_s *st)
+{
+ int result = 0;
+ unsigned char serial_data[3];
+
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ AK89XX_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+ /* Wait at least 100us */
+ udelay(100);
+
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ AK89XX_CNTL_MODE_FUSE_ACCESS);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ /* Wait at least 200us */
+ udelay(200);
+
+ result = inv_serial_read(st, AK89XX_FUSE_ASAX, 3, serial_data);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ st->asa[0] = serial_data[0];
+ st->asa[1] = serial_data[1];
+ st->asa[2] = serial_data[2];
+
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ AK89XX_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+ udelay(100);
+
+ return result;
+}
+
+int ak89xx_read(struct inv_ak89xx_state_s *st, short rawfixed[3])
+{
+ unsigned char regs[8];
+ unsigned char *stat = &regs[0];
+ unsigned char *stat2 = &regs[7];
+ int result = 0;
+ int status = 0;
+
+ result = inv_serial_read(st, AK89XX_REG_ST1, 8, regs);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+
+ rawfixed[0] = (short)((regs[2]<<8) | regs[1]);
+ rawfixed[1] = (short)((regs[4]<<8) | regs[3]);
+ rawfixed[2] = (short)((regs[6]<<8) | regs[5]);
+
+ /*
+ * ST : data ready -
+ * Measurement has been completed and data is ready to be read.
+ */
+ if (*stat & 0x01)
+ status = 0;
+
+ /*
+ * ST2 : data error -
+ * occurs when data read is started outside of a readable period;
+ * data read would not be correct.
+ * Valid in continuous measurement mode only.
+ * In single measurement mode this error should not occour but we
+ * stil account for it and return an error, since the data would be
+ * corrupted.
+ * DERR bit is self-clearing when ST2 register is read.
+ */
+ if (*stat2 & 0x04)
+ status = 0x04;
+ /*
+ * ST2 : overflow -
+ * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
+ * This is likely to happen in presence of an external magnetic
+ * disturbance; it indicates, the sensor data is incorrect and should
+ * be ignored.
+ * An error is returned.
+ * HOFL bit clears when a new measurement starts.
+ */
+ if (*stat2 & 0x08)
+ status = 0x08;
+ /*
+ * ST : overrun -
+ * the previous sample was not fetched and lost.
+ * Valid in continuous measurement mode only.
+ * In single measurement mode this error should not occour and we
+ * don't consider this condition an error.
+ * DOR bit is self-clearing when ST2 or any meas. data register is
+ * read.
+ */
+ if (*stat & 0x02) {
+ /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
+ status = 0;
+ }
+
+ /*
+ * trigger next measurement if:
+ * - stat is non zero;
+ * - if stat is zero and stat2 is non zero.
+ * Won't trigger if data is not ready and there was no error.
+ */
+ if (1) {
+ unsigned char scale = 0;
+ if (st->compass_id == COMPASS_ID_AK8963)
+ scale = st->compass_scale;
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ (scale << 4) | AK89XX_CNTL_MODE_SNG_MEASURE);
+ if (result) {
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ return result;
+ }
+ } else
+ pr_err("%s, no next measure(0x%x,0x%x)\n", __func__,
+ *stat, *stat2);
+
+ if (status)
+ pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status);
+
+ return status;
+}
+
+/**
+ * ak89xx_read_raw() - read raw method.
+ */
+static int ak89xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ int scale = 0;
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ if (st->compass_id == COMPASS_ID_AK8975)
+ scale = 9830;
+ else if (st->compass_id == COMPASS_ID_AK8972)
+ scale = 19661;
+ else if (st->compass_id == COMPASS_ID_AK8963) {
+ if (st->compass_scale)
+ scale = 4915; /* 16 bit */
+ else
+ scale = 19661; /* 14 bit */
+ }
+ scale *= (1L << 15);
+ *val = scale;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ak89xx_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ short c[3];
+
+ mutex_lock(&indio_dev->mlock);
+ c[0] = st->compass_data[0];
+ c[1] = st->compass_data[1];
+ c[2] = st->compass_data[2];
+ mutex_unlock(&indio_dev->mlock);
+ return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]);
+}
+
+static ssize_t ak89xx_scale_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ int scale = 0;
+
+ if (st->compass_id == COMPASS_ID_AK8975)
+ scale = 9830;
+ else if (st->compass_id == COMPASS_ID_AK8972)
+ scale = 19661;
+ else if (st->compass_id == COMPASS_ID_AK8963) {
+ if (st->compass_scale)
+ scale = 4915; /* 16 bit */
+ else
+ scale = 19661; /* 14 bit */
+ }
+ scale *= (1L << 15);
+ return sprintf(buf, "%d\n", scale);
+}
+
+static ssize_t ak89xx_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", (1000 / st->delay));
+}
+
+/**
+ * ak89xx_matrix_show() - show orientation matrix
+ */
+static ssize_t ak89xx_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+void set_ak89xx_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ int result = 0;
+ unsigned char scale = 0;
+
+ if (st->compass_id == COMPASS_ID_AK8963)
+ scale = st->compass_scale;
+
+ if (enable) {
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ (scale << 4) | AK89XX_CNTL_MODE_SNG_MEASURE);
+ if (result)
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ schedule_delayed_work(&st->work,
+ msecs_to_jiffies(st->delay));
+ } else {
+ cancel_delayed_work_sync(&st->work);
+ result = inv_serial_single_write(st, AK89XX_REG_CNTL,
+ (scale << 4) | AK89XX_CNTL_MODE_POWER_DOWN);
+ if (result)
+ pr_err("%s, line=%d\n", __func__, __LINE__);
+ mdelay(1); /* wait at least 100us */
+ }
+}
+
+static ssize_t ak89xx_scale_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ unsigned long data, result;
+
+ result = kstrtoul(buf, 10, &data);
+ if (result)
+ return result;
+ if (st->compass_id == COMPASS_ID_AK8963)
+ st->compass_scale = !!data;
+ return count;
+}
+
+static ssize_t ak89xx_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ /* transform rate to delay in ms */
+ data = 1000 / data;
+ if (data > AK89XX_MAX_DELAY)
+ data = AK89XX_MAX_DELAY;
+ if (data < AK89XX_MIN_DELAY)
+ data = AK89XX_MIN_DELAY;
+ st->delay = (unsigned int) data;
+ return count;
+}
+
+static void ak89xx_work_func(struct work_struct *work)
+{
+ struct inv_ak89xx_state_s *st =
+ container_of((struct delayed_work *)work,
+ struct inv_ak89xx_state_s, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ unsigned long delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ st->timestamp = get_time_ns();
+ schedule_delayed_work(&st->work, delay);
+ inv_read_ak89xx_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK89XX_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK89XX_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AK89XX_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_AK89XX_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(value, S_IRUGO, ak89xx_value_show, NULL);
+static DEVICE_ATTR(scale, S_IRUGO | S_IWUSR, ak89xx_scale_show,
+ ak89xx_scale_store);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ak89xx_rate_show,
+ ak89xx_rate_store);
+static DEVICE_ATTR(compass_matrix, S_IRUGO, ak89xx_matrix_show, NULL);
+
+static struct attribute *inv_ak89xx_attributes[] = {
+ &dev_attr_value.attr,
+ &dev_attr_scale.attr,
+ &dev_attr_sampling_frequency.attr,
+ &dev_attr_compass_matrix.attr,
+ NULL,
+};
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "ak89xx",
+ .attrs = inv_ak89xx_attributes
+};
+
+static const struct iio_info ak89xx_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ak89xx_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_ak89xx_probe() - probe function.
+ */
+static int inv_ak89xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_ak89xx_state_s *st;
+ struct iio_dev *indio_dev;
+ int result;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->i2c = client;
+ st->sl_handle = client->adapter;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->i2c_addr = client->addr;
+ st->delay = AK89XX_DEFAULT_DELAY;
+ st->compass_id = id->driver_data;
+ st->compass_scale = 0;
+
+ i2c_set_clientdata(client, indio_dev);
+ result = ak89xx_init(st);
+ if (result)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &ak89xx_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_ak89xx_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_ak89xx_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, ak89xx_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_ak89xx_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_ak89xx_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_ak89xx_remove() - remove function.
+ */
+static int inv_ak89xx_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_ak89xx_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_ak89xx_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-ak89xx-iio module removed.\n");
+ return 0;
+}
+
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_ak89xx_id[] = {
+ {"akm8975", COMPASS_ID_AK8975},
+ {"akm8972", COMPASS_ID_AK8972},
+ {"akm8963", COMPASS_ID_AK8963},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_ak89xx_id);
+
+static struct i2c_driver inv_ak89xx_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_ak89xx_probe,
+ .remove = inv_ak89xx_remove,
+ .id_table = inv_ak89xx_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-ak89xx-iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_ak89xx_init(void)
+{
+ int result = i2c_add_driver(&inv_ak89xx_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_ak89xx_exit(void)
+{
+ i2c_del_driver(&inv_ak89xx_driver);
+}
+
+module_init(inv_ak89xx_init);
+module_exit(inv_ak89xx_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-ak89xx-iio");
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h
new file mode 100755
index 00000000000..9a9f14a73ec
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h
@@ -0,0 +1,144 @@
+/*
+* 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_AK89XX_IIO_H_
+#define _INV_AK89XX_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+/**
+ * struct inv_ak89xx_state_s - Driver state variables.
+ * @plat_data: board file platform data.
+ * @i2c: i2c client handle.
+ * @trig: not used. for compatibility.
+ * @work: work data structure.
+ * @delay: delay between each scheduled work.
+ * @dev: Represents read-only node for accessing buffered data.
+ * @inv_dev: Handle to input device.
+ * @sl_handle: Handle to I2C port.
+ */
+struct inv_ak89xx_state_s {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *i2c;
+ struct iio_trigger *trig;
+ struct delayed_work work;
+ int delay; /* msec */
+ unsigned char compass_id;
+ int compass_scale; /* for ak8963, 1:16-bit, 0:14-bit */
+ short compass_data[3];
+ u8 asa[3]; /* axis sensitivity adjustment */
+ s64 timestamp;
+ short i2c_addr;
+ void *sl_handle;
+ struct device *inv_dev;
+ struct input_dev *idev;
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_AK89XX_SCAN_MAGN_X,
+ INV_AK89XX_SCAN_MAGN_Y,
+ INV_AK89XX_SCAN_MAGN_Z,
+ INV_AK89XX_SCAN_TIMESTAMP,
+};
+
+#define AK89XX_I2C_NAME "ak89xx"
+
+#define SENSOR_DATA_SIZE 8
+#define YPR_DATA_SIZE 12
+#define RWBUF_SIZE 16
+
+#define ACC_DATA_FLAG 0
+#define MAG_DATA_FLAG 1
+#define ORI_DATA_FLAG 2
+#define AKM_NUM_SENSORS 3
+
+#define ACC_DATA_READY (1 << (ACC_DATA_FLAG))
+#define MAG_DATA_READY (1 << (MAG_DATA_FLAG))
+#define ORI_DATA_READY (1 << (ORI_DATA_FLAG))
+
+#define AKM_MINOR_NUMBER 254
+
+/*! \name AK89XX constant definition
+ \anchor AK89XX_Def
+ Constant definitions of the AK89XX.*/
+#define AK89XX_MEASUREMENT_TIME_US 10000
+
+/*! \name AK89XX operation mode
+ \anchor AK89XX_Mode
+ Defines an operation mode of the AK89XX.*/
+/*! @{*/
+#define AK89XX_CNTL_MODE_SNG_MEASURE 0x01
+#define AK89XX_CNTL_MODE_SELF_TEST 0x08
+#define AK89XX_CNTL_MODE_FUSE_ACCESS 0x0F
+#define AK89XX_CNTL_MODE_POWER_DOWN 0x00
+/*! @}*/
+
+/*! \name AK89XX register address
+\anchor AK89XX_REG
+Defines a register address of the AK89XX.*/
+/*! @{*/
+#define AK89XX_REG_WIA 0x00
+#define AK89XX_REG_INFO 0x01
+#define AK89XX_REG_ST1 0x02
+#define AK89XX_REG_HXL 0x03
+#define AK89XX_REG_HXH 0x04
+#define AK89XX_REG_HYL 0x05
+#define AK89XX_REG_HYH 0x06
+#define AK89XX_REG_HZL 0x07
+#define AK89XX_REG_HZH 0x08
+#define AK89XX_REG_ST2 0x09
+#define AK89XX_REG_CNTL 0x0A
+#define AK89XX_REG_RSV 0x0B
+#define AK89XX_REG_ASTC 0x0C
+#define AK89XX_REG_TS1 0x0D
+#define AK89XX_REG_TS2 0x0E
+#define AK89XX_REG_I2CDIS 0x0F
+/*! @}*/
+
+/*! \name AK89XX fuse-rom address
+\anchor AK89XX_FUSE
+Defines a read-only address of the fuse ROM of the AK89XX.*/
+/*! @{*/
+#define AK89XX_FUSE_ASAX 0x10
+#define AK89XX_FUSE_ASAY 0x11
+#define AK89XX_FUSE_ASAZ 0x12
+/*! @}*/
+
+#define AK89XX_MAX_DELAY (200)
+#define AK89XX_MIN_DELAY (10)
+#define AK89XX_DEFAULT_DELAY (100)
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+int inv_ak89xx_configure_ring(struct iio_dev *indio_dev);
+void inv_ak89xx_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_ak89xx_probe_trigger(struct iio_dev *indio_dev);
+void inv_ak89xx_remove_trigger(struct iio_dev *indio_dev);
+void set_ak89xx_enable(struct iio_dev *indio_dev, bool enable);
+int ak89xx_read_raw_data(struct inv_ak89xx_state_s *st,
+ short dat[3]);
+void inv_read_ak89xx_fifo(struct iio_dev *indio_dev);
+int ak89xx_read(struct inv_ak89xx_state_s *st, short rawfixed[3]);
+
+#endif
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c
new file mode 100755
index 00000000000..a8c1090bed5
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c
@@ -0,0 +1,138 @@
+/*
+* Copyright (C) 2013 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 <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 "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_ak89xx_iio.h"
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+
+ d_ind = 0;
+ 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;
+}
+
+/**
+ * inv_read_ak89xx_fifo() - Transfer data from FIFO to ring buffer.
+ */
+void inv_read_ak89xx_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int d_ind;
+ s8 *tmp;
+ s64 tmp_buf[2];
+
+ if (!ak89xx_read(st, st->compass_data)) {
+ st->compass_data[0] = (short)(((int)st->compass_data[0] * (st->asa[0] + 128)) >> 8);
+ st->compass_data[1] = (short)(((int)st->compass_data[1] * (st->asa[1] + 128)) >> 8);
+ st->compass_data[2] = (short)(((int)st->compass_data[2] * (st->asa[2] + 128)) >> 8);
+ tmp = (u8 *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_AK89XX_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7)/8] = st->timestamp;
+ ring->access->store_to(indio_dev->buffer, tmp, st->timestamp);
+ }
+}
+
+void inv_ak89xx_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_ak89xx_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_Z)))
+ return 0;
+
+ set_ak89xx_enable(indio_dev, true);
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_ak89xx_predisable(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_AK89XX_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_AK89XX_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_AK89XX_SCAN_MAGN_Z, ring->scan_mask);
+ set_ak89xx_enable(indio_dev, false);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_ak89xx_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_ak89xx_postenable,
+ .predisable = &inv_ak89xx_predisable,
+};
+
+int inv_ak89xx_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_ak89xx_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c
new file mode 100755
index 00000000000..04c77ab5c79
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c
@@ -0,0 +1,75 @@
+/*
+* Copyright (C) 2013 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+#include "inv_ak89xx_iio.h"
+
+static const struct iio_trigger_ops inv_ak89xx_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_ak89xx_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->i2c->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_ak89xx_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_ak89xx_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_ak89xx_state_s *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c
new file mode 100755
index 00000000000..612ba72b59e
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c
@@ -0,0 +1,570 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_core.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_ami306_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+static unsigned char late_initialize = true;
+
+s32 i2c_write(const struct i2c_client *client,
+ u8 command, u8 length, const u8 *values)
+{
+ INV_I2C_INC_COMPASSWRITE(3);
+ return i2c_smbus_write_i2c_block_data(client, command, length, values);
+}
+
+s32 i2c_read(const struct i2c_client *client,
+ u8 command, u8 length, u8 *values)
+{
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ return i2c_smbus_read_i2c_block_data(client, command, length, values);
+}
+
+static int ami306_read_param(struct inv_ami306_state_s *st)
+{
+ int result = 0;
+ unsigned char regs[AMI_PARAM_LEN];
+ struct ami_sensor_parametor *param = &st->param;
+
+ result = i2c_read(st->i2c, REG_AMI_SENX,
+ AMI_PARAM_LEN, regs);
+ if (result < 0)
+ return result;
+
+ /* Little endian 16 bit registers */
+ param->m_gain.x = le16_to_cpup((__le16 *)(&regs[0]));
+ param->m_gain.y = le16_to_cpup((__le16 *)(&regs[2]));
+ param->m_gain.z = le16_to_cpup((__le16 *)(&regs[4]));
+
+ param->m_interference.xy = regs[7];
+ param->m_interference.xz = regs[6];
+ param->m_interference.yx = regs[9];
+ param->m_interference.yz = regs[8];
+ param->m_interference.zx = regs[11];
+ param->m_interference.zy = regs[10];
+
+ param->m_offset.x = AMI_STANDARD_OFFSET;
+ param->m_offset.y = AMI_STANDARD_OFFSET;
+ param->m_offset.z = AMI_STANDARD_OFFSET;
+
+ param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT;
+
+ return 0;
+}
+
+static int ami306_write_offset(const struct i2c_client *client,
+ unsigned char *fine)
+{
+ int result = 0;
+ unsigned char dat[3];
+ dat[0] = (0x7f & fine[0]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFX, 2, dat);
+ dat[0] = (0x7f & fine[1]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFY, 2, dat);
+ dat[0] = (0x7f & fine[2]);
+ dat[1] = 0;
+ result = i2c_write(client, REG_AMI_OFFZ, 2, dat);
+
+ return result;
+}
+
+static int ami306_wait_data_ready(struct inv_ami306_state_s *st,
+ unsigned long usecs, unsigned long times)
+{
+ int result = 0;
+ unsigned char buf;
+
+ for (; 0 < times; --times) {
+ udelay(usecs);
+ result = i2c_read(st->i2c, REG_AMI_STA1, 1, &buf);
+ if (result < 0)
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ if (buf & AMI_STA1_DRDY_BIT)
+ return 0;
+ else if (buf & AMI_STA1_DOR_BIT)
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+}
+int ami306_read_raw_data(struct inv_ami306_state_s *st,
+ short dat[3])
+{
+ int result;
+ unsigned char buf[6];
+ result = i2c_read(st->i2c, REG_AMI_DATAX, sizeof(buf), buf);
+ if (result < 0)
+ return result;
+ dat[0] = le16_to_cpup((__le16 *)(&buf[0]));
+ dat[1] = le16_to_cpup((__le16 *)(&buf[2]));
+ dat[2] = le16_to_cpup((__le16 *)(&buf[4]));
+
+ return 0;
+}
+
+#define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */
+#define AMI_DRDYWAIT 800 /* u(micro) sec */
+static int ami306_force_measurement(struct inv_ami306_state_s *st,
+ short ver[3])
+{
+ int result;
+ int status;
+ char buf;
+ buf = AMI_CTRL3_FORCE_BIT;
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf);
+ if (result < 0)
+ return result;
+
+ result = ami306_wait_data_ready(st,
+ AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY);
+ if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW)
+ return result;
+ /* READ DATA X,Y,Z */
+ status = ami306_read_raw_data(st, ver);
+ if (status)
+ return status;
+
+ return result;
+}
+
+static int ami306_initial_b0_adjust(struct inv_ami306_state_s *st)
+{
+ int result;
+ unsigned char fine[3] = { 0 };
+ short data[3];
+ int diff[3] = { 0x7fff, 0x7fff, 0x7fff };
+ int fn = 0;
+ int ax = 0;
+ unsigned char buf[3];
+
+ buf[0] = AMI_CTRL2_DREN;
+ result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf);
+ if (result)
+ return result;
+
+ buf[0] = AMI_CTRL4_HS & 0xFF;
+ buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF;
+ result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf);
+ if (result < 0)
+ return result;
+
+ for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */
+ fine[0] = fine[1] = fine[2] = fn;
+ result = ami306_write_offset(st->i2c, fine);
+ if (result)
+ return result;
+
+ result = ami306_force_measurement(st, data);
+ if (result)
+ return result;
+
+ for (ax = 0; ax < 3; ax++) {
+ /* search point most close to zero. */
+ if (diff[ax] > abs(data[ax])) {
+ st->fine[ax] = fn;
+ diff[ax] = abs(data[ax]);
+ }
+ }
+ }
+ result = ami306_write_offset(st->i2c, st->fine);
+ if (result)
+ return result;
+
+ /* Software Reset */
+ buf[0] = AMI_CTRL3_SRST_BIT;
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, buf);
+ if (result < 0)
+ return result;
+ else
+ return 0;
+}
+
+static int ami306_start_sensor(struct inv_ami306_state_s *st)
+{
+ int result = 0;
+ unsigned char buf[2];
+
+ /* Step 1 */
+ buf[0] = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, buf);
+ if (result < 0)
+ return result;
+ /* Step 2 */
+ buf[0] = AMI_CTRL2_DREN;
+ result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf);
+ if (result < 0)
+ return result;
+ /* Step 3 */
+ buf[0] = (AMI_CTRL4_HS & 0xFF);
+ buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF;
+
+ result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf);
+ if (result < 0)
+ return result;
+
+ /* Step 4 */
+ result = ami306_write_offset(st->i2c, st->fine);
+
+ return result;
+}
+
+int set_ami306_enable(struct iio_dev *indio_dev, int state)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ int result;
+ char buf;
+
+ buf = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, &buf);
+ if (result < 0)
+ return result;
+
+ result = ami306_read_param(st);
+ if (result)
+ return result;
+ if (late_initialize) {
+ result = ami306_initial_b0_adjust(st);
+ if (result)
+ return result;
+ late_initialize = false;
+ }
+ result = ami306_start_sensor(st);
+ if (result)
+ return result;
+ buf = AMI_CTRL3_FORCE_BIT;
+ st->timestamp = iio_get_time_ns();
+ result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/**
+ * ami306_read_raw() - read raw method.
+ */
+static int ami306_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = AMI_SCALE;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_compass_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_compass_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+static ssize_t ami306_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (0 == data)
+ return -EINVAL;
+ /* transform rate to delay in ms */
+ data = 1000 / data;
+ if (data > AMI_MAX_DELAY)
+ data = AMI_MAX_DELAY;
+ if (data < AMI_MIN_DELAY)
+ data = AMI_MIN_DELAY;
+ st->delay = data;
+ return count;
+}
+
+static ssize_t ami306_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", 1000 / st->delay);
+}
+
+
+static void ami306_work_func(struct work_struct *work)
+{
+ struct inv_ami306_state_s *st =
+ container_of((struct delayed_work *)work,
+ struct inv_ami306_state_s, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ unsigned long delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ st->timestamp = iio_get_time_ns();
+ schedule_delayed_work(&st->work, delay);
+ inv_read_ami306_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_AMI306_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_AMI306_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ami306_rate_show,
+ ami306_rate_store);
+
+static struct attribute *inv_ami306_attributes[] = {
+ &dev_attr_compass_matrix.attr,
+ &dev_attr_sampling_frequency.attr,
+ NULL,
+};
+static const struct attribute_group inv_attribute_group = {
+ .name = "ami306",
+ .attrs = inv_ami306_attributes
+};
+
+static const struct iio_info ami306_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ami306_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_ami306_probe() - probe function.
+ */
+static int inv_ami306_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_ami306_state_s *st;
+ struct iio_dev *indio_dev;
+ int result;
+ char data;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->i2c = client;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->delay = 10;
+
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ result = i2c_read(st->i2c, REG_AMI_WIA, 1, &data);
+ if (result < 0)
+ goto out_free;
+ if (data != DATA_WIA)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &ami306_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_ami306_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_ami306_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, ami306_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_ami306_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_ami306_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_ami306_remove() - remove function.
+ */
+static int inv_ami306_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_ami306_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_ami306_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-ami306-iio module removed.\n");
+ return 0;
+}
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_ami306_id[] = {
+ {"ami306", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_ami306_id);
+
+static struct i2c_driver inv_ami306_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_ami306_probe,
+ .remove = inv_ami306_remove,
+ .id_table = inv_ami306_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-ami306-iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_ami306_init(void)
+{
+ int result = i2c_add_driver(&inv_ami306_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_ami306_exit(void)
+{
+ i2c_del_driver(&inv_ami306_driver);
+}
+
+module_init(inv_ami306_init);
+module_exit(inv_ami306_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-ami306-iio");
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h
new file mode 100755
index 00000000000..fa4f4ee1e5d
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h
@@ -0,0 +1,159 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_iio.h
+ * @brief Struct definitions for the Invensense implementation
+ * of ami306 driver.
+ */
+
+#ifndef _INV_GYRO_H_
+#define _INV_GYRO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+/** axis sensitivity(gain) calibration parameter information */
+struct ami_vector3d {
+ signed short x; /**< X-axis */
+ signed short y; /**< Y-axis */
+ signed short z; /**< Z-axis */
+};
+
+/** axis interference information */
+struct ami_interference {
+ /**< Y-axis magnetic field for X-axis correction value */
+ signed short xy;
+ /**< Z-axis magnetic field for X-axis correction value */
+ signed short xz;
+ /**< X-axis magnetic field for Y-axis correction value */
+ signed short yx;
+ /**< Z-axis magnetic field for Y-axis correction value */
+ signed short yz;
+ /**< X-axis magnetic field for Z-axis correction value */
+ signed short zx;
+ /**< Y-axis magnetic field for Z-axis correction value */
+ signed short zy;
+};
+
+/** sensor calibration Parameter information */
+struct ami_sensor_parametor {
+ /**< geomagnetic field sensor gain */
+ struct ami_vector3d m_gain;
+ /**< geomagnetic field sensor gain correction parameter */
+ struct ami_vector3d m_gain_cor;
+ /**< geomagnetic field sensor offset */
+ struct ami_vector3d m_offset;
+ /**< geomagnetic field sensor axis interference parameter */
+ struct ami_interference m_interference;
+};
+
+/**
+ * struct inv_ami306_state_s - Driver state variables.
+ * @plat_data: board file platform data.
+ * @i2c: i2c client handle.
+ * @trig: not used. for compatibility.
+ * @param: ami specific sensor data.
+ * @work: work data structure.
+ * @delay: delay between each scheduled work.
+ * @fine: fine tunign parameters.
+ * @compass_data: compass data store.
+ * @timestamp: time stamp.
+ */
+struct inv_ami306_state_s {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *i2c;
+ struct iio_trigger *trig;
+ struct ami_sensor_parametor param;
+ struct delayed_work work;
+ int delay;
+ s8 fine[3];
+ short compass_data[3];
+ s64 timestamp;
+};
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_AMI306_SCAN_MAGN_X,
+ INV_AMI306_SCAN_MAGN_Y,
+ INV_AMI306_SCAN_MAGN_Z,
+ INV_AMI306_SCAN_TIMESTAMP,
+};
+
+#define REG_AMI_WIA 0x0f
+#define REG_AMI_DATAX 0x10
+#define REG_AMI_STA1 0x18
+#define REG_AMI_CTRL1 0x1b
+#define REG_AMI_CTRL2 0x1c
+#define REG_AMI_CTRL3 0x1d
+#define REG_AMI_B0X 0x20
+#define REG_AMI_B0Y 0x22
+#define REG_AMI_B0Z 0x24
+#define REG_AMI_CTRL5 0x40
+#define REG_AMI_CTRL4 0x5c
+#define REG_AMI_TEMP 0x60
+#define REG_AMI_SENX 0x96
+#define REG_AMI_OFFX 0x6c
+#define REG_AMI_OFFY 0x72
+#define REG_AMI_OFFZ 0x78
+
+
+#define DATA_WIA 0x46
+#define AMI_CTRL1_PC1 0x80
+#define AMI_CTRL1_FS1_FORCE 0x02
+#define AMI_CTRL1_ODR1 0x10
+#define AMI_CTRL2_DREN 0x08
+#define AMI_CTRL2_DRP 0x04
+#define AMI_CTRL3_FORCE_BIT 0x40
+#define AMI_CTRL3_B0_LO_BIT 0x10
+#define AMI_CTRL3_SRST_BIT 0x80
+#define AMI_CTRL4_HS 0xa07e
+#define AMI_CTRL4_AB 0x0001
+#define AMI_STA1_DRDY_BIT 0x40
+#define AMI_STA1_DOR_BIT 0x20
+
+#define AMI_PARAM_LEN 12
+#define AMI_STANDARD_OFFSET 0x800
+#define AMI_GAIN_COR_DEFAULT 1000
+#define AMI_FINE_MAX 96
+#define AMI_MAX_DELAY 1000
+#define AMI_MIN_DELAY 10
+#define AMI_SCALE (5461 * (1<<15))
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+
+int inv_ami306_configure_ring(struct iio_dev *indio_dev);
+void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_ami306_probe_trigger(struct iio_dev *indio_dev);
+void inv_ami306_remove_trigger(struct iio_dev *indio_dev);
+int set_ami306_enable(struct iio_dev *indio_dev, int state);
+int ami306_read_raw_data(struct inv_ami306_state_s *st,
+ short dat[3]);
+int inv_read_ami306_fifo(struct iio_dev *indio_dev);
+
+#endif /* #ifndef _INV_GYRO_H_ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c
new file mode 100755
index 00000000000..ed91cf49516
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c
@@ -0,0 +1,163 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_ring.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#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 "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_ami306_iio.h"
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index) {
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+ d_ind = 0;
+ 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;
+}
+
+/**
+ * inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+int inv_read_ami306_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result, status, d_ind;
+ char b;
+ char *tmp;
+ s64 tmp_buf[2];
+
+ result = i2c_smbus_read_i2c_block_data(st->i2c, REG_AMI_STA1, 1, &b);
+ if (result < 0)
+ goto end_session;
+ if (b & AMI_STA1_DRDY_BIT) {
+ status = ami306_read_raw_data(st, st->compass_data);
+ if (status) {
+ pr_err("error reading raw\n");
+ goto end_session;
+ }
+ tmp = (unsigned char *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_AMI306_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7)/8] = st->timestamp;
+ ring->access->store_to(indio_dev->buffer, tmp, st->timestamp);
+ } else if (b & AMI_STA1_DOR_BIT)
+ pr_err("not ready\n");
+end_session:
+ b = AMI_CTRL3_FORCE_BIT;
+ result = i2c_smbus_write_i2c_block_data(st->i2c, REG_AMI_CTRL3, 1, &b);
+
+ return IRQ_HANDLED;
+}
+
+void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+static int inv_ami306_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int result;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Z)))
+ return 0;
+
+ result = set_ami306_enable(indio_dev, true);
+ if (result)
+ return result;
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_ami306_predisable(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_AMI306_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_AMI306_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_AMI306_SCAN_MAGN_Z, ring->scan_mask);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_ami306_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_ami306_postenable,
+ .predisable = &inv_ami306_predisable,
+};
+
+int inv_ami306_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_ami306_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c
new file mode 100755
index 00000000000..f7fe59ef5df
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_ami306_trigger.c
+ * @brief Invensense implementation for AMI306
+ * @details This driver currently works for the AMI306
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+#include "inv_ami306_iio.h"
+
+static const struct iio_trigger_ops inv_ami306_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_ami306_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->i2c->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_ami306_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_ami306_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_ami306_state_s *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c
new file mode 100755
index 00000000000..6af420bb5cf
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c
@@ -0,0 +1,969 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_core.c
+ * @brief Invensense implementation for yas530/yas532/yas533.
+ * @details This driver currently works for yas530/yas532/yas533.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_yas53x_iio.h"
+#include "sysfs.h"
+#include "inv_test/inv_counters.h"
+
+/* -------------------------------------------------------------------------- */
+static int Cx, Cy1, Cy2;
+static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9;
+static int k;
+
+static u8 dx, dy1, dy2;
+static u8 d2, d3, d4, d5, d6, d7, d8, d9, d0;
+static u8 dck, ver;
+
+/**
+ * inv_serial_read() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE: The slave register will not increment when reading from the FIFO.
+ */
+int inv_serial_read(struct inv_compass_state *st, u8 reg, u16 length, u8 *data)
+{
+ int result;
+ INV_I2C_INC_COMPASSWRITE(3);
+ INV_I2C_INC_COMPASSREAD(length);
+ result = i2c_smbus_read_i2c_block_data(st->client, reg, length, data);
+ if (result != length) {
+ if (result < 0)
+ return result;
+ else
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * inv_serial_single_write() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ */
+int inv_serial_single_write(struct inv_compass_state *st, u8 reg, u8 data)
+{
+ u8 d[1];
+ d[0] = data;
+ INV_I2C_INC_COMPASSWRITE(3);
+
+ return i2c_smbus_write_i2c_block_data(st->client, reg, 1, d);
+}
+
+static int set_hardware_offset(struct inv_compass_state *st,
+ char offset_x, char offset_y1, char offset_y2)
+{
+ char data;
+ int result = 0;
+
+ data = offset_x & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_X, data);
+ if (result)
+ return result;
+
+ data = offset_y1 & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y1, data);
+ if (result)
+ return result;
+
+ data = offset_y2 & 0x3f;
+ result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y2, data);
+ return result;
+}
+
+static int set_measure_command(struct inv_compass_state *st)
+{
+ int result = 0;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_MEASURE_COMMAND, 0x01);
+ return result;
+}
+
+static int measure_normal(struct inv_compass_state *st,
+ int *busy, unsigned short *t,
+ unsigned short *x, unsigned short *y1,
+ unsigned short *y2)
+{
+ int result;
+ ktime_t sleeptime;
+ result = set_measure_command(st);
+ sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL);
+
+ result = st->read_data(st, busy, t, x, y1, y2);
+
+ return result;
+}
+
+static int measure_int(struct inv_compass_state *st,
+ int *busy, unsigned short *t,
+ unsigned short *x, unsigned short *y1,
+ unsigned short *y2)
+{
+ int result;
+ if (st->first_read_after_reset) {
+ st->first_read_after_reset = 0;
+ result = 1;
+ } else {
+ result = st->read_data(st, busy, t, x, y1, y2);
+ }
+ result |= set_measure_command(st);
+
+ return result;
+}
+
+static int yas530_read_data(struct inv_compass_state *st,
+ int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2)
+{
+ u8 data[8];
+ u16 b, to, xo, y1o, y2o;
+ int result;
+
+ result = inv_serial_read(st,
+ YAS530_REGADDR_MEASURE_DATA, 8, data);
+ if (result)
+ return result;
+
+ b = (data[0] >> 7) & 0x01;
+ to = (s16)(((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03));
+ xo = (s16)(((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f));
+ y1o = (s16)(((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f));
+ y2o = (s16)(((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f));
+
+ *busy = b;
+ *t = to;
+ *x = xo;
+ *y1 = y1o;
+ *y2 = y2o;
+
+ return 0;
+}
+
+static int yas532_533_read_data(struct inv_compass_state *st,
+ int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2)
+{
+ u8 data[8];
+ u16 b, to, xo, y1o, y2o;
+ int result;
+
+ result = inv_serial_read(st,
+ YAS530_REGADDR_MEASURE_DATA, 8, data);
+ if (result)
+ return result;
+
+ b = (data[0] >> 7) & 0x01;
+ to = (s16)((((s32)data[0] << 3) & 0x3f8) | ((data[1] >> 5) & 0x07));
+ xo = (s16)((((s32)data[2] << 6) & 0x1fc0) | ((data[3] >> 2) & 0x3f));
+ y1o = (s16)((((s32)data[4] << 6) & 0x1fc0) | ((data[5] >> 2) & 0x3f));
+ y2o = (s16)((((s32)data[6] << 6) & 0x1fc0) | ((data[7] >> 2) & 0x3f));
+
+ *busy = b;
+ *t = to;
+ *x = xo;
+ *y1 = y1o;
+ *y2 = y2o;
+
+ return 0;
+}
+
+static int check_offset(struct inv_compass_state *st,
+ char offset_x, char offset_y1, char offset_y2,
+ int *flag_x, int *flag_y1, int *flag_y2)
+{
+ int result;
+ int busy;
+ short t, x, y1, y2;
+
+ result = set_hardware_offset(st, offset_x, offset_y1, offset_y2);
+ if (result)
+ return result;
+ result = measure_normal(st, &busy, &t, &x, &y1, &y2);
+ if (result)
+ return result;
+ *flag_x = 0;
+ *flag_y1 = 0;
+ *flag_y2 = 0;
+
+ if (x > st->center)
+ *flag_x = 1;
+ if (y1 > st->center)
+ *flag_y1 = 1;
+ if (y2 > st->center)
+ *flag_y2 = 1;
+ if (x < st->center)
+ *flag_x = -1;
+ if (y1 < st->center)
+ *flag_y1 = -1;
+ if (y2 < st->center)
+ *flag_y2 = -1;
+
+ return result;
+}
+
+static int measure_and_set_offset(struct inv_compass_state *st,
+ char *offset)
+{
+ int i;
+ int result = 0;
+ char offset_x = 0, offset_y1 = 0, offset_y2 = 0;
+ int flag_x = 0, flag_y1 = 0, flag_y2 = 0;
+ static const int correct[5] = {16, 8, 4, 2, 1};
+
+ for (i = 0; i < 5; i++) {
+ result = check_offset(st,
+ offset_x, offset_y1, offset_y2,
+ &flag_x, &flag_y1, &flag_y2);
+ if (result)
+ return result;
+ if (flag_x)
+ offset_x += flag_x * correct[i];
+ if (flag_y1)
+ offset_y1 += flag_y1 * correct[i];
+ if (flag_y2)
+ offset_y2 += flag_y2 * correct[i];
+ }
+
+ result = set_hardware_offset(st, offset_x, offset_y1, offset_y2);
+ if (result)
+ return result;
+ offset[0] = offset_x;
+ offset[1] = offset_y1;
+ offset[2] = offset_y2;
+
+ return result;
+}
+
+static void coordinate_conversion(short x, short y1, short y2, short t,
+ int *xo, int *yo, int *zo)
+{
+ int sx, sy1, sy2, sy, sz;
+ int hx, hy, hz;
+
+ sx = x - (Cx * t) / 100;
+ sy1 = y1 - (Cy1 * t) / 100;
+ sy2 = y2 - (Cy2 * t) / 100;
+
+ sy = sy1 - sy2;
+ sz = -sy1 - sy2;
+
+ hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10);
+ hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10);
+ hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10);
+
+ *xo = hx;
+ *yo = hy;
+ *zo = hz;
+}
+
+static int get_cal_data_yas532_533(struct inv_compass_state *st)
+{
+ u8 data[YAS_YAS532_533_CAL_DATA_SIZE];
+ int result;
+
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS532_533_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /* CAL data Second Read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS532_533_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+
+ dx = data[0];
+ dy1 = data[1];
+ dy2 = data[2];
+ d2 = (data[3] >> 2) & 0x03f;
+ d3 = (u8)(((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03));
+ d4 = (u8)(data[4] & 0x3f);
+ d5 = (data[5] >> 2) & 0x3f;
+ d6 = (u8)(((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f));
+ d7 = (u8)(((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07));
+ d8 = (u8)(((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01));
+ d9 = (u8)(((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01));
+ d0 = (u8)((data[9] >> 2) & 0x1f);
+ dck = (u8)(((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01));
+ ver = (u8)((data[13]) & 0x01);
+
+ Cx = dx * 10 - 1280;
+ Cy1 = dy1 * 10 - 1280;
+ Cy2 = dy2 * 10 - 1280;
+ a2 = d2 - 32;
+ a3 = d3 - 8;
+ a4 = d4 - 32;
+ a5 = d5 + 38;
+ a6 = d6 - 32;
+ a7 = d7 - 64;
+ a8 = d8 - 32;
+ a9 = d9;
+ k = d0;
+
+ return 0;
+}
+
+static int get_cal_data_yas530(struct inv_compass_state *st)
+{
+ u8 data[YAS_YAS530_CAL_DATA_SIZE];
+ int result;
+ /* CAL data read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS530_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /* CAL data Second Read */
+ result = inv_serial_read(st, YAS530_REGADDR_CAL,
+ YAS_YAS530_CAL_DATA_SIZE, data);
+ if (result)
+ return result;
+ /*Cal data */
+ dx = data[0];
+ dy1 = data[1];
+ dy2 = data[2];
+ d2 = (data[3] >> 2) & 0x03f;
+ d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03);
+ d4 = data[4] & 0x3f;
+ d5 = (data[5] >> 2) & 0x3f;
+ d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f);
+ d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07);
+ d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01);
+ d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01);
+ d0 = (data[9] >> 2) & 0x1f;
+ dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01);
+ ver = (u8)((data[15]) & 0x03);
+
+ /*Correction Data */
+ Cx = (int)dx * 6 - 768;
+ Cy1 = (int)dy1 * 6 - 768;
+ Cy2 = (int)dy2 * 6 - 768;
+ a2 = (int)d2 - 32;
+ a3 = (int)d3 - 8;
+ a4 = (int)d4 - 32;
+ a5 = (int)d5 + 38;
+ a6 = (int)d6 - 32;
+ a7 = (int)d7 - 64;
+ a8 = (int)d8 - 32;
+ a9 = (int)d9;
+ k = (int)d0 + 10;
+
+ return 0;
+}
+
+
+static void thresh_filter_init(struct yas_thresh_filter *thresh_filter,
+ int threshold)
+{
+ thresh_filter->threshold = threshold;
+ thresh_filter->last = 0;
+}
+
+static void
+adaptive_filter_init(struct yas_adaptive_filter *adap_filter, int len,
+ int noise)
+{
+ int i;
+
+ adap_filter->num = 0;
+ adap_filter->index = 0;
+ adap_filter->filter_noise = noise;
+ adap_filter->filter_len = len;
+
+ for (i = 0; i < adap_filter->filter_len; ++i)
+ adap_filter->sequence[i] = 0;
+}
+
+static void yas_init_adap_filter(struct inv_compass_state *st)
+{
+ struct yas_filter *f;
+ int i;
+ int noise[] = {YAS_MAG_DEFAULT_FILTER_NOISE_X,
+ YAS_MAG_DEFAULT_FILTER_NOISE_Y,
+ YAS_MAG_DEFAULT_FILTER_NOISE_Z};
+
+ f = &st->filter;
+ f->filter_len = YAS_MAG_DEFAULT_FILTER_LEN;
+ for (i = 0; i < 3; i++)
+ f->filter_noise[i] = noise[i];
+
+ for (i = 0; i < 3; i++) {
+ adaptive_filter_init(&f->adap_filter[i], f->filter_len,
+ f->filter_noise[i]);
+ thresh_filter_init(&f->thresh_filter[i], f->filter_thresh);
+ }
+}
+
+int yas53x_resume(struct inv_compass_state *st)
+{
+ int result = 0;
+
+ unsigned char dummyData = 0x00;
+ unsigned char read_reg[1];
+
+ /* =============================================== */
+
+ /* Step 1 - Test register initialization */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_TEST1, dummyData);
+ if (result)
+ return result;
+ result =
+ inv_serial_single_write(st,
+ YAS530_REGADDR_TEST2, dummyData);
+ if (result)
+ return result;
+ /* Device ID read */
+ result = inv_serial_read(st,
+ YAS530_REGADDR_DEVICE_ID, 1, read_reg);
+
+ /*Step 2 Read the CAL register */
+ st->get_cal_data(st);
+
+ /*Obtain the [49:47] bits */
+ dck &= 0x07;
+
+ /*Step 3 : Storing the CONFIG with the CLK value */
+ dummyData = 0x00 | (dck << 2);
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_CONFIG, dummyData);
+ if (result)
+ return result;
+ /*Step 4 : Set Acquisition Interval Register */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_MEASURE_INTERVAL,
+ dummyData);
+ if (result)
+ return result;
+
+ /*Step 5 : Reset Coil */
+ dummyData = 0x00;
+ result = inv_serial_single_write(st,
+ YAS530_REGADDR_ACTUATE_INIT_COIL,
+ dummyData);
+ if (result)
+ return result;
+ /* Offset Measurement and Set */
+ result = measure_and_set_offset(st, st->offset);
+ if (result)
+ return result;
+ st->first_measure_after_reset = 1;
+ st->first_read_after_reset = 1;
+ st->reset_timer = 0;
+
+ yas_init_adap_filter(st);
+
+ return result;
+}
+
+static int inv_check_range(struct inv_compass_state *st, s16 x, s16 y1, s16 y2)
+{
+ int result = 0;
+
+ if (x == 0)
+ result |= 0x01;
+ if (x == st->overflow_bound)
+ result |= 0x02;
+ if (y1 == 0)
+ result |= 0x04;
+ if (y1 == st->overflow_bound)
+ result |= 0x08;
+ if (y2 == 0)
+ result |= 0x10;
+ if (y2 == st->overflow_bound)
+ result |= 0x20;
+
+ return result;
+}
+static int square(int data)
+{
+ return data * data;
+}
+
+static int
+adaptive_filter_filter(struct yas_adaptive_filter *adap_filter, int in)
+{
+ int avg, sum;
+ int i;
+
+ if (adap_filter->filter_len == 0)
+ return in;
+ if (adap_filter->num < adap_filter->filter_len) {
+ adap_filter->sequence[adap_filter->index++] = in / 100;
+ adap_filter->num++;
+ return in;
+ }
+ if (adap_filter->filter_len <= adap_filter->index)
+ adap_filter->index = 0;
+ adap_filter->sequence[adap_filter->index++] = in / 100;
+
+ avg = 0;
+ for (i = 0; i < adap_filter->filter_len; i++)
+ avg += adap_filter->sequence[i];
+ avg /= adap_filter->filter_len;
+
+ sum = 0;
+ for (i = 0; i < adap_filter->filter_len; i++)
+ sum += square(avg - adap_filter->sequence[i]);
+ sum /= adap_filter->filter_len;
+
+ if (sum <= adap_filter->filter_noise)
+ return avg * 100;
+
+ return ((in/100 - avg) * (sum - adap_filter->filter_noise) / sum + avg)
+ * 100;
+}
+
+static int
+thresh_filter_filter(struct yas_thresh_filter *thresh_filter, int in)
+{
+ if (in < thresh_filter->last - thresh_filter->threshold
+ || thresh_filter->last
+ + thresh_filter->threshold < in) {
+ thresh_filter->last = in;
+ return in;
+ } else {
+ return thresh_filter->last;
+ }
+}
+
+static void
+filter_filter(struct yas_filter *d, int *orig, int *filtered)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ filtered[i] = adaptive_filter_filter(&d->adap_filter[i],
+ orig[i]);
+ filtered[i] = thresh_filter_filter(&d->thresh_filter[i],
+ filtered[i]);
+ }
+}
+
+int yas53x_read(struct inv_compass_state *st, short rawfixed[3],
+ int *overunderflow)
+{
+ int result = 0;
+
+ int busy, i, ov;
+ short t, x, y1, y2;
+ s32 xyz[3], disturb[3];
+
+ result = measure_int(st, &busy, &t, &x, &y1, &y2);
+ if (result)
+ return result;
+ if (busy)
+ return -1;
+ coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]);
+ filter_filter(&st->filter, xyz, xyz);
+ for (i = 0; i < 3; i++)
+ rawfixed[i] = (short)(xyz[i] / 100);
+
+ if (st->first_measure_after_reset) {
+ for (i = 0; i < 3; i++)
+ st->base_compass_data[i] = rawfixed[i];
+ st->first_measure_after_reset = 0;
+ }
+ ov = 0;
+ for (i = 0; i < 3; i++) {
+ disturb[i] = abs(st->base_compass_data[i] - rawfixed[i]);
+ if (disturb[i] > YAS_MAG_DISTURBURNCE_THRESHOLD)
+ ov = 1;
+ }
+ if (ov)
+ st->reset_timer += st->delay;
+ else
+ st->reset_timer = 0;
+
+ if (st->reset_timer > YAS_RESET_COIL_TIME_THRESHOLD)
+ *overunderflow = (1<<8);
+ else
+ *overunderflow = 0;
+ *overunderflow |= inv_check_range(st, x, y1, y2);
+
+ return 0;
+}
+
+/**
+ * yas53x_read_raw() - read raw method.
+ */
+static int yas53x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask) {
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case 0:
+ if (!(iio_buffer_enabled(indio_dev)))
+ return -EINVAL;
+ if (chan->type == IIO_MAGN) {
+ *val = st->compass_data[chan->channel2 - IIO_MOD_X];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = YAS530_SCALE;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * inv_compass_matrix_show() - show orientation matrix
+ */
+static ssize_t inv_compass_matrix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ signed char *m;
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ m = st->plat_data.orientation;
+ return sprintf(buf,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+}
+
+static ssize_t yas53x_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ error = kstrtoint(buf, 10, &data);
+ if (error)
+ return error;
+ if (0 == data)
+ return -EINVAL;
+ /* transform rate to delay in ms */
+ data = MSEC_PER_SEC / data;
+
+ if (data > YAS530_MAX_DELAY)
+ data = YAS530_MAX_DELAY;
+ if (data < YAS530_MIN_DELAY)
+ data = YAS530_MIN_DELAY;
+ st->delay = data;
+
+ return count;
+}
+
+static ssize_t yas53x_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ /* transform delay in ms to rate */
+ return sprintf(buf, "%d\n", (int)MSEC_PER_SEC / st->delay);
+}
+
+static ssize_t yas53x_overunderflow_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 data;
+ int error;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ error = kstrtoint(buf, 10, &data);
+ if (error)
+ return error;
+ if (data)
+ return -EINVAL;
+ st->overunderflow = data;
+
+ return count;
+}
+
+static ssize_t yas53x_overunderflow_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->overunderflow);
+}
+
+void set_yas53x_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ yas_init_adap_filter(st);
+ st->first_measure_after_reset = 1;
+ st->first_read_after_reset = 1;
+ schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay));
+}
+
+static void yas53x_work_func(struct work_struct *work)
+{
+ struct inv_compass_state *st =
+ container_of((struct delayed_work *)work,
+ struct inv_compass_state, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u32 delay = msecs_to_jiffies(st->delay);
+
+ mutex_lock(&indio_dev->mlock);
+ if (!(iio_buffer_enabled(indio_dev)))
+ goto error_ret;
+
+ schedule_delayed_work(&st->work, delay);
+ inv_read_yas53x_fifo(indio_dev);
+ INV_I2C_INC_COMPASSIRQ();
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static const struct iio_chan_spec compass_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_X,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_Y,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ .scan_index = INV_YAS53X_SCAN_MAGN_Z,
+ .scan_type = IIO_ST('s', 16, 16, 0)
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(INV_YAS53X_SCAN_TIMESTAMP)
+};
+
+static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL);
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, yas53x_rate_show,
+ yas53x_rate_store);
+static DEVICE_ATTR(overunderflow, S_IRUGO | S_IWUSR,
+ yas53x_overunderflow_show, yas53x_overunderflow_store);
+
+static struct attribute *inv_yas53x_attributes[] = {
+ &dev_attr_compass_matrix.attr,
+ &dev_attr_sampling_frequency.attr,
+ &dev_attr_overunderflow.attr,
+ NULL,
+};
+static const struct attribute_group inv_attribute_group = {
+ .name = "yas53x",
+ .attrs = inv_yas53x_attributes
+};
+
+static const struct iio_info yas53x_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &yas53x_read_raw,
+ .attrs = &inv_attribute_group,
+};
+
+/*constant IIO attribute */
+/**
+ * inv_yas53x_probe() - probe function.
+ */
+static int inv_yas53x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_compass_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+ indio_dev = iio_allocate_device(sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+ st->delay = 10;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ if (!strcmp(id->name, "yas530")) {
+ st->read_data = yas530_read_data;
+ st->get_cal_data = get_cal_data_yas530;
+ st->overflow_bound = YAS_YAS530_DATA_OVERFLOW;
+ st->center = YAS_YAS530_DATA_CENTER;
+ st->filter.filter_thresh = YAS530_MAG_DEFAULT_FILTER_THRESH;
+ } else {
+ st->read_data = yas532_533_read_data;
+ st->get_cal_data = get_cal_data_yas532_533;
+ st->overflow_bound = YAS_YAS532_533_DATA_OVERFLOW;
+ st->center = YAS_YAS532_533_DATA_CENTER;
+ st->filter.filter_thresh = YAS532_MAG_DEFAULT_FILTER_THRESH;
+ }
+ st->upper_bound = st->center + (st->center >> 1);
+ st->lower_bound = (st->center >> 1);
+
+ result = yas53x_resume(st);
+ if (result)
+ goto out_free;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = compass_channels;
+ indio_dev->num_channels = ARRAY_SIZE(compass_channels);
+ indio_dev->info = &yas53x_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_yas53x_configure_ring(indio_dev);
+ if (result)
+ goto out_free;
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result)
+ goto out_unreg_ring;
+ result = inv_yas53x_probe_trigger(indio_dev);
+ if (result)
+ goto out_remove_ring;
+
+ result = iio_device_register(indio_dev);
+ if (result)
+ goto out_remove_trigger;
+ INIT_DELAYED_WORK(&st->work, yas53x_work_func);
+ pr_info("%s: Probe name %s\n", __func__, id->name);
+
+ return 0;
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_yas53x_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_yas53x_unconfigure_ring(indio_dev);
+out_free:
+ iio_free_device(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+/**
+ * inv_yas53x_remove() - remove function.
+ */
+static int inv_yas53x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ cancel_delayed_work_sync(&st->work);
+ iio_device_unregister(indio_dev);
+ inv_yas53x_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_yas53x_unconfigure_ring(indio_dev);
+ iio_free_device(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv_yas53x_iio module removed.\n");
+ return 0;
+}
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_yas53x_id[] = {
+ {"yas530", 0},
+ {"yas532", 0},
+ {"yas533", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_yas53x_id);
+
+static struct i2c_driver inv_yas53x_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_yas53x_probe,
+ .remove = inv_yas53x_remove,
+ .id_table = inv_yas53x_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv_yas53x_iio",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_yas53x_init(void)
+{
+ int result = i2c_add_driver(&inv_yas53x_driver);
+ if (result) {
+ pr_err("%s failed\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_yas53x_exit(void)
+{
+ i2c_del_driver(&inv_yas53x_driver);
+}
+
+module_init(inv_yas53x_init);
+module_exit(inv_yas53x_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv_yas53x_iio");
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h
new file mode 100755
index 00000000000..92bf0af7ec7
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h
@@ -0,0 +1,172 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_iio.h
+ * @brief Struct definitions for the Invensense implementation
+ * of yas53x driver.
+ */
+
+#ifndef _INV_GYRO_H_
+#define _INV_GYRO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+#include <linux/mpu.h>
+
+#include "iio.h"
+#include "buffer.h"
+#include "trigger.h"
+
+#define YAS_MAG_MAX_FILTER_LEN 30
+struct yas_adaptive_filter {
+ int num;
+ int index;
+ int filter_len;
+ int filter_noise;
+ int sequence[YAS_MAG_MAX_FILTER_LEN];
+};
+
+struct yas_thresh_filter {
+ int threshold;
+ int last;
+};
+
+struct yas_filter {
+ int filter_len;
+ int filter_thresh;
+ int filter_noise[3];
+ struct yas_adaptive_filter adap_filter[3];
+ struct yas_thresh_filter thresh_filter[3];
+};
+
+/**
+ * struct inv_compass_state - Driver state variables.
+ * @plat_data: mpu platform data from board file.
+ * @client: i2c client handle.
+ * @chan_info: channel information.
+ * @trig: IIO trigger.
+ * @work: work structure.
+ * @delay: delay to schedule the next work.
+ * @overflow_bound: bound to determine overflow.
+ * @center: center of the measurement.
+ * @compass_data[3]: compass data store.
+ * @offset[3]: yas530 specific data.
+ * @base_compass_data[3]: first measure data after reset.
+ * @first_measure_after_reset:1: flag for first measurement after reset.
+ * @first_read_after_reset:1: flag for first read after reset.
+ * @reset_timer: timer to accumulate overflow conditions.
+ * @overunderflow:1: overflow and underflow flag.
+ * @filter: filter data structure.
+ * @read_data: function pointer of reading data from device.
+ * @get_cal_data: function pointer of reading cal data.
+ */
+struct inv_compass_state {
+ struct mpu_platform_data plat_data;
+ struct i2c_client *client;
+ struct iio_trigger *trig;
+ struct delayed_work work;
+ s16 delay;
+ s16 overflow_bound;
+ s16 upper_bound;
+ s16 lower_bound;
+ s16 center;
+ s16 compass_data[3];
+ s8 offset[3];
+ s16 base_compass_data[3];
+ u8 first_measure_after_reset:1;
+ u8 first_read_after_reset:1;
+ u8 overunderflow:1;
+ s32 reset_timer;
+ struct yas_filter filter;
+ int (*read_data)(struct inv_compass_state *st,
+ int *, u16 *, u16 *, u16 *, u16 *);
+ int (*get_cal_data)(struct inv_compass_state *);
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_YAS53X_SCAN_MAGN_X,
+ INV_YAS53X_SCAN_MAGN_Y,
+ INV_YAS53X_SCAN_MAGN_Z,
+ INV_YAS53X_SCAN_TIMESTAMP,
+};
+
+#define YAS530_REGADDR_DEVICE_ID 0x80
+#define YAS530_REGADDR_ACTUATE_INIT_COIL 0x81
+#define YAS530_REGADDR_MEASURE_COMMAND 0x82
+#define YAS530_REGADDR_CONFIG 0x83
+#define YAS530_REGADDR_MEASURE_INTERVAL 0x84
+#define YAS530_REGADDR_OFFSET_X 0x85
+#define YAS530_REGADDR_OFFSET_Y1 0x86
+#define YAS530_REGADDR_OFFSET_Y2 0x87
+#define YAS530_REGADDR_TEST1 0x88
+#define YAS530_REGADDR_TEST2 0x89
+#define YAS530_REGADDR_CAL 0x90
+#define YAS530_REGADDR_MEASURE_DATA 0xb0
+
+#define YAS530_MAX_DELAY 200
+#define YAS530_MIN_DELAY 5
+#define YAS530_SCALE 107374182L
+
+#define YAS_YAS530_VERSION_A 0 /* YAS530 (MS-3E Aver) */
+#define YAS_YAS530_VERSION_B 1 /* YAS530B (MS-3E Bver) */
+#define YAS_YAS530_VERSION_A_COEF 380
+#define YAS_YAS530_VERSION_B_COEF 550
+#define YAS_YAS530_DATA_CENTER 2048
+#define YAS_YAS530_DATA_OVERFLOW 4095
+#define YAS_YAS530_CAL_DATA_SIZE 16
+
+/*filter related defines */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_X 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_Y 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_NOISE_Z 144 /* sd: 1200 nT */
+#define YAS_MAG_DEFAULT_FILTER_LEN 20
+
+#define YAS530_MAG_DEFAULT_FILTER_THRESH 100
+#define YAS532_MAG_DEFAULT_FILTER_THRESH 300
+
+#define YAS_YAS532_533_VERSION_AB 0 /* YAS532_533AB (MS-3R/3F ABver) */
+#define YAS_YAS532_533_VERSION_AC 1 /* YAS532_533AC (MS-3R/3F ACver) */
+#define YAS_YAS532_533_VERSION_AB_COEF 1800
+#define YAS_YAS532_533_VERSION_AC_COEF 900
+#define YAS_YAS532_533_DATA_CENTER 4096
+#define YAS_YAS532_533_DATA_OVERFLOW 8190
+#define YAS_YAS532_533_CAL_DATA_SIZE 14
+
+#define YAS_MAG_DISTURBURNCE_THRESHOLD 1600
+#define YAS_RESET_COIL_TIME_THRESHOLD 3000
+
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (-1)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (-2)
+
+int inv_yas53x_configure_ring(struct iio_dev *indio_dev);
+void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev);
+int inv_yas53x_probe_trigger(struct iio_dev *indio_dev);
+void inv_yas53x_remove_trigger(struct iio_dev *indio_dev);
+void set_yas53x_enable(struct iio_dev *indio_dev, bool enable);
+void inv_read_yas53x_fifo(struct iio_dev *indio_dev);
+int yas53x_read(struct inv_compass_state *st, short rawfixed[3],
+ s32 *overunderflow);
+int yas53x_resume(struct inv_compass_state *st);
+
+#endif /* #ifndef _INV_GYRO_H_ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c
new file mode 100755
index 00000000000..efcf49c6839
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c
@@ -0,0 +1,165 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_ring.c
+ * @brief Invensense implementation for yas530/yas532/yas533.
+ * @details This driver currently works for the yas530/yas532/yas533.
+ */
+
+#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 "iio.h"
+#include "kfifo_buf.h"
+#include "trigger_consumer.h"
+#include "sysfs.h"
+
+#include "inv_yas53x_iio.h"
+
+static s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+
+ return timespec_to_ns(&ts);
+}
+
+static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d,
+ short *s, int scan_index)
+{
+ struct iio_buffer *ring = indio_dev->buffer;
+ int st;
+ int i, d_ind;
+
+ d_ind = 0;
+ 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;
+}
+
+/**
+ * inv_read_yas53x_fifo() - Transfer data from FIFO to ring buffer.
+ */
+void inv_read_yas53x_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+ int d_ind;
+ s32 overunderflow;
+ s8 *tmp;
+ s64 tmp_buf[2];
+
+ if (!yas53x_read(st, st->compass_data, &overunderflow)) {
+ tmp = (u8 *)tmp_buf;
+ d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data,
+ INV_YAS53X_SCAN_MAGN_X);
+ if (ring->scan_timestamp)
+ tmp_buf[(d_ind + 7) / 8] = get_time_ns();
+ ring->access->store_to(indio_dev->buffer, tmp, 0);
+
+ if (overunderflow) {
+ yas53x_resume(st);
+ if (!st->overunderflow)
+ st->overunderflow = 1;
+ }
+ }
+}
+
+void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_yas53x_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ /* when all the outputs are disabled, even though buffer/enable is on,
+ do nothing */
+ if (!(iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_X) ||
+ iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Y) ||
+ iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Z)))
+ return 0;
+
+ set_yas53x_enable(indio_dev, true);
+ schedule_delayed_work(&st->work,
+ msecs_to_jiffies(st->delay));
+
+ return 0;
+}
+
+static int inv_yas53x_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring = indio_dev->buffer;
+
+ cancel_delayed_work_sync(&st->work);
+ clear_bit(INV_YAS53X_SCAN_MAGN_X, ring->scan_mask);
+ clear_bit(INV_YAS53X_SCAN_MAGN_Y, ring->scan_mask);
+ clear_bit(INV_YAS53X_SCAN_MAGN_Z, ring->scan_mask);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_yas53x_ring_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &inv_yas53x_postenable,
+ .predisable = &inv_yas53x_predisable,
+};
+
+int inv_yas53x_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct iio_buffer *ring;
+
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_yas53x_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c
new file mode 100755
index 00000000000..a20ce2baa7e
--- /dev/null
+++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+/**
+ * @addtogroup DRIVERS
+ * @brief Hardware drivers.
+ *
+ * @{
+ * @file inv_yas53x_trigger.c
+ * @brief Invensense implementation for yas530/yas532/yas533
+ * @details This driver currently works for the yas530/yas532/yas533
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "iio.h"
+#include "sysfs.h"
+#include "trigger.h"
+
+#include "inv_yas53x_iio.h"
+
+static const struct iio_trigger_ops inv_yas53x_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_yas53x_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_allocate_trigger("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ /* select default trigger */
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->private_data = indio_dev;
+ st->trig->ops = &inv_yas53x_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = st->trig;
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_free_trigger(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_yas53x_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_compass_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_free_trigger(st->trig);
+}
+/**
+ * @}
+ */
+
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 59aa24002c7..c4249f8b5ba 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2,6 +2,9 @@
* Atmel maXTouch Touchscreen driver
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2014 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ *
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -13,40 +16,31 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/i2c/atmel_mxt_ts.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
-/* Version */
-#define MXT_VER_20 20
-#define MXT_VER_21 21
-#define MXT_VER_22 22
-
-/* Slave addresses */
-#define MXT_APP_LOW 0x4a
-#define MXT_APP_HIGH 0x4b
-#define MXT_BOOT_LOW 0x24
-#define MXT_BOOT_HIGH 0x25
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
-/* Firmware */
-#define MXT_FW_NAME "maxtouch.fw"
+/* Configuration file */
+#define MXT_CFG_MAGIC "OBP_RAW V1"
/* Registers */
-#define MXT_INFO 0x00
-#define MXT_FAMILY_ID 0x00
-#define MXT_VARIANT_ID 0x01
-#define MXT_VERSION 0x02
-#define MXT_BUILD 0x03
-#define MXT_MATRIX_X_SIZE 0x04
-#define MXT_MATRIX_Y_SIZE 0x05
-#define MXT_OBJECT_NUM 0x06
#define MXT_OBJECT_START 0x07
-
#define MXT_OBJECT_SIZE 6
+#define MXT_INFO_CHECKSUM_SIZE 3
+#define MXT_MAX_BLOCK_WRITE 255
/* Object types */
#define MXT_DEBUG_DIAGNOSTIC_T37 37
@@ -76,6 +70,11 @@
#define MXT_SPT_DIGITIZER_T43 43
#define MXT_SPT_MESSAGECOUNT_T44 44
#define MXT_SPT_CTECONFIG_T46 46
+#define MXT_PROCI_ACTIVE_STYLUS_T63 63
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
+
+/* MXT_GEN_MESSAGE_T5 object */
+#define MXT_RPTID_NOMSG 0xff
/* MXT_GEN_COMMAND_T6 field */
#define MXT_COMMAND_RESET 0
@@ -84,108 +83,102 @@
#define MXT_COMMAND_REPORTALL 3
#define MXT_COMMAND_DIAGNOSTIC 5
+/* Define for T6 status byte */
+#define MXT_T6_STATUS_RESET (1 << 7)
+#define MXT_T6_STATUS_OFL (1 << 6)
+#define MXT_T6_STATUS_SIGERR (1 << 5)
+#define MXT_T6_STATUS_CAL (1 << 4)
+#define MXT_T6_STATUS_CFGERR (1 << 3)
+#define MXT_T6_STATUS_COMSERR (1 << 2)
+
/* MXT_GEN_POWER_T7 field */
-#define MXT_POWER_IDLEACQINT 0
-#define MXT_POWER_ACTVACQINT 1
-#define MXT_POWER_ACTV2IDLETO 2
+struct t7_config {
+ u8 idle;
+ u8 active;
+} __packed;
-/* MXT_GEN_ACQUIRE_T8 field */
-#define MXT_ACQUIRE_CHRGTIME 0
-#define MXT_ACQUIRE_TCHDRIFT 2
-#define MXT_ACQUIRE_DRIFTST 3
-#define MXT_ACQUIRE_TCHAUTOCAL 4
-#define MXT_ACQUIRE_SYNC 5
-#define MXT_ACQUIRE_ATCHCALST 6
-#define MXT_ACQUIRE_ATCHCALSTHR 7
+#define MXT_POWER_CFG_RUN 0
+#define MXT_POWER_CFG_DEEPSLEEP 1
/* MXT_TOUCH_MULTI_T9 field */
-#define MXT_TOUCH_CTRL 0
-#define MXT_TOUCH_XORIGIN 1
-#define MXT_TOUCH_YORIGIN 2
-#define MXT_TOUCH_XSIZE 3
-#define MXT_TOUCH_YSIZE 4
-#define MXT_TOUCH_BLEN 6
-#define MXT_TOUCH_TCHTHR 7
-#define MXT_TOUCH_TCHDI 8
-#define MXT_TOUCH_ORIENT 9
-#define MXT_TOUCH_MOVHYSTI 11
-#define MXT_TOUCH_MOVHYSTN 12
-#define MXT_TOUCH_NUMTOUCH 14
-#define MXT_TOUCH_MRGHYST 15
-#define MXT_TOUCH_MRGTHR 16
-#define MXT_TOUCH_AMPHYST 17
-#define MXT_TOUCH_XRANGE_LSB 18
-#define MXT_TOUCH_XRANGE_MSB 19
-#define MXT_TOUCH_YRANGE_LSB 20
-#define MXT_TOUCH_YRANGE_MSB 21
-#define MXT_TOUCH_XLOCLIP 22
-#define MXT_TOUCH_XHICLIP 23
-#define MXT_TOUCH_YLOCLIP 24
-#define MXT_TOUCH_YHICLIP 25
-#define MXT_TOUCH_XEDGECTRL 26
-#define MXT_TOUCH_XEDGEDIST 27
-#define MXT_TOUCH_YEDGECTRL 28
-#define MXT_TOUCH_YEDGEDIST 29
-#define MXT_TOUCH_JUMPLIMIT 30
+#define MXT_T9_ORIENT 9
+#define MXT_T9_RANGE 18
-/* MXT_PROCI_GRIPFACE_T20 field */
-#define MXT_GRIPFACE_CTRL 0
-#define MXT_GRIPFACE_XLOGRIP 1
-#define MXT_GRIPFACE_XHIGRIP 2
-#define MXT_GRIPFACE_YLOGRIP 3
-#define MXT_GRIPFACE_YHIGRIP 4
-#define MXT_GRIPFACE_MAXTCHS 5
-#define MXT_GRIPFACE_SZTHR1 7
-#define MXT_GRIPFACE_SZTHR2 8
-#define MXT_GRIPFACE_SHPTHR1 9
-#define MXT_GRIPFACE_SHPTHR2 10
-#define MXT_GRIPFACE_SUPEXTTO 11
+/* MXT_TOUCH_MULTI_T9 status */
+#define MXT_T9_UNGRIP (1 << 0)
+#define MXT_T9_SUPPRESS (1 << 1)
+#define MXT_T9_AMP (1 << 2)
+#define MXT_T9_VECTOR (1 << 3)
+#define MXT_T9_MOVE (1 << 4)
+#define MXT_T9_RELEASE (1 << 5)
+#define MXT_T9_PRESS (1 << 6)
+#define MXT_T9_DETECT (1 << 7)
-/* MXT_PROCI_NOISE field */
-#define MXT_NOISE_CTRL 0
-#define MXT_NOISE_OUTFLEN 1
-#define MXT_NOISE_GCAFUL_LSB 3
-#define MXT_NOISE_GCAFUL_MSB 4
-#define MXT_NOISE_GCAFLL_LSB 5
-#define MXT_NOISE_GCAFLL_MSB 6
-#define MXT_NOISE_ACTVGCAFVALID 7
-#define MXT_NOISE_NOISETHR 8
-#define MXT_NOISE_FREQHOPSCALE 10
-#define MXT_NOISE_FREQ0 11
-#define MXT_NOISE_FREQ1 12
-#define MXT_NOISE_FREQ2 13
-#define MXT_NOISE_FREQ3 14
-#define MXT_NOISE_FREQ4 15
-#define MXT_NOISE_IDLEGCAFVALID 16
+struct t9_range {
+ u16 x;
+ u16 y;
+} __packed;
+
+/* MXT_TOUCH_MULTI_T9 orient */
+#define MXT_T9_ORIENT_SWITCH (1 << 0)
/* MXT_SPT_COMMSCONFIG_T18 */
#define MXT_COMMS_CTRL 0
#define MXT_COMMS_CMD 1
-
-/* MXT_SPT_CTECONFIG_T28 field */
-#define MXT_CTE_CTRL 0
-#define MXT_CTE_CMD 1
-#define MXT_CTE_MODE 2
-#define MXT_CTE_IDLEGCAFDEPTH 3
-#define MXT_CTE_ACTVGCAFDEPTH 4
-#define MXT_CTE_VOLTAGE 5
-
-#define MXT_VOLTAGE_DEFAULT 2700000
-#define MXT_VOLTAGE_STEP 10000
+#define MXT_COMMS_RETRIGEN (1 << 6)
/* Define for MXT_GEN_COMMAND_T6 */
#define MXT_BOOT_VALUE 0xa5
+#define MXT_RESET_VALUE 0x01
#define MXT_BACKUP_VALUE 0x55
-#define MXT_BACKUP_TIME 50 /* msec */
-#define MXT_RESET_TIME 200 /* msec */
-#define MXT_FWRESET_TIME 175 /* msec */
+/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */
+#define MXT_T42_MSG_TCHSUP (1 << 0)
+
+/* T47 Stylus */
+#define MXT_TOUCH_MAJOR_T47_STYLUS 1
+
+/* T63 Stylus */
+#define MXT_T63_STYLUS_PRESS (1 << 0)
+#define MXT_T63_STYLUS_RELEASE (1 << 1)
+#define MXT_T63_STYLUS_MOVE (1 << 2)
+#define MXT_T63_STYLUS_SUPPRESS (1 << 3)
+
+#define MXT_T63_STYLUS_DETECT (1 << 4)
+#define MXT_T63_STYLUS_TIP (1 << 5)
+#define MXT_T63_STYLUS_ERASER (1 << 6)
+#define MXT_T63_STYLUS_BARREL (1 << 7)
+
+#define MXT_T63_STYLUS_PRESSURE_MASK 0x3F
+
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL 0
+#define MXT_T100_CFG1 1
+#define MXT_T100_TCHAUX 3
+#define MXT_T100_XRANGE 13
+#define MXT_T100_YRANGE 24
+
+#define MXT_T100_CFG_SWITCHXY (1 << 5)
-/* MXT_SPT_GPIOPWM_T19 field */
-#define MXT_GPIO0_MASK 0x04
-#define MXT_GPIO1_MASK 0x08
-#define MXT_GPIO2_MASK 0x10
-#define MXT_GPIO3_MASK 0x20
+#define MXT_T100_TCHAUX_VECT (1 << 0)
+#define MXT_T100_TCHAUX_AMPL (1 << 1)
+#define MXT_T100_TCHAUX_AREA (1 << 2)
+
+#define MXT_T100_DETECT (1 << 7)
+#define MXT_T100_TYPE_MASK 0x70
+#define MXT_T100_TYPE_STYLUS 0x20
+
+/* Delay times */
+#define MXT_BACKUP_TIME 50 /* msec */
+#define MXT_RESET_TIME 200 /* msec */
+#define MXT_RESET_TIMEOUT 3000 /* msec */
+#define MXT_CRC_TIMEOUT 1000 /* msec */
+#define MXT_FW_RESET_TIME 3000 /* msec */
+#define MXT_FW_CHG_TIMEOUT 300 /* msec */
+#define MXT_WAKEUP_TIME 25 /* msec */
+#define MXT_REGULATOR_DELAY 150 /* msec */
+#define MXT_CHG_DELAY 100 /* msec */
+#define MXT_POWERON_DELAY 150 /* msec */
/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
@@ -199,27 +192,16 @@
#define MXT_FRAME_CRC_PASS 0x04
#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
#define MXT_BOOT_STATUS_MASK 0x3f
-
-/* Touch status */
-#define MXT_UNGRIP (1 << 0)
-#define MXT_SUPPRESS (1 << 1)
-#define MXT_AMP (1 << 2)
-#define MXT_VECTOR (1 << 3)
-#define MXT_MOVE (1 << 4)
-#define MXT_RELEASE (1 << 5)
-#define MXT_PRESS (1 << 6)
-#define MXT_DETECT (1 << 7)
-
-/* Touch orient bits */
-#define MXT_XY_SWITCH (1 << 0)
-#define MXT_X_INVERT (1 << 1)
-#define MXT_Y_INVERT (1 << 2)
+#define MXT_BOOT_EXTENDED_ID (1 << 5)
+#define MXT_BOOT_ID_MASK 0x1f
/* Touchscreen absolute values */
#define MXT_MAX_AREA 0xff
#define MXT_PIXELS_PER_MM 20
+#define DEBUG_MSG_MAX 200
+
struct mxt_info {
u8 family_id;
u8 variant_id;
@@ -233,16 +215,11 @@ struct mxt_info {
struct mxt_object {
u8 type;
u16 start_address;
- u8 size; /* Size of each instance - 1 */
- u8 instances; /* Number of instances - 1 */
+ u8 size_minus_one;
+ u8 instances_minus_one;
u8 num_report_ids;
} __packed;
-struct mxt_message {
- u8 reportid;
- u8 message[7];
-};
-
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
@@ -250,20 +227,89 @@ struct mxt_data {
char phys[64]; /* device physical location */
const struct mxt_platform_data *pdata;
struct mxt_object *object_table;
- struct mxt_info info;
- bool is_tp;
-
+ struct mxt_info *info;
+ void *raw_info_block;
unsigned int irq;
unsigned int max_x;
unsigned int max_y;
+ bool in_bootloader;
+ u16 mem_size;
+ u8 t100_aux_ampl;
+ u8 t100_aux_area;
+ u8 t100_aux_vect;
+ struct bin_attribute mem_access_attr;
+ bool debug_enabled;
+ bool debug_v2_enabled;
+ u8 *debug_msg_data;
+ u16 debug_msg_count;
+ struct bin_attribute debug_msg_attr;
+ struct mutex debug_msg_lock;
+ u8 max_reportid;
+ u32 config_crc;
+ u32 info_crc;
+ u8 bootloader_addr;
+ u8 *msg_buf;
+ u8 t6_status;
+ bool update_input;
+ u8 last_message_count;
+ u8 num_touchids;
+ struct t7_config t7_cfg;
+ u8 num_stylusids;
+ unsigned long t15_keystatus;
+ bool use_retrigen_workaround;
+ bool use_regulator;
+ struct regulator *reg_vdd;
+ struct regulator *reg_avdd;
+ char *fw_name;
+ char *cfg_name;
/* Cached parameters from object table */
+ u16 T5_address;
+ u8 T5_msg_size;
u8 T6_reportid;
+ u16 T6_address;
+ u16 T7_address;
u8 T9_reportid_min;
u8 T9_reportid_max;
+ u8 T15_reportid_min;
+ u8 T15_reportid_max;
+ u16 T18_address;
u8 T19_reportid;
+ u8 T42_reportid_min;
+ u8 T42_reportid_max;
+ u16 T44_address;
+ u8 T48_reportid;
+ u8 T63_reportid_min;
+ u8 T63_reportid_max;
+ u8 T100_reportid_min;
+ u8 T100_reportid_max;
+
+ /* for fw update in bootloader */
+ struct completion bl_completion;
+
+ /* for reset handling */
+ struct completion reset_completion;
+
+ /* for config update handling */
+ struct completion crc_completion;
+
+ /* Indicates whether device is in suspend */
+ bool suspended;
+
+ /* Indicates whether device is updating configuration */
+ bool updating_config;
};
+static size_t mxt_obj_size(const struct mxt_object *obj)
+{
+ return obj->size_minus_one + 1;
+}
+
+static size_t mxt_obj_instances(const struct mxt_object *obj)
+{
+ return obj->instances_minus_one + 1;
+}
+
static bool mxt_object_readable(unsigned int type)
{
switch (type) {
@@ -297,98 +343,365 @@ static bool mxt_object_readable(unsigned int type)
}
}
-static bool mxt_object_writable(unsigned int type)
+static void mxt_dump_message(struct mxt_data *data, u8 *message)
{
- switch (type) {
- case MXT_GEN_COMMAND_T6:
- case MXT_GEN_POWER_T7:
- case MXT_GEN_ACQUIRE_T8:
- case MXT_TOUCH_MULTI_T9:
- case MXT_TOUCH_KEYARRAY_T15:
- case MXT_TOUCH_PROXIMITY_T23:
- case MXT_TOUCH_PROXKEY_T52:
- case MXT_PROCI_GRIPFACE_T20:
- case MXT_PROCG_NOISE_T22:
- case MXT_PROCI_ONETOUCH_T24:
- case MXT_PROCI_TWOTOUCH_T27:
- case MXT_PROCI_GRIP_T40:
- case MXT_PROCI_PALM_T41:
- case MXT_PROCI_TOUCHSUPPRESSION_T42:
- case MXT_PROCI_STYLUS_T47:
- case MXT_PROCG_NOISESUPPRESSION_T48:
- case MXT_SPT_COMMSCONFIG_T18:
- case MXT_SPT_GPIOPWM_T19:
- case MXT_SPT_SELFTEST_T25:
- case MXT_SPT_CTECONFIG_T28:
- case MXT_SPT_DIGITIZER_T43:
- case MXT_SPT_CTECONFIG_T46:
- return true;
+ dev_dbg(&data->client->dev, "MXT MSG: %*ph\n",
+ data->T5_msg_size, message);
+}
+
+static void mxt_debug_msg_enable(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+
+ if (data->debug_v2_enabled)
+ return;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ data->debug_msg_data = kcalloc(DEBUG_MSG_MAX,
+ data->T5_msg_size, GFP_KERNEL);
+ if (!data->debug_msg_data)
+ return;
+
+ data->debug_v2_enabled = true;
+ mutex_unlock(&data->debug_msg_lock);
+
+ dev_info(dev, "Enabled message output\n");
+}
+
+static void mxt_debug_msg_disable(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+
+ if (!data->debug_v2_enabled)
+ return;
+
+ dev_info(dev, "disabling message output\n");
+ data->debug_v2_enabled = false;
+
+ mutex_lock(&data->debug_msg_lock);
+ kfree(data->debug_msg_data);
+ data->debug_msg_data = NULL;
+ data->debug_msg_count = 0;
+ mutex_unlock(&data->debug_msg_lock);
+ dev_info(dev, "Disabled message output\n");
+}
+
+static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ if (!data->debug_msg_data) {
+ dev_err(dev, "No buffer!\n");
+ return;
+ }
+
+ if (data->debug_msg_count < DEBUG_MSG_MAX) {
+ memcpy(data->debug_msg_data +
+ data->debug_msg_count * data->T5_msg_size,
+ msg,
+ data->T5_msg_size);
+ data->debug_msg_count++;
+ } else {
+ dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count);
+ data->debug_msg_count = 0;
+ }
+
+ mutex_unlock(&data->debug_msg_lock);
+
+ sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify");
+}
+
+static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ return -EIO;
+}
+
+static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int count;
+ size_t bytes_read;
+
+ if (!data->debug_msg_data) {
+ dev_err(dev, "No buffer!\n");
+ return 0;
+ }
+
+ count = bytes / data->T5_msg_size;
+
+ if (count > DEBUG_MSG_MAX)
+ count = DEBUG_MSG_MAX;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ if (count > data->debug_msg_count)
+ count = data->debug_msg_count;
+
+ bytes_read = count * data->T5_msg_size;
+
+ memcpy(buf, data->debug_msg_data, bytes_read);
+ data->debug_msg_count = 0;
+
+ mutex_unlock(&data->debug_msg_lock);
+
+ return bytes_read;
+}
+
+static int mxt_debug_msg_init(struct mxt_data *data)
+{
+ sysfs_bin_attr_init(&data->debug_msg_attr);
+ data->debug_msg_attr.attr.name = "debug_msg";
+ data->debug_msg_attr.attr.mode = 0666;
+ data->debug_msg_attr.read = mxt_debug_msg_read;
+ data->debug_msg_attr.write = mxt_debug_msg_write;
+ data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX;
+
+ if (sysfs_create_bin_file(&data->client->dev.kobj,
+ &data->debug_msg_attr) < 0) {
+ dev_err(&data->client->dev, "Failed to create %s\n",
+ data->debug_msg_attr.attr.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void mxt_debug_msg_remove(struct mxt_data *data)
+{
+ if (data->debug_msg_attr.attr.name)
+ sysfs_remove_bin_file(&data->client->dev.kobj,
+ &data->debug_msg_attr);
+}
+
+static int mxt_wait_for_completion(struct mxt_data *data,
+ struct completion *comp,
+ unsigned int timeout_ms)
+{
+ struct device *dev = &data->client->dev;
+ unsigned long timeout = msecs_to_jiffies(timeout_ms);
+ long ret;
+
+ ret = wait_for_completion_interruptible_timeout(comp, timeout);
+ if (ret < 0) {
+ return ret;
+ } else if (ret == 0) {
+ dev_err(dev, "Wait for completion timed out.\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int mxt_bootloader_read(struct mxt_data *data,
+ u8 *val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.flags |= I2C_M_RD;
+ msg.len = count;
+ msg.buf = val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data,
+ const u8 * const val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.len = count;
+ msg.buf = (u8 *)val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
+{
+ u8 appmode = data->client->addr;
+ u8 bootloader;
+ u8 family_id = data->info ? data->info->family_id : 0;
+
+ switch (appmode) {
+ case 0x4a:
+ case 0x4b:
+ /* Chips after 1664S use different scheme */
+ if (retry || family_id >= 0xa2) {
+ bootloader = appmode - 0x24;
+ break;
+ }
+ /* Fall through for normal case */
+ case 0x4c:
+ case 0x4d:
+ case 0x5a:
+ case 0x5b:
+ bootloader = appmode - 0x26;
+ break;
+
default:
- return false;
+ dev_err(&data->client->dev,
+ "Appmode i2c address 0x%02x not found\n",
+ appmode);
+ return -EINVAL;
}
+
+ data->bootloader_addr = bootloader;
+ return 0;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ u8 val;
+ bool crc_failure;
+
+ error = mxt_lookup_bootloader_address(data, alt_address);
+ if (error)
+ return error;
+
+ error = mxt_bootloader_read(data, &val, 1);
+ if (error)
+ return error;
+
+ /* Check app crc fail mode */
+ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+ dev_err(dev, "Detected bootloader, status:%02X%s\n",
+ val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+ return 0;
}
-static void mxt_dump_message(struct device *dev,
- struct mxt_message *message)
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
{
- dev_dbg(dev, "reportid: %u\tmessage: %*ph\n",
- message->reportid, 7, message->message);
+ struct device *dev = &data->client->dev;
+ u8 buf[3];
+
+ if (val & MXT_BOOT_EXTENDED_ID) {
+ if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
+ dev_err(dev, "%s: i2c failure\n", __func__);
+ return val;
+ }
+
+ dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+ return buf[0];
+ } else {
+ dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+ return val;
+ }
}
-static int mxt_check_bootloader(struct i2c_client *client,
- unsigned int state)
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
+ bool wait)
{
+ struct device *dev = &data->client->dev;
u8 val;
+ int ret;
recheck:
- if (i2c_master_recv(client, &val, 1) != 1) {
- dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
- return -EIO;
+ if (wait) {
+ /*
+ * In application update mode, the interrupt
+ * line signals state transitions. We must wait for the
+ * CHG assertion before reading the status byte.
+ * Once the status byte has been read, the line is deasserted.
+ */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_CHG_TIMEOUT);
+ if (ret) {
+ /*
+ * TODO: handle -ERESTARTSYS better by terminating
+ * fw update process before returning to userspace
+ * by writing length 0x000 to device (iff we are in
+ * WAITING_FRAME_DATA state).
+ */
+ dev_err(dev, "Update wait error %d\n", ret);
+ return ret;
+ }
}
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret)
+ return ret;
+
+ if (state == MXT_WAITING_BOOTLOAD_CMD)
+ val = mxt_get_bootloader_version(data, val);
+
switch (state) {
case MXT_WAITING_BOOTLOAD_CMD:
case MXT_WAITING_FRAME_DATA:
+ case MXT_APP_CRC_FAIL:
val &= ~MXT_BOOT_STATUS_MASK;
break;
case MXT_FRAME_CRC_PASS:
- if (val == MXT_FRAME_CRC_CHECK)
+ if (val == MXT_FRAME_CRC_CHECK) {
goto recheck;
+ } else if (val == MXT_FRAME_CRC_FAIL) {
+ dev_err(dev, "Bootloader CRC fail\n");
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
}
if (val != state) {
- dev_err(&client->dev, "Unvalid bootloader mode state\n");
+ dev_err(dev, "Invalid bootloader state %02X != %02X\n",
+ val, state);
return -EINVAL;
}
return 0;
}
-static int mxt_unlock_bootloader(struct i2c_client *client)
+static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
{
+ int ret;
u8 buf[2];
- buf[0] = MXT_UNLOCK_CMD_LSB;
- buf[1] = MXT_UNLOCK_CMD_MSB;
-
- if (i2c_master_send(client, buf, 2) != 2) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
+ if (unlock) {
+ buf[0] = MXT_UNLOCK_CMD_LSB;
+ buf[1] = MXT_UNLOCK_CMD_MSB;
+ } else {
+ buf[0] = 0x01;
+ buf[1] = 0x01;
}
- return 0;
-}
-
-static int mxt_fw_write(struct i2c_client *client,
- const u8 *data, unsigned int frame_size)
-{
- if (i2c_master_send(client, data, frame_size) != frame_size) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
+ ret = mxt_bootloader_write(data, buf, 2);
+ if (ret)
+ return ret;
return 0;
}
@@ -399,6 +712,7 @@ static int __mxt_read_reg(struct i2c_client *client,
struct i2c_msg xfer[2];
u8 buf[2];
int ret;
+ bool retry = false;
buf[0] = reg & 0xff;
buf[1] = (reg >> 8) & 0xff;
@@ -415,22 +729,22 @@ static int __mxt_read_reg(struct i2c_client *client,
xfer[1].len = len;
xfer[1].buf = val;
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- if (ret >= 0)
- ret = -EIO;
- dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
- __func__, ret);
+retry_read:
+ ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+ if (ret != ARRAY_SIZE(xfer)) {
+ if (!retry) {
+ dev_dbg(&client->dev, "%s: i2c retry\n", __func__);
+ msleep(MXT_WAKEUP_TIME);
+ retry = true;
+ goto retry_read;
+ } else {
+ dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+ __func__, ret);
+ return -EIO;
+ }
}
- return ret;
-}
-
-static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
-{
- return __mxt_read_reg(client, reg, 1, val);
+ return 0;
}
static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
@@ -439,6 +753,7 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
u8 *buf;
size_t count;
int ret;
+ bool retry = false;
count = len + 2;
buf = kmalloc(count, GFP_KERNEL);
@@ -449,14 +764,21 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
buf[1] = (reg >> 8) & 0xff;
memcpy(&buf[2], val, len);
+retry_write:
ret = i2c_master_send(client, buf, count);
- if (ret == count) {
- ret = 0;
- } else {
- if (ret >= 0)
+ if (ret != count) {
+ if (!retry) {
+ dev_dbg(&client->dev, "%s: i2c retry\n", __func__);
+ msleep(MXT_WAKEUP_TIME);
+ retry = true;
+ goto retry_write;
+ } else {
+ dev_err(&client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
ret = -EIO;
- dev_err(&client->dev, "%s: i2c send failed (%d)\n",
- __func__, ret);
+ }
+ } else {
+ ret = 0;
}
kfree(buf);
@@ -474,298 +796,1050 @@ mxt_get_object(struct mxt_data *data, u8 type)
struct mxt_object *object;
int i;
- for (i = 0; i < data->info.object_num; i++) {
+ for (i = 0; i < data->info->object_num; i++) {
object = data->object_table + i;
if (object->type == type)
return object;
}
- dev_err(&data->client->dev, "Invalid object type\n");
+ dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
return NULL;
}
-static int mxt_read_message(struct mxt_data *data,
- struct mxt_message *message)
+static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
{
- struct mxt_object *object;
- u16 reg;
+ struct device *dev = &data->client->dev;
+ u8 status = msg[1];
+ u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
- object = mxt_get_object(data, MXT_GEN_MESSAGE_T5);
- if (!object)
- return -EINVAL;
+ if (crc != data->config_crc) {
+ data->config_crc = crc;
+ dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+ }
- reg = object->start_address;
- return __mxt_read_reg(data->client, reg,
- sizeof(struct mxt_message), message);
-}
+ complete(&data->crc_completion);
-static int mxt_write_object(struct mxt_data *data,
- u8 type, u8 offset, u8 val)
-{
- struct mxt_object *object;
- u16 reg;
+ /* Detect reset */
+ if (status & MXT_T6_STATUS_RESET)
+ complete(&data->reset_completion);
- object = mxt_get_object(data, type);
- if (!object || offset >= object->size + 1)
- return -EINVAL;
+ /* Output debug if status has changed */
+ if (status != data->t6_status)
+ dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
+ status,
+ status == 0 ? " OK" : "",
+ status & MXT_T6_STATUS_RESET ? " RESET" : "",
+ status & MXT_T6_STATUS_OFL ? " OFL" : "",
+ status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "",
+ status & MXT_T6_STATUS_CAL ? " CAL" : "",
+ status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "",
+ status & MXT_T6_STATUS_COMSERR ? " COMSERR" : "");
- reg = object->start_address;
- return mxt_write_reg(data->client, reg + offset, val);
+ /* Save current status */
+ data->t6_status = status;
}
-static void mxt_input_button(struct mxt_data *data, struct mxt_message *message)
+static void mxt_input_button(struct mxt_data *data, u8 *message)
{
struct input_dev *input = data->input_dev;
+ const struct mxt_platform_data *pdata = data->pdata;
bool button;
int i;
/* Active-low switch */
- for (i = 0; i < MXT_NUM_GPIO; i++) {
- if (data->pdata->key_map[i] == KEY_RESERVED)
+ for (i = 0; i < pdata->t19_num_keys; i++) {
+ if (pdata->t19_keymap[i] == KEY_RESERVED)
continue;
- button = !(message->message[0] & MXT_GPIO0_MASK << i);
- input_report_key(input, data->pdata->key_map[i], button);
+ button = !(message[1] & (1 << i));
+ input_report_key(input, pdata->t19_keymap[i], button);
+ }
+}
+
+static void mxt_input_sync(struct mxt_data *data)
+{
+ if (data->input_dev) {
+ input_mt_report_pointer_emulation(data->input_dev,
+ data->pdata->t19_num_keys);
+ input_sync(data->input_dev);
}
}
-static void mxt_input_touchevent(struct mxt_data *data,
- struct mxt_message *message, int id)
+static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
{
struct device *dev = &data->client->dev;
- u8 status = message->message[0];
struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
int x;
int y;
int area;
- int pressure;
+ int amplitude;
+ u8 vector;
+ int tool;
+
+ id = message[0] - data->T9_reportid_min;
+ status = message[1];
+ x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
+ y = (message[3] << 4) | ((message[4] & 0xf));
- x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
- y = (message->message[2] << 4) | ((message->message[3] & 0xf));
+ /* Handle 10/12 bit switching */
if (data->max_x < 1024)
- x = x >> 2;
+ x >>= 2;
if (data->max_y < 1024)
- y = y >> 2;
+ y >>= 2;
- area = message->message[4];
- pressure = message->message[5];
+ area = message[5];
+
+ amplitude = message[6];
+ vector = message[7];
dev_dbg(dev,
- "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
+ "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n",
id,
- (status & MXT_DETECT) ? 'D' : '.',
- (status & MXT_PRESS) ? 'P' : '.',
- (status & MXT_RELEASE) ? 'R' : '.',
- (status & MXT_MOVE) ? 'M' : '.',
- (status & MXT_VECTOR) ? 'V' : '.',
- (status & MXT_AMP) ? 'A' : '.',
- (status & MXT_SUPPRESS) ? 'S' : '.',
- (status & MXT_UNGRIP) ? 'U' : '.',
- x, y, area, pressure);
+ (status & MXT_T9_DETECT) ? 'D' : '.',
+ (status & MXT_T9_PRESS) ? 'P' : '.',
+ (status & MXT_T9_RELEASE) ? 'R' : '.',
+ (status & MXT_T9_MOVE) ? 'M' : '.',
+ (status & MXT_T9_VECTOR) ? 'V' : '.',
+ (status & MXT_T9_AMP) ? 'A' : '.',
+ (status & MXT_T9_SUPPRESS) ? 'S' : '.',
+ (status & MXT_T9_UNGRIP) ? 'U' : '.',
+ x, y, area, amplitude, vector);
input_mt_slot(input_dev, id);
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
- status & MXT_DETECT);
- if (status & MXT_DETECT) {
+ if (status & MXT_T9_DETECT) {
+ /*
+ * Multiple bits may be set if the host is slow to read
+ * the status messages, indicating all the events that
+ * have happened.
+ */
+ if (status & MXT_T9_RELEASE) {
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, 0);
+ mxt_input_sync(data);
+ }
+
+ /* A size of zero indicates touch is from a linked T47 Stylus */
+ if (area == 0) {
+ area = MXT_TOUCH_MAJOR_T47_STYLUS;
+ tool = MT_TOOL_PEN;
+ } else {
+ tool = MT_TOOL_FINGER;
+ }
+
+ /* Touch active */
+ input_mt_report_slot_state(input_dev, tool, 1);
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+ input_report_abs(input_dev, ABS_MT_ORIENTATION, vector);
+ } else {
+ /* Touch no longer active, close out slot */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
}
+
+ data->update_input = true;
}
-static unsigned mxt_extract_T6_csum(const u8 *csum)
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
{
- return csum[0] | (csum[1] << 8) | (csum[2] << 16);
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ int x;
+ int y;
+ int tool;
+
+ id = message[0] - data->T100_reportid_min - 2;
+
+ /* ignore SCRSTATUS events */
+ if (id < 0)
+ return;
+
+ status = message[1];
+ x = (message[3] << 8) | message[2];
+ y = (message[5] << 8) | message[4];
+
+ dev_dbg(dev,
+ "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n",
+ id,
+ status,
+ x, y,
+ data->t100_aux_area ? message[data->t100_aux_area] : 0,
+ data->t100_aux_ampl ? message[data->t100_aux_ampl] : 0,
+ data->t100_aux_vect ? message[data->t100_aux_vect] : 0);
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T100_DETECT) {
+ if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS)
+ tool = MT_TOOL_PEN;
+ else
+ tool = MT_TOOL_FINGER;
+
+ /* Touch active */
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+ if (data->t100_aux_ampl)
+ input_report_abs(input_dev, ABS_MT_PRESSURE,
+ message[data->t100_aux_ampl]);
+
+ if (data->t100_aux_area) {
+ if (tool == MT_TOOL_PEN)
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ MXT_TOUCH_MAJOR_T47_STYLUS);
+ else
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ message[data->t100_aux_area]);
+ }
+
+ if (data->t100_aux_vect)
+ input_report_abs(input_dev, ABS_MT_ORIENTATION,
+ message[data->t100_aux_vect]);
+ } else {
+ /* Touch no longer active, close out slot */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ data->update_input = true;
}
-static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg)
+
+static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg)
{
- u8 id = msg->reportid;
- return (id >= data->T9_reportid_min && id <= data->T9_reportid_max);
+ struct input_dev *input_dev = data->input_dev;
+ struct device *dev = &data->client->dev;
+ int key;
+ bool curr_state, new_state;
+ bool sync = false;
+ unsigned long keystates = le32_to_cpu(msg[2]);
+
+ for (key = 0; key < data->pdata->t15_num_keys; key++) {
+ curr_state = test_bit(key, &data->t15_keystatus);
+ new_state = test_bit(key, &keystates);
+
+ if (!curr_state && new_state) {
+ dev_dbg(dev, "T15 key press: %u\n", key);
+ __set_bit(key, &data->t15_keystatus);
+ input_event(input_dev, EV_KEY,
+ data->pdata->t15_keymap[key], 1);
+ sync = true;
+ } else if (curr_state && !new_state) {
+ dev_dbg(dev, "T15 key release: %u\n", key);
+ __clear_bit(key, &data->t15_keystatus);
+ input_event(input_dev, EV_KEY,
+ data->pdata->t15_keymap[key], 0);
+ sync = true;
+ }
+ }
+
+ if (sync)
+ input_sync(input_dev);
}
-static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg)
{
- struct mxt_data *data = dev_id;
- struct mxt_message message;
- const u8 *payload = &message.message[0];
struct device *dev = &data->client->dev;
- u8 reportid;
- bool update_input = false;
+ u8 status = msg[1];
- do {
- if (mxt_read_message(data, &message)) {
- dev_err(dev, "Failed to read message\n");
- goto end;
- }
+ if (status & MXT_T42_MSG_TCHSUP)
+ dev_info(dev, "T42 suppress\n");
+ else
+ dev_info(dev, "T42 normal\n");
+}
- reportid = message.reportid;
+static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ u8 status, state;
- if (reportid == data->T6_reportid) {
- u8 status = payload[0];
- unsigned csum = mxt_extract_T6_csum(&payload[1]);
- dev_dbg(dev, "Status: %02x Config Checksum: %06x\n",
- status, csum);
- } else if (mxt_is_T9_message(data, &message)) {
- int id = reportid - data->T9_reportid_min;
- mxt_input_touchevent(data, &message, id);
- update_input = true;
- } else if (message.reportid == data->T19_reportid) {
- mxt_input_button(data, &message);
- update_input = true;
- } else {
- mxt_dump_message(dev, &message);
- }
- } while (reportid != 0xff);
+ status = msg[1];
+ state = msg[4];
- if (update_input) {
- input_mt_report_pointer_emulation(data->input_dev, false);
- input_sync(data->input_dev);
+ dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", state, status,
+ status & 0x01 ? "FREQCHG " : "",
+ status & 0x02 ? "APXCHG " : "",
+ status & 0x04 ? "ALGOERR " : "",
+ status & 0x10 ? "STATCHG " : "",
+ status & 0x20 ? "NLVLCHG " : "");
+
+ return 0;
+}
+
+static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ u8 id;
+ u16 x, y;
+ u8 pressure;
+
+ /* stylus slots come after touch slots */
+ id = data->num_touchids + (msg[0] - data->T63_reportid_min);
+
+ if (id < 0 || id > (data->num_touchids + data->num_stylusids)) {
+ dev_err(dev, "invalid stylus id %d, max slot is %d\n",
+ id, data->num_stylusids);
+ return;
+ }
+
+ x = msg[3] | (msg[4] << 8);
+ y = msg[5] | (msg[6] << 8);
+ pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK;
+
+ dev_dbg(dev,
+ "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n",
+ id,
+ msg[1] & MXT_T63_STYLUS_SUPPRESS ? 'S' : '.',
+ msg[1] & MXT_T63_STYLUS_MOVE ? 'M' : '.',
+ msg[1] & MXT_T63_STYLUS_RELEASE ? 'R' : '.',
+ msg[1] & MXT_T63_STYLUS_PRESS ? 'P' : '.',
+ x, y, pressure,
+ msg[2] & MXT_T63_STYLUS_BARREL ? 'B' : '.',
+ msg[2] & MXT_T63_STYLUS_ERASER ? 'E' : '.',
+ msg[2] & MXT_T63_STYLUS_TIP ? 'T' : '.',
+ msg[2] & MXT_T63_STYLUS_DETECT ? 'D' : '.');
+
+ input_mt_slot(input_dev, id);
+
+ if (msg[2] & MXT_T63_STYLUS_DETECT) {
+ input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ } else {
+ input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0);
+ }
+
+ input_report_key(input_dev, BTN_STYLUS,
+ (msg[2] & MXT_T63_STYLUS_ERASER));
+ input_report_key(input_dev, BTN_STYLUS2,
+ (msg[2] & MXT_T63_STYLUS_BARREL));
+
+ mxt_input_sync(data);
+}
+
+static int mxt_proc_message(struct mxt_data *data, u8 *message)
+{
+ u8 report_id = message[0];
+ bool dump = data->debug_enabled;
+
+ if (report_id == MXT_RPTID_NOMSG)
+ return 0;
+
+ if (report_id == data->T6_reportid) {
+ mxt_proc_t6_messages(data, message);
+ } else if (report_id >= data->T42_reportid_min
+ && report_id <= data->T42_reportid_max) {
+ mxt_proc_t42_messages(data, message);
+ } else if (report_id == data->T48_reportid) {
+ mxt_proc_t48_messages(data, message);
+ } else if (!data->input_dev || data->suspended) {
+ /*
+ * Do not report events if input device is not
+ * yet registered or returning from suspend
+ */
+ mxt_dump_message(data, message);
+ } else if (report_id >= data->T9_reportid_min
+ && report_id <= data->T9_reportid_max) {
+ mxt_proc_t9_message(data, message);
+ } else if (report_id >= data->T100_reportid_min
+ && report_id <= data->T100_reportid_max) {
+ mxt_proc_t100_message(data, message);
+ } else if (report_id == data->T19_reportid) {
+ mxt_input_button(data, message);
+ data->update_input = true;
+ } else if (report_id >= data->T63_reportid_min
+ && report_id <= data->T63_reportid_max) {
+ mxt_proc_t63_messages(data, message);
+ } else if (report_id >= data->T15_reportid_min
+ && report_id <= data->T15_reportid_max) {
+ mxt_proc_t15_messages(data, message);
+ } else {
+ dump = true;
+ }
+
+ if (dump)
+ mxt_dump_message(data, message);
+
+ if (data->debug_v2_enabled)
+ mxt_debug_msg_add(data, message);
+
+ return 1;
+}
+
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ int i;
+ u8 num_valid = 0;
+
+ /* Safety check for msg_buf */
+ if (count > data->max_reportid)
+ return -EINVAL;
+
+ /* Process remaining messages if necessary */
+ ret = __mxt_read_reg(data->client, data->T5_address,
+ data->T5_msg_size * count, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
+ return ret;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = mxt_proc_message(data,
+ data->msg_buf + data->T5_msg_size * i);
+
+ if (ret == 1)
+ num_valid++;
+ }
+
+ /* return number of messages read */
+ return num_valid;
+}
+
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ u8 count, num_left;
+
+ /* Read T44 and T5 together */
+ ret = __mxt_read_reg(data->client, data->T44_address,
+ data->T5_msg_size + 1, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+ return IRQ_NONE;
+ }
+
+ count = data->msg_buf[0];
+
+ if (count == 0) {
+ /*
+ * This condition is caused by the CHG line being configured
+ * in Mode 0. It results in unnecessary I2C operations but it
+ * is benign.
+ */
+ dev_dbg(dev, "Interrupt triggered but zero messages\n");
+ return IRQ_NONE;
+ } else if (count > data->max_reportid) {
+ dev_err(dev, "T44 count %d exceeded max report id\n", count);
+ count = data->max_reportid;
+ }
+
+ /* Process first message */
+ ret = mxt_proc_message(data, data->msg_buf + 1);
+ if (ret < 0) {
+ dev_warn(dev, "Unexpected invalid message\n");
+ return IRQ_NONE;
+ }
+
+ num_left = count - 1;
+
+ /* Process remaining messages if necessary */
+ if (num_left) {
+ ret = mxt_read_and_process_messages(data, num_left);
+ if (ret < 0)
+ goto end;
+ else if (ret != num_left)
+ dev_warn(dev, "Unexpected invalid message\n");
}
end:
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
return IRQ_HANDLED;
}
-static int mxt_check_reg_init(struct mxt_data *data)
+static int mxt_process_messages_until_invalid(struct mxt_data *data)
{
- const struct mxt_platform_data *pdata = data->pdata;
- struct mxt_object *object;
struct device *dev = &data->client->dev;
- int index = 0;
- int i, size;
+ int count, read;
+ u8 tries = 2;
+
+ count = data->max_reportid;
+
+ /* Read messages until we force an invalid */
+ do {
+ read = mxt_read_and_process_messages(data, count);
+ if (read < count)
+ return 0;
+ } while (--tries);
+
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+ int total_handled, num_handled;
+ u8 count = data->last_message_count;
+
+ if (count < 1 || count > data->max_reportid)
+ count = 1;
+
+ /* include final invalid message */
+ total_handled = mxt_read_and_process_messages(data, count + 1);
+ if (total_handled < 0)
+ return IRQ_NONE;
+ /* if there were invalid messages, then we are done */
+ else if (total_handled <= count)
+ goto update_count;
+
+ /* keep reading two msgs until one is invalid or reportid limit */
+ do {
+ num_handled = mxt_read_and_process_messages(data, 2);
+ if (num_handled < 0)
+ return IRQ_NONE;
+
+ total_handled += num_handled;
+
+ if (num_handled < 2)
+ break;
+ } while (total_handled < data->num_touchids);
+
+update_count:
+ data->last_message_count = total_handled;
+
+ if (data->update_input) {
+ mxt_input_sync(data);
+ data->update_input = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+ struct mxt_data *data = dev_id;
+
+ if (data->in_bootloader) {
+ /* bootloader state transition completion */
+ complete(&data->bl_completion);
+ return IRQ_HANDLED;
+ }
+
+ if (!data->object_table)
+ return IRQ_HANDLED;
+
+ if (data->T44_address) {
+ return mxt_process_messages_t44(data);
+ } else {
+ return mxt_process_messages(data);
+ }
+}
+
+static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
+ u8 value, bool wait)
+{
+ u16 reg;
+ u8 command_register;
+ int timeout_counter = 0;
int ret;
- if (!pdata->config) {
- dev_dbg(dev, "No cfg data defined, skipping reg init\n");
+ reg = data->T6_address + cmd_offset;
+
+ ret = mxt_write_reg(data->client, reg, value);
+ if (ret)
+ return ret;
+
+ if (!wait)
return 0;
+
+ do {
+ msleep(20);
+ ret = __mxt_read_reg(data->client, reg, 1, &command_register);
+ if (ret)
+ return ret;
+ } while (command_register != 0 && timeout_counter++ <= 100);
+
+ if (timeout_counter > 100) {
+ dev_err(&data->client->dev, "Command failed!\n");
+ return -EIO;
}
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
+ return 0;
+}
+
+static int mxt_soft_reset(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret = 0;
+
+ dev_info(dev, "Resetting chip\n");
+
+ INIT_COMPLETION(data->reset_completion);
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
+ if (ret)
+ return ret;
+
+ ret = mxt_wait_for_completion(data, &data->reset_completion,
+ MXT_RESET_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
+{
+ /*
+ * On failure, CRC is set to 0 and config will always be
+ * downloaded.
+ */
+ data->config_crc = 0;
+ INIT_COMPLETION(data->crc_completion);
+
+ mxt_t6_command(data, cmd, value, true);
+
+ /*
+ * Wait for crc message. On failure, CRC is set to 0 and config will
+ * always be downloaded.
+ */
+ mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
+}
+
+static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
+{
+ static const unsigned int crcpoly = 0x80001B;
+ u32 result;
+ u32 data_word;
+
+ data_word = (secondbyte << 8) | firstbyte;
+ result = ((*crc << 1) ^ data_word);
+
+ if (result & 0x1000000)
+ result ^= crcpoly;
+
+ *crc = result;
+}
+
+static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
+{
+ u32 crc = 0;
+ u8 *ptr = base + start_off;
+ u8 *last_val = base + end_off - 1;
+
+ if (end_off < start_off)
+ return -EINVAL;
+
+ while (ptr < last_val) {
+ mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
+ ptr += 2;
+ }
+
+ /* if len is odd, fill the last byte with 0 */
+ if (ptr == last_val)
+ mxt_calc_crc24(&crc, *ptr, 0);
+
+ /* Mask to 24-bit */
+ crc &= 0x00FFFFFF;
+
+ return crc;
+}
+
+static int mxt_check_retrigen(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct irq_data *irqd;
+ int error;
+ int val;
+
+ irqd = irq_get_irq_data(data->irq);
+ if (irqd_get_trigger_type(irqd) & IRQF_TRIGGER_LOW)
+ return 0;
+
+ if (data->T18_address) {
+ error = __mxt_read_reg(client,
+ data->T18_address + MXT_COMMS_CTRL,
+ 1, &val);
+ if (error)
+ return error;
+
+ if (val & MXT_COMMS_RETRIGEN)
+ return 0;
+ }
+
+ dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
+ data->use_retrigen_workaround = true;
+ return 0;
+}
- if (!mxt_object_writable(object->type))
+static int mxt_prepare_cfg_mem(struct mxt_data *data,
+ const struct firmware *cfg,
+ unsigned int data_pos,
+ unsigned int cfg_start_ofs,
+ u8 *config_mem,
+ size_t config_mem_size)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_object *object;
+ unsigned int type, instance, size, byte_offset;
+ int offset;
+ int ret;
+ int i;
+ u16 reg;
+ u8 val;
+
+ while (data_pos < cfg->size) {
+ /* Read type, instance, length */
+ ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
+ &type, &instance, &size, &offset);
+ if (ret == 0) {
+ /* EOF */
+ break;
+ } else if (ret != 3) {
+ dev_err(dev, "Bad format: failed to parse object\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ object = mxt_get_object(data, type);
+ if (!object) {
+ /* Skip object */
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format in T%d at %d\n",
+ type, i);
+ return -EINVAL;
+ }
+ data_pos += offset;
+ }
continue;
+ }
+
+ if (size > mxt_obj_size(object)) {
+ /*
+ * Either we are in fallback mode due to wrong
+ * config or config from a later fw version,
+ * or the file is corrupt or hand-edited.
+ */
+ dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
+ size - mxt_obj_size(object), type);
+ } else if (mxt_obj_size(object) > size) {
+ /*
+ * If firmware is upgraded, new bytes may be added to
+ * end of objects. It is generally forward compatible
+ * to zero these bytes - previous behaviour will be
+ * retained. However this does invalidate the CRC and
+ * will force fallback mode until the configuration is
+ * updated. We warn here but do nothing else - the
+ * malloc has zeroed the entire configuration.
+ */
+ dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
+ mxt_obj_size(object) - size, type);
+ }
- size = (object->size + 1) * (object->instances + 1);
- if (index + size > pdata->config_length) {
- dev_err(dev, "Not enough config data!\n");
+ if (instance >= mxt_obj_instances(object)) {
+ dev_err(dev, "Object instances exceeded!\n");
return -EINVAL;
}
- ret = __mxt_write_reg(data->client, object->start_address,
- size, &pdata->config[index]);
- if (ret)
- return ret;
- index += size;
+ reg = object->start_address + mxt_obj_size(object) * instance;
+
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format in T%d at %d\n",
+ type, i);
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ if (i > mxt_obj_size(object))
+ continue;
+
+ byte_offset = reg + i - cfg_start_ofs;
+
+ if (byte_offset >= 0 && byte_offset < config_mem_size) {
+ *(config_mem + byte_offset) = val;
+ } else {
+ dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
+ reg, object->type, byte_offset);
+ return -EINVAL;
+ }
+ }
}
return 0;
}
-static int mxt_make_highchg(struct mxt_data *data)
+static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
+ u8 *config_mem, size_t config_mem_size)
{
- struct device *dev = &data->client->dev;
- struct mxt_message message;
- int count = 10;
+ unsigned int byte_offset = 0;
int error;
- /* Read dummy message to make high CHG pin */
- do {
- error = mxt_read_message(data, &message);
- if (error)
+ /* Write configuration as blocks */
+ while (byte_offset < config_mem_size) {
+ unsigned int size = config_mem_size - byte_offset;
+
+ if (size > MXT_MAX_BLOCK_WRITE)
+ size = MXT_MAX_BLOCK_WRITE;
+
+ error = __mxt_write_reg(data->client,
+ cfg_start + byte_offset,
+ size, config_mem + byte_offset);
+ if (error) {
+ dev_err(&data->client->dev,
+ "Config write error, ret=%d\n", error);
return error;
- } while (message.reportid != 0xff && --count);
+ }
- if (!count) {
- dev_err(dev, "CHG pin isn't cleared\n");
- return -EBUSY;
+ byte_offset += size;
}
return 0;
}
-static void mxt_handle_pdata(struct mxt_data *data)
+static int mxt_init_t7_power_cfg(struct mxt_data *data);
+
+/*
+ * mxt_update_cfg - download configuration to chip
+ *
+ * Atmel Raw Config File Format
+ *
+ * The first four lines of the raw config file contain:
+ * 1) Version
+ * 2) Chip ID Information (first 7 bytes of device memory)
+ * 3) Chip Information Block 24-bit CRC Checksum
+ * 4) Chip Configuration 24-bit CRC Checksum
+ *
+ * The rest of the file consists of one line per object instance:
+ * <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ * <TYPE> - 2-byte object type as hex
+ * <INSTANCE> - 2-byte object instance number as hex
+ * <SIZE> - 2-byte object size as hex
+ * <CONTENTS> - array of <SIZE> 1-byte hex values
+ */
+static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
{
- const struct mxt_platform_data *pdata = data->pdata;
- u8 voltage;
+ struct device *dev = &data->client->dev;
+ struct mxt_info cfg_info;
+ int ret;
+ int offset;
+ int data_pos;
+ int i;
+ int cfg_start_ofs;
+ u32 info_crc, config_crc, calculated_crc;
+ u8 *config_mem;
+ size_t config_mem_size;
+
+ mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
+
+ if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+ dev_err(dev, "Unrecognised config file\n");
+ return -EINVAL;
+ }
- /* Set touchscreen lines */
- mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
- pdata->x_line);
- mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
- pdata->y_line);
+ data_pos = strlen(MXT_CFG_MAGIC);
- /* Set touchscreen orient */
- mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
- pdata->orient);
+ /* Load information block and check */
+ for (i = 0; i < sizeof(struct mxt_info); i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ (unsigned char *)&cfg_info + i,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format\n");
+ return -EINVAL;
+ }
- /* Set touchscreen burst length */
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_BLEN, pdata->blen);
+ data_pos += offset;
+ }
+
+ if (cfg_info.family_id != data->info->family_id) {
+ dev_err(dev, "Family ID mismatch!\n");
+ return -EINVAL;
+ }
+
+ if (cfg_info.variant_id != data->info->variant_id) {
+ dev_err(dev, "Variant ID mismatch!\n");
+ return -EINVAL;
+ }
+
+ /* Read CRCs */
+ ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Info CRC\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
+
+ ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Config CRC\n");
+ return -EINVAL;
+ }
+ data_pos += offset;
- /* Set touchscreen threshold */
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_TCHTHR, pdata->threshold);
+ /*
+ * The Info Block CRC is calculated over mxt_info and the object
+ * table. If it does not match then we are trying to load the
+ * configuration from a different chip or firmware version, so
+ * the configuration CRC is invalid anyway.
+ */
+ if (info_crc == data->info_crc) {
+ if (config_crc == 0 || data->config_crc == 0) {
+ dev_info(dev, "CRC zero, attempting to apply config\n");
+ } else if (config_crc == data->config_crc) {
+ dev_dbg(dev, "Config CRC 0x%06X: OK\n",
+ data->config_crc);
+ return 0;
+ } else {
+ dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
+ data->config_crc, config_crc);
+ }
+ } else {
+ dev_warn(dev,
+ "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
+ data->info_crc, info_crc);
+ }
- /* Set touchscreen resolution */
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
- mxt_write_object(data, MXT_TOUCH_MULTI_T9,
- MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
+ /* Malloc memory to store configuration */
+ cfg_start_ofs = MXT_OBJECT_START +
+ data->info->object_num * sizeof(struct mxt_object) +
+ MXT_INFO_CHECKSUM_SIZE;
+ config_mem_size = data->mem_size - cfg_start_ofs;
+ config_mem = kzalloc(config_mem_size, GFP_KERNEL);
+ if (!config_mem) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
- /* Set touchscreen voltage */
- if (pdata->voltage) {
- if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
- voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
- MXT_VOLTAGE_STEP;
- voltage = 0xff - voltage + 1;
- } else
- voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
- MXT_VOLTAGE_STEP;
+ ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs,
+ config_mem, config_mem_size);
+ if (ret)
+ goto release_mem;
- mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
- MXT_CTE_VOLTAGE, voltage);
+ /* Calculate crc of the received configs (not the raw config file) */
+ if (data->T7_address < cfg_start_ofs) {
+ dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n",
+ data->T7_address, cfg_start_ofs);
+ ret = 0;
+ goto release_mem;
}
+
+ calculated_crc = mxt_calculate_crc(config_mem,
+ data->T7_address - cfg_start_ofs,
+ config_mem_size);
+
+ if (config_crc > 0 && config_crc != calculated_crc)
+ dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
+ calculated_crc, config_crc);
+
+ ret = mxt_upload_cfg_mem(data, cfg_start_ofs,
+ config_mem, config_mem_size);
+ if (ret)
+ goto release_mem;
+
+ mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+
+ ret = mxt_check_retrigen(data);
+ if (ret)
+ goto release_mem;
+
+ ret = mxt_soft_reset(data);
+ if (ret)
+ goto release_mem;
+
+ dev_info(dev, "Config successfully updated\n");
+
+ /* T7 config may have changed */
+ mxt_init_t7_power_cfg(data);
+
+release_mem:
+ kfree(config_mem);
+ return ret;
}
-static int mxt_get_info(struct mxt_data *data)
+static int mxt_acquire_irq(struct mxt_data *data)
{
- struct i2c_client *client = data->client;
- struct mxt_info *info = &data->info;
int error;
- /* Read 7-byte info block starting at address 0 */
- error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
- if (error)
- return error;
+ enable_irq(data->irq);
+
+ if (data->use_retrigen_workaround) {
+ error = mxt_process_messages_until_invalid(data);
+ if (error)
+ return error;
+ }
return 0;
}
-static int mxt_get_object_table(struct mxt_data *data)
+static void mxt_free_input_device(struct mxt_data *data)
+{
+ if (data->input_dev) {
+ input_unregister_device(data->input_dev);
+ data->input_dev = NULL;
+ }
+}
+
+static void mxt_free_object_table(struct mxt_data *data)
+{
+ mxt_debug_msg_remove(data);
+
+ data->object_table = NULL;
+ data->info = NULL;
+ kfree(data->raw_info_block);
+ data->raw_info_block = NULL;
+ kfree(data->msg_buf);
+ data->msg_buf = NULL;
+
+ data->T5_address = 0;
+ data->T5_msg_size = 0;
+ data->T6_reportid = 0;
+ data->T7_address = 0;
+ data->T9_reportid_min = 0;
+ data->T9_reportid_max = 0;
+ data->T15_reportid_min = 0;
+ data->T15_reportid_max = 0;
+ data->T18_address = 0;
+ data->T19_reportid = 0;
+ data->T42_reportid_min = 0;
+ data->T42_reportid_max = 0;
+ data->T44_address = 0;
+ data->T48_reportid = 0;
+ data->T63_reportid_min = 0;
+ data->T63_reportid_max = 0;
+ data->T100_reportid_min = 0;
+ data->T100_reportid_max = 0;
+ data->max_reportid = 0;
+}
+
+static int mxt_parse_object_table(struct mxt_data *data,
+ struct mxt_object *object_table)
{
struct i2c_client *client = data->client;
- size_t table_size;
- int error;
int i;
u8 reportid;
-
- table_size = data->info.object_num * sizeof(struct mxt_object);
- error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
- data->object_table);
- if (error)
- return error;
+ u16 end_address;
/* Valid Report IDs start counting from 1 */
reportid = 1;
- for (i = 0; i < data->info.object_num; i++) {
- struct mxt_object *object = data->object_table + i;
+ data->mem_size = 0;
+ for (i = 0; i < data->info->object_num; i++) {
+ struct mxt_object *object = object_table + i;
u8 min_id, max_id;
le16_to_cpus(&object->start_address);
@@ -773,7 +1847,7 @@ static int mxt_get_object_table(struct mxt_data *data)
if (object->num_report_ids) {
min_id = reportid;
reportid += object->num_report_ids *
- (object->instances + 1);
+ mxt_obj_instances(object);
max_id = reportid - 1;
} else {
min_id = 0;
@@ -781,99 +1855,672 @@ static int mxt_get_object_table(struct mxt_data *data)
}
dev_dbg(&data->client->dev,
- "Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n",
- object->type, object->start_address, object->size + 1,
- object->instances + 1, min_id, max_id);
+ "T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n",
+ object->type, object->start_address,
+ mxt_obj_size(object), mxt_obj_instances(object),
+ min_id, max_id);
switch (object->type) {
+ case MXT_GEN_MESSAGE_T5:
+ if (data->info->family_id == 0x80 &&
+ data->info->version < 0x20) {
+ /*
+ * On mXT224 firmware versions prior to V2.0
+ * read and discard unused CRC byte otherwise
+ * DMA reads are misaligned.
+ */
+ data->T5_msg_size = mxt_obj_size(object);
+ } else {
+ /* CRC not enabled, so skip last byte */
+ data->T5_msg_size = mxt_obj_size(object) - 1;
+ }
+ data->T5_address = object->start_address;
+ break;
case MXT_GEN_COMMAND_T6:
data->T6_reportid = min_id;
+ data->T6_address = object->start_address;
+ break;
+ case MXT_GEN_POWER_T7:
+ data->T7_address = object->start_address;
break;
case MXT_TOUCH_MULTI_T9:
+ /* Only handle messages from first T9 instance */
data->T9_reportid_min = min_id;
- data->T9_reportid_max = max_id;
+ data->T9_reportid_max = min_id +
+ object->num_report_ids - 1;
+ data->num_touchids = object->num_report_ids;
+ break;
+ case MXT_TOUCH_KEYARRAY_T15:
+ data->T15_reportid_min = min_id;
+ data->T15_reportid_max = max_id;
+ break;
+ case MXT_SPT_COMMSCONFIG_T18:
+ data->T18_address = object->start_address;
+ break;
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ data->T42_reportid_min = min_id;
+ data->T42_reportid_max = max_id;
+ break;
+ case MXT_SPT_MESSAGECOUNT_T44:
+ data->T44_address = object->start_address;
break;
case MXT_SPT_GPIOPWM_T19:
data->T19_reportid = min_id;
break;
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ data->T48_reportid = min_id;
+ break;
+ case MXT_PROCI_ACTIVE_STYLUS_T63:
+ /* Only handle messages from first T63 instance */
+ data->T63_reportid_min = min_id;
+ data->T63_reportid_max = min_id;
+ data->num_stylusids = 1;
+ break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ data->T100_reportid_min = min_id;
+ data->T100_reportid_max = max_id;
+ /* first two report IDs reserved */
+ data->num_touchids = object->num_report_ids - 2;
+ break;
}
+
+ end_address = object->start_address
+ + mxt_obj_size(object) * mxt_obj_instances(object) - 1;
+
+ if (end_address >= data->mem_size)
+ data->mem_size = end_address + 1;
+ }
+
+ /* Store maximum reportid */
+ data->max_reportid = reportid;
+
+ /* If T44 exists, T5 position has to be directly after */
+ if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
+ dev_err(&client->dev, "Invalid T44 position\n");
+ return -EINVAL;
+ }
+
+ data->msg_buf = kcalloc(data->max_reportid,
+ data->T5_msg_size, GFP_KERNEL);
+ if (!data->msg_buf) {
+ dev_err(&client->dev, "Failed to allocate message buffer\n");
+ return -ENOMEM;
}
return 0;
}
-static void mxt_free_object_table(struct mxt_data *data)
+static int mxt_read_info_block(struct mxt_data *data)
{
- kfree(data->object_table);
- data->object_table = NULL;
- data->T6_reportid = 0;
- data->T9_reportid_min = 0;
- data->T9_reportid_max = 0;
- data->T19_reportid = 0;
+ struct i2c_client *client = data->client;
+ int error;
+ size_t size;
+ void *id_buf, *buf;
+ uint8_t num_objects;
+ u32 calculated_crc;
+ u8 *crc_ptr;
+
+ /* If info block already allocated, free it */
+ if (data->raw_info_block != NULL)
+ mxt_free_object_table(data);
+
+ /* Read 7-byte ID information block starting at address 0 */
+ size = sizeof(struct mxt_info);
+ id_buf = kzalloc(size, GFP_KERNEL);
+ if (!id_buf) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ error = __mxt_read_reg(client, 0, size, id_buf);
+ if (error) {
+ kfree(id_buf);
+ return error;
+ }
+
+ /* Resize buffer to give space for rest of info block */
+ num_objects = ((struct mxt_info *)id_buf)->object_num;
+ size += (num_objects * sizeof(struct mxt_object))
+ + MXT_INFO_CHECKSUM_SIZE;
+
+ buf = krealloc(id_buf, size, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ /* Read rest of info block */
+ error = __mxt_read_reg(client, MXT_OBJECT_START,
+ size - MXT_OBJECT_START,
+ buf + MXT_OBJECT_START);
+ if (error)
+ goto err_free_mem;
+
+ /* Extract & calculate checksum */
+ crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE;
+ data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
+
+ calculated_crc = mxt_calculate_crc(buf, 0,
+ size - MXT_INFO_CHECKSUM_SIZE);
+
+ /*
+ * CRC mismatch can be caused by data corruption due to I2C comms
+ * issue or else device is not using Object Based Protocol (eg i2c-hid)
+ */
+ if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
+ dev_err(&client->dev,
+ "Info Block CRC error calculated=0x%06X read=0x%06X\n",
+ calculated_crc, data->info_crc);
+ error = -EIO;
+ goto err_free_mem;
+ }
+
+ data->raw_info_block = buf;
+ data->info = (struct mxt_info *)buf;
+
+ dev_info(&client->dev,
+ "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+ data->info->family_id, data->info->variant_id,
+ data->info->version >> 4, data->info->version & 0xf,
+ data->info->build, data->info->object_num);
+
+ /* Parse object table information */
+ error = mxt_parse_object_table(data, buf + MXT_OBJECT_START);
+ if (error) {
+ dev_err(&client->dev, "Error %d parsing object table\n", error);
+ mxt_free_object_table(data);
+ return error;
+ }
+
+ data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
+
+ return 0;
+
+err_free_mem:
+ kfree(buf);
+ return error;
}
-static int mxt_initialize(struct mxt_data *data)
+static void mxt_regulator_enable(struct mxt_data *data)
+{
+ int error;
+
+ gpio_set_value(data->pdata->gpio_reset, 0);
+
+ error = regulator_enable(data->reg_vdd);
+ if (error)
+ return;
+
+ error = regulator_enable(data->reg_avdd);
+ if (error)
+ return;
+
+ msleep(MXT_REGULATOR_DELAY);
+ gpio_set_value(data->pdata->gpio_reset, 1);
+ msleep(MXT_CHG_DELAY);
+
+retry_wait:
+ INIT_COMPLETION(data->bl_completion);
+ data->in_bootloader = true;
+ error = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_POWERON_DELAY);
+ if (error == -EINTR)
+ goto retry_wait;
+
+ data->in_bootloader = false;
+}
+
+static void mxt_regulator_disable(struct mxt_data *data)
+{
+ regulator_disable(data->reg_vdd);
+ regulator_disable(data->reg_avdd);
+}
+
+static void mxt_probe_regulators(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+
+ /*
+ * According to maXTouch power sequencing specification, RESET line
+ * must be kept low until some time after regulators come up to
+ * voltage
+ */
+ if (!gpio_is_valid(data->pdata->gpio_reset)) {
+ dev_dbg(dev, "Must have reset GPIO to use regulator support\n");
+ goto fail;
+ }
+
+ data->reg_vdd = regulator_get(dev, "vdd");
+ if (IS_ERR(data->reg_vdd)) {
+ error = PTR_ERR(data->reg_vdd);
+ dev_err(dev, "Error %d getting vdd regulator\n", error);
+ goto fail;
+ }
+
+ data->reg_avdd = regulator_get(dev, "avdd");
+ if (IS_ERR(data->reg_avdd)) {
+ error = PTR_ERR(data->reg_avdd);
+ dev_err(dev, "Error %d getting avdd regulator\n", error);
+ goto fail_release;
+ }
+
+ data->use_regulator = true;
+ mxt_regulator_enable(data);
+
+ dev_dbg(dev, "Initialised regulators\n");
+ return;
+
+fail_release:
+ regulator_put(data->reg_vdd);
+fail:
+ data->reg_vdd = NULL;
+ data->reg_avdd = NULL;
+ data->use_regulator = false;
+}
+
+static int mxt_read_t9_resolution(struct mxt_data *data)
{
struct i2c_client *client = data->client;
- struct mxt_info *info = &data->info;
int error;
- u8 val;
+ struct t9_range range;
+ unsigned char orient;
+ struct mxt_object *object;
- error = mxt_get_info(data);
+ object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_RANGE,
+ sizeof(range), &range);
if (error)
return error;
- data->object_table = kcalloc(info->object_num,
- sizeof(struct mxt_object),
- GFP_KERNEL);
- if (!data->object_table) {
- dev_err(&client->dev, "Failed to allocate memory\n");
+ le16_to_cpus(&range.x);
+ le16_to_cpus(&range.y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_ORIENT,
+ 1, &orient);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range.x == 0)
+ range.x = 1023;
+
+ if (range.y == 0)
+ range.y = 1023;
+
+ if (orient & MXT_T9_ORIENT_SWITCH) {
+ data->max_x = range.y;
+ data->max_y = range.x;
+ } else {
+ data->max_x = range.x;
+ data->max_y = range.y;
+ }
+
+ dev_dbg(&client->dev,
+ "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static void mxt_start(struct mxt_data *data);
+static void mxt_stop(struct mxt_data *data);
+static int mxt_input_open(struct input_dev *dev);
+static void mxt_input_close(struct input_dev *dev);
+
+static int mxt_initialize_t9_input_device(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct input_dev *input_dev;
+ int error;
+ unsigned int num_mt_slots;
+ unsigned int mt_flags = 0;
+ int i;
+
+ error = mxt_read_t9_resolution(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
return -ENOMEM;
}
- /* Get object table information */
- error = mxt_get_object_table(data);
+ input_dev->name = "Atmel maXTouch Touchscreen";
+ input_dev->phys = data->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ if (pdata->t19_num_keys) {
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
+ input_set_capability(input_dev, EV_KEY,
+ pdata->t19_keymap[i]);
+
+ mt_flags |= INPUT_MT_POINTER;
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+
+ input_dev->name = "Atmel maXTouch Touchpad";
+ } else {
+ mt_flags |= INPUT_MT_DIRECT;
+ }
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
+
+ /* For multi touch */
+ num_mt_slots = data->num_touchids + data->num_stylusids;
+ error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
+ if (error) {
+ dev_err(dev, "Error %d initialising slots\n", error);
+ goto err_free_mem;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+
+ /* For T63 active stylus */
+ if (data->T63_reportid_min) {
+ input_set_capability(input_dev, EV_KEY, BTN_STYLUS);
+ input_set_capability(input_dev, EV_KEY, BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ }
+
+ /* For T15 key array */
+ if (data->T15_reportid_min) {
+ data->t15_keystatus = 0;
+
+ for (i = 0; i < data->pdata->t15_num_keys; i++)
+ input_set_capability(input_dev, EV_KEY,
+ data->pdata->t15_keymap[i]);
+ }
+
+ input_set_drvdata(input_dev, data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Error %d registering input device\n", error);
+ goto err_free_mem;
+ }
+
+ data->input_dev = input_dev;
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct mxt_object *object;
+ u16 range_x, range_y;
+ u8 cfg, tchaux;
+ u8 aux;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_XRANGE,
+ sizeof(range_x), &range_x);
if (error)
- goto err_free_object_table;
+ return error;
- /* Check register init values */
- error = mxt_check_reg_init(data);
+ le16_to_cpus(&range_x);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_YRANGE,
+ sizeof(range_y), &range_y);
if (error)
- goto err_free_object_table;
+ return error;
+
+ le16_to_cpus(&range_y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_CFG1,
+ 1, &cfg);
+ if (error)
+ return error;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_TCHAUX,
+ 1, &tchaux);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ if (range_y == 0)
+ range_y = 1023;
+
+ if (cfg & MXT_T100_CFG_SWITCHXY) {
+ data->max_x = range_y;
+ data->max_y = range_x;
+ } else {
+ data->max_x = range_x;
+ data->max_y = range_y;
+ }
+
+ /* allocate aux bytes */
+ aux = 6;
+
+ if (tchaux & MXT_T100_TCHAUX_VECT)
+ data->t100_aux_vect = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AMPL)
+ data->t100_aux_ampl = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AREA)
+ data->t100_aux_area = aux++;
+
+ dev_info(&client->dev,
+ "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static int mxt_initialize_t100_input_device(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev;
+ int error;
+
+ error = mxt_read_t100_config(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ if (data->pdata->input_name)
+ input_dev->name = data->pdata->input_name;
+ else
+ input_dev->name = "atmel_mxt_ts T100 touchscreen";
+
+ input_dev->phys = data->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &data->client->dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, data->max_y, 0, 0);
+
+ if (data->t100_aux_ampl)
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
- mxt_handle_pdata(data);
+ /* For multi touch */
+ error = input_mt_init_slots(input_dev, data->num_touchids,
+ INPUT_MT_DIRECT);
+ if (error) {
+ dev_err(dev, "Error %d initialising slots\n", error);
+ goto err_free_mem;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+
+ if (data->t100_aux_area)
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+
+ if (data->t100_aux_ampl)
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+
+ if (data->t100_aux_vect)
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Error %d registering input device\n", error);
+ goto err_free_mem;
+ }
+
+ data->input_dev = input_dev;
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+ const struct firmware *cfg);
+
+static void mxt_config_cb(const struct firmware *cfg, void *ctx)
+{
+ mxt_configure_objects(ctx, cfg);
+ release_firmware(cfg);
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int recovery_attempts = 0;
+ int error;
+
+ while (1) {
+ error = mxt_read_info_block(data);
+ if (!error)
+ break;
- /* Backup to memory */
- mxt_write_object(data, MXT_GEN_COMMAND_T6,
- MXT_COMMAND_BACKUPNV,
- MXT_BACKUP_VALUE);
- msleep(MXT_BACKUP_TIME);
+ /* Check bootloader state */
+ error = mxt_probe_bootloader(data, false);
+ if (error) {
+ dev_info(&client->dev, "Trying alternate bootloader address\n");
+ error = mxt_probe_bootloader(data, true);
+ if (error) {
+ /* Chip is not in appmode or bootloader mode */
+ return error;
+ }
+ }
- /* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND_T6,
- MXT_COMMAND_RESET, 1);
- msleep(MXT_RESET_TIME);
+ /* OK, we are in bootloader, see if we can recover */
+ if (++recovery_attempts > 1) {
+ dev_err(&client->dev, "Could not recover from bootloader mode\n");
+ /*
+ * We can reflash from this state, so do not
+ * abort initialization.
+ */
+ data->in_bootloader = true;
+ return 0;
+ }
+
+ /* Attempt to exit bootloader into app mode */
+ mxt_send_bootloader_cmd(data, false);
+ msleep(MXT_FW_RESET_TIME);
+ }
- /* Update matrix size at info struct */
- error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
+ error = mxt_check_retrigen(data);
if (error)
goto err_free_object_table;
- info->matrix_xsize = val;
- error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
+ error = mxt_acquire_irq(data);
if (error)
goto err_free_object_table;
- info->matrix_ysize = val;
- dev_info(&client->dev,
- "Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n",
- info->family_id, info->variant_id, info->version >> 4,
- info->version & 0xf, info->build);
+ error = mxt_debug_msg_init(data);
+ if (error)
+ goto err_free_object_table;
- dev_info(&client->dev,
- "Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n",
- info->matrix_xsize, info->matrix_ysize,
- info->object_num);
+ if (data->cfg_name) {
+ error = request_firmware_nowait(THIS_MODULE, true,
+ data->cfg_name, &data->client->dev,
+ GFP_KERNEL, data, mxt_config_cb);
+ if (error) {
+ dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
+ error);
+ goto err_free_object_table;
+ }
+ } else {
+ error = mxt_configure_objects(data, NULL);
+ if (error)
+ goto err_free_object_table;
+ }
return 0;
@@ -882,18 +2529,104 @@ err_free_object_table:
return error;
}
-static void mxt_calc_resolution(struct mxt_data *data)
+static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ struct t7_config *new_config;
+ struct t7_config deepsleep = { .active = 0, .idle = 0 };
+
+ if (sleep == MXT_POWER_CFG_DEEPSLEEP)
+ new_config = &deepsleep;
+ else
+ new_config = &data->t7_cfg;
+
+ error = __mxt_write_reg(data->client, data->T7_address,
+ sizeof(data->t7_cfg), new_config);
+ if (error)
+ return error;
+
+ dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
+ new_config->active, new_config->idle);
+
+ return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ bool retry = false;
+
+recheck:
+ error = __mxt_read_reg(data->client, data->T7_address,
+ sizeof(data->t7_cfg), &data->t7_cfg);
+ if (error)
+ return error;
+
+ if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
+ if (!retry) {
+ dev_dbg(dev, "T7 cfg zero, resetting\n");
+ mxt_soft_reset(data);
+ retry = true;
+ goto recheck;
+ } else {
+ dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
+ data->t7_cfg.active = 20;
+ data->t7_cfg.idle = 100;
+ return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+ }
+ }
+
+ dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
+ data->t7_cfg.active, data->t7_cfg.idle);
+ return 0;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+ const struct firmware *cfg)
{
- unsigned int max_x = data->pdata->x_size - 1;
- unsigned int max_y = data->pdata->y_size - 1;
+ struct device *dev = &data->client->dev;
+ int error;
+
+ error = mxt_init_t7_power_cfg(data);
+ if (error) {
+ dev_err(dev, "Failed to initialize power cfg\n");
+ goto err_free_object_table;
+ }
- if (data->pdata->orient & MXT_XY_SWITCH) {
- data->max_x = max_y;
- data->max_y = max_x;
+ if (cfg) {
+ error = mxt_update_cfg(data, cfg);
+ if (error)
+ dev_warn(dev, "Error %d updating config\n", error);
+ }
+
+ if (data->T9_reportid_min) {
+ error = mxt_initialize_t9_input_device(data);
+ if (error)
+ goto err_free_object_table;
+ } else if (data->T100_reportid_min) {
+ error = mxt_initialize_t100_input_device(data);
+ if (error)
+ goto err_free_object_table;
} else {
- data->max_x = max_x;
- data->max_y = max_y;
+ dev_warn(dev, "No touch object detected\n");
}
+
+ return 0;
+
+err_free_object_table:
+ mxt_free_object_table(data);
+ return error;
+}
+
+/* Configuration crc check sum is returned as hex xxxxxx */
+static ssize_t mxt_config_csum_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%06x\n", data->config_crc);
}
/* Firmware Version is returned as Major.Minor.Build */
@@ -901,9 +2634,13 @@ static ssize_t mxt_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mxt_data *data = dev_get_drvdata(dev);
- struct mxt_info *info = &data->info;
+
+ if (!data->object_table)
+ return -EINVAL;
+
return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
- info->version >> 4, info->version & 0xf, info->build);
+ data->info->version >> 4, data->info->version & 0xf,
+ data->info->build);
}
/* Hardware Version is returned as FamilyID.VariantID */
@@ -911,9 +2648,12 @@ static ssize_t mxt_hw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mxt_data *data = dev_get_drvdata(dev);
- struct mxt_info *info = &data->info;
+
+ if (!data->object_table)
+ return -EINVAL;
+
return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
- info->family_id, info->variant_id);
+ data->info->family_id, data->info->variant_id);
}
static ssize_t mxt_show_instance(char *buf, int count,
@@ -922,11 +2662,11 @@ static ssize_t mxt_show_instance(char *buf, int count,
{
int i;
- if (object->instances > 0)
+ if (mxt_obj_instances(object) > 1)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Instance %u\n", instance);
- for (i = 0; i < object->size + 1; i++)
+ for (i = 0; i < mxt_obj_size(object); i++)
count += scnprintf(buf + count, PAGE_SIZE - count,
"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
@@ -944,13 +2684,16 @@ static ssize_t mxt_object_show(struct device *dev,
int error;
u8 *obuf;
+ if (!data->object_table)
+ return -EINVAL;
+
/* Pre-allocate buffer large enough to hold max sized object. */
obuf = kmalloc(256, GFP_KERNEL);
if (!obuf)
return -ENOMEM;
error = 0;
- for (i = 0; i < data->info.object_num; i++) {
+ for (i = 0; i < data->info->object_num; i++) {
object = data->object_table + i;
if (!mxt_object_readable(object->type))
@@ -959,8 +2702,8 @@ static ssize_t mxt_object_show(struct device *dev,
count += scnprintf(buf + count, PAGE_SIZE - count,
"T%u:\n", object->type);
- for (j = 0; j < object->instances + 1; j++) {
- u16 size = object->size + 1;
+ for (j = 0; j < mxt_obj_instances(object); j++) {
+ u16 size = mxt_obj_size(object);
u16 addr = object->start_address + j * size;
error = __mxt_read_reg(data->client, addr, size, obuf);
@@ -976,75 +2719,186 @@ done:
return error ?: count;
}
-static int mxt_load_fw(struct device *dev, const char *fn)
+static int mxt_check_firmware_format(struct device *dev,
+ const struct firmware *fw)
+{
+ unsigned int pos = 0;
+ char c;
+
+ while (pos < fw->size) {
+ c = *(fw->data + pos);
+
+ if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+ return 0;
+
+ pos++;
+ }
+
+ /*
+ * To convert file try:
+ * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw
+ */
+ dev_err(dev, "Aborting: firmware file must be in binary format\n");
+
+ return -EINVAL;
+}
+
+static int mxt_load_fw(struct device *dev)
{
struct mxt_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
const struct firmware *fw = NULL;
unsigned int frame_size;
unsigned int pos = 0;
+ unsigned int retry = 0;
+ unsigned int frame = 0;
int ret;
- ret = request_firmware(&fw, fn, dev);
+ ret = request_firmware(&fw, data->fw_name, dev);
if (ret) {
- dev_err(dev, "Unable to open firmware %s\n", fn);
+ dev_err(dev, "Unable to open firmware %s\n", data->fw_name);
return ret;
}
- /* Change to the bootloader mode */
- mxt_write_object(data, MXT_GEN_COMMAND_T6,
- MXT_COMMAND_RESET, MXT_BOOT_VALUE);
- msleep(MXT_RESET_TIME);
+ /* Check for incorrect enc file */
+ ret = mxt_check_firmware_format(dev, fw);
+ if (ret)
+ goto release_firmware;
- /* Change to slave address of bootloader */
- if (client->addr == MXT_APP_LOW)
- client->addr = MXT_BOOT_LOW;
- else
- client->addr = MXT_BOOT_HIGH;
+ if (data->suspended) {
+ if (data->use_regulator)
+ mxt_regulator_enable(data);
- ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
- if (ret)
- goto out;
+ enable_irq(data->irq);
+ data->suspended = false;
+ }
+
+ if (!data->in_bootloader) {
+ /* Change to the bootloader mode */
+ data->in_bootloader = true;
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET,
+ MXT_BOOT_VALUE, false);
+ if (ret)
+ goto release_firmware;
+
+ msleep(MXT_RESET_TIME);
+
+ /* Do not need to scan since we know family ID */
+ ret = mxt_lookup_bootloader_address(data, 0);
+ if (ret)
+ goto release_firmware;
- /* Unlock bootloader */
- mxt_unlock_bootloader(client);
+ mxt_free_input_device(data);
+ mxt_free_object_table(data);
+ } else {
+ enable_irq(data->irq);
+ }
+
+ INIT_COMPLETION(data->bl_completion);
+
+ ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
+ if (ret) {
+ /* Bootloader may still be unlocked from previous attempt */
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
+ if (ret)
+ goto disable_irq;
+ } else {
+ dev_info(dev, "Unlocking bootloader\n");
+
+ /* Unlock bootloader */
+ ret = mxt_send_bootloader_cmd(data, true);
+ if (ret)
+ goto disable_irq;
+ }
while (pos < fw->size) {
- ret = mxt_check_bootloader(client,
- MXT_WAITING_FRAME_DATA);
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
if (ret)
- goto out;
+ goto disable_irq;
frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
- /* We should add 2 at frame size as the the firmware data is not
- * included the CRC bytes.
- */
+ /* Take account of CRC bytes */
frame_size += 2;
/* Write one frame to device */
- mxt_fw_write(client, fw->data + pos, frame_size);
-
- ret = mxt_check_bootloader(client,
- MXT_FRAME_CRC_PASS);
+ ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
if (ret)
- goto out;
+ goto disable_irq;
- pos += frame_size;
+ ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
+ if (ret) {
+ retry++;
- dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+ /* Back off by 20ms per retry */
+ msleep(retry * 20);
+
+ if (retry > 20) {
+ dev_err(dev, "Retry count exceeded\n");
+ goto disable_irq;
+ }
+ } else {
+ retry = 0;
+ pos += frame_size;
+ frame++;
+ }
+
+ if (frame % 50 == 0)
+ dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n",
+ frame, pos, fw->size);
}
-out:
+ /* Wait for flash. */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_RESET_TIME);
+ if (ret)
+ goto disable_irq;
+
+ dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos);
+
+ /*
+ * Wait for device to reset. Some bootloader versions do not assert
+ * the CHG line after bootloading has finished, so ignore potential
+ * errors.
+ */
+ mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);
+
+ data->in_bootloader = false;
+
+disable_irq:
+ disable_irq(data->irq);
+release_firmware:
release_firmware(fw);
+ return ret;
+}
- /* Change to slave address of application */
- if (client->addr == MXT_BOOT_LOW)
- client->addr = MXT_APP_LOW;
+static int mxt_update_file_name(struct device *dev, char **file_name,
+ const char *buf, size_t count)
+{
+ char *file_name_tmp;
+
+ /* Simple sanity check */
+ if (count > 64) {
+ dev_warn(dev, "File name too long\n");
+ return -EINVAL;
+ }
+
+ file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL);
+ if (!file_name_tmp) {
+ dev_warn(dev, "no memory\n");
+ return -ENOMEM;
+ }
+
+ *file_name = file_name_tmp;
+ memcpy(*file_name, buf, count);
+
+ /* Echo into the sysfs entry may append newline at the end of buf */
+ if (buf[count - 1] == '\n')
+ (*file_name)[count - 1] = '\0';
else
- client->addr = MXT_APP_HIGH;
+ (*file_name)[count] = '\0';
- return ret;
+ return 0;
}
static ssize_t mxt_update_fw_store(struct device *dev,
@@ -1054,42 +2908,211 @@ static ssize_t mxt_update_fw_store(struct device *dev,
struct mxt_data *data = dev_get_drvdata(dev);
int error;
- disable_irq(data->irq);
+ error = mxt_update_file_name(dev, &data->fw_name, buf, count);
+ if (error)
+ return error;
- error = mxt_load_fw(dev, MXT_FW_NAME);
+ error = mxt_load_fw(dev);
if (error) {
dev_err(dev, "The firmware update failed(%d)\n", error);
count = error;
} else {
- dev_dbg(dev, "The firmware update succeeded\n");
+ dev_info(dev, "The firmware update succeeded\n");
- /* Wait for reset */
- msleep(MXT_FWRESET_TIME);
+ data->suspended = false;
- mxt_free_object_table(data);
+ error = mxt_initialize(data);
+ if (error)
+ return error;
+ }
- mxt_initialize(data);
+ return count;
+}
+
+static ssize_t mxt_update_cfg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ const struct firmware *cfg;
+ int ret;
+
+ if (data->in_bootloader) {
+ dev_err(dev, "Not in appmode\n");
+ return -EINVAL;
}
- enable_irq(data->irq);
+ ret = mxt_update_file_name(dev, &data->cfg_name, buf, count);
+ if (ret)
+ return ret;
- error = mxt_make_highchg(data);
- if (error)
- return error;
+ ret = request_firmware(&cfg, data->cfg_name, dev);
+ if (ret < 0) {
+ dev_err(dev, "Failure to request config file %s\n",
+ data->cfg_name);
+ ret = -ENOENT;
+ goto out;
+ }
- return count;
+ data->updating_config = true;
+
+ mxt_free_input_device(data);
+
+ if (data->suspended) {
+ if (data->use_regulator) {
+ enable_irq(data->irq);
+ mxt_regulator_enable(data);
+ } else {
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+ mxt_acquire_irq(data);
+ }
+
+ data->suspended = false;
+ }
+
+ ret = mxt_configure_objects(data, cfg);
+ if (ret)
+ goto release;
+
+ ret = count;
+
+release:
+ release_firmware(cfg);
+out:
+ data->updating_config = false;
+ return ret;
+}
+
+static ssize_t mxt_debug_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ char c;
+
+ c = data->debug_enabled ? '1' : '0';
+ return scnprintf(buf, PAGE_SIZE, "%c\n", c);
+}
+
+static ssize_t mxt_debug_notify_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t mxt_debug_v2_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ u8 i;
+ ssize_t ret;
+
+ if (kstrtou8(buf, 0, &i) == 0 && i < 2) {
+ if (i == 1)
+ mxt_debug_msg_enable(data);
+ else
+ mxt_debug_msg_disable(data);
+
+ ret = count;
+ } else {
+ dev_dbg(dev, "debug_enabled write error\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static ssize_t mxt_debug_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ u8 i;
+ ssize_t ret;
+
+ if (kstrtou8(buf, 0, &i) == 0 && i < 2) {
+ data->debug_enabled = (i == 1);
+
+ dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled");
+ ret = count;
+ } else {
+ dev_dbg(dev, "debug_enabled write error\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off,
+ size_t *count)
+{
+ if (off >= data->mem_size)
+ return -EIO;
+
+ if (off + *count > data->mem_size)
+ *count = data->mem_size - off;
+
+ if (*count > MXT_MAX_BLOCK_WRITE)
+ *count = MXT_MAX_BLOCK_WRITE;
+
+ return 0;
+}
+
+static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mxt_check_mem_access_params(data, off, &count);
+ if (ret < 0)
+ return ret;
+
+ if (count > 0)
+ ret = __mxt_read_reg(data->client, off, count, buf);
+
+ return ret == 0 ? count : ret;
+}
+
+static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mxt_check_mem_access_params(data, off, &count);
+ if (ret < 0)
+ return ret;
+
+ if (count > 0)
+ ret = __mxt_write_reg(data->client, off, count, buf);
+
+ return ret == 0 ? count : ret;
}
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store);
+static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL);
+static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show,
+ mxt_debug_enable_store);
+static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL,
+ mxt_debug_v2_enable_store);
+static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);
static struct attribute *mxt_attrs[] = {
&dev_attr_fw_version.attr,
&dev_attr_hw_version.attr,
&dev_attr_object.attr,
&dev_attr_update_fw.attr,
+ &dev_attr_update_cfg.attr,
+ &dev_attr_config_csum.attr,
+ &dev_attr_debug_enable.attr,
+ &dev_attr_debug_v2_enable.attr,
+ &dev_attr_debug_notify.attr,
NULL
};
@@ -1097,18 +3120,66 @@ static const struct attribute_group mxt_attr_group = {
.attrs = mxt_attrs,
};
+static void mxt_reset_slots(struct mxt_data *data)
+{
+ struct input_dev *input_dev = data->input_dev;
+ unsigned int num_mt_slots;
+ int id;
+
+ if (!input_dev)
+ return;
+
+ num_mt_slots = data->num_touchids + data->num_stylusids;
+
+ for (id = 0; id < num_mt_slots; id++) {
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ mxt_input_sync(data);
+}
+
static void mxt_start(struct mxt_data *data)
{
- /* Touch enable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+ if (!data->suspended || data->in_bootloader)
+ return;
+
+ if (data->use_regulator) {
+ enable_irq(data->irq);
+
+ mxt_regulator_enable(data);
+ } else {
+ /*
+ * Discard any messages still in message buffer
+ * from before chip went to sleep
+ */
+ mxt_process_messages_until_invalid(data);
+
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+ /* Recalibrate since chip has been in deep sleep */
+ mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
+
+ mxt_acquire_irq(data);
+ }
+
+ data->suspended = false;
}
static void mxt_stop(struct mxt_data *data)
{
- /* Touch disable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+ if (data->suspended || data->in_bootloader || data->updating_config)
+ return;
+
+ disable_irq(data->irq);
+
+ if (data->use_regulator)
+ mxt_regulator_disable(data);
+ else
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
+
+ mxt_reset_slots(data);
+ data->suspended = true;
}
static int mxt_input_open(struct input_dev *dev)
@@ -1127,134 +3198,155 @@ static void mxt_input_close(struct input_dev *dev)
mxt_stop(data);
}
-static int mxt_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+#ifdef CONFIG_OF
+static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
{
- const struct mxt_platform_data *pdata = client->dev.platform_data;
- struct mxt_data *data;
- struct input_dev *input_dev;
- int error;
- unsigned int num_mt_slots;
+ struct mxt_platform_data *pdata;
+ u32 *keymap;
+ int proplen, ret;
+ if (!client->dev.of_node)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
- return -EINVAL;
+ return ERR_PTR(-ENOMEM);
- data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!data || !input_dev) {
- dev_err(&client->dev, "Failed to allocate memory\n");
- error = -ENOMEM;
- goto err_free_mem;
- }
+ /* reset gpio */
+ pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node,
+ "atmel,reset-gpio", 0, NULL);
- data->is_tp = pdata && pdata->is_tp;
+ of_property_read_string(client->dev.of_node, "atmel,cfg_name",
+ &pdata->cfg_name);
- input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" :
- "Atmel maXTouch Touchscreen";
- snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
- client->adapter->nr, client->addr);
+ of_property_read_string(client->dev.of_node, "atmel,input_name",
+ &pdata->input_name);
- input_dev->phys = data->phys;
+ if (of_find_property(client->dev.of_node, "linux,gpio-keymap",
+ &proplen)) {
+ pdata->t19_num_keys = proplen / sizeof(u32);
- input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
- input_dev->open = mxt_input_open;
- input_dev->close = mxt_input_close;
+ keymap = devm_kzalloc(&client->dev,
+ pdata->t19_num_keys * sizeof(keymap[0]),
+ GFP_KERNEL);
+ if (!keymap)
+ return ERR_PTR(-ENOMEM);
- data->client = client;
- data->input_dev = input_dev;
- data->pdata = pdata;
- data->irq = client->irq;
+ ret = of_property_read_u32_array(client->dev.of_node,
+ "linux,gpio-keymap", keymap, pdata->t19_num_keys);
+ if (ret) {
+ dev_err(&client->dev,
+ "Unable to read device tree key codes: %d\n",
+ ret);
+ return NULL;
+ }
- mxt_calc_resolution(data);
+ pdata->t19_keymap = keymap;
+ }
- error = mxt_initialize(data);
- if (error)
- goto err_free_mem;
+ return pdata;
+}
+#else
+static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+{
+ struct mxt_platform_data *pdata;
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
- if (data->is_tp) {
- int i;
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+ /* Set default parameters */
+ pdata->irqflags = IRQF_TRIGGER_FALLING;
- for (i = 0; i < MXT_NUM_GPIO; i++)
- if (pdata->key_map[i] != KEY_RESERVED)
- __set_bit(pdata->key_map[i], input_dev->keybit);
+ return pdata;
+}
+#endif
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
- __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit);
+static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mxt_data *data;
+ const struct mxt_platform_data *pdata;
+ int error;
- input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_X,
- MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
- MXT_PIXELS_PER_MM);
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = mxt_parse_dt(client);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
}
- /* For single touch */
- input_set_abs_params(input_dev, ABS_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- 0, 255, 0, 0);
+ data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
- /* For multi touch */
- num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
- error = input_mt_init_slots(input_dev, num_mt_slots, 0);
- if (error)
- goto err_free_object;
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, MXT_MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_PRESSURE,
- 0, 255, 0, 0);
+ snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+ client->adapter->nr, client->addr);
- input_set_drvdata(input_dev, data);
+ data->client = client;
+ data->pdata = pdata;
+ data->irq = client->irq;
i2c_set_clientdata(client, data);
+ if (data->pdata->cfg_name)
+ mxt_update_file_name(&data->client->dev,
+ &data->cfg_name,
+ data->pdata->cfg_name,
+ strlen(data->pdata->cfg_name));
+
+ init_completion(&data->bl_completion);
+ init_completion(&data->reset_completion);
+ init_completion(&data->crc_completion);
+ mutex_init(&data->debug_msg_lock);
+
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
pdata->irqflags | IRQF_ONESHOT,
client->name, data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_free_object;
+ goto err_free_mem;
}
- error = mxt_make_highchg(data);
- if (error)
- goto err_free_irq;
+ mxt_probe_regulators(data);
- error = input_register_device(input_dev);
- if (error)
- goto err_free_irq;
+ disable_irq(data->irq);
error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+ if (error) {
+ dev_err(&client->dev, "Failure %d creating sysfs group\n",
+ error);
+ goto err_free_irq;
+ }
+
+ sysfs_bin_attr_init(&data->mem_access_attr);
+ data->mem_access_attr.attr.name = "mem_access";
+ data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR;
+ data->mem_access_attr.read = mxt_mem_access_read;
+ data->mem_access_attr.write = mxt_mem_access_write;
+ data->mem_access_attr.size = data->mem_size;
+
+ if (sysfs_create_bin_file(&client->dev.kobj,
+ &data->mem_access_attr) < 0) {
+ dev_err(&client->dev, "Failed to create %s\n",
+ data->mem_access_attr.attr.name);
+ goto err_remove_sysfs_group;
+ }
+
+ error = mxt_initialize(data);
if (error)
- goto err_unregister_device;
+ goto err_remove_mem_access;
return 0;
-err_unregister_device:
- input_unregister_device(input_dev);
- input_dev = NULL;
+err_remove_mem_access:
+ sysfs_remove_bin_file(&client->dev.kobj, &data->mem_access_attr);
+ data->mem_access_attr.attr.name = NULL;
+err_remove_sysfs_group:
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
err_free_irq:
free_irq(client->irq, data);
-err_free_object:
- kfree(data->object_table);
err_free_mem:
- input_free_device(input_dev);
kfree(data);
return error;
}
@@ -1263,10 +3355,16 @@ static int mxt_remove(struct i2c_client *client)
{
struct mxt_data *data = i2c_get_clientdata(client);
+ if (data->mem_access_attr.attr.name)
+ sysfs_remove_bin_file(&client->dev.kobj,
+ &data->mem_access_attr);
+
sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
free_irq(data->irq, data);
- input_unregister_device(data->input_dev);
- kfree(data->object_table);
+ regulator_put(data->reg_avdd);
+ regulator_put(data->reg_vdd);
+ mxt_free_input_device(data);
+ mxt_free_object_table(data);
kfree(data);
return 0;
@@ -1295,12 +3393,6 @@ static int mxt_resume(struct device *dev)
struct mxt_data *data = i2c_get_clientdata(client);
struct input_dev *input_dev = data->input_dev;
- /* Soft reset */
- mxt_write_object(data, MXT_GEN_COMMAND_T6,
- MXT_COMMAND_RESET, 1);
-
- msleep(MXT_RESET_TIME);
-
mutex_lock(&input_dev->mutex);
if (input_dev->users)
@@ -1314,6 +3406,12 @@ static int mxt_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
+static const struct of_device_id mxt_of_match[] = {
+ { .compatible = "atmel,maxtouch", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mxt_of_match);
+
static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
@@ -1327,6 +3425,7 @@ static struct i2c_driver mxt_driver = {
.driver = {
.name = "atmel_mxt_ts",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mxt_of_match),
.pm = &mxt_pm_ops,
},
.probe = mxt_probe,
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index a036a19040f..41b5f574ad0 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -1,12 +1,15 @@
/*
+ * Copyright (C) 2015 Olio Devices
* Copyright (C) 2011 ST-Ericsson SA.
* Copyright (C) 2009 Motorola, Inc.
*
* License Terms: GNU General Public License v2
*
- * Simple driver for National Semiconductor LM3530 Backlight driver chip
+ * Simple driver for National Semiconductor LM3530 Backlight driver chip,
+ * with basic suspend and resume.
*
- * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ * Author: Mattis Fjallstrom <mattis@oliodevices.com>
+ * based on leds-lm3530.c by Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
* based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
*/
@@ -486,6 +489,64 @@ static int lm3530_remove(struct i2c_client *client)
return 0;
}
+
+/**
+ * lm3530_shutdown - make sure brightness is set to 0
+ *
+ * The PMIC of the Olio H1 can't provide the power needed by the lm3530,
+ * in other words, it does it's own power management. This means we need
+ * to shut off power usage. Brightness of 0 should do this.
+ */
+
+static void lm3530_shutdown(struct i2c_client *client)
+{
+ struct lm3530_data *drvdata = i2c_get_clientdata(client);
+
+ lm3530_brightness_set(&(drvdata->led_dev), LED_OFF);
+
+ return;
+}
+
+/**
+ * lm3530_suspend - suspend backlight
+ *
+ * For now, off. Should also save the previous setting.
+ */
+
+static int lm3530_suspend(struct device *dev) {
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ printk ("OLIO %s:%s Suspending\n", __FILE__, __FUNCTION__);
+
+ lm3530_brightness_set(led_cdev, LED_OFF);
+
+ return 0;
+}
+
+/**
+ * lm3530_resume - reset backlight
+ *
+ * Turn the backlight on again (Does android take care of this for us?)
+ * For now, leave this as a no-op - Android turns on the lights.
+ */
+
+static int lm3530_resume(struct device *dev) {
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ printk ("OLIO %s:%s Resuming\n", __FILE__, __FUNCTION__);
+
+ /* lm3530_brightness_set(led_cdev, 100); */
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+static const struct dev_pm_ops lm3530_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(lm3530_suspend, lm3530_resume)
+};
+
static const struct i2c_device_id lm3530_id[] = {
{LM3530_NAME, 0},
{}
@@ -495,10 +556,12 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id);
static struct i2c_driver lm3530_i2c_driver = {
.probe = lm3530_probe,
.remove = lm3530_remove,
+ .shutdown = lm3530_shutdown,
.id_table = lm3530_id,
.driver = {
.name = LM3530_NAME,
.owner = THIS_MODULE,
+ .pm = &lm3530_pm_ops,
},
};
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index d7927720483..f4b99d84396 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -446,7 +446,7 @@ static void tps65910_power_off(void)
tps65910 = dev_get_drvdata(&tps65910_i2c_client->dev);
if (tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
- DEVCTRL_PWR_OFF_MASK) < 0)
+ DEVCTRL_DEV_OFF_MASK) < 0)
return;
tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 2d23d292943..023541d7a30 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -1502,7 +1502,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
if (res)
goto err_ecc_ioremap;
} else {
- memcpy(&host->board, pdev->dev.platform_data,
+ memcpy(&host->board, dev_get_platdata(&pdev->dev),
sizeof(struct atmel_nand_data));
}
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 217459d02b2..ae8dd7c4103 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev)
struct resource *r;
int ret, cs;
- pd = pdev->dev.platform_data;
+ pd = dev_get_platdata(&pdev->dev);
if (!pd) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENODEV;
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 776df3694f7..d7f7df3cd96 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
{
- return pdev->dev.platform_data;
+ return dev_get_platdata(&pdev->dev);
}
/*
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index c3e15a55817..758a111a3bc 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
- if (!pdev->dev.platform_data && pdev->dev.of_node) {
+ if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {
struct davinci_nand_pdata *pdata;
const char *mode;
u32 prop;
@@ -575,13 +575,13 @@ static struct davinci_nand_pdata
pdata->bbt_options = NAND_BBT_USE_FLASH;
}
- return pdev->dev.platform_data;
+ return dev_get_platdata(&pdev->dev);
}
#else
static struct davinci_nand_pdata
*nand_davinci_get_pdata(struct platform_device *pdev)
{
- return pdev->dev.platform_data;
+ return dev_get_platdata(&pdev->dev);
}
#endif
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
index 89065dd83d6..e826f898241 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/gpio.c
@@ -17,6 +17,7 @@
*/
#include <linux/kernel.h>
+#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -86,59 +87,11 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
gpio_nand_dosync(gpiomtd);
}
-static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *this = mtd->priv;
-
- iowrite8_rep(this->IO_ADDR_W, buf, len);
-}
-
-static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd->priv;
-
- ioread8_rep(this->IO_ADDR_R, buf, len);
-}
-
-static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf,
- int len)
-{
- struct nand_chip *this = mtd->priv;
-
- if (IS_ALIGNED((unsigned long)buf, 2)) {
- iowrite16_rep(this->IO_ADDR_W, buf, len>>1);
- } else {
- int i;
- unsigned short *ptr = (unsigned short *)buf;
-
- for (i = 0; i < len; i += 2, ptr++)
- writew(*ptr, this->IO_ADDR_W);
- }
-}
-
-static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd->priv;
-
- if (IS_ALIGNED((unsigned long)buf, 2)) {
- ioread16_rep(this->IO_ADDR_R, buf, len>>1);
- } else {
- int i;
- unsigned short *ptr = (unsigned short *)buf;
-
- for (i = 0; i < len; i += 2, ptr++)
- *ptr = readw(this->IO_ADDR_R);
- }
-}
-
static int gpio_nand_devready(struct mtd_info *mtd)
{
struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
- if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
- return gpio_get_value(gpiomtd->plat.gpio_rdy);
-
- return 1;
+ return gpio_get_value(gpiomtd->plat.gpio_rdy);
}
#ifdef CONFIG_OF
@@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev,
{
u32 val;
+ if (!dev->of_node)
+ return -ENODEV;
+
if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
if (val == 2) {
plat->options |= NAND_BUSWIDTH_16;
@@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev,
if (!ret)
return ret;
- if (dev->platform_data) {
- memcpy(plat, dev->platform_data, sizeof(*plat));
+ if (dev_get_platdata(dev)) {
+ memcpy(plat, dev_get_platdata(dev), sizeof(*plat));
return 0;
}
@@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev)
return platform_get_resource(pdev, IORESOURCE_MEM, 1);
}
-static int gpio_nand_remove(struct platform_device *dev)
+static int gpio_nand_remove(struct platform_device *pdev)
{
- struct gpiomtd *gpiomtd = platform_get_drvdata(dev);
- struct resource *res;
+ struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
nand_release(&gpiomtd->mtd_info);
- res = gpio_nand_get_io_sync(dev);
- iounmap(gpiomtd->io_sync);
- if (res)
- release_mem_region(res->start, resource_size(res));
-
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- iounmap(gpiomtd->nand_chip.IO_ADDR_R);
- release_mem_region(res->start, resource_size(res));
-
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
gpio_set_value(gpiomtd->plat.gpio_nce, 1);
- gpio_free(gpiomtd->plat.gpio_cle);
- gpio_free(gpiomtd->plat.gpio_ale);
- gpio_free(gpiomtd->plat.gpio_nce);
- if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
- gpio_free(gpiomtd->plat.gpio_nwp);
- if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
- gpio_free(gpiomtd->plat.gpio_rdy);
-
return 0;
}
-static void __iomem *request_and_remap(struct resource *res, size_t size,
- const char *name, int *err)
-{
- void __iomem *ptr;
-
- if (!request_mem_region(res->start, resource_size(res), name)) {
- *err = -EBUSY;
- return NULL;
- }
-
- ptr = ioremap(res->start, size);
- if (!ptr) {
- release_mem_region(res->start, resource_size(res));
- *err = -ENOMEM;
- }
- return ptr;
-}
-
-static int gpio_nand_probe(struct platform_device *dev)
+static int gpio_nand_probe(struct platform_device *pdev)
{
struct gpiomtd *gpiomtd;
- struct nand_chip *this;
- struct resource *res0, *res1;
+ struct nand_chip *chip;
+ struct resource *res;
struct mtd_part_parser_data ppdata = {};
int ret = 0;
- if (!dev->dev.of_node && !dev->dev.platform_data)
- return -EINVAL;
-
- res0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res0)
+ if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev))
return -EINVAL;
- gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL);
- if (gpiomtd == NULL) {
- dev_err(&dev->dev, "failed to create NAND MTD\n");
+ gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL);
+ if (!gpiomtd) {
+ dev_err(&pdev->dev, "failed to create NAND MTD\n");
return -ENOMEM;
}
- this = &gpiomtd->nand_chip;
- this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret);
- if (!this->IO_ADDR_R) {
- dev_err(&dev->dev, "unable to map NAND\n");
- goto err_map;
- }
+ chip = &gpiomtd->nand_chip;
- res1 = gpio_nand_get_io_sync(dev);
- if (res1) {
- gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret);
- if (!gpiomtd->io_sync) {
- dev_err(&dev->dev, "unable to map sync NAND\n");
- goto err_sync;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(chip->IO_ADDR_R))
+ return PTR_ERR(chip->IO_ADDR_R);
+
+ res = gpio_nand_get_io_sync(pdev);
+ if (res) {
+ gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpiomtd->io_sync))
+ return PTR_ERR(gpiomtd->io_sync);
}
- ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat);
+ ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat);
if (ret)
- goto err_nce;
+ return ret;
- ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE");
+ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE");
if (ret)
- goto err_nce;
+ return ret;
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
+
if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) {
- ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP");
+ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp,
+ "NAND NWP");
if (ret)
- goto err_nwp;
- gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
+ return ret;
}
- ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE");
+
+ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE");
if (ret)
- goto err_ale;
+ return ret;
gpio_direction_output(gpiomtd->plat.gpio_ale, 0);
- ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE");
+
+ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE");
if (ret)
- goto err_cle;
+ return ret;
gpio_direction_output(gpiomtd->plat.gpio_cle, 0);
+
if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) {
- ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY");
+ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy,
+ "NAND RDY");
if (ret)
- goto err_rdy;
+ return ret;
gpio_direction_input(gpiomtd->plat.gpio_rdy);
+ chip->dev_ready = gpio_nand_devready;
}
+ chip->IO_ADDR_W = chip->IO_ADDR_R;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->options = gpiomtd->plat.options;
+ chip->chip_delay = gpiomtd->plat.chip_delay;
+ chip->cmd_ctrl = gpio_nand_cmd_ctrl;
- this->IO_ADDR_W = this->IO_ADDR_R;
- this->ecc.mode = NAND_ECC_SOFT;
- this->options = gpiomtd->plat.options;
- this->chip_delay = gpiomtd->plat.chip_delay;
-
- /* install our routines */
- this->cmd_ctrl = gpio_nand_cmd_ctrl;
- this->dev_ready = gpio_nand_devready;
+ gpiomtd->mtd_info.priv = chip;
+ gpiomtd->mtd_info.owner = THIS_MODULE;
- if (this->options & NAND_BUSWIDTH_16) {
- this->read_buf = gpio_nand_readbuf16;
- this->write_buf = gpio_nand_writebuf16;
- } else {
- this->read_buf = gpio_nand_readbuf;
- this->write_buf = gpio_nand_writebuf;
- }
+ platform_set_drvdata(pdev, gpiomtd);
- /* set the mtd private data for the nand driver */
- gpiomtd->mtd_info.priv = this;
- gpiomtd->mtd_info.owner = THIS_MODULE;
+ if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
+ gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
if (nand_scan(&gpiomtd->mtd_info, 1)) {
- dev_err(&dev->dev, "no nand chips found?\n");
ret = -ENXIO;
goto err_wp;
}
@@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev)
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
gpiomtd->mtd_info.size);
- ppdata.of_node = dev->dev.of_node;
+ ppdata.of_node = pdev->dev.of_node;
ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,
gpiomtd->plat.parts,
gpiomtd->plat.num_parts);
- if (ret)
- goto err_wp;
- platform_set_drvdata(dev, gpiomtd);
-
- return 0;
+ if (!ret)
+ return 0;
err_wp:
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
- if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
- gpio_free(gpiomtd->plat.gpio_rdy);
-err_rdy:
- gpio_free(gpiomtd->plat.gpio_cle);
-err_cle:
- gpio_free(gpiomtd->plat.gpio_ale);
-err_ale:
- if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
- gpio_free(gpiomtd->plat.gpio_nwp);
-err_nwp:
- gpio_free(gpiomtd->plat.gpio_nce);
-err_nce:
- iounmap(gpiomtd->io_sync);
- if (res1)
- release_mem_region(res1->start, resource_size(res1));
-err_sync:
- iounmap(gpiomtd->nand_chip.IO_ADDR_R);
- release_mem_region(res0->start, resource_size(res0));
-err_map:
+
return ret;
}
@@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = {
.remove = gpio_nand_remove,
.driver = {
.name = "gpio-nand",
+ .owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_nand_id_table),
},
};
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index b76460eeaf2..976d5662c3b 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev)
struct jz_nand *nand;
struct nand_chip *chip;
struct mtd_info *mtd;
- struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
size_t chipnr, bank_idx;
uint8_t nand_maf_id = 0, nand_dev_id = 0;
@@ -549,7 +549,7 @@ err_free:
static int jz_nand_remove(struct platform_device *pdev)
{
struct jz_nand *nand = platform_get_drvdata(pdev);
- struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
size_t i;
nand_release(&nand->mtd);
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c
index fd1df5e13ae..a379c39dab0 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/lpc32xx_mlc.c
@@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
lpc32xx_wp_disable(host);
- host->pdata = pdev->dev.platform_data;
+ host->pdata = dev_get_platdata(&pdev->dev);
nand_chip->priv = host; /* link the private data structures */
mtd->priv = nand_chip;
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c
index be94ed5abef..81cd0719f03 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/lpc32xx_slc.c
@@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
lpc32xx_wp_disable(host);
- host->pdata = pdev->dev.platform_data;
+ host->pdata = dev_get_platdata(&pdev->dev);
mtd = &host->mtd;
chip = &host->nand_chip;
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 07e5784e5cd..302c676c7ec 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1432,7 +1432,8 @@ static int mxcnd_probe(struct platform_device *pdev)
err = mxcnd_probe_dt(host);
if (err > 0) {
- struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct mxc_nand_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
if (pdata) {
host->pdata = *pdata;
host->devtype_data = (struct mxc_nand_devtype_data *)
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 267264320e0..b5208d1c790 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -86,33 +86,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
* @td: search pattern descriptor
*
* Check for a pattern at the given place. Used to search bad block tables and
- * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if
- * all bytes except the pattern area contain 0xff.
+ * good / bad block identifiers.
*/
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
- int end = 0;
- uint8_t *p = buf;
-
if (td->options & NAND_BBT_NO_OOB)
return check_pattern_no_oob(buf, td);
- end = paglen + td->offs;
- if (td->options & NAND_BBT_SCANEMPTY)
- if (memchr_inv(p, 0xff, end))
- return -1;
- p += end;
-
/* Compare the pattern */
- if (memcmp(p, td->pattern, td->len))
+ if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
return -1;
- if (td->options & NAND_BBT_SCANEMPTY) {
- p += td->len;
- end += td->len;
- if (memchr_inv(p, 0xff, len - end))
- return -1;
- }
return 0;
}
@@ -478,15 +462,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
else
numpages = 1;
- if (!(bd->options & NAND_BBT_SCANEMPTY)) {
- /* We need only read few bytes from the OOB area */
- scanlen = 0;
- readlen = bd->len;
- } else {
- /* Full page content should be read */
- scanlen = mtd->writesize + mtd->oobsize;
- readlen = numpages * mtd->writesize;
- }
+ /* We need only read few bytes from the OOB area */
+ scanlen = 0;
+ readlen = bd->len;
if (chip == -1) {
/*
@@ -865,7 +843,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
{
struct nand_chip *this = mtd->priv;
- bd->options &= ~NAND_BBT_SCANEMPTY;
return create_bbt(mtd, this->buffers->databuf, bd, -1);
}
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 81b80af5587..64d8e32b6ca 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -25,10 +25,8 @@
#include <linux/of.h>
#include <linux/of_device.h>
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-#include <linux/bch.h>
+#include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h>
-#endif
#include <linux/platform_data/mtd-nand-omap2.h>
@@ -141,6 +139,8 @@
#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
+#define BADBLOCK_MARKER_LENGTH 2
+
#ifdef CONFIG_MTD_NAND_OMAP_BCH
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
0xac, 0x6b, 0xff, 0x99, 0x7b};
@@ -149,17 +149,6 @@ static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
/* oob info generated runtime depending on ecc algorithm and layout selected */
static struct nand_ecclayout omap_oobinfo;
-/* Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks
- */
-static uint8_t scan_ff_pattern[] = { 0xff };
-static struct nand_bbt_descr bb_descrip_flashbased = {
- .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
- .offs = 0,
- .len = 1,
- .pattern = scan_ff_pattern,
-};
-
struct omap_nand_info {
struct nand_hw_control controller;
@@ -182,14 +171,10 @@ struct omap_nand_info {
u_char *buf;
int buf_len;
struct gpmc_nand_regs reg;
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
- struct bch_control *bch;
- struct nand_ecclayout ecclayout;
+ /* fields specific for BCHx_HW ECC scheme */
bool is_elm_used;
struct device *elm_dev;
struct device_node *of_node;
-#endif
};
/**
@@ -948,7 +933,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u32 val;
val = readl(info->reg.gpmc_ecc_config);
- if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs)
+ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
return -EINVAL;
/* read ecc result */
@@ -1058,8 +1043,7 @@ static int omap_dev_ready(struct mtd_info *mtd)
}
}
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-
+#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)
/**
* omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
* @mtd: MTD device structure
@@ -1140,7 +1124,9 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
/* Clear ecc and enable bits */
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
}
+#endif
+#ifdef CONFIG_MTD_NAND_ECC_BCH
/**
* omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
* @mtd: MTD device structure
@@ -1225,7 +1211,9 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
return 0;
}
+#endif /* CONFIG_MTD_NAND_ECC_BCH */
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
/**
* omap3_calculate_ecc_bch - Generate bytes of ECC bytes
* @mtd: MTD device structure
@@ -1463,7 +1451,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
/* Check if any error reported */
if (!is_error_reported)
- return 0;
+ return stat;
/* Decode BCH error using ELM module */
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
@@ -1519,38 +1507,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
}
/**
- * omap3_correct_data_bch - Decode received data and correct errors
- * @mtd: MTD device structure
- * @data: page data
- * @read_ecc: ecc read from nand flash
- * @calc_ecc: ecc read from HW ECC registers
- */
-static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data,
- u_char *read_ecc, u_char *calc_ecc)
-{
- int i, count;
- /* cannot correct more than 8 errors */
- unsigned int errloc[8];
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
-
- count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL,
- errloc);
- if (count > 0) {
- /* correct errors */
- for (i = 0; i < count; i++) {
- /* correct data only, not ecc bytes */
- if (errloc[i] < 8*512)
- data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
- pr_debug("corrected bitflip %u\n", errloc[i]);
- }
- } else if (count < 0) {
- pr_err("ecc unrecoverable error\n");
- }
- return count;
-}
-
-/**
* omap_write_page_bch - BCH ecc based write page function for entire page
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -1637,207 +1593,58 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * omap3_free_bch - Release BCH ecc resources
- * @mtd: MTD device structure
- */
-static void omap3_free_bch(struct mtd_info *mtd)
-{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
- if (info->bch) {
- free_bch(info->bch);
- info->bch = NULL;
- }
-}
-
-/**
- * omap3_init_bch - Initialize BCH ECC
- * @mtd: MTD device structure
- * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW)
+ * is_elm_present - checks for presence of ELM module by scanning DT nodes
+ * @omap_nand_info: NAND device structure containing platform data
+ * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16
*/
-static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
+static int is_elm_present(struct omap_nand_info *info,
+ struct device_node *elm_node, enum bch_ecc bch_type)
{
- int max_errors;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
-#ifdef CONFIG_MTD_NAND_OMAP_BCH8
- const int hw_errors = BCH8_MAX_ERROR;
-#else
- const int hw_errors = BCH4_MAX_ERROR;
-#endif
- enum bch_ecc bch_type;
- const __be32 *parp;
- int lenp;
- struct device_node *elm_node;
-
- info->bch = NULL;
-
- max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ?
- BCH8_MAX_ERROR : BCH4_MAX_ERROR;
- if (max_errors != hw_errors) {
- pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported",
- max_errors, hw_errors);
- goto fail;
- }
-
- info->nand.ecc.size = 512;
- info->nand.ecc.hwctl = omap3_enable_hwecc_bch;
- info->nand.ecc.mode = NAND_ECC_HW;
- info->nand.ecc.strength = max_errors;
-
- if (hw_errors == BCH8_MAX_ERROR)
- bch_type = BCH8_ECC;
- else
- bch_type = BCH4_ECC;
-
- /* Detect availability of ELM module */
- parp = of_get_property(info->of_node, "elm_id", &lenp);
- if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) {
- pr_err("Missing elm_id property, fall back to Software BCH\n");
- info->is_elm_used = false;
- } else {
- struct platform_device *pdev;
-
- elm_node = of_find_node_by_phandle(be32_to_cpup(parp));
- pdev = of_find_device_by_node(elm_node);
- info->elm_dev = &pdev->dev;
-
- if (elm_config(info->elm_dev, bch_type) == 0)
- info->is_elm_used = true;
- }
-
- if (info->is_elm_used && (mtd->writesize <= 4096)) {
-
- if (hw_errors == BCH8_MAX_ERROR)
- info->nand.ecc.bytes = BCH8_SIZE;
- else
- info->nand.ecc.bytes = BCH4_SIZE;
-
- info->nand.ecc.correct = omap_elm_correct_data;
- info->nand.ecc.calculate = omap3_calculate_ecc_bch;
- info->nand.ecc.read_page = omap_read_page_bch;
- info->nand.ecc.write_page = omap_write_page_bch;
- } else {
- /*
- * software bch library is only used to detect and
- * locate errors
- */
- info->bch = init_bch(13, max_errors,
- 0x201b /* hw polynomial */);
- if (!info->bch)
- goto fail;
-
- info->nand.ecc.correct = omap3_correct_data_bch;
-
- /*
- * The number of corrected errors in an ecc block that will
- * trigger block scrubbing defaults to the ecc strength (4 or 8)
- * Set mtd->bitflip_threshold here to define a custom threshold.
- */
-
- if (max_errors == 8) {
- info->nand.ecc.bytes = 13;
- info->nand.ecc.calculate = omap3_calculate_ecc_bch8;
- } else {
- info->nand.ecc.bytes = 7;
- info->nand.ecc.calculate = omap3_calculate_ecc_bch4;
- }
- }
-
- pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors);
- return 0;
-fail:
- omap3_free_bch(mtd);
- return -1;
-}
-
-/**
- * omap3_init_bch_tail - Build an oob layout for BCH ECC correction.
- * @mtd: MTD device structure
- */
-static int omap3_init_bch_tail(struct mtd_info *mtd)
-{
- int i, steps, offset;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
- struct nand_ecclayout *layout = &info->ecclayout;
-
- /* build oob layout */
- steps = mtd->writesize/info->nand.ecc.size;
- layout->eccbytes = steps*info->nand.ecc.bytes;
-
- /* do not bother creating special oob layouts for small page devices */
- if (mtd->oobsize < 64) {
- pr_err("BCH ecc is not supported on small page devices\n");
- goto fail;
+ struct platform_device *pdev;
+ info->is_elm_used = false;
+ /* check whether elm-id is passed via DT */
+ if (!elm_node) {
+ pr_err("nand: error: ELM DT node not found\n");
+ return -ENODEV;
}
-
- /* reserve 2 bytes for bad block marker */
- if (layout->eccbytes+2 > mtd->oobsize) {
- pr_err("no oob layout available for oobsize %d eccbytes %u\n",
- mtd->oobsize, layout->eccbytes);
- goto fail;
+ pdev = of_find_device_by_node(elm_node);
+ /* check whether ELM device is registered */
+ if (!pdev) {
+ pr_err("nand: error: ELM device not found\n");
+ return -ENODEV;
}
-
- /* ECC layout compatible with RBL for BCH8 */
- if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE))
- offset = 2;
- else
- offset = mtd->oobsize - layout->eccbytes;
-
- /* put ecc bytes at oob tail */
- for (i = 0; i < layout->eccbytes; i++)
- layout->eccpos[i] = offset + i;
-
- if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE))
- layout->oobfree[0].offset = 2 + layout->eccbytes * steps;
- else
- layout->oobfree[0].offset = 2;
-
- layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
- info->nand.ecc.layout = layout;
-
- if (!(info->nand.options & NAND_BUSWIDTH_16))
- info->nand.badblock_pattern = &bb_descrip_flashbased;
+ /* ELM module available, now configure it */
+ info->elm_dev = &pdev->dev;
+ if (elm_config(info->elm_dev, bch_type))
+ return -ENODEV;
+ info->is_elm_used = true;
return 0;
-fail:
- omap3_free_bch(mtd);
- return -1;
-}
-
-#else
-static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
-{
- pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n");
- return -1;
-}
-static int omap3_init_bch_tail(struct mtd_info *mtd)
-{
- return -1;
-}
-static void omap3_free_bch(struct mtd_info *mtd)
-{
}
-#endif /* CONFIG_MTD_NAND_OMAP_BCH */
+#endif /* CONFIG_MTD_NAND_ECC_BCH */
static int omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ struct nand_ecclayout *ecclayout;
int err;
- int i, offset;
- dma_cap_mask_t mask;
- unsigned sig;
+ int i;
+ dma_cap_mask_t mask;
+ unsigned sig;
+ unsigned oob_index;
struct resource *res;
struct mtd_part_parser_data ppdata = {};
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
- info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
+ GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -1846,47 +1653,45 @@ static int omap_nand_probe(struct platform_device *pdev)
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
- info->pdev = pdev;
-
+ info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
-
- info->mtd.priv = &info->nand;
- info->mtd.name = dev_name(&pdev->dev);
- info->mtd.owner = THIS_MODULE;
-
- info->nand.options = pdata->devsize;
- info->nand.options |= NAND_SKIP_BBTSCAN;
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
info->of_node = pdata->of_node;
-#endif
+ mtd = &info->mtd;
+ mtd->priv = &info->nand;
+ mtd->name = dev_name(&pdev->dev);
+ mtd->owner = THIS_MODULE;
+ nand_chip = &info->nand;
+ nand_chip->ecc.priv = NULL;
+ nand_chip->options |= NAND_SKIP_BBTSCAN;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
err = -EINVAL;
dev_err(&pdev->dev, "error getting memory resource\n");
- goto out_free_info;
+ goto return_error;
}
info->phys_base = res->start;
info->mem_size = resource_size(res);
- if (!request_mem_region(info->phys_base, info->mem_size,
- pdev->dev.driver->name)) {
+ if (!devm_request_mem_region(&pdev->dev, info->phys_base,
+ info->mem_size, pdev->dev.driver->name)) {
err = -EBUSY;
- goto out_free_info;
+ goto return_error;
}
- info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size);
- if (!info->nand.IO_ADDR_R) {
+ nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base,
+ info->mem_size);
+ if (!nand_chip->IO_ADDR_R) {
err = -ENOMEM;
- goto out_release_mem_region;
+ goto return_error;
}
- info->nand.controller = &info->controller;
+ nand_chip->controller = &info->controller;
- info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
- info->nand.cmd_ctrl = omap_hwcontrol;
+ nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
+ nand_chip->cmd_ctrl = omap_hwcontrol;
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
@@ -1896,27 +1701,37 @@ static int omap_nand_probe(struct platform_device *pdev)
* device and read status register until you get a failure or success
*/
if (pdata->dev_ready) {
- info->nand.dev_ready = omap_dev_ready;
- info->nand.chip_delay = 0;
+ nand_chip->dev_ready = omap_dev_ready;
+ nand_chip->chip_delay = 0;
} else {
- info->nand.waitfunc = omap_wait;
- info->nand.chip_delay = 50;
+ nand_chip->waitfunc = omap_wait;
+ nand_chip->chip_delay = 50;
}
+ /* scan NAND device connected to chip controller */
+ nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16;
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ pr_err("nand device scan failed, may be bus-width mismatch\n");
+ err = -ENXIO;
+ goto return_error;
+ }
+
+ /* check for small page devices */
+ if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) {
+ pr_err("small page devices are not supported\n");
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ /* re-populate low-level callbacks based on xfer modes */
switch (pdata->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED:
- info->nand.read_buf = omap_read_buf_pref;
- info->nand.write_buf = omap_write_buf_pref;
+ nand_chip->read_buf = omap_read_buf_pref;
+ nand_chip->write_buf = omap_write_buf_pref;
break;
case NAND_OMAP_POLLED:
- if (info->nand.options & NAND_BUSWIDTH_16) {
- info->nand.read_buf = omap_read_buf16;
- info->nand.write_buf = omap_write_buf16;
- } else {
- info->nand.read_buf = omap_read_buf8;
- info->nand.write_buf = omap_write_buf8;
- }
+ /* Use nand_base defaults for {read,write}_buf */
break;
case NAND_OMAP_PREFETCH_DMA:
@@ -1927,7 +1742,7 @@ static int omap_nand_probe(struct platform_device *pdev)
if (!info->dma) {
dev_err(&pdev->dev, "DMA engine request failed\n");
err = -ENXIO;
- goto out_release_mem_region;
+ goto return_error;
} else {
struct dma_slave_config cfg;
@@ -1942,10 +1757,10 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
err);
- goto out_release_mem_region;
+ goto return_error;
}
- info->nand.read_buf = omap_read_buf_dma_pref;
- info->nand.write_buf = omap_write_buf_dma_pref;
+ nand_chip->read_buf = omap_read_buf_dma_pref;
+ nand_chip->write_buf = omap_write_buf_dma_pref;
}
break;
@@ -1954,34 +1769,36 @@ static int omap_nand_probe(struct platform_device *pdev)
if (info->gpmc_irq_fifo <= 0) {
dev_err(&pdev->dev, "error getting fifo irq\n");
err = -ENODEV;
- goto out_release_mem_region;
+ goto return_error;
}
- err = request_irq(info->gpmc_irq_fifo, omap_nand_irq,
- IRQF_SHARED, "gpmc-nand-fifo", info);
+ err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
+ omap_nand_irq, IRQF_SHARED,
+ "gpmc-nand-fifo", info);
if (err) {
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
info->gpmc_irq_fifo, err);
info->gpmc_irq_fifo = 0;
- goto out_release_mem_region;
+ goto return_error;
}
info->gpmc_irq_count = platform_get_irq(pdev, 1);
if (info->gpmc_irq_count <= 0) {
dev_err(&pdev->dev, "error getting count irq\n");
err = -ENODEV;
- goto out_release_mem_region;
+ goto return_error;
}
- err = request_irq(info->gpmc_irq_count, omap_nand_irq,
- IRQF_SHARED, "gpmc-nand-count", info);
+ err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
+ omap_nand_irq, IRQF_SHARED,
+ "gpmc-nand-count", info);
if (err) {
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
info->gpmc_irq_count, err);
info->gpmc_irq_count = 0;
- goto out_release_mem_region;
+ goto return_error;
}
- info->nand.read_buf = omap_read_buf_irq_pref;
- info->nand.write_buf = omap_write_buf_irq_pref;
+ nand_chip->read_buf = omap_read_buf_irq_pref;
+ nand_chip->write_buf = omap_write_buf_irq_pref;
break;
@@ -1989,118 +1806,241 @@ static int omap_nand_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"xfer_type(%d) not supported!\n", pdata->xfer_type);
err = -EINVAL;
- goto out_release_mem_region;
+ goto return_error;
}
- /* select the ecc type */
- if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
- info->nand.ecc.mode = NAND_ECC_SOFT;
- else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) ||
- (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
- info->nand.ecc.bytes = 3;
- info->nand.ecc.size = 512;
- info->nand.ecc.strength = 1;
- info->nand.ecc.calculate = omap_calculate_ecc;
- info->nand.ecc.hwctl = omap_enable_hwecc;
- info->nand.ecc.correct = omap_correct_data;
- info->nand.ecc.mode = NAND_ECC_HW;
- } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
- (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) {
- err = omap3_init_bch(&info->mtd, pdata->ecc_opt);
- if (err) {
+ /* populate MTD interface based on ECC scheme */
+ nand_chip->ecc.layout = &omap_oobinfo;
+ ecclayout = &omap_oobinfo;
+ switch (pdata->ecc_opt) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.bytes = 3;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.calculate = omap_calculate_ecc;
+ nand_chip->ecc.hwctl = omap_enable_hwecc;
+ nand_chip->ecc.correct = omap_correct_data;
+ /* define ECC layout */
+ ecclayout->eccbytes = nand_chip->ecc.bytes *
+ (mtd->writesize /
+ nand_chip->ecc.size);
+ if (nand_chip->options & NAND_BUSWIDTH_16)
+ oob_index = BADBLOCK_MARKER_LENGTH;
+ else
+ oob_index = 1;
+ for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
+ ecclayout->eccpos[i] = oob_index;
+ /* no reserved-marker in ecclayout for this ecc-scheme */
+ ecclayout->oobfree->offset =
+ ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ break;
+
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+#ifdef CONFIG_MTD_NAND_ECC_BCH
+ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.bytes = 7;
+ nand_chip->ecc.strength = 4;
+ nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.correct = nand_bch_correct_data;
+ nand_chip->ecc.calculate = omap3_calculate_ecc_bch4;
+ /* define ECC layout */
+ ecclayout->eccbytes = nand_chip->ecc.bytes *
+ (mtd->writesize /
+ nand_chip->ecc.size);
+ oob_index = BADBLOCK_MARKER_LENGTH;
+ for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
+ ecclayout->eccpos[i] = oob_index;
+ if (((i + 1) % nand_chip->ecc.bytes) == 0)
+ oob_index++;
+ }
+ /* include reserved-marker in ecclayout->oobfree calculation */
+ ecclayout->oobfree->offset = 1 +
+ ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ /* software bch library is used for locating errors */
+ nand_chip->ecc.priv = nand_bch_init(mtd,
+ nand_chip->ecc.size,
+ nand_chip->ecc.bytes,
+ &nand_chip->ecc.layout);
+ if (!nand_chip->ecc.priv) {
+ pr_err("nand: error: unable to use s/w BCH library\n");
err = -EINVAL;
- goto out_release_mem_region;
}
- }
+ break;
+#else
+ pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ err = -EINVAL;
+ goto return_error;
+#endif
- /* DIP switches on some boards change between 8 and 16 bit
- * bus widths for flash. Try the other width if the first try fails.
- */
- if (nand_scan_ident(&info->mtd, 1, NULL)) {
- info->nand.options ^= NAND_BUSWIDTH_16;
- if (nand_scan_ident(&info->mtd, 1, NULL)) {
- err = -ENXIO;
- goto out_release_mem_region;
+ case OMAP_ECC_BCH4_CODE_HW:
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
+ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ /* 14th bit is kept reserved for ROM-code compatibility */
+ nand_chip->ecc.bytes = 7 + 1;
+ nand_chip->ecc.strength = 4;
+ nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.correct = omap_elm_correct_data;
+ nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.read_page = omap_read_page_bch;
+ nand_chip->ecc.write_page = omap_write_page_bch;
+ /* define ECC layout */
+ ecclayout->eccbytes = nand_chip->ecc.bytes *
+ (mtd->writesize /
+ nand_chip->ecc.size);
+ oob_index = BADBLOCK_MARKER_LENGTH;
+ for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
+ ecclayout->eccpos[i] = oob_index;
+ /* reserved marker already included in ecclayout->eccbytes */
+ ecclayout->oobfree->offset =
+ ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ /* This ECC scheme requires ELM H/W block */
+ if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) {
+ pr_err("nand: error: could not initialize ELM\n");
+ err = -ENODEV;
+ goto return_error;
}
- }
+ break;
+#else
+ pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
+ err = -EINVAL;
+ goto return_error;
+#endif
- /* rom code layout */
- if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) {
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#ifdef CONFIG_MTD_NAND_ECC_BCH
+ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.bytes = 13;
+ nand_chip->ecc.strength = 8;
+ nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.correct = nand_bch_correct_data;
+ nand_chip->ecc.calculate = omap3_calculate_ecc_bch8;
+ /* define ECC layout */
+ ecclayout->eccbytes = nand_chip->ecc.bytes *
+ (mtd->writesize /
+ nand_chip->ecc.size);
+ oob_index = BADBLOCK_MARKER_LENGTH;
+ for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
+ ecclayout->eccpos[i] = oob_index;
+ if (((i + 1) % nand_chip->ecc.bytes) == 0)
+ oob_index++;
+ }
+ /* include reserved-marker in ecclayout->oobfree calculation */
+ ecclayout->oobfree->offset = 1 +
+ ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ /* software bch library is used for locating errors */
+ nand_chip->ecc.priv = nand_bch_init(mtd,
+ nand_chip->ecc.size,
+ nand_chip->ecc.bytes,
+ &nand_chip->ecc.layout);
+ if (!nand_chip->ecc.priv) {
+ pr_err("nand: error: unable to use s/w BCH library\n");
+ err = -EINVAL;
+ goto return_error;
+ }
+ break;
+#else
+ pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ err = -EINVAL;
+ goto return_error;
+#endif
- if (info->nand.options & NAND_BUSWIDTH_16)
- offset = 2;
- else {
- offset = 1;
- info->nand.badblock_pattern = &bb_descrip_flashbased;
+ case OMAP_ECC_BCH8_CODE_HW:
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
+ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ /* 14th bit is kept reserved for ROM-code compatibility */
+ nand_chip->ecc.bytes = 13 + 1;
+ nand_chip->ecc.strength = 8;
+ nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.correct = omap_elm_correct_data;
+ nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.read_page = omap_read_page_bch;
+ nand_chip->ecc.write_page = omap_write_page_bch;
+ /* This ECC scheme requires ELM H/W block */
+ err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC);
+ if (err < 0) {
+ pr_err("nand: error: could not initialize ELM\n");
+ goto return_error;
}
- omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16);
- for (i = 0; i < omap_oobinfo.eccbytes; i++)
- omap_oobinfo.eccpos[i] = i+offset;
+ /* define ECC layout */
+ ecclayout->eccbytes = nand_chip->ecc.bytes *
+ (mtd->writesize /
+ nand_chip->ecc.size);
+ oob_index = BADBLOCK_MARKER_LENGTH;
+ for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
+ ecclayout->eccpos[i] = oob_index;
+ /* reserved marker already included in ecclayout->eccbytes */
+ ecclayout->oobfree->offset =
+ ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ break;
+#else
+ pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
+ err = -EINVAL;
+ goto return_error;
+#endif
- omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes;
- omap_oobinfo.oobfree->length = info->mtd.oobsize -
- (offset + omap_oobinfo.eccbytes);
+ default:
+ pr_err("nand: error: invalid or unsupported ECC scheme\n");
+ err = -EINVAL;
+ goto return_error;
+ }
- info->nand.ecc.layout = &omap_oobinfo;
- } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
- (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) {
- /* build OOB layout for BCH ECC correction */
- err = omap3_init_bch_tail(&info->mtd);
- if (err) {
- err = -EINVAL;
- goto out_release_mem_region;
- }
+ /* all OOB bytes from oobfree->offset till end off OOB are free */
+ ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
+ /* check if NAND device's OOB is enough to store ECC signatures */
+ if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) {
+ pr_err("not enough OOB bytes required = %d, available=%d\n",
+ ecclayout->eccbytes, mtd->oobsize);
+ err = -EINVAL;
+ goto return_error;
}
/* second phase scan */
- if (nand_scan_tail(&info->mtd)) {
+ if (nand_scan_tail(mtd)) {
err = -ENXIO;
- goto out_release_mem_region;
+ goto return_error;
}
ppdata.of_node = pdata->of_node;
- mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts,
+ mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts,
pdata->nr_parts);
- platform_set_drvdata(pdev, &info->mtd);
+ platform_set_drvdata(pdev, mtd);
return 0;
-out_release_mem_region:
+return_error:
if (info->dma)
dma_release_channel(info->dma);
- if (info->gpmc_irq_count > 0)
- free_irq(info->gpmc_irq_count, info);
- if (info->gpmc_irq_fifo > 0)
- free_irq(info->gpmc_irq_fifo, info);
- release_mem_region(info->phys_base, info->mem_size);
-out_free_info:
- kfree(info);
-
+ if (nand_chip->ecc.priv) {
+ nand_bch_free(nand_chip->ecc.priv);
+ nand_chip->ecc.priv = NULL;
+ }
return err;
}
static int omap_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct nand_chip *nand_chip = mtd->priv;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
- omap3_free_bch(&info->mtd);
-
- platform_set_drvdata(pdev, NULL);
+ if (nand_chip->ecc.priv) {
+ nand_bch_free(nand_chip->ecc.priv);
+ nand_chip->ecc.priv = NULL;
+ }
if (info->dma)
dma_release_channel(info->dma);
-
- if (info->gpmc_irq_count > 0)
- free_irq(info->gpmc_irq_count, info);
- if (info->gpmc_irq_fifo > 0)
- free_irq(info->gpmc_irq_fifo, info);
-
- /* Release NAND device, its internal structures and partitions */
- nand_release(&info->mtd);
- iounmap(info->nand.IO_ADDR_R);
- release_mem_region(info->phys_base, info->mem_size);
- kfree(info);
+ nand_release(mtd);
return 0;
}
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 8fbd0020861..834cb3541b8 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node,
"chip-delay", &val))
board->chip_delay = (u8)val;
- } else
- board = pdev->dev.platform_data;
+ } else {
+ board = dev_get_platdata(&pdev->dev);
+ }
mtd->priv = nc;
mtd->owner = THIS_MODULE;
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index c004566a9ad..08766d1ea44 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL };
*/
static int plat_nand_probe(struct platform_device *pdev)
{
- struct platform_nand_data *pdata = pdev->dev.platform_data;
+ struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
struct mtd_part_parser_data ppdata;
struct plat_nand_data *data;
struct resource *res;
@@ -137,7 +137,7 @@ out_free:
static int plat_nand_remove(struct platform_device *pdev)
{
struct plat_nand_data *data = platform_get_drvdata(pdev);
- struct platform_nand_data *pdata = pdev->dev.platform_data;
+ struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index dec80ca6a5c..501e380211e 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -506,6 +506,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
info->buf_count = 0;
info->oob_size = 0;
info->use_ecc = 0;
+ info->use_dma = (use_dma) ? 1 : 0;
info->is_ready = 0;
info->retcode = ERR_NONE;
if (info->cs != 0)
@@ -589,12 +590,23 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
| addr_cycle;
break;
+ case NAND_CMD_PARAM:
+ cmd = NAND_CMD_PARAM;
+ info->buf_count = 256;
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | NDCB0_ADDR_CYC(1)
+ | cmd;
+ info->ndcb1 = (column & 0xFF);
+ info->data_size = 256;
+ break;
+
case NAND_CMD_READID:
cmd = host->cmdset->read_id;
info->buf_count = host->read_id_bytes;
info->ndcb0 |= NDCB0_CMD_TYPE(3)
| NDCB0_ADDR_CYC(1)
| cmd;
+ info->ndcb1 = (column & 0xFF);
info->data_size = 8;
break;
@@ -803,7 +815,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_flash *f)
{
struct platform_device *pdev = info->pdev;
- struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct pxa3xx_nand_host *host = info->host[info->cs];
uint32_t ndcr = 0x0; /* enable all interrupts */
@@ -912,6 +924,18 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
return 0;
}
+static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
+{
+ struct platform_device *pdev = info->pdev;
+ if (use_dma) {
+ pxa_free_dma(info->data_dma_ch);
+ dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
+ info->data_buff, info->data_buff_phys);
+ } else {
+ kfree(info->data_buff);
+ }
+}
+
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
{
struct mtd_info *mtd;
@@ -934,7 +958,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
struct pxa3xx_nand_host *host = mtd->priv;
struct pxa3xx_nand_info *info = host->info_data;
struct platform_device *pdev = info->pdev;
- struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
const struct pxa3xx_nand_flash *f = NULL;
struct nand_chip *chip = mtd->priv;
@@ -1034,13 +1058,11 @@ static int alloc_nand_resource(struct platform_device *pdev)
struct resource *r;
int ret, irq, cs;
- pdata = pdev->dev.platform_data;
- info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
- sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
- if (!info) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ pdata = dev_get_platdata(&pdev->dev);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) +
+ sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
+ if (!info)
return -ENOMEM;
- }
info->pdev = pdev;
for (cs = 0; cs < pdata->num_cs; cs++) {
@@ -1069,20 +1091,21 @@ static int alloc_nand_resource(struct platform_device *pdev)
spin_lock_init(&chip->controller->lock);
init_waitqueue_head(&chip->controller->wq);
- info->clk = clk_get(&pdev->dev, NULL);
+ info->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get nand clock\n");
- ret = PTR_ERR(info->clk);
- goto fail_free_mtd;
+ return PTR_ERR(info->clk);
}
- clk_enable(info->clk);
+ ret = clk_prepare_enable(info->clk);
+ if (ret < 0)
+ return ret;
/*
* This is a dirty hack to make this driver work from devicetree
* bindings. It can be removed once we have a prober DMA controller
* framework for DT.
*/
- if (pdev->dev.of_node && cpu_is_pxa3xx()) {
+ if (pdev->dev.of_node && of_machine_is_compatible("marvell,pxa3xx")) {
info->drcmr_dat = 97;
info->drcmr_cmd = 99;
} else {
@@ -1090,7 +1113,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
if (r == NULL) {
dev_err(&pdev->dev, "no resource defined for data DMA\n");
ret = -ENXIO;
- goto fail_put_clk;
+ goto fail_disable_clk;
}
info->drcmr_dat = r->start;
@@ -1098,7 +1121,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
if (r == NULL) {
dev_err(&pdev->dev, "no resource defined for command DMA\n");
ret = -ENXIO;
- goto fail_put_clk;
+ goto fail_disable_clk;
}
info->drcmr_cmd = r->start;
}
@@ -1107,34 +1130,20 @@ static int alloc_nand_resource(struct platform_device *pdev)
if (irq < 0) {
dev_err(&pdev->dev, "no IRQ resource defined\n");
ret = -ENXIO;
- goto fail_put_clk;
+ goto fail_disable_clk;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL) {
- dev_err(&pdev->dev, "no IO memory resource defined\n");
- ret = -ENODEV;
- goto fail_put_clk;
- }
-
- r = request_mem_region(r->start, resource_size(r), pdev->name);
- if (r == NULL) {
- dev_err(&pdev->dev, "failed to request memory resource\n");
- ret = -EBUSY;
- goto fail_put_clk;
- }
-
- info->mmio_base = ioremap(r->start, resource_size(r));
- if (info->mmio_base == NULL) {
- dev_err(&pdev->dev, "ioremap() failed\n");
- ret = -ENODEV;
- goto fail_free_res;
+ info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(info->mmio_base)) {
+ ret = PTR_ERR(info->mmio_base);
+ goto fail_disable_clk;
}
info->mmio_phys = r->start;
ret = pxa3xx_nand_init_buff(info);
if (ret)
- goto fail_free_io;
+ goto fail_disable_clk;
/* initialize all interrupts to be disabled */
disable_int(info, NDSR_MASK);
@@ -1152,21 +1161,9 @@ static int alloc_nand_resource(struct platform_device *pdev)
fail_free_buf:
free_irq(irq, info);
- if (use_dma) {
- pxa_free_dma(info->data_dma_ch);
- dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
- info->data_buff, info->data_buff_phys);
- } else
- kfree(info->data_buff);
-fail_free_io:
- iounmap(info->mmio_base);
-fail_free_res:
- release_mem_region(r->start, resource_size(r));
-fail_put_clk:
- clk_disable(info->clk);
- clk_put(info->clk);
-fail_free_mtd:
- kfree(info);
+ pxa3xx_nand_free_buff(info);
+fail_disable_clk:
+ clk_disable_unprepare(info->clk);
return ret;
}
@@ -1174,35 +1171,22 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
{
struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
struct pxa3xx_nand_platform_data *pdata;
- struct resource *r;
int irq, cs;
if (!info)
return 0;
- pdata = pdev->dev.platform_data;
- platform_set_drvdata(pdev, NULL);
+ pdata = dev_get_platdata(&pdev->dev);
irq = platform_get_irq(pdev, 0);
if (irq >= 0)
free_irq(irq, info);
- if (use_dma) {
- pxa_free_dma(info->data_dma_ch);
- dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
- info->data_buff, info->data_buff_phys);
- } else
- kfree(info->data_buff);
-
- iounmap(info->mmio_base);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(r->start, resource_size(r));
+ pxa3xx_nand_free_buff(info);
- clk_disable(info->clk);
- clk_put(info->clk);
+ clk_disable_unprepare(info->clk);
for (cs = 0; cs < pdata->num_cs; cs++)
nand_release(info->host[cs]->mtd);
- kfree(info);
return 0;
}
@@ -1211,7 +1195,7 @@ static struct of_device_id pxa3xx_nand_dt_ids[] = {
{ .compatible = "marvell,pxa3xx-nand" },
{}
};
-MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
+MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids);
static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
{
@@ -1255,7 +1239,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
if (ret)
return ret;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -ENODEV;
@@ -1302,7 +1286,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
struct mtd_info *mtd;
int cs;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (info->state) {
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
return -EAGAIN;
@@ -1323,7 +1307,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
struct mtd_info *mtd;
int cs;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
/* We don't want to handle interrupt without calling mtd routine */
disable_int(info, NDCR_INT_MASK);
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index d65afd23e17..b707d0f8247 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)
static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
{
- return dev->dev.platform_data;
+ return dev_get_platdata(&dev->dev);
}
static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index e57e18e8c28..a3c84ebbe39 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
dma_cap_mask_t mask;
struct dma_slave_config cfg;
struct platform_device *pdev = flctl->pdev;
- struct sh_flctl_platform_data *pdata = pdev->dev.platform_data;
+ struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret;
if (!pdata)
@@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev)
if (pdev->dev.of_node)
pdata = flctl_parse_dt(&pdev->dev);
else
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no setup data defined\n");
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 127bc427182..26b27201520 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
struct resource *r;
int err = 0;
struct sharpsl_nand *sharpsl;
- struct sharpsl_nand_platform_data *data = pdev->dev.platform_data;
+ struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev);
if (!data) {
dev_err(&pdev->dev, "no platform data!\n");
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index 508e9e04b09..396530d87ec 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
static int tmio_probe(struct platform_device *dev)
{
- struct tmio_nand_data *data = dev->dev.platform_data;
+ struct tmio_nand_data *data = dev_get_platdata(&dev->dev);
struct resource *fcr = platform_get_resource(dev,
IORESOURCE_MEM, 0);
struct resource *ccr = platform_get_resource(dev,
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
index 7ed654c68b0..388b13a6d7e 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
{
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
- struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
return drvdata->base + (reg << plat->shift);
}
@@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
struct nand_chip *chip = mtd->priv;
struct txx9ndfmc_priv *txx9_priv = chip->priv;
struct platform_device *dev = txx9_priv->dev;
- struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
if (ctrl & NAND_CTRL_CHANGE) {
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
@@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
static void txx9ndfmc_initialize(struct platform_device *dev)
{
- struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
int tmout = 100;
@@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
static int __init txx9ndfmc_probe(struct platform_device *dev)
{
- struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
int hold, spw;
int i;
struct txx9ndfmc_drvdata *drvdata;
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index 66fe3b7e785..08d0085f3e9 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc
{
struct onenand_chip *this = mtd->priv;
- bd->options &= ~NAND_BBT_SCANEMPTY;
return create_bbt(mtd, this->page_buf, bd, -1);
}
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index 26037ca7efb..7bed61e6229 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -22,7 +22,10 @@
* Datasheets:
* http://focus.ti.com/docs/prod/folders/print/bq27000.html
* http://focus.ti.com/docs/prod/folders/print/bq27500.html
+ * http://www.ti.com/product/bq27411-g1
+ * http://www.ti.com/product/bq27421-g1
* http://www.ti.com/product/bq27425-g1
+ * http://www.ti.com/product/bq27441-g1
*/
#include <linux/module.h>
@@ -41,48 +44,215 @@
#define DRIVER_VERSION "1.2.0"
-#define BQ27x00_REG_TEMP 0x06
-#define BQ27x00_REG_VOLT 0x08
-#define BQ27x00_REG_AI 0x14
-#define BQ27x00_REG_FLAGS 0x0A
-#define BQ27x00_REG_TTE 0x16
-#define BQ27x00_REG_TTF 0x18
-#define BQ27x00_REG_TTECP 0x26
-#define BQ27x00_REG_NAC 0x0C /* Nominal available capacity */
-#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */
-#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */
-#define BQ27x00_REG_AE 0x22 /* Available energy */
-#define BQ27x00_POWER_AVG 0x24
+#define INVALID_REG_ADDR 0xFF
-#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */
-#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */
-#define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */
-#define BQ27000_FLAG_FC BIT(5)
-#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
+enum bq27xxx_reg_index {
+ BQ27XXX_REG_CTRL = 0,
+ BQ27XXX_REG_TEMP,
+ BQ27XXX_REG_INT_TEMP,
+ BQ27XXX_REG_VOLT,
+ BQ27XXX_REG_AI,
+ BQ27XXX_REG_FLAGS,
+ BQ27XXX_REG_TTE,
+ BQ27XXX_REG_TTF,
+ BQ27XXX_REG_TTES,
+ BQ27XXX_REG_TTECP,
+ BQ27XXX_REG_NAC,
+ BQ27XXX_REG_FCC,
+ BQ27XXX_REG_CYCT,
+ BQ27XXX_REG_AE,
+ BQ27XXX_REG_SOC,
+ BQ27XXX_REG_DCAP,
+ BQ27XXX_POWER_AVG,
+ NUM_REGS
+};
+
+/* bq27500 registers */
+static __initdata u8 bq27500_regs[NUM_REGS] = {
+ 0x00, /* CONTROL */
+ 0x06, /* TEMP */
+ 0xFF, /* INT TEMP -NA */
+ 0x08, /* VOLT */
+ 0x14, /* AVG CURR */
+ 0x0A, /* FLAGS */
+ 0x16, /* TTE */
+ 0x18, /* TTF */
+ 0x1c, /* TTES */
+ 0x26, /* TTECP */
+ 0x0C, /* NAC */
+ 0x12, /* LMD(FCC) */
+ 0x2A, /* CYCT */
+ 0x22, /* AE */
+ 0x2C, /* SOC(RSOC) */
+ 0x3C, /* DCAP(ILMD) */
+ 0x24, /* AP */
+};
+
+/* bq27520 registers */
+static __initdata u8 bq27520_regs[] = {
+ 0x00, /* CONTROL */
+ 0x06, /* TEMP */
+ 0xFF, /* INT TEMP - NA*/
+ 0x08, /* VOLT */
+ 0x14, /* AVG CURR */
+ 0x0A, /* FLAGS */
+ 0x16, /* TTE */
+ 0x18, /* TTF */
+ 0x1c, /* TTES */
+ 0x26, /* TTECP */
+ 0x0C, /* NAC */
+ 0x12, /* LMD */
+ 0xFF, /* CYCT - NA */
+ 0x22, /* AE */
+ 0x2C, /* SOC(RSOC */
+ 0xFF, /* DCAP(ILMD) - NA */
+ 0x24, /* AP */
+};
+
+/* bq2753x registers */
+static __initdata u8 bq2753x_regs[] = {
+ 0x00, /* CONTROL */
+ 0x06, /* TEMP */
+ 0xFF, /* INT TEMP - NA*/
+ 0x08, /* VOLT */
+ 0x14, /* AVG CURR */
+ 0x0A, /* FLAGS */
+ 0x16, /* TTE */
+ 0xFF, /* TTF - NA */
+ 0xFF, /* TTES - NA */
+ 0xFF, /* TTECP - NA */
+ 0x0C, /* NAC */
+ 0x12, /* LMD(FCC) */
+ 0x2A, /* CYCT */
+ 0xFF, /* AE - NA */
+ 0x2C, /* SOC(RSOC) */
+ 0xFF, /* DCAP(ILMD) - NA */
+ 0x24, /* AP */
+};
+
+/* bq27200 registers */
+static __initdata u8 bq27200_regs[NUM_REGS] = {
+ 0x00, /* CONTROL */
+ 0x06, /* TEMP */
+ 0xFF, /* INT TEMP - NA */
+ 0x08, /* VOLT */
+ 0x14, /* AVG CURR */
+ 0x0A, /* FLAGS */
+ 0x16, /* TTE */
+ 0x18, /* TTF */
+ 0x1c, /* TTES */
+ 0x26, /* TTECP */
+ 0x0C, /* NAC */
+ 0x12, /* LMD(FCC) */
+ 0x2A, /* CYCT */
+ 0x22, /* AE */
+ 0x0B, /* SOC(RSOC) */
+ 0x76, /* DCAP(ILMD) */
+ 0x24, /* AP */
+};
+
+/* bq274xx registers */
+static __initdata u8 bq274xx_regs[NUM_REGS] = {
+ 0x00, /* CONTROL */
+ 0x02, /* TEMP */
+ 0x1e, /* INT TEMP */
+ 0x04, /* VOLT */
+ 0x10, /* AVG CURR */
+ 0x06, /* FLAGS */
+ 0xFF, /* TTE - NA */
+ 0xFF, /* TTF - NA */
+ 0xFF, /* TTES - NA */
+ 0xFF, /* TTECP - NA */
+ 0x08, /* NAC */
+ 0x0E, /* FCC */
+ 0xFF, /* CYCT - NA */
+ 0xFF, /* AE - NA */
+ 0x1C, /* SOC */
+ 0x3C, /* DCAP - NA */
+ 0x18, /* AP */
+};
+
+/* bq276xx registers - same as bq274xx except CYCT */
+static __initdata u8 bq276xx_regs[NUM_REGS] = {
+ 0x00, /* CONTROL */
+ 0x02, /* TEMP */
+ 0x1e, /* INT TEMP */
+ 0x04, /* VOLT */
+ 0x10, /* AVG CURR */
+ 0x06, /* FLAGS */
+ 0xFF, /* TTE - NA */
+ 0xFF, /* TTF - NA */
+ 0xFF, /* TTES - NA */
+ 0xFF, /* TTECP - NA */
+ 0x08, /* NAC */
+ 0x0E, /* FCC */
+ 0x22, /* CYCT */
+ 0xFF, /* AE - NA */
+ 0x1C, /* SOC */
+ 0x3C, /* DCAP - NA */
+ 0x18, /* AP */
+};
+
+/*
+ * SBS Commands for DF access - these are pretty standard
+ * So, no need to go in the command array
+ */
+#define BLOCK_DATA_CLASS 0x3E
+#define DATA_BLOCK 0x3F
+#define BLOCK_DATA 0x40
+#define BLOCK_DATA_CHECKSUM 0x60
+#define BLOCK_DATA_CONTROL 0x61
+
+/* bq274xx/bq276xx specific command information */
+#define BQ274XX_UNSEAL_KEY 0x80008000
+#define BQ274XX_SOFT_RESET 0x43
+
+#define BQ274XX_FLAG_ITPOR 0x20
+#define BQ274XX_CTRL_STATUS_INITCOMP 0x80
-#define BQ27500_REG_SOC 0x2C
-#define BQ27500_REG_DCAP 0x3C /* Design capacity */
-#define BQ27500_FLAG_DSC BIT(0)
-#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */
-#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */
-#define BQ27500_FLAG_FC BIT(9)
-#define BQ27500_FLAG_OTC BIT(15)
+#define BQ27XXX_FLAG_DSC BIT(0)
+#define BQ27XXX_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */
+#define BQ27XXX_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_FC BIT(9)
+#define BQ27XXX_FLAG_OTD BIT(14)
+#define BQ27XXX_FLAG_OTC BIT(15)
-/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
-#define BQ27425_REG_OFFSET 0x04
-#define BQ27425_REG_SOC 0x18 /* Register address plus offset */
+/* BQ27000 has different layout for Flags register */
+#define BQ27200_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */
+#define BQ27200_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */
+#define BQ27200_FLAG_CI BIT(4) /* Capacity Inaccurate flag */
+#define BQ27200_FLAG_FC BIT(5)
+#define BQ27200_FLAG_CHGS BIT(7) /* Charge state flag */
-#define BQ27000_RS 20 /* Resistor sense */
-#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000)
+#define BQ27200_RS 20 /* Resistor sense */
+#define BQ27200_POWER_CONSTANT (256 * 29200 / 1000)
+
+/* Subcommands of Control() */
+#define CONTROL_STATUS_SUBCMD 0x0000
+#define DEV_TYPE_SUBCMD 0x0001
+#define FW_VER_SUBCMD 0x0002
+#define DF_VER_SUBCMD 0x001F
+#define RESET_SUBCMD 0x0041
+#define SET_CFGUPDATE_SUBCMD 0x0013
+#define SEAL_SUBCMD 0x0020
+
+/* Location of SEAL enable bit in bq276xx DM */
+#define BQ276XX_OP_CFG_B_SUBCLASS 64
+#define BQ276XX_OP_CFG_B_OFFSET 2
+#define BQ276XX_OP_CFG_B_DEF_SEAL_BIT (1 << 5)
struct bq27x00_device_info;
struct bq27x00_access_methods {
int (*read)(struct bq27x00_device_info *di, u8 reg, bool single);
+ int (*write)(struct bq27x00_device_info *di, u8 reg, int value,
+ bool single);
+ int (*blk_read)(struct bq27x00_device_info *di, u8 reg, u8 *data,
+ u8 sz);
+ int (*blk_write)(struct bq27x00_device_info *di, u8 reg, u8 *data,
+ u8 sz);
};
-enum bq27x00_chip { BQ27000, BQ27500, BQ27425};
+enum bq27x00_chip { BQ27200, BQ27500, BQ27520, BQ274XX, BQ276XX, BQ2753X};
struct bq27x00_reg_cache {
int temperature;
@@ -98,6 +268,13 @@ struct bq27x00_reg_cache {
int health;
};
+struct dm_reg {
+ u8 subclass;
+ u8 offset;
+ u8 len;
+ u32 data;
+};
+
struct bq27x00_device_info {
struct device *dev;
int id;
@@ -114,9 +291,15 @@ struct bq27x00_device_info {
struct bq27x00_access_methods bus;
struct mutex lock;
+
+ int fw_ver;
+ int df_ver;
+ u8 regs[NUM_REGS];
+ struct dm_reg *dm_regs;
+ u16 dm_regs_count;
};
-static enum power_supply_property bq27x00_battery_props[] = {
+static __initdata enum power_supply_property bq27x00_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -137,7 +320,57 @@ static enum power_supply_property bq27x00_battery_props[] = {
POWER_SUPPLY_PROP_HEALTH,
};
-static enum power_supply_property bq27425_battery_props[] = {
+static __initdata enum power_supply_property bq27520_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+static __initdata enum power_supply_property bq2753x_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+};
+
+static __initdata enum power_supply_property bq274xx_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+};
+
+static __initdata enum power_supply_property bq276xx_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -149,6 +382,31 @@ static enum power_supply_property bq27425_battery_props[] = {
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+};
+
+/*
+ * Ordering the parameters based on subclass and then offset will help in
+ * having fewer flash writes while updating.
+ * Customize these values and, if necessary, add more based on system needs.
+ */
+static struct dm_reg bq274xx_dm_regs[] = {
+ {82, 0, 2, 16384}, /* Qmax */
+ {82, 5, 1, 0x81}, /* Load Select */
+ {82, 10, 2, 300}, /* Design Capacity */
+ {82, 12, 2, 1140}, /* Design Energy */
+ {82, 16, 2, 3090}, /* Terminate Voltage */
+ {82, 27, 2, 40}, /* Taper rate */
+};
+
+static struct dm_reg bq276xx_dm_regs[] = {
+ {64, 2, 1, 0x2C}, /* Op Config B */
+ {82, 0, 2, 1000}, /* Qmax */
+ {82, 2, 1, 0x81}, /* Load Select */
+ {82, 3, 2, 1340}, /* Design Capacity */
+ {82, 5, 2, 3700}, /* Design Energy */
+ {82, 9, 2, 3250}, /* Terminate Voltage */
+ {82, 20, 2, 110}, /* Taper rate */
};
static unsigned int poll_interval = 360;
@@ -157,48 +415,319 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \
"0 disables polling");
/*
+ * Forward Declarations
+ */
+static int read_dm_block(struct bq27x00_device_info *di, u8 subclass,
+ u8 offset, u8 *data);
+
+
+/*
* Common code for BQ27x00 devices
*/
-static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
+static inline int bq27xxx_read(struct bq27x00_device_info *di, int reg_index,
bool single)
{
- if (di->chip == BQ27425)
- return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
- return di->bus.read(di, reg, single);
+ int val;
+
+ /* Reports 0 for invalid/missing registers */
+ if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
+ return 0;
+
+ val = di->bus.read(di, di->regs[reg_index], single);
+
+ return val;
}
+static inline int bq27xxx_write(struct bq27x00_device_info *di, int reg_index,
+ int value, bool single)
+{
+ if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
+ return -1;
+
+ return di->bus.write(di, di->regs[reg_index], value, single);
+}
+
+static int control_cmd_wr(struct bq27x00_device_info *di, u16 cmd)
+{
+ dev_dbg(di->dev, "%s: cmd - %04x\n", __func__, cmd);
+
+ return di->bus.write(di, BQ27XXX_REG_CTRL, cmd, false);
+}
+
+static int control_cmd_read(struct bq27x00_device_info *di, u16 cmd)
+{
+ dev_dbg(di->dev, "%s: cmd - %04x\n", __func__, cmd);
+
+ di->bus.write(di, BQ27XXX_REG_CTRL, cmd, false);
+
+ msleep(5);
+
+ return di->bus.read(di, BQ27XXX_REG_CTRL, false);
+}
/*
- * Higher versions of the chip like BQ27425 and BQ27500
- * differ from BQ27000 and BQ27200 in calculation of certain
- * parameters. Hence we need to check for the chip type.
+ * It is assumed that the gauge is in unsealed mode when this function
+ * is called
*/
-static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di)
+static int bq276xx_seal_enabled(struct bq27x00_device_info *di)
{
- if (di->chip == BQ27425 || di->chip == BQ27500)
- return true;
- return false;
+ u8 buf[32];
+ u8 op_cfg_b;
+
+ if (!read_dm_block(di, BQ276XX_OP_CFG_B_SUBCLASS,
+ BQ276XX_OP_CFG_B_OFFSET, buf)) {
+ return 1; /* Err on the side of caution and try to seal */
+ }
+
+ op_cfg_b = buf[BQ276XX_OP_CFG_B_OFFSET & 0x1F];
+
+ if (op_cfg_b & BQ276XX_OP_CFG_B_DEF_SEAL_BIT)
+ return 1;
+
+ return 0;
+}
+
+#define SEAL_UNSEAL_POLLING_RETRY_LIMIT 1000
+
+static inline int sealed(struct bq27x00_device_info *di)
+{
+ return control_cmd_read(di, CONTROL_STATUS_SUBCMD) & (1 << 13);
+}
+
+static int unseal(struct bq27x00_device_info *di, u32 key)
+{
+ int i = 0;
+
+ dev_dbg(di->dev, "%s: key - %08x\n", __func__, key);
+
+ if (!sealed(di))
+ goto out;
+
+ di->bus.write(di, BQ27XXX_REG_CTRL, key & 0xFFFF, false);
+ msleep(5);
+ di->bus.write(di, BQ27XXX_REG_CTRL, (key & 0xFFFF0000) >> 16, false);
+ msleep(5);
+
+ while (i < SEAL_UNSEAL_POLLING_RETRY_LIMIT) {
+ i++;
+ if (!sealed(di))
+ break;
+ msleep(10);
+ }
+
+out:
+ if (i == SEAL_UNSEAL_POLLING_RETRY_LIMIT) {
+ dev_err(di->dev, "%s: failed\n", __func__);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int seal(struct bq27x00_device_info *di)
+{
+ int i = 0;
+ int is_sealed;
+
+ dev_dbg(di->dev, "%s:\n", __func__);
+
+ is_sealed = sealed(di);
+ if (is_sealed)
+ return is_sealed;
+
+ if (di->chip == BQ276XX && !bq276xx_seal_enabled(di)) {
+ dev_dbg(di->dev, "%s: sealing is not enabled\n", __func__);
+ return is_sealed;
+ }
+
+ di->bus.write(di, BQ27XXX_REG_CTRL, SEAL_SUBCMD, false);
+
+ while (i < SEAL_UNSEAL_POLLING_RETRY_LIMIT) {
+ i++;
+ is_sealed = sealed(di);
+ if (is_sealed)
+ break;
+ msleep(10);
+ }
+
+ if (!is_sealed)
+ dev_err(di->dev, "%s: failed\n", __func__);
+
+ return is_sealed;
+}
+
+#define CFG_UPDATE_POLLING_RETRY_LIMIT 50
+static int enter_cfg_update_mode(struct bq27x00_device_info *di)
+{
+ int i = 0;
+ u16 flags;
+
+ dev_dbg(di->dev, "%s:\n", __func__);
+
+ if (!unseal(di, BQ274XX_UNSEAL_KEY))
+ return 0;
+
+ control_cmd_wr(di, SET_CFGUPDATE_SUBCMD);
+ msleep(5);
+
+ while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {
+ i++;
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+ if (flags & (1 << 4))
+ break;
+ msleep(100);
+ }
+
+ if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {
+ dev_err(di->dev, "%s: failed %04x\n", __func__, flags);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int exit_cfg_update_mode(struct bq27x00_device_info *di)
+{
+ int i = 0;
+ u16 flags;
+
+ dev_dbg(di->dev, "%s:\n", __func__);
+
+ control_cmd_wr(di, BQ274XX_SOFT_RESET);
+
+ while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {
+ i++;
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+ if (!(flags & (1 << 4)))
+ break;
+ msleep(100);
+ }
+
+ if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {
+ dev_err(di->dev, "%s: failed %04x\n", __func__, flags);
+ return 0;
+ }
+
+ if (seal(di))
+ return 1;
+ else
+ return 0;
+}
+static u8 checksum(u8 *data)
+{
+ u16 sum = 0;
+ int i;
+
+ for (i = 0; i < 32; i++)
+ sum += data[i];
+
+ sum &= 0xFF;
+
+ return 0xFF - sum;
+}
+
+#ifdef DEBUG
+static void print_buf(const char *msg, u8 *buf)
+{
+ int i;
+
+ printk("\nbq: %s buf: ", msg);
+ for (i = 0; i < 32; i++)
+ printk("%02x ", buf[i]);
+
+ printk("\n");
+}
+#else
+#define print_buf(a, b)
+#endif
+
+static int update_dm_block(struct bq27x00_device_info *di, u8 subclass,
+ u8 offset, u8 *data)
+{
+ u8 buf[32];
+ u8 cksum;
+ u8 blk_offset = offset >> 5;
+
+ dev_dbg(di->dev, "%s: subclass %d offset %d\n",
+ __func__, subclass, offset);
+
+ di->bus.write(di, BLOCK_DATA_CONTROL, 0, true);
+ msleep(5);
+
+ di->bus.write(di, BLOCK_DATA_CLASS, subclass, true);
+ msleep(5);
+
+ di->bus.write(di, DATA_BLOCK, blk_offset, true);
+ msleep(5);
+
+ di->bus.blk_write(di, BLOCK_DATA, data, 32);
+ msleep(5);
+ print_buf(__func__, data);
+
+ cksum = checksum(data);
+ di->bus.write(di, BLOCK_DATA_CHECKSUM, cksum, true);
+ msleep(5);
+
+ /* Read back and compare to make sure write is successful */
+ di->bus.write(di, DATA_BLOCK, blk_offset, true);
+ msleep(5);
+ di->bus.blk_read(di, BLOCK_DATA, buf, 32);
+ if (memcmp(data, buf, 32)) {
+ dev_err(di->dev, "%s: error updating subclass %d offset %d\n",
+ __func__, subclass, offset);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int read_dm_block(struct bq27x00_device_info *di, u8 subclass,
+ u8 offset, u8 *data)
+{
+ u8 cksum_calc, cksum;
+ u8 blk_offset = offset >> 5;
+
+ dev_dbg(di->dev, "%s: subclass %d offset %d\n",
+ __func__, subclass, offset);
+
+ di->bus.write(di, BLOCK_DATA_CONTROL, 0, true);
+ msleep(5);
+
+ di->bus.write(di, BLOCK_DATA_CLASS, subclass, true);
+ msleep(5);
+
+ di->bus.write(di, DATA_BLOCK, blk_offset, true);
+ msleep(5);
+
+ di->bus.blk_read(di, BLOCK_DATA, data, 32);
+
+ cksum_calc = checksum(data);
+ cksum = di->bus.read(di, BLOCK_DATA_CHECKSUM, true);
+ if (cksum != cksum_calc) {
+ dev_err(di->dev, "%s: error reading subclass %d offset %d\n",
+ __func__, subclass, offset);
+ return 0;
+ }
+
+ print_buf(__func__, data);
+
+ return 1;
}
/*
- * Return the battery Relative State-of-Charge
+ * Return the battery State-of-Charge
* Or < 0 if something fails.
*/
-static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
+static int bq27x00_battery_read_soc(struct bq27x00_device_info *di)
{
- int rsoc;
+ int soc;
- if (di->chip == BQ27500)
- rsoc = bq27x00_read(di, BQ27500_REG_SOC, false);
- else if (di->chip == BQ27425)
- rsoc = bq27x00_read(di, BQ27425_REG_SOC, false);
- else
- rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
+ soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
- if (rsoc < 0)
+ if (soc < 0)
dev_dbg(di->dev, "error reading relative State-of-Charge\n");
- return rsoc;
+ return soc;
}
/*
@@ -209,17 +738,17 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
{
int charge;
- charge = bq27x00_read(di, reg, false);
+ charge = bq27xxx_read(di, reg, false);
if (charge < 0) {
dev_dbg(di->dev, "error reading charge register %02x: %d\n",
reg, charge);
return charge;
}
- if (bq27xxx_is_chip_version_higher(di))
- charge *= 1000;
+ if (di->chip == BQ27200)
+ charge = charge * 3570 / BQ27200_RS;
else
- charge = charge * 3570 / BQ27000_RS;
+ charge *= 1000;
return charge;
}
@@ -231,49 +760,46 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
{
int flags;
- bool is_bq27500 = di->chip == BQ27500;
- bool is_higher = bq27xxx_is_chip_version_higher(di);
- flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
- if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
- return -ENODATA;
+ if (di->chip == BQ27200) {
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
+ if (flags >= 0 && (flags & BQ27200_FLAG_CI))
+ return -ENODATA;
+ }
- return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC);
+ return bq27x00_battery_read_charge(di, BQ27XXX_REG_NAC);
}
/*
* Return the battery Last measured discharge in µAh
* Or < 0 if something fails.
*/
-static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di)
+static inline int bq27x00_battery_read_fcc(struct bq27x00_device_info *di)
{
- return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD);
+ return bq27x00_battery_read_charge(di, BQ27XXX_REG_FCC);
}
/*
- * Return the battery Initial last measured discharge in µAh
+ * Return the Design Capacity in µAh
* Or < 0 if something fails.
*/
-static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
+static int bq27x00_battery_read_dcap(struct bq27x00_device_info *di)
{
- int ilmd;
+ int dcap;
- if (bq27xxx_is_chip_version_higher(di))
- ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
- else
- ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
+ dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
- if (ilmd < 0) {
+ if (dcap < 0) {
dev_dbg(di->dev, "error reading initial last measured discharge\n");
- return ilmd;
+ return dcap;
}
- if (bq27xxx_is_chip_version_higher(di))
- ilmd *= 1000;
+ if (di->chip == BQ27200)
+ dcap = dcap * 256 * 3570 / BQ27200_RS;
else
- ilmd = ilmd * 256 * 3570 / BQ27000_RS;
+ dcap *= 1000;
- return ilmd;
+ return dcap;
}
/*
@@ -284,16 +810,16 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
{
int ae;
- ae = bq27x00_read(di, BQ27x00_REG_AE, false);
+ ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);
if (ae < 0) {
dev_dbg(di->dev, "error reading available energy\n");
return ae;
}
- if (di->chip == BQ27500)
- ae *= 1000;
+ if (di->chip == BQ27200)
+ ae = ae * 29200 / BQ27200_RS;
else
- ae = ae * 29200 / BQ27000_RS;
+ ae *= 1000;
return ae;
}
@@ -306,13 +832,13 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
{
int temp;
- temp = bq27x00_read(di, BQ27x00_REG_TEMP, false);
+ temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);
if (temp < 0) {
dev_err(di->dev, "error reading temperature\n");
return temp;
}
- if (!bq27xxx_is_chip_version_higher(di))
+ if (di->chip == BQ27200)
temp = 5 * temp / 2;
return temp;
@@ -326,7 +852,7 @@ static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di)
{
int cyct;
- cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false);
+ cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);
if (cyct < 0)
dev_err(di->dev, "error reading cycle count total\n");
@@ -341,7 +867,7 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
{
int tval;
- tval = bq27x00_read(di, reg, false);
+ tval = bq27xxx_read(di, reg, false);
if (tval < 0) {
dev_dbg(di->dev, "error reading time register %02x: %d\n",
reg, tval);
@@ -362,17 +888,25 @@ static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)
{
int tval;
- tval = bq27x00_read(di, reg, false);
+ tval = bq27xxx_read(di, reg, false);
if (tval < 0) {
dev_err(di->dev, "error reading power avg rgister %02x: %d\n",
reg, tval);
return tval;
}
- if (di->chip == BQ27500)
+ if (di->chip == BQ27200)
+ return (tval * BQ27200_POWER_CONSTANT) / BQ27200_RS;
+ else
return tval;
+}
+
+static int overtemperature(struct bq27x00_device_info *di, u16 flags)
+{
+ if (di->chip == BQ27520)
+ return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
else
- return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS;
+ return flags & BQ27XXX_FLAG_OTC;
}
/*
@@ -381,25 +915,25 @@ static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)
*/
static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
{
- int tval;
+ u16 tval;
- tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
+ tval = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
if (tval < 0) {
dev_err(di->dev, "error reading flag register:%d\n", tval);
return tval;
}
- if ((di->chip == BQ27500)) {
- if (tval & BQ27500_FLAG_SOCF)
+ if ((di->chip == BQ27200)) {
+ if (tval & BQ27200_FLAG_EDV1)
tval = POWER_SUPPLY_HEALTH_DEAD;
- else if (tval & BQ27500_FLAG_OTC)
- tval = POWER_SUPPLY_HEALTH_OVERHEAT;
else
tval = POWER_SUPPLY_HEALTH_GOOD;
return tval;
} else {
- if (tval & BQ27000_FLAG_EDV1)
+ if (tval & BQ27XXX_FLAG_SOCF)
tval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (overtemperature(di, tval))
+ tval = POWER_SUPPLY_HEALTH_OVERHEAT;
else
tval = POWER_SUPPLY_HEALTH_GOOD;
return tval;
@@ -411,13 +945,14 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
static void bq27x00_update(struct bq27x00_device_info *di)
{
struct bq27x00_reg_cache cache = {0, };
+ bool is_bq27200 = di->chip == BQ27200;
bool is_bq27500 = di->chip == BQ27500;
- bool is_bq27425 = di->chip == BQ27425;
+ bool is_bq274xx = di->chip == BQ274XX;
+ bool is_bq276xx = di->chip == BQ276XX;
- cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
+ cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500);
if (cache.flags >= 0) {
- if (!is_bq27500 && !is_bq27425
- && (cache.flags & BQ27000_FLAG_CI)) {
+ if (is_bq27200 && (cache.flags & BQ27200_FLAG_CI)) {
dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
cache.capacity = -ENODATA;
cache.energy = -ENODATA;
@@ -427,31 +962,31 @@ static void bq27x00_update(struct bq27x00_device_info *di)
cache.charge_full = -ENODATA;
cache.health = -ENODATA;
} else {
- cache.capacity = bq27x00_battery_read_rsoc(di);
- if (!is_bq27425) {
+ cache.capacity = bq27x00_battery_read_soc(di);
+ if (!(is_bq274xx || is_bq276xx)) {
cache.energy = bq27x00_battery_read_energy(di);
cache.time_to_empty =
bq27x00_battery_read_time(di,
- BQ27x00_REG_TTE);
+ BQ27XXX_REG_TTE);
cache.time_to_empty_avg =
bq27x00_battery_read_time(di,
- BQ27x00_REG_TTECP);
+ BQ27XXX_REG_TTECP);
cache.time_to_full =
bq27x00_battery_read_time(di,
- BQ27x00_REG_TTF);
+ BQ27XXX_REG_TTF);
}
- cache.charge_full = bq27x00_battery_read_lmd(di);
+ cache.charge_full = bq27x00_battery_read_fcc(di);
cache.health = bq27x00_battery_read_health(di);
}
cache.temperature = bq27x00_battery_read_temperature(di);
- if (!is_bq27425)
+ if (!is_bq274xx)
cache.cycle_count = bq27x00_battery_read_cyct(di);
cache.power_avg =
- bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
+ bq27x00_battery_read_pwr_avg(di, BQ27XXX_POWER_AVG);
/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
- di->charge_design_full = bq27x00_battery_read_ilmd(di);
+ di->charge_design_full = bq27x00_battery_read_dcap(di);
}
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {
@@ -462,11 +997,131 @@ static void bq27x00_update(struct bq27x00_device_info *di)
di->last_update = jiffies;
}
+static void copy_to_dm_buf_big_endian(struct bq27x00_device_info *di,
+ u8 *buf, u8 offset, u8 sz, u32 val)
+{
+ dev_dbg(di->dev, "%s: offset %d sz %d val %d\n",
+ __func__, offset, sz, val);
+
+ switch (sz) {
+ case 1:
+ buf[offset] = (u8) val;
+ break;
+ case 2:
+ put_unaligned_be16((u16) val, &buf[offset]);
+ break;
+ case 4:
+ put_unaligned_be32(val, &buf[offset]);
+ break;
+ default:
+ dev_err(di->dev, "%s: bad size for dm parameter - %d",
+ __func__, sz);
+ break;
+ }
+}
+
+static int rom_mode_gauge_init_completed(struct bq27x00_device_info *di)
+{
+ dev_dbg(di->dev, "%s:\n", __func__);
+
+ return control_cmd_read(di, CONTROL_STATUS_SUBCMD) &
+ BQ274XX_CTRL_STATUS_INITCOMP;
+}
+
+static bool rom_mode_gauge_dm_initialized(struct bq27x00_device_info *di)
+{
+ u16 flags;
+
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+
+ dev_dbg(di->dev, "%s: flags - 0x%04x\n", __func__, flags);
+
+ if (flags & BQ274XX_FLAG_ITPOR)
+ return false;
+ else
+ return true;
+}
+
+#define INITCOMP_TIMEOUT_MS 10000
+static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di)
+{
+ int i;
+ int timeout = INITCOMP_TIMEOUT_MS;
+ u8 subclass, offset;
+ u32 blk_number;
+ u32 blk_number_prev = 0;
+ u8 buf[32];
+ bool buf_valid = false;
+ struct dm_reg *dm_reg;
+
+ dev_dbg(di->dev, "%s:\n", __func__);
+
+ while (!rom_mode_gauge_init_completed(di) && timeout > 0) {
+ msleep(100);
+ timeout -= 100;
+ }
+
+ if (timeout <= 0) {
+ dev_err(di->dev, "%s: INITCOMP not set after %d seconds\n",
+ __func__, INITCOMP_TIMEOUT_MS/100);
+ return;
+ }
+
+ if (!di->dm_regs || !di->dm_regs_count) {
+ dev_err(di->dev, "%s: Data not available for DM initialization\n",
+ __func__);
+ return;
+ }
+
+ enter_cfg_update_mode(di);
+ for (i = 0; i < di->dm_regs_count; i++) {
+ dm_reg = &di->dm_regs[i];
+ subclass = dm_reg->subclass;
+ offset = dm_reg->offset;
+
+ /*
+ * Create a composite block number to see if the subsequent
+ * register also belongs to the same 32 btye block in the DM
+ */
+ blk_number = subclass << 8;
+ blk_number |= offset >> 5;
+
+ if (blk_number == blk_number_prev) {
+ copy_to_dm_buf_big_endian(di, buf, offset,
+ dm_reg->len, dm_reg->data);
+ } else {
+
+ if (buf_valid)
+ update_dm_block(di, blk_number_prev >> 8,
+ (blk_number_prev << 5) & 0xFF , buf);
+ else
+ buf_valid = true;
+
+ read_dm_block(di, dm_reg->subclass, dm_reg->offset,
+ buf);
+ copy_to_dm_buf_big_endian(di, buf, offset,
+ dm_reg->len, dm_reg->data);
+ }
+ blk_number_prev = blk_number;
+ }
+
+ /* Last buffer to be written */
+ if (buf_valid)
+ update_dm_block(di, subclass, offset, buf);
+
+ exit_cfg_update_mode(di);
+}
+
static void bq27x00_battery_poll(struct work_struct *work)
{
struct bq27x00_device_info *di =
container_of(work, struct bq27x00_device_info, work.work);
+ if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) &&
+ !rom_mode_gauge_dm_initialized(di)) {
+ rom_mode_gauge_dm_init(di);
+ }
+
bq27x00_update(di);
if (poll_interval > 0) {
@@ -487,23 +1142,23 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,
int curr;
int flags;
- curr = bq27x00_read(di, BQ27x00_REG_AI, false);
+ curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);
if (curr < 0) {
dev_err(di->dev, "error reading current\n");
return curr;
}
- if (bq27xxx_is_chip_version_higher(di)) {
- /* bq27500 returns signed value */
- val->intval = (int)((s16)curr) * 1000;
- } else {
- flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
- if (flags & BQ27000_FLAG_CHGS) {
+ if (di->chip == BQ27200) {
+ flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+ if (flags & BQ27200_FLAG_CHGS) {
dev_dbg(di->dev, "negative current!\n");
curr = -curr;
}
- val->intval = curr * 3570 / BQ27000_RS;
+ val->intval = curr * 3570 / BQ27200_RS;
+ } else {
+ /* Other gauges return signed value */
+ val->intval = (int)((s16)curr) * 1000;
}
return 0;
@@ -514,22 +1169,22 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
{
int status;
- if (bq27xxx_is_chip_version_higher(di)) {
- if (di->cache.flags & BQ27500_FLAG_FC)
- status = POWER_SUPPLY_STATUS_FULL;
- else if (di->cache.flags & BQ27500_FLAG_DSC)
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- status = POWER_SUPPLY_STATUS_CHARGING;
- } else {
- if (di->cache.flags & BQ27000_FLAG_FC)
+ if (di->chip == BQ27200) {
+ if (di->cache.flags & BQ27200_FLAG_FC)
status = POWER_SUPPLY_STATUS_FULL;
- else if (di->cache.flags & BQ27000_FLAG_CHGS)
+ else if (di->cache.flags & BQ27200_FLAG_CHGS)
status = POWER_SUPPLY_STATUS_CHARGING;
else if (power_supply_am_i_supplied(&di->bat))
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
else
status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if (di->cache.flags & BQ27XXX_FLAG_FC)
+ status = POWER_SUPPLY_STATUS_FULL;
+ else if (di->cache.flags & BQ27XXX_FLAG_DSC)
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ status = POWER_SUPPLY_STATUS_CHARGING;
}
val->intval = status;
@@ -542,21 +1197,21 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
{
int level;
- if (bq27xxx_is_chip_version_higher(di)) {
- if (di->cache.flags & BQ27500_FLAG_FC)
+ if (di->chip == BQ27200) {
+ if (di->cache.flags & BQ27200_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
- else if (di->cache.flags & BQ27500_FLAG_SOC1)
+ else if (di->cache.flags & BQ27200_FLAG_EDV1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
- else if (di->cache.flags & BQ27500_FLAG_SOCF)
+ else if (di->cache.flags & BQ27200_FLAG_EDVF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
} else {
- if (di->cache.flags & BQ27000_FLAG_FC)
+ if (di->cache.flags & BQ27XXX_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
- else if (di->cache.flags & BQ27000_FLAG_EDV1)
+ else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
- else if (di->cache.flags & BQ27000_FLAG_EDVF)
+ else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
@@ -576,7 +1231,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
{
int volt;
- volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
+ volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);
if (volt < 0) {
dev_err(di->dev, "error reading voltage\n");
return volt;
@@ -690,17 +1345,41 @@ static void bq27x00_external_power_changed(struct power_supply *psy)
schedule_delayed_work(&di->work, 0);
}
-static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
+static void __init set_properties_array(struct bq27x00_device_info *di,
+ enum power_supply_property *props, int num_props)
+{
+ int tot_sz = num_props * sizeof(enum power_supply_property);
+
+ di->bat.properties = devm_kzalloc(di->dev, tot_sz, GFP_KERNEL);
+
+ if (di->bat.properties) {
+ memcpy(di->bat.properties, props, tot_sz);
+ di->bat.num_properties = num_props;
+ } else {
+ di->bat.num_properties = 0;
+ }
+}
+
+static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di)
{
int ret;
di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
- if (di->chip == BQ27425) {
- di->bat.properties = bq27425_battery_props;
- di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
+ if (di->chip == BQ274XX) {
+ set_properties_array(di, bq274xx_battery_props,
+ ARRAY_SIZE(bq274xx_battery_props));
+ } else if (di->chip == BQ276XX) {
+ set_properties_array(di, bq276xx_battery_props,
+ ARRAY_SIZE(bq276xx_battery_props));
+ } else if (di->chip == BQ27520) {
+ set_properties_array(di, bq27520_battery_props,
+ ARRAY_SIZE(bq27520_battery_props));
+ } else if (di->chip == BQ2753X) {
+ set_properties_array(di, bq2753x_battery_props,
+ ARRAY_SIZE(bq2753x_battery_props));
} else {
- di->bat.properties = bq27x00_battery_props;
- di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
+ set_properties_array(di, bq27x00_battery_props,
+ ARRAY_SIZE(bq27x00_battery_props));
}
di->bat.get_property = bq27x00_battery_get_property;
di->bat.external_power_changed = bq27x00_external_power_changed;
@@ -748,7 +1427,7 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
static DEFINE_IDR(battery_id);
static DEFINE_MUTEX(battery_mutex);
-static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
+static int bq27xxx_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
{
struct i2c_client *client = to_i2c_client(di->dev);
struct i2c_msg msg[2];
@@ -782,20 +1461,205 @@ static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
return ret;
}
-static int bq27x00_battery_probe(struct i2c_client *client,
+static int bq27xxx_write_i2c(struct bq27x00_device_info *di, u8 reg, int value, bool single)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg;
+ unsigned char data[4];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ data[0] = reg;
+ if (single) {
+ data[1] = (unsigned char)value;
+ msg.len = 2;
+ } else {
+ put_unaligned_le16(value, &data[1]);
+ msg.len = 3;
+ }
+
+ msg.buf = data;
+ msg.addr = client->addr;
+ msg.flags = 0;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int bq27xxx_read_i2c_blk(struct bq27x00_device_info *di, u8 reg,
+ u8 *data, u8 len)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg[2];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &reg;
+ msg[0].len = 1;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = data;
+ msg[1].len = len;
+
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int bq27xxx_write_i2c_blk(struct bq27x00_device_info *di, u8 reg,
+ u8 *data, u8 sz)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg;
+ int ret;
+ u8 buf[33];
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ buf[0] = reg;
+ memcpy(&buf[1], data, sz);
+
+ msg.buf = buf;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sz + 1;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int bq27x00_battery_reset(struct bq27x00_device_info *di)
+{
+ dev_info(di->dev, "Gas Gauge Reset\n");
+
+ bq27xxx_write(di, BQ27XXX_REG_CTRL, RESET_SUBCMD, false);
+
+ msleep(10);
+
+ return bq27xxx_read(di, BQ27XXX_REG_CTRL, false);
+}
+
+static int bq27x00_battery_read_fw_version(struct bq27x00_device_info *di)
+{
+ bq27xxx_write(di, BQ27XXX_REG_CTRL, FW_VER_SUBCMD, false);
+
+ msleep(10);
+
+ return bq27xxx_read(di, BQ27XXX_REG_CTRL, false);
+}
+
+static int bq27x00_battery_read_device_type(struct bq27x00_device_info *di)
+{
+ bq27xxx_write(di, BQ27XXX_REG_CTRL, DEV_TYPE_SUBCMD, false);
+
+ msleep(10);
+
+ return bq27xxx_read(di, BQ27XXX_REG_CTRL, false);
+}
+
+static int bq27x00_battery_read_dataflash_version(struct bq27x00_device_info *di)
+{
+ bq27xxx_write(di, BQ27XXX_REG_CTRL, DF_VER_SUBCMD, false);
+
+ msleep(10);
+
+ return bq27xxx_read(di, BQ27XXX_REG_CTRL, false);
+}
+
+static ssize_t show_firmware_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bq27x00_device_info *di = dev_get_drvdata(dev);
+ int ver;
+
+ ver = bq27x00_battery_read_fw_version(di);
+
+ return sprintf(buf, "%d\n", ver);
+}
+
+static ssize_t show_dataflash_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bq27x00_device_info *di = dev_get_drvdata(dev);
+ int ver;
+
+ ver = bq27x00_battery_read_dataflash_version(di);
+
+ return sprintf(buf, "%d\n", ver);
+}
+
+static ssize_t show_device_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bq27x00_device_info *di = dev_get_drvdata(dev);
+ int dev_type;
+
+ dev_type = bq27x00_battery_read_device_type(di);
+
+ return sprintf(buf, "%d\n", dev_type);
+}
+
+static ssize_t show_reset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bq27x00_device_info *di = dev_get_drvdata(dev);
+
+ bq27x00_battery_reset(di);
+
+ return sprintf(buf, "okay\n");
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, show_firmware_version, NULL);
+static DEVICE_ATTR(df_version, S_IRUGO, show_dataflash_version, NULL);
+static DEVICE_ATTR(device_type, S_IRUGO, show_device_type, NULL);
+static DEVICE_ATTR(reset, S_IRUGO, show_reset, NULL);
+
+static struct attribute *bq27x00_attributes[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_df_version.attr,
+ &dev_attr_device_type.attr,
+ &dev_attr_reset.attr,
+ NULL
+};
+
+static const struct attribute_group bq27x00_attr_group = {
+ .attrs = bq27x00_attributes,
+};
+
+static int __init bq27x00_battery_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
char *name;
struct bq27x00_device_info *di;
int num;
int retval = 0;
+ u8 *regs;
/* Get new ID for the new battery device */
+ retval = idr_pre_get(&battery_id, GFP_KERNEL);
+ if (retval == 0)
+ return -ENOMEM;
mutex_lock(&battery_mutex);
- num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
+ retval = idr_get_new(&battery_id, client, &num);
mutex_unlock(&battery_mutex);
- if (num < 0)
- return num;
+ if (retval < 0)
+ return retval;
name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
if (!name) {
@@ -815,13 +1679,52 @@ static int bq27x00_battery_probe(struct i2c_client *client,
di->dev = &client->dev;
di->chip = id->driver_data;
di->bat.name = name;
- di->bus.read = &bq27x00_read_i2c;
+ di->bus.read = &bq27xxx_read_i2c;
+ di->bus.write = &bq27xxx_write_i2c;
+ di->bus.blk_read = bq27xxx_read_i2c_blk;
+ di->bus.blk_write = bq27xxx_write_i2c_blk;
+ di->dm_regs = NULL;
+ di->dm_regs_count = 0;
+
+ if (di->chip == BQ27200)
+ regs = bq27200_regs;
+ else if (di->chip == BQ27500)
+ regs = bq27500_regs;
+ else if (di->chip == BQ27520)
+ regs = bq27520_regs;
+ else if (di->chip == BQ2753X)
+ regs = bq2753x_regs;
+ else if (di->chip == BQ274XX) {
+ regs = bq274xx_regs;
+ di->dm_regs = bq274xx_dm_regs;
+ di->dm_regs_count = ARRAY_SIZE(bq274xx_dm_regs);
+ } else if (di->chip == BQ276XX) {
+ /* commands are same as bq274xx, only DM is different */
+ regs = bq276xx_regs;
+ di->dm_regs = bq276xx_dm_regs;
+ di->dm_regs_count = ARRAY_SIZE(bq276xx_dm_regs);
+ } else {
+ dev_err(&client->dev,
+ "Unexpected gas gague: %d\n", di->chip);
+ regs = bq27520_regs;
+ }
+
+ memcpy(di->regs, regs, NUM_REGS);
+
+ di->fw_ver = bq27x00_battery_read_fw_version(di);
+ dev_info(&client->dev, "Gas Guage fw version is 0x%04x\n", di->fw_ver);
retval = bq27x00_powersupply_init(di);
if (retval)
goto batt_failed_3;
+ /* Schedule a polling after about 1 min */
+ schedule_delayed_work(&di->work, 60 * HZ);
+
i2c_set_clientdata(client, di);
+ retval = sysfs_create_group(&client->dev.kobj, &bq27x00_attr_group);
+ if (retval)
+ dev_err(&client->dev, "could not create sysfs files\n");
return 0;
@@ -855,9 +1758,12 @@ static int bq27x00_battery_remove(struct i2c_client *client)
}
static const struct i2c_device_id bq27x00_id[] = {
- { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
+ { "bq27200", BQ27200 },
{ "bq27500", BQ27500 },
- { "bq27425", BQ27425 },
+ { "bq27520", BQ27520 },
+ { "bq274xx", BQ274XX },
+ { "bq276xx", BQ276XX },
+ { "bq2753x", BQ2753X },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27x00_id);
@@ -871,7 +1777,7 @@ static struct i2c_driver bq27x00_battery_driver = {
.id_table = bq27x00_id,
};
-static inline int bq27x00_battery_i2c_init(void)
+static inline int __init bq27x00_battery_i2c_init(void)
{
int ret = i2c_add_driver(&bq27x00_battery_driver);
if (ret)
@@ -880,7 +1786,7 @@ static inline int bq27x00_battery_i2c_init(void)
return ret;
}
-static inline void bq27x00_battery_i2c_exit(void)
+static inline void __exit bq27x00_battery_i2c_exit(void)
{
i2c_del_driver(&bq27x00_battery_driver);
}
@@ -929,7 +1835,7 @@ static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg,
return pdata->read(dev, reg);
}
-static int bq27000_battery_probe(struct platform_device *pdev)
+static int __devinit bq27000_battery_probe(struct platform_device *pdev)
{
struct bq27x00_device_info *di;
struct bq27000_platform_data *pdata = pdev->dev.platform_data;
@@ -954,7 +1860,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di);
di->dev = &pdev->dev;
- di->chip = BQ27000;
+ di->chip = BQ27200;
di->bat.name = pdata->name ?: dev_name(&pdev->dev);
di->bus.read = &bq27000_read_platform;
@@ -966,27 +1872,25 @@ static int bq27000_battery_probe(struct platform_device *pdev)
return 0;
err_free:
- platform_set_drvdata(pdev, NULL);
kfree(di);
return ret;
}
-static int bq27000_battery_remove(struct platform_device *pdev)
+static int __devexit bq27000_battery_remove(struct platform_device *pdev)
{
struct bq27x00_device_info *di = platform_get_drvdata(pdev);
bq27x00_powersupply_unregister(di);
- platform_set_drvdata(pdev, NULL);
kfree(di);
return 0;
}
-static struct platform_driver bq27000_battery_driver = {
+static struct platform_driver __initdata bq27000_battery_driver = {
.probe = bq27000_battery_probe,
- .remove = bq27000_battery_remove,
+ .remove = __devexit_p(bq27000_battery_remove),
.driver = {
.name = "bq27000-battery",
.owner = THIS_MODULE,
@@ -997,7 +1901,7 @@ static inline int bq27x00_battery_platform_init(void)
{
int ret = platform_driver_register(&bq27000_battery_driver);
if (ret)
- printk(KERN_ERR "Unable to register BQ27000 platform driver\n");
+ printk(KERN_ERR "Unable to register BQ27200 platform driver\n");
return ret;
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 815d6df8bd5..7c75ab88f9b 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -3673,8 +3673,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
add_dev:
/* add consumers devices */
+
if (init_data) {
- for (i = 0; i < init_data->num_consumer_supplies; i++) {
+ for (i = 0; i < init_data->num_consumer_supplies; i++) {
ret = set_consumer_device_supply(rdev,
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
@@ -3684,13 +3685,14 @@ add_dev:
goto unset_supplies;
}
}
- }
+ }
list_add(&rdev->list, &regulator_list);
rdev_init_debugfs(rdev);
out:
mutex_unlock(&regulator_list_mutex);
+
return rdev;
unset_supplies:
@@ -3710,7 +3712,8 @@ wash:
clean:
kfree(rdev);
rdev = ERR_PTR(ret);
- goto out;
+
+ goto out;
}
EXPORT_SYMBOL_GPL(regulator_register);
@@ -3980,8 +3983,12 @@ static int __init regulator_init_complete(void)
* with DT to provide them just assume that a DT enabled
* system has full constraints.
*/
+
+#if 0
+ // TODO OLIO: This assumption isn't true for us. Not yet, at least.
if (of_have_populated_dt())
has_full_constraints = true;
+#endif
mutex_lock(&regulator_list_mutex);
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 45c16447744..035eb750e28 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -906,7 +906,7 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
}
ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0);
if (ret < 0) {
- dev_err(mfd->dev, "Error in settting sr register\n");
+ dev_err(mfd->dev, "Error in setting sr register\n");
return ret;
}
}
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index db4d6dc0324..be8d14ab644 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -16,6 +16,7 @@ source "drivers/staging/iio/magnetometer/Kconfig"
source "drivers/staging/iio/meter/Kconfig"
source "drivers/staging/iio/resolver/Kconfig"
source "drivers/staging/iio/trigger/Kconfig"
+source "drivers/staging/iio/imu/inv_mpu/Kconfig"
config IIO_DUMMY_EVGEN
tristate
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index d87106135b2..9c91287a307 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -16,6 +16,7 @@ obj-y += cdc/
obj-y += frequency/
obj-y += gyro/
obj-y += impedance-analyzer/
+obj-y += imu/inv_mpu/
obj-y += light/
obj-y += magnetometer/
obj-y += meter/
diff --git a/drivers/staging/iio/imu/inv_mpu/Kconfig b/drivers/staging/iio/imu/inv_mpu/Kconfig
new file mode 100644
index 00000000000..1b8c95484f0
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/Kconfig
@@ -0,0 +1,32 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices and combos
+#
+
+config INV_MPU_IIO
+ tristate "Invensense MPU devices"
+ depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU
+ default n
+ help
+ This driver supports the Invensense MPU devices.
+ This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio.
+
+config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250
+ bool "Invensense MPU3050 slave accelerometer device for bma250"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This is slave device enable MPU3050 accelerometer slave device.
+ Right now, it is only bma250. For other acceleromter device,
+ it can be added to this menu if the proper interface is filled.
+ There are some interface function to be defined.
+
+config DTS_INV_MPU_IIO
+ bool "Invensense MPU driver using Device Tree Structure (DTS)"
+ depends on INV_MPU_IIO
+ default n
+ help
+ This enables Invensense MPU devices to use Device Tree Structure.
+ DTS must be enabled in the system.
+
diff --git a/drivers/staging/iio/imu/inv_mpu/Makefile b/drivers/staging/iio/imu/inv_mpu/Makefile
new file mode 100644
index 00000000000..31b765b34d0
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs := inv_mpu_core.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_trigger.o
+inv-mpu-iio-objs += inv_mpu_misc.o
+inv-mpu-iio-objs += inv_mpu3050_iio.o
+inv-mpu-iio-objs += dmpDefaultMPU6050.o
+inv-mpu-iio-objs += inv_slave_compass.o
+inv-mpu-iio-objs += inv_slave_pressure.o
+
+CFLAGS_inv_mpu_core.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_ring.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_trigger.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu_misc.o += -Idrivers/staging/iio
+CFLAGS_inv_mpu3050_iio.o += -Idrivers/staging/iio
+CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio
+CFLAGS_inv_slave_compass.o += -Idrivers/staging/iio
+CFLAGS_inv_slave_pressure.o += -Idrivers/staging/iio
+
+# the Bosch BMA250 driver is added to the inv-mpu device driver because it
+# must be connected to an MPU3050 device on the secondary slave bus.
+ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y)
+inv-mpu-iio-objs += inv_slave_bma250.o
+CFLAGS_inv_slave_bma250.o += -Idrivers/staging/iio
+endif
+
+# compile Invensense MPU IIO driver as DTS
+ifeq ($(CONFIG_DTS_INV_MPU_IIO), y)
+inv-mpu-iio-objs += inv_mpu_dts.o
+CFLAGS_inv_mpu_dts.o += -Idrivers/staging/iio
+endif
+
diff --git a/drivers/staging/iio/imu/inv_mpu/README b/drivers/staging/iio/imu/inv_mpu/README
new file mode 100644
index 00000000000..0963954a844
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/README
@@ -0,0 +1,659 @@
+Kernel driver inv-mpu-iio
+Author: Invensense <http://invensense.com>
+
+Table of Contents:
+==================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Board and Platform Data
+ > Interrupt Pin
+ > Platform Data
+- Board File Modifications for Secondary I2C Configuration
+ > MPU-6050 + AKM8963 on the secondary I2C interface
+ > MPU-6500 + AKM8963 on the secondary I2C interface
+ > MPU-6515 + AKM8975 and BMP280 on the secondary I2C interface
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Board File Modifications for Invensense Devices
+ > MPU-3050
+ > ITG-3500
+ > MPU-6050
+ > MPU-6515
+ > MPU-6500
+ > MPU-6XXX
+ > MPU-9150
+ > MPU-9250
+- IIO Subsystem
+ > Communicating with the Driver in Userspace
+ > ITG-3500
+ > MPU-6050 and MPU-6500
+ > MPU-9150
+ > MPU-9250
+ > MPU-3050 + BMA250 on the secondary I2C interface
+- Suspend and Resume
+- Wake up CPU using event interrupt
+- DMP Event
+- Motion Event
+- Streaming Data to an Userspace Application
+- Recommended Sysfs Entry Setup Sequence
+ > With DMP Firmware
+ > Without DMP Firmware
+- Test Applications
+ > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+ > Running Test Applications with MPU-3050/ITG-3500
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The Invensense driver currently supports the following sensors:
+- ITG-3500
+- MPU-6050
+- MPU-9150
+- MPU-6500
+- MPU-9250
+- MPU-3050
+- MPU-6XXX(either MPU6050 or MPU6500, driver to do auto detection)
+
+The slave address of each device is either 0x68 or 0x69, depending on the AD0
+pin value of the device. Please refer to the appropriate product specification
+document for further information regarding the AD0 pin. The driver supports both
+addresses.
+
+The following files are included in this package:
+- Kconfig
+- Makefile
+- inv_mpu_core.c
+- inv_mpu_misc.c
+- inv_mpu_trigger.c
+- inv_mpu3050_iio.c
+- inv_mpu_iio.h
+- inv_mpu_ring.c
+- inv_slave_bma250.c
+- inv_slave_compass.c
+- dmpDefaultMPU6050.c
+- dmpkey.h
+- dmpmap.h
+- mpu.h
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows:
+- Add mpu.h to "kernel/include/linux".
+- Add all other files to drivers/staging/iio/imu/inv_mpu
+(another directory is acceptable, but this is the recommended destination)
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ modify "drivers/staging/iio/imu/Kconfig" with:
+ >> source "drivers/staging/iio/imu/inv_mpu/Kconfig"
+
+ modify "drivers/staging/iio/imu/Makefile" with:
+ >> obj-y += inv_mpu/
+
+
+Board and Platform Data
+=======================
+In order to recognize the Invensense device on the I2C bus, the board file must
+be modified.
+The i2c_board_info instance must be defined as shown below.
+
+Interrupt Pin
+-------------
+The hardcoded value of 140 corresponds to the GPIO input pin connected to the
+Invensense device's interrupt pin.
+This pin will most likely be different for your platform, and the value should
+be changed accordingly.
+
+Platform Data
+-------------
+The platform data (orientation matrix and secondary bus configurations) must be
+modified as show below, according to your particular platform configuration.
+
+Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the
+device's secondary I2C interface. Thus the secondary I2C address must be
+provided.
+
+Board File Modifications for Secondary I2C Configuration
+========================================================
+For the Panda Board, the board file can be found at
+arch/arm/mach-omap2/board-omap4panda.c.
+Please modify the pertinent baord file in your system according to the examples
+shown below:
+
+MPU-6050 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6500 + AKM8963 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6500 + AK09911 on the secondary I2C interface
+-------------------------------------------------
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK09911,
+ .secondary_i2c_addr = 0x0D,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-6515 + AK8975 + BMP280 on the secondary I2C interface
+Note: sec_slave_XXX can only be compass or accelerometer in 3050 case.
+ aux_slave_XXX can only be pressure.
+--------------------------------------------------
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8975,
+ .secondary_i2c_addr = 0x0E,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+ .aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE,
+ .aux_slave_id = PRESSURE_ID_BMP280,
+ .aux_i2c_addr = 0x77,
+};
+static struct i2c_board_info __initdata chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6515", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &mpu_data,
+ }
+};
+
+MPU-9150
+--------
+For MPU-9150, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8975,
+ .secondary_i2c_addr = 0x0C,
+ .secondary_orientation = { 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1 },
+};
+
+MPU-9250
+--------
+For MPU-9250, please provide the following secondary I2C bus information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x00,
+ .level_shifter = 0,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
+ .sec_slave_id = COMPASS_ID_AK8963,
+ .secondary_i2c_addr = 0x0C,
+ .secondary_orientation = { 0, 1, 0,
+ -1, 0, 0,
+ 0, 0, 1 },
+};
+
+MPU-3050 + BMA250 on the secondary I2C interface
+------------------------------------------------
+For BMA250 on the secondary I2C bus, please provide the following information.
+
+static struct mpu_platform_data gyro_platform_data = {
+ .int_config = 0x10,
+ .level_shifter = 0,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ .sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL,
+ .sec_slave_id = ACCEL_ID_BMA250,
+ .secondary_i2c_addr = 0x18,
+};
+
+
+Board File Modifications for Invensense Devices
+===============================================
+For Invensense devices, please provide the i2c init data as shown in the
+examples below.
+
+In the _i2c_init function, the device is registered in the following manner:
+
+ // arch/arm/mach-omap2/board-omap4panda.c
+ // in static int __init omap4_panda_i2c_init(void)
+ omap_register_i2c_bus(4, 400,
+ single_chip_board_info,
+ ARRAY_SIZE(single_chip_board_info));
+
+MPU-3050
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu3050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+ITG-3500
+--------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("itg3500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6050
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6050", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6500
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6500", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU6XXX
+-------
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu6xxx", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9150
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9150", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+MPU9250
+-------
+arch/arm/mach-omap2/board-omap4panda.c
+static struct i2c_board_info __initdata single_chip_board_info[] = {
+ {
+ I2C_BOARD_INFO("mpu9250", 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &gyro_platform_data,
+ },
+};
+
+IIO subsystem
+=============
+A successful installation will create the following two new directories under
+/sys/bus/iio/devices:
+ - iio:device0
+ - trigger0
+
+Also, a new file, "iio:device0", will be created in the /dev/ diretory.
+(if you have more than one IIO device, the file will be named "iio:deviceX",
+where X is a number)
+
+
+Communicating with the Driver in Userspace
+------------------------------------------
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found
+at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above).
+
+A brief description of the pertinent files for each Invensense device is shown
+below:
+
+ITG-3500
+--------
+temperature (Read-only)
+--Read temperature data directly from the temperature register.
+
+sampling_frequency (Read/write)
+--Configure the ADC sampling rate and FIFO output rate.
+
+sampling_frequency_available(read-only)
+--show commonly used frequency
+
+clock_source (Read-only)
+--Check which clock-source is used by the chip.
+
+power_state (Read/write)
+--turn on/off the power supply
+
+self_test (read-only)
+--read this entry trigger self test. The return value is D.
+D is the success/fail.
+For different chip, the result is different for success/fail.
+1 means success 0 means fail. The LSB of D is for gyro; the bit
+next to LSB of D is for accel. The bit 2 of D is for compass result.
+
+key (read-only)
+--show the key value of this driver. Used by MPL.
+
+gyro_matrix (read-only)
+--show the orientation matrix obtained from the board file.
+
+MPU-6050 and MPU-6500
+---------------------
+MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above).
+In addition, it has the files below:
+
+gyro_enable (read/write)
+--enable/disable gyro functionality. Affects raw_gyro. Turning this off this
+ will shut down gyro and save power.
+
+accl_enable (read/write)
+--enable/disable accelerometer functionality. Affects raw_accl.
+Turning this off this will shut down accel and save power.
+
+firmware_loaded (read/write)
+--Flag indicating the whether firmware is loaded or not in the DMP engine.
+0 means no firmware loaded. 1 means firmware is already loaded . This
+flag can only be written as 0. It internally updates to 1.
+
+dmp_on(read/write)
+--This entry controls whether to run DMP or not.
+Write 1 to enable DMP and write 0 to disable dmp.
+Please note that firmware_loaded must be 1 in order to enable DMP.
+
+dmp_int_on(read/write)
+--This entry controls whether dmp interrupt is on/off.
+Please note that firmware_loaded must be 1.
+Also, we'd like to remind you that it is sometimes advantageous to
+turn interrupts off while the DMP is running.
+
+dmp_output_rate
+--control dmp output rate when dmp is on.
+
+dmp_event_int_on(read/write)
+--This entry controls whether dmp event interrupt is on/off.
+Please note that turning this on will turn off the data interrupt.
+Interrupts will be generated only when events occur.
+This is useful for saving power when the system is waiting for a special event
+to wake up.
+
+dmp_firmware (write only binary file)
+--DMP firmware code is loaded into this entry.
+If loading is successful, the firmware_loaded flag will be updated to 1.
+In order to load new firmware, the firmware_loaded flag must be first set to 0.
+
+accel_matrix
+--orientation matrix for accelerometer.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+pedometer_time
+pedometer_steps,
+--Pedometer related entries
+
+event_tap
+event_display_orientation
+event_accel_motion
+event_smd
+--Event related entries.
+Please poll these entries to read their values. Direct reads will yield
+meaningless results.
+Further details are provided in the DMP Events section of this README.
+
+tap_on
+--Controls tap function of DMP
+
+tap_time
+tap_min_count
+tap_threshold
+--Tap related entries. Controls various parameters of tap function.
+
+display_orientation_on
+--Turn on/off display orientation function of DMP.
+
+smd_enable
+enable SMD(Significant Motion Detection) detection.
+
+smd_threshold
+This set the threshold of the motion when SMD start to be triggered. The
+value is in acclerometer counts.
+
+smd_delay_threshold
+This sets the threshold of time after which SMD can be triggered.
+The value is in seconds.
+
+smd_delay_threshold2
+This sets the threshold of time during which SMD can be triggered (after the
+smd_delay_threshold timer has expired).
+The value is in seconds.
+
+quaternion_on
+--Turn on/off quaterniion data output. DMP is required for this feature.
+
+Low power accel motion interrupt related settings.
+if motion_lpa_on is set, this will disable all engines except accel. Accel will
+enter low power mode and the whole chip will be turned on/off at specific frequency.
+-----------------------------------------------------------------------------
+motion_lpa_threshold
+--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg.
+
+motion_lpa_on
+--turn on/off motion function.
+
+motion_lpa_freq
+--motion lpa frequency. which determines power on/off frequency.
+------------------------------------------------------------------------------
+MPU-9150
+--------
+MPU-9150 has all of MPU-6050's entries. It also has two additional entries,
+described below.
+
+compass_enable (read/write)
+--Enables compass function.
+
+compass_matrix (read-only)
+--Compass orientation matrix
+
+MPU-3050 with BMA250 on secondary I2C interface
+-----------------------------------------------
+MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry.
+It also has two additional entries, shown below:
+
+accl_matrix
+
+accl_enable
+
+Suspend and Resume
+===================================================
+The suspend and resume functions are call backs registered to the system
+and executed when the system goes in suspend and resumes.
+It is enabled when CONFIG_PM is defined.
+The current behavior is simple:
+- suspend will turn off the chip
+- resume will turn on the chip
+
+However, it is possible for the driver to do more complex things;
+for example, leaving pedometers running when system is off. This can save whole
+system power while letting pedometer working. Other behaviors are possible
+too.
+
+Wake up CPU using event interrupt
+====================================================
+It is common practice in current mobile device to save power as much as
+possible. Ususally the device can sleep when not use and only be wake up when in use.
+Invensense MPU series provides an efficient way of waking up CPU by events.
+The events could be pedometer steps, tap action, SMD(significant Motion Detection),
+or any customer defined actions. CPU can sleep to save power, when event happens,
+DMP will calculate the motion data to detemine whether a pre-defined event happens
+or not. If an pre-defined happens, an interrupt is generated to wake up the CPU
+to do further processing.
+The requirements of doing event wake is the following:
+1. Add enable_irq_wake() either in the driver or in the board file.
+2. Connect the interrupt pin to the CPU wake up pin.
+3. Configure CPU to be woken up by wake up pin.
+
+DMP Event
+=========
+A DMP Event is an event that is output by the DMP unit within the Invensense
+device (MPU).
+Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP.
+
+There are four sysfs entries for DMP events:
+- event_tap
+- event_display_orientation
+- event_accel_motion
+- event_smd
+
+These events must be polled before reading.
+
+The proper method to poll sysfs is as follows:
+1. open file.
+2. dummy read.
+3. poll.
+4. once the poll passed, use fopen and fread to read the sysfs entry.
+5. interpret the data.
+
+Streaming Data to an Userspace Application
+==========================================
+When streaming data to an userspace application, we recommend that you access
+gyro/accel/compass data via /dev/iio:device0.
+
+Please follow the steps below to read data at a constant rate from the driver:
+
+1. Write a 1 to power_state to turn on the chip. This is the default setting
+ after installing the driver.
+2. Write the desired output rate to fifo_rate.
+3. Write 1 to enable to turn on the event.
+4. Read /dev/iio:device0 to get a string of gyro/accel/compass data.
+5. Parse this string to obtain each gyro/accel/compass element.
+6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp.
+7. If compass is enabled, the output will contain compass data.
+
+
+Recommended Sysfs Entry Setup Senquence
+=======================================
+
+Without DMP Firmware
+--------------------
+1. Set "power_state" to 1,
+2. Set the scale and fifo rate values according to your needs.
+3. Set gyro_enable, accel_enable, and compass_enable according to your needs.
+ For example:
+ - If you only want gyro data, set accel_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want accel data, set gyro_enable to 0 (and compass_enable to
+ 0, if applicable).
+ - If you only want compass data, set gyro_enable to 0 and accel_enable to 0.
+4. Set "enable" to 1.
+5. You will now get the output that you want.
+
+With DMP Firmware
+-----------------
+1. Set "power_state" to 1.
+2. Write "0" to firmware_loaded if it is not zero already.
+3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware
+ image.
+4. Make sure firmware_loaded is 1 after loading the DMP image.
+5. Make appropriate configurations as shown above in the "without DMP firmware"
+ case.
+6. Set dmp_on to 1.
+7. Set "enable" to 1.
+
+Please note that the enable function uses the enable entry under
+"/sys/bus/iio/devices/iio:device0/buffer"
+
+Test Applications
+=================
+A test application is located under software/simple_apps/mpu_iio.
+This application is stand-alone in that it cannot be run concurrently with other
+entities trying to access the device node(s) or sysfs entries; in particular,
+the
+
+Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250
+---------------------------------------------------------
+To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices,
+please use the following commands:
+
+1. For tap/display orientation events:
+ mpu_iio -c 10 -l 3 -p
+
+2. In addition, to test the motion interrupt (and no_motion on MPU6050) use:
+ mpu_iio -c 10 -l 3 -p -m
+
+3. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Running Test Applications with MPU-3050/ITG-3500
+------------------------------------------------
+To run test applications with MPU-3050 or ITG-3500 devices,
+please use the following command:
+
+1. For printing data normally:
+ mpu_iio -c 10 -l 3 -r
+
+Please use mpu_iio.c and iio_utils.h as example code for your development
+purposes.
+
+Stress test application
+=================================
+A stress test application is located under software/simple_apps/stress_iio.
+This application simulates HAL's usage calls to the driver. It creates three
+threads. One for data read; one for event read; one for sysfs control.
+It can run without any parameters or run with some control parameters. Please
+see README in the same directories for details.
+
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c b/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c
new file mode 100644
index 00000000000..81e63960f39
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include "inv_mpu_iio.h"
+#include "dmpKey.h"
+#include "dmpmap.h"
+
+#define CFG_OUT_PRESS (1823)
+#define CFG_PED_ENABLE (1936)
+#define CFG_OUT_GYRO (1755)
+#define CFG_PEDSTEP_DET (2417)
+#define OUT_GYRO_DAT (1764)
+#define CFG_FIFO_INT (1934)
+#define OUT_CPASS_DAT (1798)
+#define CFG_AUTH (1051)
+#define OUT_ACCL_DAT (1730)
+#define FCFG_1 (1078)
+#define FCFG_3 (1103)
+#define FCFG_2 (1082)
+#define CFG_OUT_CPASS (1789)
+#define FCFG_7 (1089)
+#define CFG_OUT_3QUAT (1617)
+#define OUT_PRESS_DAT (1832)
+#define OUT_3QUAT_DAT (1627)
+#define CFG_7 (1300)
+#define OUT_PQUAT_DAT (1696)
+#define CFG_OUT_6QUAT (1652)
+#define CFG_PED_INT (2406)
+#define SMD_TP2 (1265)
+#define SMD_TP1 (1244)
+#define CFG_MOTION_BIAS (1302)
+#define CFG_OUT_ACCL (1721)
+#define CFG_OUT_STEPDET (1587)
+#define OUT_6QUAT_DAT (1662)
+#define CFG_OUT_PQUAT (1687)
+
+#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 (30 * 16 + 4)
+#define CPASS_BIAS_Y (30 * 16 + 8)
+#define CPASS_BIAS_Z (30 * 16 + 12)
+#define CPASS_MTX_00 (32 * 16 + 4)
+#define CPASS_MTX_01 (32 * 16 + 8)
+#define CPASS_MTX_02 (36 * 16 + 12)
+#define CPASS_MTX_10 (33 * 16)
+#define CPASS_MTX_11 (33 * 16 + 4)
+#define CPASS_MTX_12 (33 * 16 + 8)
+#define CPASS_MTX_20 (33 * 16 + 12)
+#define CPASS_MTX_21 (34 * 16 + 4)
+#define CPASS_MTX_22 (34 * 16 + 8)
+#define D_EXT_GYRO_BIAS_X (61 * 16)
+#define D_EXT_GYRO_BIAS_Y (61 * 16 + 4)
+#define D_EXT_GYRO_BIAS_Z (61 * 16 + 8)
+#define D_ACT0 (40 * 16)
+#define D_ACSX (40 * 16 + 4)
+#define D_ACSY (40 * 16 + 8)
+#define D_ACSZ (40 * 16 + 12)
+
+#define FLICK_MSG (45 * 16 + 4)
+#define FLICK_COUNTER (45 * 16 + 8)
+#define FLICK_LOWER (45 * 16 + 12)
+#define FLICK_UPPER (46 * 16 + 12)
+
+#define D_SMD_ENABLE (18 * 16)
+#define D_SMD_MOT_THLD (21 * 16 + 8)
+#define D_SMD_DELAY_THLD (21 * 16 + 4)
+#define D_SMD_DELAY2_THLD (21 * 16 + 12)
+#define D_SMD_EXE_STATE (22 * 16)
+#define D_SMD_DELAY_CNTR (21 * 16)
+
+#define D_WF_GESTURE_TIME_THRSH (25 * 16 + 8)
+#define D_WF_GESTURE_TILT_ERROR (25 * 16 + 12)
+#define D_WF_GESTURE_TILT_THRSH (26 * 16 + 8)
+#define D_WF_GESTURE_TILT_REJECT_THRSH (26 * 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_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_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_STEPCTR2 (58 * 16 + 8)
+#define D_PEDSTD_TIMECTR (964)
+#define D_PEDSTD_DECI (768 + 0xA0)
+#define D_PEDSTD_SB2 (60 * 16 + 14)
+#define D_STPDET_TIMESTAMP (28 * 16 + 8)
+#define D_PEDSTD_DRIVE_STATE (58)
+
+#define D_HOST_NO_MOT (976)
+#define D_ACCEL_BIAS (660)
+
+#define D_BM_BATCH_CNTR (27 * 16 + 4)
+#define D_BM_BATCH_THLD (27 * 16 + 12)
+#define D_BM_ENABLE (28 * 16 + 6)
+#define D_BM_NUMWORD_TOFILL (28 * 16 + 4)
+
+#define D_SO_DATA (4 * 16 + 2)
+
+#define D_P_HW_ID (22 * 16 + 10)
+#define D_P_INIT (22 * 16 + 2)
+
+#define D_DMP_RUN_CNTR (24*16)
+
+#define D_ODR_S0 (45*16+8)
+#define D_ODR_S1 (45*16+12)
+#define D_ODR_S2 (45*16+10)
+#define D_ODR_S3 (45*16+14)
+#define D_ODR_S4 (46*16+8)
+#define D_ODR_S5 (46*16+12)
+#define D_ODR_S6 (46*16+10)
+#define D_ODR_S7 (46*16+14)
+#define D_ODR_S8 (42*16+8)
+#define D_ODR_S9 (42*16+12)
+
+#define D_ODR_CNTR_S0 (45*16)
+#define D_ODR_CNTR_S1 (45*16+4)
+#define D_ODR_CNTR_S2 (45*16+2)
+#define D_ODR_CNTR_S3 (45*16+6)
+#define D_ODR_CNTR_S4 (46*16)
+#define D_ODR_CNTR_S5 (46*16+4)
+#define D_ODR_CNTR_S6 (46*16+2)
+#define D_ODR_CNTR_S7 (46*16+6)
+#define D_ODR_CNTR_S8 (42*16)
+#define D_ODR_CNTR_S9 (42*16+4)
+
+#define D_FS_LPQ0 (59*16)
+#define D_FS_LPQ1 (59*16 + 4)
+#define D_FS_LPQ2 (59*16 + 8)
+#define D_FS_LPQ3 (59*16 + 12)
+
+#define D_FS_Q0 (12*16)
+#define D_FS_Q1 (12*16 + 4)
+#define D_FS_Q2 (12*16 + 8)
+#define D_FS_Q3 (12*16 + 12)
+
+#define D_FS_9Q0 (2*16)
+#define D_FS_9Q1 (2*16 + 4)
+#define D_FS_9Q2 (2*16 + 8)
+#define D_FS_9Q3 (2*16 + 12)
+
+static const struct tKeyLabel dmpTConfig[] = {
+ {KEY_CFG_OUT_ACCL, CFG_OUT_ACCL},
+ {KEY_CFG_OUT_GYRO, CFG_OUT_GYRO},
+ {KEY_CFG_OUT_3QUAT, CFG_OUT_3QUAT},
+ {KEY_CFG_OUT_6QUAT, CFG_OUT_6QUAT},
+ {KEY_CFG_OUT_PQUAT, CFG_OUT_PQUAT},
+ {KEY_CFG_PED_ENABLE, CFG_PED_ENABLE},
+ {KEY_CFG_FIFO_INT, CFG_FIFO_INT},
+ {KEY_CFG_AUTH, CFG_AUTH},
+ {KEY_FCFG_1, FCFG_1},
+ {KEY_FCFG_3, FCFG_3},
+ {KEY_FCFG_2, FCFG_2},
+ {KEY_FCFG_7, FCFG_7},
+ {KEY_CFG_7, CFG_7},
+ {KEY_CFG_MOTION_BIAS, CFG_MOTION_BIAS},
+ {KEY_CFG_PEDSTEP_DET, CFG_PEDSTEP_DET},
+ {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_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_D_PEDSTD_BP_B, D_PEDSTD_BP_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_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_STEPCTR2, D_PEDSTD_STEPCTR2},
+ {KEY_D_PEDSTD_TIMECTR, D_PEDSTD_TIMECTR},
+ {KEY_D_PEDSTD_DECI, D_PEDSTD_DECI},
+ {KEY_D_PEDSTD_SB2, D_PEDSTD_SB2},
+ {KEY_D_PEDSTD_DRIVE_STATE, D_PEDSTD_DRIVE_STATE},
+ {KEY_D_STPDET_TIMESTAMP, D_STPDET_TIMESTAMP},
+ {KEY_D_HOST_NO_MOT, D_HOST_NO_MOT},
+ {KEY_D_ACCEL_BIAS, D_ACCEL_BIAS},
+ {KEY_CFG_EXT_GYRO_BIAS_X, D_EXT_GYRO_BIAS_X},
+ {KEY_CFG_EXT_GYRO_BIAS_Y, D_EXT_GYRO_BIAS_Y},
+ {KEY_CFG_EXT_GYRO_BIAS_Z, D_EXT_GYRO_BIAS_Z},
+ {KEY_CFG_PED_INT, CFG_PED_INT},
+ {KEY_SMD_ENABLE, D_SMD_ENABLE},
+ {KEY_SMD_ACCEL_THLD, D_SMD_MOT_THLD},
+ {KEY_SMD_DELAY_THLD, D_SMD_DELAY_THLD},
+ {KEY_SMD_DELAY2_THLD, D_SMD_DELAY2_THLD},
+ {KEY_SMD_ENABLE_TESTPT1, SMD_TP1},
+ {KEY_SMD_ENABLE_TESTPT2, SMD_TP2},
+ {KEY_SMD_EXE_STATE, D_SMD_EXE_STATE},
+ {KEY_SMD_DELAY_CNTR, D_SMD_DELAY_CNTR},
+ {KEY_BM_ENABLE, D_BM_ENABLE},
+ {KEY_BM_BATCH_CNTR, D_BM_BATCH_CNTR},
+ {KEY_BM_BATCH_THLD, D_BM_BATCH_THLD},
+ {KEY_BM_NUMWORD_TOFILL, D_BM_NUMWORD_TOFILL},
+ {KEY_SO_DATA, D_SO_DATA},
+ {KEY_P_INIT, D_P_INIT},
+ {KEY_CFG_OUT_CPASS, CFG_OUT_CPASS},
+ {KEY_CFG_OUT_PRESS, CFG_OUT_PRESS},
+ {KEY_CFG_OUT_STEPDET, CFG_OUT_STEPDET},
+ {KEY_CFG_3QUAT_ODR, D_ODR_S1},
+ {KEY_CFG_6QUAT_ODR, D_ODR_S2},
+ {KEY_CFG_PQUAT6_ODR, D_ODR_S3},
+ {KEY_CFG_ACCL_ODR, D_ODR_S4},
+ {KEY_CFG_GYRO_ODR, D_ODR_S5},
+ {KEY_CFG_CPASS_ODR, D_ODR_S6},
+ {KEY_CFG_PRESS_ODR, D_ODR_S7},
+ {KEY_CFG_9QUAT_ODR, D_ODR_S8},
+ {KEY_CFG_PQUAT9_ODR, D_ODR_S9},
+ {KEY_ODR_CNTR_3QUAT, D_ODR_CNTR_S1},
+ {KEY_ODR_CNTR_6QUAT, D_ODR_CNTR_S2},
+ {KEY_ODR_CNTR_PQUAT, D_ODR_CNTR_S3},
+ {KEY_ODR_CNTR_ACCL, D_ODR_CNTR_S4},
+ {KEY_ODR_CNTR_GYRO, D_ODR_CNTR_S5},
+ {KEY_ODR_CNTR_CPASS, D_ODR_CNTR_S6},
+ {KEY_ODR_CNTR_PRESS, D_ODR_CNTR_S7},
+ {KEY_ODR_CNTR_9QUAT, D_ODR_CNTR_S8},
+ {KEY_ODR_CNTR_PQUAT9, D_ODR_CNTR_S9},
+ {KEY_DMP_RUN_CNTR, D_DMP_RUN_CNTR},
+ {KEY_DMP_LPQ0, D_FS_LPQ0},
+ {KEY_DMP_LPQ1, D_FS_LPQ1},
+ {KEY_DMP_LPQ2, D_FS_LPQ2},
+ {KEY_DMP_LPQ3, D_FS_LPQ3},
+ {KEY_DMP_Q0, D_FS_Q0},
+ {KEY_DMP_Q1, D_FS_Q1},
+ {KEY_DMP_Q2, D_FS_Q2},
+ {KEY_DMP_Q3, D_FS_Q3},
+ {KEY_DMP_9Q0, D_FS_9Q0},
+ {KEY_DMP_9Q1, D_FS_9Q1},
+ {KEY_DMP_9Q2, D_FS_9Q2},
+ {KEY_DMP_9Q3, D_FS_9Q3},
+ {KEY_TEST_01, OUT_ACCL_DAT},
+ {KEY_TEST_02, OUT_GYRO_DAT},
+ {KEY_TEST_03, OUT_CPASS_DAT},
+ {KEY_TEST_04, OUT_PRESS_DAT},
+ {KEY_TEST_05, OUT_3QUAT_DAT},
+ {KEY_TEST_06, OUT_6QUAT_DAT},
+ {KEY_TEST_07, OUT_PQUAT_DAT}
+};
+#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) {
+ pr_err("ERROR!! key not exist=%d!\n", key);
+ return 0xffff;
+ }
+ if (0xffff == keys[key].addr)
+ pr_err("ERROR!!key not local=%d!\n", key);
+ return keys[key].addr;
+}
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpKey.h b/drivers/staging/iio/imu/inv_mpu/dmpKey.h
new file mode 100644
index 00000000000..f6173b3c0f8
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/dmpKey.h
@@ -0,0 +1,607 @@
+/*
+ * 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)
+#define KEY_CFG_GYRO_RAW_DATA (KEY_CFG_LP_QUAT + 1)
+#define KEY_CFG_EXT_GYRO_BIAS (KEY_CFG_GYRO_RAW_DATA + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_X (KEY_CFG_EXT_GYRO_BIAS + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Y (KEY_CFG_EXT_GYRO_BIAS_X + 1)
+#define KEY_CFG_EXT_GYRO_BIAS_Z (KEY_CFG_EXT_GYRO_BIAS_Y + 1)
+#define KEY_bad_compass (KEY_CFG_EXT_GYRO_BIAS_Z + 1)
+#define KEY_COMPASS_CHG_SENSITIVITY (KEY_bad_compass + 1)
+#define KEY_CCS_HEADING_THLD (KEY_COMPASS_CHG_SENSITIVITY + 1)
+#define KEY_CCS_TIME_THLD (KEY_CCS_HEADING_THLD + 1)
+#define KEY_CCS_DOTP_THLD (KEY_CCS_TIME_THLD + 1)
+#define KEY_CCS_COMP_CNTR (KEY_CCS_DOTP_THLD + 1)
+#define KEY_CFG_NM_DET (KEY_CCS_COMP_CNTR + 1)
+#define KEY_SMD_ENABLE (KEY_CFG_NM_DET + 1)
+#define KEY_SMD_ACCEL_THLD (KEY_SMD_ENABLE + 1)
+#define KEY_SMD_DELAY_THLD (KEY_SMD_ACCEL_THLD + 1)
+#define KEY_SMD_DELAY2_THLD (KEY_SMD_DELAY_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT1 (KEY_SMD_DELAY2_THLD + 1)
+#define KEY_SMD_ENABLE_TESTPT2 (KEY_SMD_ENABLE_TESTPT1 + 1)
+#define KEY_SMD_EXE_STATE (KEY_SMD_ENABLE_TESTPT2 + 1)
+#define KEY_SMD_DELAY_CNTR (KEY_SMD_EXE_STATE + 1)
+
+#define KEY_BREAK (81)
+#if KEY_SMD_DELAY_CNTR != KEY_BREAK
+#error
+#endif
+
+/* MPU6050 keys */
+#define KEY_CFG_ACCEL_FILTER (KEY_BREAK + 1)
+#define KEY_CFG_MOTION_BIAS (KEY_CFG_ACCEL_FILTER + 1)
+#define KEY_TEMPLABEL (KEY_CFG_MOTION_BIAS + 1)
+
+#define KEY_D_0_22 (KEY_TEMPLABEL + 1)
+#define KEY_D_0_24 (KEY_D_0_22 + 1)
+#define KEY_D_0_36 (KEY_D_0_24 + 1)
+#define KEY_D_0_52 (KEY_D_0_36 + 1)
+#define KEY_D_0_96 (KEY_D_0_52 + 1)
+#define KEY_D_0_104 (KEY_D_0_96 + 1)
+#define KEY_D_0_108 (KEY_D_0_104 + 1)
+#define KEY_D_0_163 (KEY_D_0_108 + 1)
+#define KEY_D_0_188 (KEY_D_0_163 + 1)
+#define KEY_D_0_192 (KEY_D_0_188 + 1)
+#define KEY_D_0_224 (KEY_D_0_192 + 1)
+#define KEY_D_0_228 (KEY_D_0_224 + 1)
+#define KEY_D_0_232 (KEY_D_0_228 + 1)
+#define KEY_D_0_236 (KEY_D_0_232 + 1)
+
+#define KEY_DMP_PREVPTAT (KEY_D_0_236 + 1)
+#define KEY_D_1_2 (KEY_DMP_PREVPTAT + 1)
+#define KEY_D_1_4 (KEY_D_1_2 + 1)
+#define KEY_D_1_8 (KEY_D_1_4 + 1)
+#define KEY_D_1_10 (KEY_D_1_8 + 1)
+#define KEY_D_1_24 (KEY_D_1_10 + 1)
+#define KEY_D_1_28 (KEY_D_1_24 + 1)
+#define KEY_D_1_36 (KEY_D_1_28 + 1)
+#define KEY_D_1_40 (KEY_D_1_36 + 1)
+#define KEY_D_1_44 (KEY_D_1_40 + 1)
+#define KEY_D_1_72 (KEY_D_1_44 + 1)
+#define KEY_D_1_74 (KEY_D_1_72 + 1)
+#define KEY_D_1_79 (KEY_D_1_74 + 1)
+#define KEY_D_1_88 (KEY_D_1_79 + 1)
+#define KEY_D_1_90 (KEY_D_1_88 + 1)
+#define KEY_D_1_92 (KEY_D_1_90 + 1)
+#define KEY_D_1_96 (KEY_D_1_92 + 1)
+#define KEY_D_1_98 (KEY_D_1_96 + 1)
+#define KEY_D_1_100 (KEY_D_1_98 + 1)
+#define KEY_D_1_106 (KEY_D_1_100 + 1)
+#define KEY_D_1_108 (KEY_D_1_106 + 1)
+#define KEY_D_1_112 (KEY_D_1_108 + 1)
+#define KEY_D_1_128 (KEY_D_1_112 + 1)
+#define KEY_D_1_152 (KEY_D_1_128 + 1)
+#define KEY_D_1_160 (KEY_D_1_152 + 1)
+#define KEY_D_1_168 (KEY_D_1_160 + 1)
+#define KEY_D_1_175 (KEY_D_1_168 + 1)
+#define KEY_D_1_176 (KEY_D_1_175 + 1)
+#define KEY_D_1_178 (KEY_D_1_176 + 1)
+#define KEY_D_1_179 (KEY_D_1_178 + 1)
+#define KEY_D_1_218 (KEY_D_1_179 + 1)
+#define KEY_D_1_232 (KEY_D_1_218 + 1)
+#define KEY_D_1_236 (KEY_D_1_232 + 1)
+#define KEY_D_1_240 (KEY_D_1_236 + 1)
+#define KEY_D_1_244 (KEY_D_1_240 + 1)
+#define KEY_D_1_250 (KEY_D_1_244 + 1)
+#define KEY_D_1_252 (KEY_D_1_250 + 1)
+#define KEY_D_2_12 (KEY_D_1_252 + 1)
+#define KEY_D_2_96 (KEY_D_2_12 + 1)
+#define KEY_D_2_108 (KEY_D_2_96 + 1)
+#define KEY_D_2_208 (KEY_D_2_108 + 1)
+#define KEY_FLICK_MSG (KEY_D_2_208 + 1)
+#define KEY_FLICK_COUNTER (KEY_FLICK_MSG + 1)
+#define KEY_FLICK_LOWER (KEY_FLICK_COUNTER + 1)
+#define KEY_CFG_FLICK_IN (KEY_FLICK_LOWER + 1)
+#define KEY_FLICK_UPPER (KEY_CFG_FLICK_IN + 1)
+#define KEY_CGNOTICE_INTR (KEY_FLICK_UPPER + 1)
+#define KEY_D_2_224 (KEY_CGNOTICE_INTR + 1)
+#define KEY_D_2_244 (KEY_D_2_224 + 1)
+#define KEY_D_2_248 (KEY_D_2_244 + 1)
+#define KEY_D_2_252 (KEY_D_2_248 + 1)
+
+#define KEY_D_GYRO_BIAS_X (KEY_D_2_252 + 1)
+#define KEY_D_GYRO_BIAS_Y (KEY_D_GYRO_BIAS_X + 1)
+#define KEY_D_GYRO_BIAS_Z (KEY_D_GYRO_BIAS_Y + 1)
+#define KEY_D_ACC_BIAS_X (KEY_D_GYRO_BIAS_Z + 1)
+#define KEY_D_ACC_BIAS_Y (KEY_D_ACC_BIAS_X + 1)
+#define KEY_D_ACC_BIAS_Z (KEY_D_ACC_BIAS_Y + 1)
+#define KEY_D_GYRO_ENABLE (KEY_D_ACC_BIAS_Z + 1)
+#define KEY_D_ACCEL_ENABLE (KEY_D_GYRO_ENABLE + 1)
+#define KEY_D_QUAT_ENABLE (KEY_D_ACCEL_ENABLE + 1)
+#define KEY_D_OUTPUT_ENABLE (KEY_D_QUAT_ENABLE + 1)
+#define KEY_D_ACCEL_CNTR (KEY_D_OUTPUT_ENABLE + 1)
+#define KEY_D_GYRO_CNTR (KEY_D_ACCEL_CNTR + 1)
+#define KEY_D_QUAT0_CNTR (KEY_D_GYRO_CNTR + 1)
+#define KEY_D_QUAT1_CNTR (KEY_D_QUAT0_CNTR + 1)
+#define KEY_D_QUAT2_CNTR (KEY_D_QUAT1_CNTR + 1)
+#define KEY_D_CR_TIME_G (KEY_D_QUAT2_CNTR + 1)
+#define KEY_D_CR_TIME_A (KEY_D_CR_TIME_G + 1)
+#define KEY_D_CR_TIME_Q (KEY_D_CR_TIME_A + 1)
+#define KEY_D_CS_TAX (KEY_D_CR_TIME_Q + 1)
+#define KEY_D_CS_TAY (KEY_D_CS_TAX + 1)
+#define KEY_D_CS_TAZ (KEY_D_CS_TAY + 1)
+#define KEY_D_CS_TGX (KEY_D_CS_TAZ + 1)
+#define KEY_D_CS_TGY (KEY_D_CS_TGX + 1)
+#define KEY_D_CS_TGZ (KEY_D_CS_TGY + 1)
+#define KEY_D_CS_TQ0 (KEY_D_CS_TGZ + 1)
+#define KEY_D_CS_TQ1 (KEY_D_CS_TQ0 + 1)
+#define KEY_D_CS_TQ2 (KEY_D_CS_TQ1 + 1)
+#define KEY_D_CS_TQ3 (KEY_D_CS_TQ2 + 1)
+
+/* Compass keys */
+#define KEY_CPASS_GAIN (KEY_D_CS_TQ3 + 1)
+#define KEY_CPASS_BIAS_X (KEY_CPASS_GAIN + 1)
+#define KEY_CPASS_BIAS_Y (KEY_CPASS_BIAS_X + 1)
+#define KEY_CPASS_BIAS_Z (KEY_CPASS_BIAS_Y + 1)
+#define KEY_CPASS_MTX_00 (KEY_CPASS_BIAS_Z + 1)
+#define KEY_CPASS_MTX_01 (KEY_CPASS_MTX_00 + 1)
+#define KEY_CPASS_MTX_02 (KEY_CPASS_MTX_01 + 1)
+#define KEY_CPASS_MTX_10 (KEY_CPASS_MTX_02 + 1)
+#define KEY_CPASS_MTX_11 (KEY_CPASS_MTX_10 + 1)
+#define KEY_CPASS_MTX_12 (KEY_CPASS_MTX_11 + 1)
+#define KEY_CPASS_MTX_20 (KEY_CPASS_MTX_12 + 1)
+#define KEY_CPASS_MTX_21 (KEY_CPASS_MTX_20 + 1)
+#define KEY_CPASS_MTX_22 (KEY_CPASS_MTX_21 + 1)
+
+/* Tap Keys */
+#define KEY_DMP_TAP_GATE (KEY_CPASS_MTX_22 + 1)
+#define KEY_DMP_TAPW_MIN (KEY_DMP_TAP_GATE + 1)
+#define KEY_DMP_TAP_THR_Z (KEY_DMP_TAPW_MIN + 1)
+#define KEY_DMP_TAP_PREV_JERK_Z (KEY_DMP_TAP_THR_Z + 1)
+#define KEY_DMP_TAP_MIN_TAPS (KEY_DMP_TAP_PREV_JERK_Z + 1)
+#define KEY_DMP_TAP_NEXT_TAP_THRES (KEY_DMP_TAP_MIN_TAPS + 1)
+#define KEY_DMP_TAP_SHAKE_REJECT (KEY_DMP_TAP_NEXT_TAP_THRES + 1)
+#define KEY_DMP_TAP_SHAKE_COUNT_MAX (KEY_DMP_TAP_SHAKE_REJECT + 1)
+#define KEY_DMP_TAP_SHAKE_TIMEOUT_MAX (KEY_DMP_TAP_SHAKE_COUNT_MAX + 1)
+#define KEY_DMP_TAP_DIRECTION (KEY_DMP_TAP_SHAKE_TIMEOUT_MAX + 1)
+#define KEY_DMP_TAP_COUNT (KEY_DMP_TAP_DIRECTION + 1)
+/* Gesture Keys */
+#define KEY_DMP_SH_TH_Y (KEY_DMP_TAP_COUNT + 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_CFG_DISPLAY_ORIENT_INT (KEY_D_ACSZ + 1)
+#define KEY_NO_ORIENT_INTERRUPT (KEY_CFG_DISPLAY_ORIENT_INT + 1)
+#define KEY_X_GRT_Y_TMP2 (KEY_NO_ORIENT_INTERRUPT + 1)
+
+/*Shake Keys */
+#define KEY_D_0_64 (KEY_X_GRT_Y_TMP2 + 1)
+#define KEY_D_2_4 (KEY_D_0_64 + 1)
+#define KEY_D_2_8 (KEY_D_2_4 + 1)
+#define KEY_D_2_48 (KEY_D_2_8 + 1)
+#define KEY_D_2_92 (KEY_D_2_48 + 1)
+#define KEY_D_2_94 (KEY_D_2_92 + 1)
+#define KEY_D_2_160 (KEY_D_2_94 + 1)
+#define KEY_D_3_180 (KEY_D_2_160 + 1)
+#define KEY_D_3_184 (KEY_D_3_180 + 1)
+#define KEY_D_3_188 (KEY_D_3_184 + 1)
+#define KEY_D_3_208 (KEY_D_3_188 + 1)
+#define KEY_D_3_240 (KEY_D_3_208 + 1)
+#define KEY_RETRACTION_1 (KEY_D_3_240 + 1)
+#define KEY_RETRACTION_2 (KEY_RETRACTION_1 + 1)
+#define KEY_RETRACTION_3 (KEY_RETRACTION_2 + 1)
+#define KEY_RETRACTION_4 (KEY_RETRACTION_3 + 1)
+#define KEY_CFG_SHAKE_INT (KEY_RETRACTION_4 + 1)
+
+/* Authenticate Keys */
+#define KEY_D_AUTH_OUT (KEY_CFG_SHAKE_INT + 1)
+#define KEY_D_AUTH_IN (KEY_D_AUTH_OUT + 1)
+#define KEY_D_AUTH_A (KEY_D_AUTH_IN + 1)
+#define KEY_D_AUTH_B (KEY_D_AUTH_A + 1)
+
+/* Pedometer standalone only keys */
+#define KEY_D_PEDSTD_BP_B (KEY_D_AUTH_B + 1)
+#define KEY_D_PEDSTD_HP_A (KEY_D_PEDSTD_BP_B + 1)
+#define KEY_D_PEDSTD_HP_B (KEY_D_PEDSTD_HP_A + 1)
+#define KEY_D_PEDSTD_BP_A4 (KEY_D_PEDSTD_HP_B + 1)
+#define KEY_D_PEDSTD_BP_A3 (KEY_D_PEDSTD_BP_A4 + 1)
+#define KEY_D_PEDSTD_BP_A2 (KEY_D_PEDSTD_BP_A3 + 1)
+#define KEY_D_PEDSTD_BP_A1 (KEY_D_PEDSTD_BP_A2 + 1)
+#define KEY_D_PEDSTD_INT_THRSH (KEY_D_PEDSTD_BP_A1 + 1)
+#define KEY_D_PEDSTD_CLIP (KEY_D_PEDSTD_INT_THRSH + 1)
+#define KEY_D_PEDSTD_SB (KEY_D_PEDSTD_CLIP + 1)
+#define KEY_D_PEDSTD_SB_TIME (KEY_D_PEDSTD_SB + 1)
+#define KEY_D_PEDSTD_PEAKTHRSH (KEY_D_PEDSTD_SB_TIME + 1)
+#define KEY_D_PEDSTD_TIML (KEY_D_PEDSTD_PEAKTHRSH + 1)
+#define KEY_D_PEDSTD_TIMH (KEY_D_PEDSTD_TIML + 1)
+#define KEY_D_PEDSTD_PEAK (KEY_D_PEDSTD_TIMH + 1)
+#define KEY_D_PEDSTD_TIMECTR (KEY_D_PEDSTD_PEAK + 1)
+#define KEY_D_PEDSTD_STEPCTR (KEY_D_PEDSTD_TIMECTR + 1)
+#define KEY_D_PEDSTD_STEPCTR2 (KEY_D_PEDSTD_STEPCTR + 1)
+#define KEY_D_PEDSTD_WALKTIME (KEY_D_PEDSTD_STEPCTR2 + 1)
+#define KEY_D_PEDSTD_DECI (KEY_D_PEDSTD_WALKTIME + 1)
+#define KEY_D_PEDSTD_SB2 (KEY_D_PEDSTD_DECI + 1)
+#define KEY_D_PEDSTD_DRIVE_STATE (KEY_D_PEDSTD_SB2 + 1)
+#define KEY_CFG_PED_INT (KEY_D_PEDSTD_DRIVE_STATE + 1)
+#define KEY_CFG_PED_ENABLE (KEY_CFG_PED_INT + 1)
+#define KEY_D_STPDET_TIMESTAMP (KEY_CFG_PED_ENABLE + 1)
+
+/*Host Based No Motion*/
+#define KEY_D_HOST_NO_MOT (KEY_D_STPDET_TIMESTAMP + 1)
+
+/*Host Based Accel Bias*/
+#define KEY_D_ACCEL_BIAS (KEY_D_HOST_NO_MOT + 1)
+
+/*Screen/Display Orientation Keys*/
+#define KEY_D_ORIENT_GAP (KEY_D_ACCEL_BIAS + 1)
+#define KEY_D_TILT0_H (KEY_D_ORIENT_GAP + 1)
+#define KEY_D_TILT0_L (KEY_D_TILT0_H + 1)
+#define KEY_D_TILT1_H (KEY_D_TILT0_L + 1)
+#define KEY_D_TILT1_L (KEY_D_TILT1_H + 1)
+#define KEY_D_TILT2_H (KEY_D_TILT1_L + 1)
+#define KEY_D_TILT2_L (KEY_D_TILT2_H + 1)
+#define KEY_D_TILT3_H (KEY_D_TILT2_L + 1)
+#define KEY_D_TILT3_L (KEY_D_TILT3_H + 1)
+
+#define KEY_STREAM_P_ACCEL_Z (KEY_D_TILT3_L + 1)
+
+/* Batch mode */
+#define KEY_BM_ENABLE (KEY_STREAM_P_ACCEL_Z + 1)
+#define KEY_BM_BATCH_THLD (KEY_BM_ENABLE + 1)
+#define KEY_BM_BATCH_CNTR (KEY_BM_BATCH_THLD + 1)
+#define KEY_BM_NUMWORD_TOFILL (KEY_BM_BATCH_CNTR + 1)
+
+/* Watermark */
+#define KEY_CFG_WATERMARK_H (KEY_BM_NUMWORD_TOFILL + 1)
+#define KEY_CFG_WATERMARK_L (KEY_CFG_WATERMARK_H + 1)
+
+/* FIFO output control */
+#define KEY_CFG_OUT_ACCL (KEY_CFG_WATERMARK_L + 1)
+#define KEY_CFG_OUT_GYRO (KEY_CFG_OUT_ACCL + 1)
+#define KEY_CFG_OUT_3QUAT (KEY_CFG_OUT_GYRO + 1)
+#define KEY_CFG_OUT_6QUAT (KEY_CFG_OUT_3QUAT + 1)
+#define KEY_CFG_OUT_9QUAT (KEY_CFG_OUT_6QUAT + 1)
+#define KEY_CFG_OUT_PQUAT (KEY_CFG_OUT_9QUAT + 1)
+#define KEY_CFG_OUT_PQUAT9 (KEY_CFG_OUT_PQUAT + 1)
+#define KEY_CFG_OUT_CPASS (KEY_CFG_OUT_PQUAT9 + 1)
+#define KEY_CFG_OUT_PRESS (KEY_CFG_OUT_CPASS + 1)
+#define KEY_CFG_OUT_STEPDET (KEY_CFG_OUT_PRESS + 1)
+#define KEY_CFG_FIFO_INT (KEY_CFG_OUT_STEPDET + 1)
+
+/* Ped Step detection */
+#define KEY_CFG_PEDSTEP_DET (KEY_CFG_FIFO_INT + 1)
+
+/* Screen Orientation data */
+#define KEY_SO_DATA (KEY_CFG_PEDSTEP_DET + 1)
+
+/* MPU for DMP Android K */
+#define KEY_P_INIT (KEY_SO_DATA + 1)
+#define KEY_P_HW_ID (KEY_P_INIT + 1)
+
+/* DMP running counter */
+#define KEY_DMP_RUN_CNTR (KEY_P_HW_ID + 1)
+
+/* Sensor's ODR */
+#define KEY_CFG_3QUAT_ODR (KEY_DMP_RUN_CNTR + 1)
+#define KEY_CFG_6QUAT_ODR (KEY_CFG_3QUAT_ODR + 1)
+#define KEY_CFG_9QUAT_ODR (KEY_CFG_6QUAT_ODR + 1)
+#define KEY_CFG_PQUAT6_ODR (KEY_CFG_9QUAT_ODR + 1)
+#define KEY_CFG_PQUAT9_ODR (KEY_CFG_PQUAT6_ODR + 1)
+#define KEY_CFG_ACCL_ODR (KEY_CFG_PQUAT9_ODR + 1)
+#define KEY_CFG_GYRO_ODR (KEY_CFG_ACCL_ODR + 1)
+#define KEY_CFG_CPASS_ODR (KEY_CFG_GYRO_ODR + 1)
+#define KEY_CFG_PRESS_ODR (KEY_CFG_CPASS_ODR + 1)
+
+#define KEY_ODR_CNTR_3QUAT (KEY_CFG_PRESS_ODR + 1)
+#define KEY_ODR_CNTR_6QUAT (KEY_ODR_CNTR_3QUAT + 1)
+#define KEY_ODR_CNTR_9QUAT (KEY_ODR_CNTR_6QUAT + 1)
+#define KEY_ODR_CNTR_PQUAT (KEY_ODR_CNTR_9QUAT + 1)
+#define KEY_ODR_CNTR_PQUAT9 (KEY_ODR_CNTR_PQUAT + 1)
+#define KEY_ODR_CNTR_ACCL (KEY_ODR_CNTR_PQUAT9 + 1)
+#define KEY_ODR_CNTR_GYRO (KEY_ODR_CNTR_ACCL + 1)
+#define KEY_ODR_CNTR_CPASS (KEY_ODR_CNTR_GYRO + 1)
+#define KEY_ODR_CNTR_PRESS (KEY_ODR_CNTR_CPASS + 1)
+
+/* DMP fusion LP-Quat */
+#define KEY_DMP_LPQ0 (KEY_ODR_CNTR_PRESS + 1)
+#define KEY_DMP_LPQ1 (KEY_DMP_LPQ0 + 1)
+#define KEY_DMP_LPQ2 (KEY_DMP_LPQ1 + 1)
+#define KEY_DMP_LPQ3 (KEY_DMP_LPQ2 + 1)
+
+/* DMP fusion 6-axis Quat */
+#define KEY_DMP_Q0 (KEY_DMP_LPQ3 + 1)
+#define KEY_DMP_Q1 (KEY_DMP_Q0 + 1)
+#define KEY_DMP_Q2 (KEY_DMP_Q1 + 1)
+#define KEY_DMP_Q3 (KEY_DMP_Q2 + 1)
+
+/* 9-axis fusion */
+#define KEY_CPASS_VALID (KEY_DMP_Q3 + 1)
+#define KEY_9AXIS_ACCURACY (KEY_CPASS_VALID + 1)
+#define KEY_DMP_9Q0 (KEY_9AXIS_ACCURACY + 1)
+#define KEY_DMP_9Q1 (KEY_DMP_9Q0 + 1)
+#define KEY_DMP_9Q2 (KEY_DMP_9Q1 + 1)
+#define KEY_DMP_9Q3 (KEY_DMP_9Q2 + 1)
+
+/* Test key */
+#define KEY_TEST_01 (KEY_9AXIS_ACCURACY + 1)
+#define KEY_TEST_02 (KEY_TEST_01 + 1)
+#define KEY_TEST_03 (KEY_TEST_02 + 1)
+#define KEY_TEST_04 (KEY_TEST_03 + 1)
+#define KEY_TEST_05 (KEY_TEST_04 + 1)
+#define KEY_TEST_06 (KEY_TEST_05 + 1)
+#define KEY_TEST_07 (KEY_TEST_06 + 1)
+#define KEY_TEST_XX (KEY_TEST_07 + 1)
+
+#define NUM_KEYS (KEY_TEST_XX + 1)
+
+struct tKeyLabel {
+ unsigned short key;
+ unsigned short addr;
+};
+
+#define DINA0A 0x0a
+#define DINA22 0x22
+#define DINA42 0x42
+#define DINA5A 0x5a
+
+#define DINA06 0x06
+#define DINA0E 0x0e
+#define DINA16 0x16
+#define DINA1E 0x1e
+#define DINA26 0x26
+#define DINA2E 0x2e
+#define DINA36 0x36
+#define DINA3E 0x3e
+#define DINA46 0x46
+#define DINA4E 0x4e
+#define DINA56 0x56
+#define DINA5E 0x5e
+#define DINA66 0x66
+#define DINA6E 0x6e
+#define DINA76 0x76
+#define DINA7E 0x7e
+
+#define DINA00 0x00
+#define DINA08 0x08
+#define DINA10 0x10
+#define DINA18 0x18
+#define DINA20 0x20
+#define DINA28 0x28
+#define DINA30 0x30
+#define DINA38 0x38
+#define DINA40 0x40
+#define DINA48 0x48
+#define DINA50 0x50
+#define DINA58 0x58
+#define DINA60 0x60
+#define DINA68 0x68
+#define DINA70 0x70
+#define DINA78 0x78
+
+#define DINA04 0x04
+#define DINA0C 0x0c
+#define DINA14 0x14
+#define DINA1C 0x1C
+#define DINA24 0x24
+#define DINA2C 0x2c
+#define DINA34 0x34
+#define DINA3C 0x3c
+#define DINA44 0x44
+#define DINA4C 0x4c
+#define DINA54 0x54
+#define DINA5C 0x5c
+#define DINA64 0x64
+#define DINA6C 0x6c
+#define DINA74 0x74
+#define DINA7C 0x7c
+
+#define DINA01 0x01
+#define DINA09 0x09
+#define DINA11 0x11
+#define DINA19 0x19
+#define DINA21 0x21
+#define DINA29 0x29
+#define DINA31 0x31
+#define DINA39 0x39
+#define DINA41 0x41
+#define DINA49 0x49
+#define DINA51 0x51
+#define DINA59 0x59
+#define DINA61 0x61
+#define DINA69 0x69
+#define DINA71 0x71
+#define DINA79 0x79
+
+#define DINA25 0x25
+#define DINA2D 0x2d
+#define DINA35 0x35
+#define DINA3D 0x3d
+#define DINA4D 0x4d
+#define DINA55 0x55
+#define DINA5D 0x5D
+#define DINA6D 0x6d
+#define DINA75 0x75
+#define DINA7D 0x7d
+
+#define DINADC 0xdc
+#define DINAF2 0xf2
+#define DINAAB 0xab
+#define DINAAA 0xaa
+#define DINAF1 0xf1
+#define DINADF 0xdf
+#define DINADA 0xda
+#define DINAB1 0xb1
+#define DINAB9 0xb9
+#define DINAF3 0xf3
+#define DINA8B 0x8b
+#define DINAA3 0xa3
+#define DINA91 0x91
+#define DINAB6 0xb6
+#define DINAB4 0xb4
+
+#define DINC00 0x00
+#define DINC01 0x01
+#define DINC02 0x02
+#define DINC03 0x03
+#define DINC08 0x08
+#define DINC09 0x09
+#define DINC0A 0x0a
+#define DINC0B 0x0b
+#define DINC10 0x10
+#define DINC11 0x11
+#define DINC12 0x12
+#define DINC13 0x13
+#define DINC18 0x18
+#define DINC19 0x19
+#define DINC1A 0x1a
+#define DINC1B 0x1b
+
+#define DINC20 0x20
+#define DINC21 0x21
+#define DINC22 0x22
+#define DINC23 0x23
+#define DINC28 0x28
+#define DINC29 0x29
+#define DINC2A 0x2a
+#define DINC2B 0x2b
+#define DINC30 0x30
+#define DINC31 0x31
+#define DINC32 0x32
+#define DINC33 0x33
+#define DINC38 0x38
+#define DINC39 0x39
+#define DINC3A 0x3a
+#define DINC3B 0x3b
+
+#define DINC40 0x40
+#define DINC41 0x41
+#define DINC42 0x42
+#define DINC43 0x43
+#define DINC48 0x48
+#define DINC49 0x49
+#define DINC4A 0x4a
+#define DINC4B 0x4b
+#define DINC50 0x50
+#define DINC51 0x51
+#define DINC52 0x52
+#define DINC53 0x53
+#define DINC58 0x58
+#define DINC59 0x59
+#define DINC5A 0x5a
+#define DINC5B 0x5b
+
+#define DINC60 0x60
+#define DINC61 0x61
+#define DINC62 0x62
+#define DINC63 0x63
+#define DINC68 0x68
+#define DINC69 0x69
+#define DINC6A 0x6a
+#define DINC6B 0x6b
+#define DINC70 0x70
+#define DINC71 0x71
+#define DINC72 0x72
+#define DINC73 0x73
+#define DINC78 0x78
+#define DINC79 0x79
+#define DINC7A 0x7a
+#define DINC7B 0x7b
+#define DIND40 0x40
+#define DINA80 0x80
+#define DINA90 0x90
+#define DINAA0 0xa0
+#define DINAC9 0xc9
+#define DINACB 0xcb
+#define DINACD 0xcd
+#define DINACF 0xcf
+#define DINAC8 0xc8
+#define DINACA 0xca
+#define DINACC 0xcc
+#define DINACE 0xce
+#define DINAD8 0xd8
+#define DINADD 0xdd
+#define DINAF8 0xf0
+#define DINAFE 0xfe
+
+#define DINBF8 0xf8
+#define DINAC0 0xb0
+#define DINAC1 0xb1
+#define DINAC2 0xb4
+#define DINAC3 0xb5
+#define DINAC4 0xb8
+#define DINAC5 0xb9
+#define DINBC0 0xc0
+#define DINBC2 0xc2
+#define DINBC4 0xc4
+#define DINBC6 0xc6
+
+#endif
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpmap.h b/drivers/staging/iio/imu/inv_mpu/dmpmap.h
new file mode 100644
index 00000000000..92936372224
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/dmpmap.h
@@ -0,0 +1,263 @@
+/*
+* 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_TAP_DIRECTION 68
+#define DMP_TAP_COUNT 70
+#define DMP_TAP_GATE 72
+#define DMP_TAP_MIN_TAPS 74
+#define DMP_BERR2NH 76
+#define DMP_SMCINC 78
+#define DMP_TAP_SHAKE_REJECT 80
+#define DMP_ANGVBXL 82
+#define DMP_TAP_SHAKE_COUNT_MAX 84
+#define DMP_TAP_SHAKE_TIMEOUT_MAX 86
+#define DMP_TAP_THZ 88
+#define DMP_TAPW_MIN 90
+#define DMP_TAP_PREV_JERK_Z 92
+#define DMP_TAP_NEXT_TAP_THRES 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_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/inv_mpu/inv_mpu3050_iio.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c
new file mode 100644
index 00000000000..1fc5ff98097
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c
@@ -0,0 +1,271 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define MPU3050_NACK_MIN_TIME (2 * 1000)
+#define MPU3050_NACK_MAX_TIME (3 * 1000)
+
+#define MPU3050_ONE_MPU_TIME 20
+#define MPU3050_BOGUS_ADDR 0x7F
+int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+int set_3050_bypass(struct inv_mpu_state *st, bool enable)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 b;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &b);
+ if (result)
+ return result;
+ if (((b & BIT_3050_AUX_IF_EN) == 0) && enable)
+ return 0;
+ if ((b & BIT_3050_AUX_IF_EN) && (enable == 0))
+ return 0;
+ b &= ~BIT_3050_AUX_IF_EN;
+ if (!enable) {
+ b |= BIT_3050_AUX_IF_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ return result;
+ } else {
+ /* Coming out of I2C is tricky due to several erratta. Do not
+ * modify this algorithm
+ */
+ /*
+ * 1) wait for the right time and send the command to change
+ * the aux i2c slave address to an invalid address that will
+ * get nack'ed
+ *
+ * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
+ */
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ MPU3050_BOGUS_ADDR);
+ if (result)
+ return result;
+ /*
+ * 2) wait enough time for a nack to occur, then go into
+ * bypass mode:
+ */
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ /*
+ * 3) wait for up to one MPU cycle then restore the slave
+ * address
+ */
+ msleep(MPU3050_ONE_MPU_TIME);
+
+ result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, b);
+ if (result)
+ return result;
+ usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME);
+ }
+ return 0;
+}
+
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg)
+{
+ reg->fifo_en = REG_3050_FIFO_EN;
+ reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV;
+ reg->lpf = REG_3050_LPF;
+ reg->fifo_count_h = REG_3050_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_3050_FIFO_R_W;
+ reg->user_ctrl = REG_3050_USER_CTRL;
+ reg->pwr_mgmt_1 = REG_3050_PWR_MGMT_1;
+ reg->raw_accel = REG_3050_AUX_XOUT_H;
+ reg->temperature = REG_3050_TEMPERATURE;
+ reg->int_enable = REG_3050_INT_ENABLE;
+ reg->int_status = REG_3050_INT_STATUS;
+}
+
+int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (en) {
+ data = INV_CLK_PLL;
+ p = (BITS_3050_POWER1 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = (BITS_3050_POWER2 | data);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ if (result)
+ return result;
+ p = data;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ msleep(SENSOR_UP_TIME);
+ } else {
+ p = BITS_3050_GYRO_STANDBY;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p);
+ }
+
+ return result;
+}
+
+int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en)
+{
+ int result;
+ if (NULL == st->slave_accel)
+ return -EPERM;
+ if (en)
+ result = st->slave_accel->resume(st);
+ else
+ result = st->slave_accel->suspend(st);
+
+ return result;
+}
+
+/**
+ * inv_init_config_mpu3050() - Initialize hardware, disable FIFO.
+ * @st: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ * Clock source: Gyro PLL
+ */
+int inv_init_config_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 data;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ if (st->chip_config.is_asleep)
+ return -EPERM;
+ /*reading AUX VDDIO register */
+ result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data);
+ if (result)
+ return result;
+ data &= ~BIT_3050_VDDIO;
+ if (st->plat_data.level_shifter)
+ data |= BIT_3050_VDDIO;
+ result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data);
+ if (result)
+ return result;
+
+ reg = &st->reg;
+ /*2000dps full scale range*/
+ result = inv_i2c_single_write(st, reg->lpf,
+ (INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT)
+ | INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.fsr = INV_FSR_2000DPS;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+ st->chip_info.multi = 1;
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ/INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) &&
+ st->slave_accel) {
+ result = st->slave_accel->setup(st);
+ if (result)
+ return result;
+ result = st->slave_accel->set_fs(st, INV_FS_02G);
+ if (result)
+ return result;
+ result = st->slave_accel->set_lpf(st, INIT_FIFO_RATE);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * set_power_mpu3050() - set power of mpu3050.
+ * @st: Device driver instance.
+ * @power_on: on/off
+ */
+int set_power_mpu3050(struct inv_mpu_state *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, p;
+ int result;
+ reg = &st->reg;
+ if (power_on) {
+ data = 0;
+ } else {
+ if (st->slave_accel) {
+ result = st->slave_accel->suspend(st);
+ if (result)
+ return result;
+ }
+ data = BIT_SLEEP;
+ }
+ if (st->chip_config.gyro_enable) {
+ p = (BITS_3050_POWER1 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = (BITS_3050_POWER2 | INV_CLK_PLL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+
+ p = INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p);
+ if (result)
+ return result;
+ } else {
+ data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL);
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+ }
+ if (power_on) {
+ msleep(POWER_UP_TIME);
+ if (st->slave_accel) {
+ result = st->slave_accel->resume(st);
+ if (result)
+ return result;
+ }
+ }
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c
new file mode 100644
index 00000000000..94c6b5d49ae
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c
@@ -0,0 +1,3148 @@
+/*
+* 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 <linux/spinlock.h>
+#include <linux/iio/sysfs.h>
+
+/* OLIO */
+#include "olio_specials.h"
+
+#include "inv_mpu_iio.h"
+
+#include "inv_test/inv_counters.h"
+
+#ifdef CONFIG_DTS_INV_MPU_IIO
+#include "inv_mpu_dts.h"
+#endif
+
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
+
+/* This is for compatibility for power state. Should remove once HAL
+ does not use power_state sysfs entry */
+static bool fake_asleep;
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+ {119, "ITG3500"},
+ { 63, "MPU3050"},
+ {117, "MPU6050"},
+ {118, "MPU9150"},
+ {128, "MPU6500"},
+ {128, "MPU9250"},
+ {128, "MPU9350"},
+ {128, "MPU6515"},
+};
+
+static const u8 reg_gyro_offset[] = {REG_XG_OFFS_USRH,
+ REG_XG_OFFS_USRH + 2,
+ REG_XG_OFFS_USRH + 4};
+
+const u8 reg_6050_accel_offset[] = {REG_XA_OFFS_H,
+ REG_XA_OFFS_H + 2,
+ REG_XA_OFFS_H + 4};
+
+const u8 reg_6500_accel_offset[] = {REG_6500_XA_OFFS_H,
+ REG_6500_YA_OFFS_H,
+ REG_6500_ZA_OFFS_H};
+#ifdef CONFIG_INV_TESTING
+static bool suspend_state;
+static int inv_mpu_suspend(struct device *dev);
+static int inv_mpu_resume(struct device *dev);
+struct test_data_out {
+ bool gyro;
+ bool accel;
+ bool compass;
+ bool pressure;
+ bool LPQ;
+ bool SIXQ;
+ bool PEDQ;
+};
+static struct test_data_out data_out_control;
+#endif
+
+static void inv_setup_reg(struct inv_reg_map_s *reg)
+{
+ reg->sample_rate_div = REG_SAMPLE_RATE_DIV;
+ reg->lpf = REG_CONFIG;
+ reg->bank_sel = REG_BANK_SEL;
+ reg->user_ctrl = REG_USER_CTRL;
+ reg->fifo_en = REG_FIFO_EN;
+ reg->gyro_config = REG_GYRO_CONFIG;
+ reg->accel_config = REG_ACCEL_CONFIG;
+ reg->fifo_count_h = REG_FIFO_COUNT_H;
+ reg->fifo_r_w = REG_FIFO_R_W;
+ reg->raw_accel = REG_RAW_ACCEL;
+ reg->temperature = REG_TEMPERATURE;
+ reg->int_enable = REG_INT_ENABLE;
+ reg->int_status = REG_INT_STATUS;
+ reg->pwr_mgmt_1 = REG_PWR_MGMT_1;
+ reg->pwr_mgmt_2 = REG_PWR_MGMT_2;
+ reg->mem_start_addr = REG_MEM_START_ADDR;
+ reg->mem_r_w = REG_MEM_RW;
+ reg->prgm_strt_addrh = REG_PRGM_STRT_ADDRH;
+};
+
+/**
+ * inv_i2c_read_base() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @i2c_addr: i2c address of device.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 2);
+
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3);
+ INV_I2C_INC_MPUREAD(length);
+#ifdef CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name,
+ i2c_addr, reg, length,
+ wr_pr_debug_begin(data, length, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+ return res;
+}
+
+/**
+ * inv_i2c_single_write_base() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @i2c_addr: I2C address of the device.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ * NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data)
+{
+ u8 tmp[2];
+ struct i2c_msg msg;
+ int res;
+ tmp[0] = reg;
+ tmp[1] = data;
+
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data);
+ INV_I2C_INC_MPUWRITE(3);
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask)
+{
+ struct inv_reg_map_s *reg;
+ u8 data, mgmt_1;
+ int result;
+
+ reg = &st->reg;
+ /* Only when gyro is on, can
+ clock source be switched to gyro. Otherwise, it must be set to
+ internal clock */
+ if (BIT_PWR_GYRO_STBY == mask) {
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1);
+ if (result)
+ return result;
+
+ mgmt_1 &= ~BIT_CLK_MASK;
+ }
+
+ if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ /* turning off gyro requires switch to internal clock first.
+ Then turn off gyro engine */
+ mgmt_1 |= INV_CLK_INTERNAL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data);
+ if (result)
+ return result;
+ if (en)
+ data &= (~mask);
+ else
+ data |= mask;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data);
+ if (result)
+ return result;
+
+ if ((BIT_PWR_GYRO_STBY == mask) && en) {
+ /* only gyro on needs sensor up time */
+ msleep(SENSOR_UP_TIME);
+ /* after gyro is on & stable, switch internal clock to PLL */
+ mgmt_1 |= INV_CLK_PLL;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1,
+ mgmt_1);
+ if (result)
+ return result;
+ }
+ if ((BIT_PWR_ACCEL_STBY == mask) && en)
+ msleep(REG_UP_TIME);
+
+ return 0;
+}
+
+/*
+ * inv_lpa_freq() - store current low power frequency setting.
+ */
+static int inv_lpa_freq(struct inv_mpu_state *st, int lpa_freq)
+{
+ unsigned long result;
+ u8 d;
+ /* 2, 4, 6, 7 corresponds to 0.98, 3.91, 15.63, 31.25 */
+ const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7};
+
+ if (lpa_freq > MAX_LPA_FREQ_PARAM)
+ return -EINVAL;
+
+ if (INV_MPU6500 == st->chip_type) {
+ d = mpu6500_lpa_mapping[lpa_freq];
+ result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d);
+ if (result)
+ return result;
+ }
+ st->chip_config.lpa_freq = lpa_freq;
+
+ return 0;
+}
+
+static int set_power_itg(struct inv_mpu_state *st, bool power_on)
+{
+ struct inv_reg_map_s *reg;
+ u8 data;
+ int result;
+
+ if ((!power_on) == st->chip_config.is_asleep)
+ return 0;
+ reg = &st->reg;
+ if (power_on)
+ data = 0;
+ else
+ data = BIT_SLEEP;
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data);
+ if (result)
+ return result;
+
+ if (power_on)
+ msleep(REG_UP_TIME);
+
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+
+/**
+ * inv_init_config() - Initialize hardware, disable FIFO.
+ * @indio_dev: Device driver instance.
+ * Initial configuration:
+ * FSR: +/- 2000DPS
+ * DLPF: 42Hz
+ * FIFO rate: 50Hz
+ */
+static int inv_init_config(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result, i;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ const u8 *ch;
+ u8 d[2];
+
+ reg = &st->reg;
+
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
+ if (result)
+ return result;
+
+ st->chip_config.fsr = INV_FSR_2000DPS;
+
+ result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
+ if (result)
+ return result;
+ st->chip_config.lpf = INV_FILTER_42HZ;
+
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ ONE_K_HZ / INIT_FIFO_RATE - 1);
+ if (result)
+ return result;
+ st->chip_config.fifo_rate = INIT_FIFO_RATE;
+ st->irq_dur_ns = INIT_DUR_TIME;
+ st->chip_config.prog_start_addr = DMP_START_ADDR;
+ if (INV_MPU6050 == st->chip_type)
+ st->self_test.samples = INIT_ST_MPU6050_SAMPLES;
+ else
+ st->self_test.samples = INIT_ST_SAMPLES;
+ st->self_test.threshold = INIT_ST_THRESHOLD;
+ st->batch.wake_fifo_on = true;
+ if (INV_ITG3500 != st->chip_type) {
+ st->chip_config.accel_fs = INV_FS_02G;
+ result = inv_i2c_single_write(st, reg->accel_config,
+ (INV_FS_02G << ACCEL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+ st->tap.time = INIT_TAP_TIME;
+ st->tap.thresh = INIT_TAP_THRESHOLD;
+ st->tap.min_count = INIT_TAP_MIN_COUNT;
+ st->sample_divider = INIT_SAMPLE_DIVIDER;
+ st->smd.threshold = MPU_INIT_SMD_THLD;
+ st->smd.delay = MPU_INIT_SMD_DELAY_THLD;
+ st->smd.delay2 = MPU_INIT_SMD_DELAY2_THLD;
+ st->ped.int_thresh = INIT_PED_INT_THRESH;
+ st->ped.step_thresh = INIT_PED_THRESH;
+ st->sensor[SENSOR_STEP].rate = MAX_DMP_OUTPUT_RATE;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR,
+ INIT_MOT_DUR);
+ if (result)
+ return result;
+ st->mot_int.mot_dur = INIT_MOT_DUR;
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR,
+ INIT_MOT_THR);
+ if (result)
+ return result;
+ st->mot_int.mot_thr = INIT_MOT_THR;
+
+ for (i = 0; i < 3; i++) {
+ result = inv_i2c_read(st, reg_gyro_offset[i], 2, d);
+ if (result)
+ return result;
+ st->rom_gyro_offset[i] =
+ (short)be16_to_cpup((__be16 *)(d));
+ st->input_gyro_offset[i] = 0;
+ st->input_gyro_dmp_bias[i] = 0;
+ }
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+ for (i = 0; i < 3; i++) {
+ result = inv_i2c_read(st, ch[i], 2, d);
+ if (result)
+ return result;
+ st->rom_accel_offset[i] =
+ (short)be16_to_cpup((__be16 *)(d));
+ st->input_accel_offset[i] = 0;
+ st->input_accel_dmp_bias[i] = 0;
+ }
+ st->ped.step = 0;
+ st->ped.time = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_write_fsr() - Configure the gyro's scale range.
+ */
+static int inv_write_fsr(struct inv_mpu_state *st, int fsr)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+
+ reg = &st->reg;
+ if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM))
+ return -EINVAL;
+ if (fsr == st->chip_config.fsr)
+ return 0;
+
+ if (INV_MPU3050 == st->chip_type)
+ result = inv_i2c_single_write(st, reg->lpf,
+ (fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf);
+ else
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ fsr << GYRO_CONFIG_FSR_SHIFT);
+
+ if (result)
+ return result;
+ st->chip_config.fsr = fsr;
+
+ return 0;
+}
+
+/*
+ * inv_write_accel_fs() - Configure the accelerometer's scale range.
+ */
+static int inv_write_accel_fs(struct inv_mpu_state *st, int fs)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ if (fs < 0 || fs > MAX_ACCEL_FS_PARAM)
+ return -EINVAL;
+ if (fs == st->chip_config.accel_fs)
+ return 0;
+ if (INV_MPU3050 == st->chip_type)
+ result = st->slave_accel->set_fs(st, fs);
+ else
+ result = inv_i2c_single_write(st, reg->accel_config,
+ (fs << ACCEL_CONFIG_FSR_SHIFT));
+ if (result)
+ return result;
+
+ st->chip_config.accel_fs = fs;
+
+ return 0;
+}
+
+static int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val)
+{
+ int result;
+ u8 d;
+
+ d = ((val >> 8) & 0xff);
+ result = inv_i2c_single_write(st, reg, d);
+ if (result)
+ return result;
+
+ d = (val & 0xff);
+ result = inv_i2c_single_write(st, reg + 1, d);
+
+ return result;
+}
+
+int inv_reset_offset_reg(struct inv_mpu_state *st, bool en)
+{
+ const u8 *ch;
+ int i, result;
+ s16 gyro[3], accel[3];
+
+ if (en) {
+ for (i = 0; i < 3; i++) {
+ gyro[i] = st->rom_gyro_offset[i];
+ accel[i] = st->rom_accel_offset[i];
+ }
+ } else {
+ for (i = 0; i < 3; i++) {
+ gyro[i] = st->rom_gyro_offset[i] +
+ st->input_gyro_offset[i];
+ accel[i] = st->rom_accel_offset[i] +
+ (st->input_accel_offset[i] << 1);
+ }
+ }
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+
+ for (i = 0; i < 3; i++) {
+ result = inv_set_offset_reg(st, reg_gyro_offset[i], gyro[i]);
+ if (result)
+ return result;
+ result = inv_set_offset_reg(st, ch[i], accel[i]);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+/*
+ * inv_fifo_rate_store() - Set fifo rate.
+ */
+static int inv_fifo_rate_store(struct inv_mpu_state *st, int fifo_rate)
+{
+ if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE))
+ return -EINVAL;
+ if (fifo_rate == st->chip_config.fifo_rate)
+ return 0;
+
+ st->irq_dur_ns = NSEC_PER_SEC / fifo_rate;
+ st->chip_config.fifo_rate = fifo_rate;
+
+ return 0;
+}
+
+/*
+ * inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ii;
+ char data;
+ ssize_t bytes_printed = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (ii = 0; ii < st->hw->num_reg; ii++) {
+ /* don't read fifo r/w register */
+ if (ii == st->reg.fifo_r_w)
+ data = 0;
+ else
+ inv_i2c_read(st, ii, 1, &data);
+ bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n",
+ ii, data);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return bytes_printed;
+}
+
+int write_be32_key_to_mem(struct inv_mpu_state *st,
+ u32 data, int key)
+{
+ cpu_to_be32s(&data);
+ return mem_w_key(key, sizeof(data), (u8 *)&data);
+}
+
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data)
+{
+ u8 d[2];
+
+ if (data < 0 || data > USHRT_MAX)
+ return -EINVAL;
+
+ d[0] = (u8)((data >> 8) & 0xff);
+ d[1] = (u8)(data & 0xff);
+
+ return mem_w_key(k, ARRAY_SIZE(d), d);
+}
+
+static ssize_t _dmp_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data, tmp;
+
+ if (!st->chip_config.firmware_loaded)
+ return -EINVAL;
+
+ if (!st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ }
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto dmp_bias_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_ACCEL_X_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[0];
+ st->input_accel_dmp_bias[0] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[0] = tmp;
+ break;
+ case ATTR_DMP_ACCEL_Y_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[1];
+ st->input_accel_dmp_bias[1] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[1] = tmp;
+ break;
+ case ATTR_DMP_ACCEL_Z_DMP_BIAS:
+ tmp = st->input_accel_dmp_bias[2];
+ st->input_accel_dmp_bias[2] = data;
+ result = inv_set_accel_bias_dmp(st);
+ if (result)
+ st->input_accel_dmp_bias[2] = tmp;
+ break;
+ case ATTR_DMP_GYRO_X_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_X);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[0] = data;
+ break;
+ case ATTR_DMP_GYRO_Y_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_Y);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[1] = data;
+ break;
+ case ATTR_DMP_GYRO_Z_DMP_BIAS:
+ result = write_be32_key_to_mem(st, data,
+ KEY_CFG_EXT_GYRO_BIAS_Z);
+ if (result)
+ goto dmp_bias_store_fail;
+ st->input_gyro_dmp_bias[2] = data;
+ break;
+ default:
+ break;
+ }
+
+dmp_bias_store_fail:
+ if (!st->chip_config.enable)
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_dmp_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _dmp_bias_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t _dmp_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ if (st->chip_config.enable)
+ return -EBUSY;
+ if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) {
+ if (!st->chip_config.firmware_loaded)
+ return -EINVAL;
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ }
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto dmp_attr_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_PED_INT_ON:
+ result = inv_enable_pedometer_interrupt(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->ped.int_on = !!data;
+ break;
+ case ATTR_DMP_PED_ON:
+ {
+ result = inv_enable_pedometer(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->ped.on = !!data;
+ break;
+ }
+ case ATTR_DMP_PED_STEP_THRESH:
+ {
+ result = inv_write_2bytes(st, KEY_D_PEDSTD_SB, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->ped.step_thresh = data;
+ break;
+ }
+ case ATTR_DMP_PED_INT_THRESH:
+ {
+ result = inv_write_2bytes(st, KEY_D_PEDSTD_SB2, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->ped.int_thresh = data;
+ break;
+ }
+ case ATTR_DMP_SMD_ENABLE:
+ result = inv_write_2bytes(st, KEY_SMD_ENABLE, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->chip_config.smd_enable = !!data;
+ break;
+ case ATTR_DMP_SMD_THLD:
+ if (data < 0 || data > SHRT_MAX)
+ goto dmp_attr_store_fail;
+ result = write_be32_key_to_mem(st, data << 16,
+ KEY_SMD_ACCEL_THLD);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->smd.threshold = data;
+ break;
+ case ATTR_DMP_SMD_DELAY_THLD:
+ if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+ goto dmp_attr_store_fail;
+ result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+ KEY_SMD_DELAY_THLD);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->smd.delay = data;
+ break;
+ case ATTR_DMP_SMD_DELAY_THLD2:
+ if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ)
+ goto dmp_attr_store_fail;
+ result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ,
+ KEY_SMD_DELAY2_THLD);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->smd.delay2 = data;
+ break;
+ case ATTR_DMP_TAP_ON:
+ result = inv_enable_tap_dmp(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.on = !!data;
+ break;
+ case ATTR_DMP_TAP_THRESHOLD:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_tap_threshold_dmp(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.thresh = data;
+ break;
+ case ATTR_DMP_TAP_MIN_COUNT:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_min_taps_dmp(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.min_count = data;
+ break;
+ case ATTR_DMP_TAP_TIME:
+ if (data < 0 || data > USHRT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ result = inv_set_tap_time_dmp(st, data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->tap.time = data;
+ break;
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ result = inv_set_display_orient_interrupt_dmp(st, !!data);
+ if (result)
+ goto dmp_attr_store_fail;
+ st->chip_config.display_orient_on = !!data;
+ break;
+ /* from here, power of chip is not turned on */
+ case ATTR_DMP_ON:
+ st->chip_config.dmp_on = !!data;
+ break;
+ case ATTR_DMP_INT_ON:
+ st->chip_config.dmp_int_on = !!data;
+ break;
+ case ATTR_DMP_EVENT_INT_ON:
+ st->chip_config.dmp_event_int_on = !!data;
+ break;
+ case ATTR_DMP_STEP_INDICATOR_ON:
+ st->chip_config.step_indicator_on = !!data;
+ break;
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ if (data < 0 || data > INT_MAX) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ st->batch.timeout = data;
+ break;
+ case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL:
+ st->batch.wake_fifo_on = !!data;
+ st->batch.overflow_on = 0;
+ break;
+ case ATTR_DMP_SIX_Q_ON:
+ st->sensor[SENSOR_SIXQ].on = !!data;
+ break;
+ case ATTR_DMP_SIX_Q_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ st->sensor[SENSOR_SIXQ].rate = data;
+ st->sensor[SENSOR_SIXQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_SIXQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_LPQ_ON:
+ st->sensor[SENSOR_LPQ].on = !!data;
+ break;
+ case ATTR_DMP_LPQ_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ st->sensor[SENSOR_LPQ].rate = data;
+ st->sensor[SENSOR_LPQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_LPQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_PED_Q_ON:
+ st->sensor[SENSOR_PEDQ].on = !!data;
+ break;
+ case ATTR_DMP_PED_Q_RATE:
+ if (data > MPU_DEFAULT_DMP_FREQ || data < 0) {
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+ st->sensor[SENSOR_PEDQ].rate = data;
+ st->sensor[SENSOR_PEDQ].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_PEDQ].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ st->sensor[SENSOR_STEP].on = !!data;
+ break;
+#ifdef CONFIG_INV_TESTING
+ case ATTR_DEBUG_SMD_ENABLE_TESTP1:
+ {
+ u8 d[] = {0x42};
+ result = st->set_power_state(st, true);
+ if (result)
+ goto dmp_attr_store_fail;
+ result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d);
+ if (result)
+ goto dmp_attr_store_fail;
+ }
+ break;
+ case ATTR_DEBUG_SMD_ENABLE_TESTP2:
+ {
+ u8 d[] = {0x42};
+ result = st->set_power_state(st, true);
+ if (result)
+ goto dmp_attr_store_fail;
+ result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d);
+ if (result)
+ goto dmp_attr_store_fail;
+ }
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ goto dmp_attr_store_fail;
+ }
+
+dmp_attr_store_fail:
+ if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON)
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_dmp_attr_store() - calling this function will store current
+ * dmp parameter settings
+ */
+static ssize_t inv_dmp_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _dmp_attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_attr64_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result;
+ u64 tmp;
+ u32 ped;
+
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable || !st->chip_config.dmp_on) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ result = 0;
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ result = inv_get_pedometer_steps(st, &ped);
+ result |= inv_read_pedometer_counter(st);
+ tmp = st->ped.step + ped;
+ break;
+ case ATTR_DMP_PEDOMETER_TIME:
+ result = inv_get_pedometer_time(st, &ped);
+ tmp = st->ped.time + ped;
+ break;
+ case ATTR_DMP_PEDOMETER_COUNTER:
+ tmp = st->ped.last_step_time;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return -EINVAL;
+ return sprintf(buf, "%lld\n", tmp);
+}
+
+static ssize_t inv_attr64_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result;
+ u64 data;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable || (!st->chip_config.firmware_loaded)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ result = st->set_power_state(st, true);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ result = kstrtoull(buf, 10, &data);
+ if (result)
+ goto attr64_store_fail;
+ switch (this_attr->address) {
+ case ATTR_DMP_PEDOMETER_STEPS:
+ result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_STEPCTR);
+ if (result)
+ goto attr64_store_fail;
+ st->ped.step = data;
+ break;
+ case ATTR_DMP_PEDOMETER_TIME:
+ result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_TIMECTR);
+ if (result)
+ goto attr64_store_fail;
+ st->ped.time = data;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+attr64_store_fail:
+ mutex_unlock(&indio_dev->mlock);
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+/*
+ * inv_attr_show() - calling this function will show current
+ * dmp parameters.
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, axis;
+ s8 *m;
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ {
+ const s16 gyro_scale[] = {250, 500, 1000, 2000};
+
+ return sprintf(buf, "%d\n", gyro_scale[st->chip_config.fsr]);
+ }
+ case ATTR_ACCEL_SCALE:
+ {
+ const s16 accel_scale[] = {2, 4, 8, 16};
+ return sprintf(buf, "%d\n",
+ accel_scale[st->chip_config.accel_fs] *
+ st->chip_info.multi);
+ }
+ case ATTR_COMPASS_SCALE:
+ st->slave_compass->get_scale(st, &result);
+
+ return sprintf(buf, "%d\n", result);
+ case ATTR_ACCEL_X_CALIBBIAS:
+ case ATTR_ACCEL_Y_CALIBBIAS:
+ case ATTR_ACCEL_Z_CALIBBIAS:
+ axis = this_attr->address - ATTR_ACCEL_X_CALIBBIAS;
+ return sprintf(buf, "%d\n", st->accel_bias[axis] *
+ st->chip_info.multi);
+ case ATTR_GYRO_X_CALIBBIAS:
+ case ATTR_GYRO_Y_CALIBBIAS:
+ case ATTR_GYRO_Z_CALIBBIAS:
+ axis = this_attr->address - ATTR_GYRO_X_CALIBBIAS;
+ return sprintf(buf, "%d\n", st->gyro_bias[axis]);
+ case ATTR_SELF_TEST_GYRO_SCALE:
+ return sprintf(buf, "%d\n", SELF_TEST_GYRO_FULL_SCALE);
+ case ATTR_SELF_TEST_ACCEL_SCALE:
+ if (INV_MPU6500 == st->chip_type)
+ return sprintf(buf, "%d\n", SELF_TEST_ACCEL_6500_SCALE);
+ else
+ return sprintf(buf, "%d\n", SELF_TEST_ACCEL_FULL_SCALE);
+ case ATTR_GYRO_X_OFFSET:
+ case ATTR_GYRO_Y_OFFSET:
+ case ATTR_GYRO_Z_OFFSET:
+ axis = this_attr->address - ATTR_GYRO_X_OFFSET;
+ return sprintf(buf, "%d\n", st->input_gyro_offset[axis]);
+ case ATTR_ACCEL_X_OFFSET:
+ case ATTR_ACCEL_Y_OFFSET:
+ case ATTR_ACCEL_Z_OFFSET:
+ axis = this_attr->address - ATTR_ACCEL_X_OFFSET;
+ return sprintf(buf, "%d\n", st->input_accel_offset[axis]);
+ case ATTR_DMP_ACCEL_X_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[0]);
+ case ATTR_DMP_ACCEL_Y_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[1]);
+ case ATTR_DMP_ACCEL_Z_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_accel_dmp_bias[2]);
+ case ATTR_DMP_GYRO_X_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[0]);
+ case ATTR_DMP_GYRO_Y_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[1]);
+ case ATTR_DMP_GYRO_Z_DMP_BIAS:
+ return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[2]);
+ case ATTR_DMP_PED_INT_ON:
+ return sprintf(buf, "%d\n", st->ped.int_on);
+ case ATTR_DMP_PED_ON:
+ return sprintf(buf, "%d\n", st->ped.on);
+ case ATTR_DMP_PED_STEP_THRESH:
+ return sprintf(buf, "%d\n", st->ped.step_thresh);
+ case ATTR_DMP_PED_INT_THRESH:
+ return sprintf(buf, "%d\n", st->ped.int_thresh);
+ case ATTR_DMP_SMD_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.smd_enable);
+ case ATTR_DMP_SMD_THLD:
+ return sprintf(buf, "%d\n", st->smd.threshold);
+ case ATTR_DMP_SMD_DELAY_THLD:
+ return sprintf(buf, "%d\n", st->smd.delay);
+ case ATTR_DMP_SMD_DELAY_THLD2:
+ return sprintf(buf, "%d\n", st->smd.delay2);
+ case ATTR_DMP_TAP_ON:
+ return sprintf(buf, "%d\n", st->tap.on);
+ case ATTR_DMP_TAP_THRESHOLD:
+ return sprintf(buf, "%d\n", st->tap.thresh);
+ case ATTR_DMP_TAP_MIN_COUNT:
+ return sprintf(buf, "%d\n", st->tap.min_count);
+ case ATTR_DMP_TAP_TIME:
+ return sprintf(buf, "%d\n", st->tap.time);
+ case ATTR_DMP_DISPLAY_ORIENTATION_ON:
+ return sprintf(buf, "%d\n",
+ st->chip_config.display_orient_on);
+ case ATTR_DMP_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_on);
+ case ATTR_DMP_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_int_on);
+ case ATTR_DMP_EVENT_INT_ON:
+ return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on);
+ case ATTR_DMP_STEP_INDICATOR_ON:
+ return sprintf(buf, "%d\n", st->chip_config.step_indicator_on);
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ return sprintf(buf, "%d\n",
+ st->batch.timeout);
+ case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL:
+ return sprintf(buf, "%d\n",
+ st->batch.wake_fifo_on);
+ case ATTR_DMP_SIX_Q_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].on);
+ case ATTR_DMP_SIX_Q_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].rate);
+ case ATTR_DMP_LPQ_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].on);
+ case ATTR_DMP_LPQ_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].rate);
+ case ATTR_DMP_PED_Q_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].on);
+ case ATTR_DMP_PED_Q_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].rate);
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_STEP].on);
+ case ATTR_MOTION_LPA_ON:
+ return sprintf(buf, "%d\n", st->mot_int.mot_on);
+ case ATTR_MOTION_LPA_FREQ:{
+ const char *f[] = {"1.25", "5", "20", "40"};
+ return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]);
+ }
+ case ATTR_MOTION_LPA_THRESHOLD:
+ return sprintf(buf, "%d\n", st->mot_int.mot_thr);
+
+ case ATTR_SELF_TEST_SAMPLES:
+ return sprintf(buf, "%d\n", st->self_test.samples);
+ case ATTR_SELF_TEST_THRESHOLD:
+ return sprintf(buf, "%d\n", st->self_test.threshold);
+ case ATTR_GYRO_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.gyro_enable);
+ case ATTR_GYRO_FIFO_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].on);
+ case ATTR_GYRO_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].rate);
+ case ATTR_ACCEL_ENABLE:
+ return sprintf(buf, "%d\n", st->chip_config.accel_enable);
+ case ATTR_ACCEL_FIFO_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].on);
+ case ATTR_ACCEL_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].rate);
+ case ATTR_COMPASS_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].on);
+ case ATTR_COMPASS_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].rate);
+ case ATTR_PRESSURE_ENABLE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].on);
+ case ATTR_PRESSURE_RATE:
+ return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].rate);
+ case ATTR_POWER_STATE:
+ return sprintf(buf, "%d\n", !fake_asleep);
+ case ATTR_FIRMWARE_LOADED:
+ return sprintf(buf, "%d\n", st->chip_config.firmware_loaded);
+ case ATTR_SAMPLING_FREQ:
+ return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
+ case ATTR_SELF_TEST:
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ if (INV_MPU3050 == st->chip_type)
+ result = 1;
+ else
+ result = inv_hw_self_test(st);
+ mutex_unlock(&indio_dev->mlock);
+ return sprintf(buf, "%d\n", result);
+ case ATTR_GYRO_MATRIX:
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_ACCEL_MATRIX:
+ if (st->plat_data.sec_slave_type ==
+ SECONDARY_SLAVE_TYPE_ACCEL)
+ m =
+ st->plat_data.secondary_orientation;
+ else
+ m = st->plat_data.orientation;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_COMPASS_MATRIX:
+ if (st->plat_data.sec_slave_type ==
+ SECONDARY_SLAVE_TYPE_COMPASS)
+ m =
+ st->plat_data.secondary_orientation;
+ else
+ return -ENODEV;
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ case ATTR_SECONDARY_NAME:
+ {
+ const char *n[] = {"NULL", "AK8975", "AK8972", "AK8963",
+ "BMA250", "MLX90399", "AK09911"};
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ return sprintf(buf, "%s\n", n[1]);
+ case COMPASS_ID_AK8972:
+ return sprintf(buf, "%s\n", n[2]);
+ case COMPASS_ID_AK8963:
+ return sprintf(buf, "%s\n", n[3]);
+ case ACCEL_ID_BMA250:
+ return sprintf(buf, "%s\n", n[4]);
+ case COMPASS_ID_MLX90399:
+ return sprintf(buf, "%s\n", n[5]);
+ case COMPASS_ID_AK09911:
+ return sprintf(buf, "%s\n", n[6]);
+ default:
+ return sprintf(buf, "%s\n", n[0]);
+ }
+ }
+#ifdef CONFIG_INV_TESTING
+ case ATTR_REG_WRITE:
+ return sprintf(buf, "1\n");
+ case ATTR_COMPASS_SENS:
+ {
+ /* these 2 conditions should never be met, since the
+ 'compass_sens' sysfs entry should be hidden if the compass
+ is not an AKM */
+ if (st->plat_data.sec_slave_type !=
+ SECONDARY_SLAVE_TYPE_COMPASS)
+ return -ENODEV;
+ if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8972 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8963)
+ return -ENODEV;
+ m = st->chip_info.compass_sens;
+ return sprintf(buf, "%d,%d,%d\n", m[0], m[1], m[2]);
+ }
+ case ATTR_DEBUG_SMD_EXE_STATE:
+ {
+ u8 d[2];
+
+ result = st->set_power_state(st, true);
+ mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_SMD_EXE_STATE), 2, d);
+ return sprintf(buf, "%d\n", (short)be16_to_cpup((__be16 *)(d)));
+ }
+ case ATTR_DEBUG_SMD_DELAY_CNTR:
+ {
+ u8 d[4];
+
+ result = st->set_power_state(st, true);
+ mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_SMD_DELAY_CNTR), 4, d);
+ return sprintf(buf, "%d\n", (int)be32_to_cpup((__be32 *)(d)));
+ }
+#endif
+ default:
+ return -EPERM;
+ }
+}
+
+/*
+ * inv_dmp_display_orient_show() - calling this function will
+ * show orientation This event must use poll.
+ */
+static ssize_t inv_dmp_display_orient_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", st->display_orient_data);
+}
+
+/*
+ * inv_accel_motion_show() - calling this function showes motion interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_accel_motion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_smd_show() - calling this function showes smd interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_smd_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_ped_show() - calling this function showes pedometer interrupt.
+ * This event must use poll.
+ */
+static ssize_t inv_ped_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+/*
+ * inv_dmp_tap_show() - calling this function will show tap
+ * This event must use poll.
+ */
+static ssize_t inv_dmp_tap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+ return sprintf(buf, "%d\n", st->tap_data);
+}
+
+/*
+ * inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result, cur_scale, cur_off;
+ short temp;
+ long scale_t;
+ u8 data[2];
+ const long scale[] = {3834792L, 3158064L, 3340827L};
+ const long offset[] = {5383314L, 2394184L, 1376256L};
+
+ reg = &st->reg;
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable)
+ result = st->set_power_state(st, true);
+ else
+ result = 0;
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ result = inv_i2c_read(st, reg->temperature, 2, data);
+ if (!st->chip_config.enable)
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result) {
+ pr_err("Could not read temperature register.\n");
+ return result;
+ }
+ temp = (signed short)(be16_to_cpup((short *)&data[0]));
+ switch (st->chip_type) {
+ case INV_MPU3050:
+ cur_scale = scale[0];
+ cur_off = offset[0];
+ break;
+ case INV_MPU6050:
+ cur_scale = scale[1];
+ cur_off = offset[1];
+ break;
+ case INV_MPU6500:
+ cur_scale = scale[2];
+ cur_off = offset[2];
+ break;
+ default:
+ return -EINVAL;
+ };
+ scale_t = cur_off +
+ inv_q30_mult((int)temp << MPU_TEMP_SHIFT, cur_scale);
+
+ INV_I2C_INC_TEMPREAD(1);
+
+ return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns());
+}
+
+static ssize_t inv_flush_batch_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+ bool has_data;
+
+ mutex_lock(&indio_dev->mlock);
+ result = inv_flush_batch_data(indio_dev, &has_data);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return sprintf(buf, "%d\n", result);
+ else
+ return sprintf(buf, "%d\n", has_data);
+}
+
+/*
+ * inv_firmware_loaded() - calling this function will change
+ * firmware load
+ */
+static int inv_firmware_loaded(struct inv_mpu_state *st, int data)
+{
+ if (data)
+ return -EINVAL;
+ st->chip_config.firmware_loaded = 0;
+
+ return 0;
+}
+
+static int inv_switch_gyro_engine(struct inv_mpu_state *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY);
+}
+
+static int inv_switch_accel_engine(struct inv_mpu_state *st, bool en)
+{
+ return inv_switch_engine(st, en, BIT_PWR_ACCEL_STBY);
+}
+
+static ssize_t _attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ u8 d, axis;
+ int result;
+
+ result = 0;
+ if (st->chip_config.enable)
+ return -EBUSY;
+ if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) {
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ }
+
+ /* check the input and validate it's format */
+ switch (this_attr->address) {
+#ifdef CONFIG_INV_TESTING
+ /* these inputs are strings */
+ case ATTR_COMPASS_MATRIX:
+ case ATTR_COMPASS_SENS:
+ break;
+#endif
+ /* these inputs are integers */
+ default:
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ break;
+ }
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_X_OFFSET:
+ case ATTR_GYRO_Y_OFFSET:
+ case ATTR_GYRO_Z_OFFSET:
+ if ((data > MPU_MAX_G_OFFSET_VALUE) ||
+ (data < MPU_MIN_G_OFFSET_VALUE))
+ return -EINVAL;
+ axis = this_attr->address - ATTR_GYRO_X_OFFSET;
+ result = inv_set_offset_reg(st,
+ reg_gyro_offset[axis],
+ st->rom_gyro_offset[axis] + data);
+
+ if (result)
+ goto attr_store_fail;
+ st->input_gyro_offset[axis] = data;
+ break;
+ case ATTR_ACCEL_X_OFFSET:
+ case ATTR_ACCEL_Y_OFFSET:
+ case ATTR_ACCEL_Z_OFFSET:
+ {
+ const u8 *ch;
+
+ if ((data > MPU_MAX_A_OFFSET_VALUE) ||
+ (data < MPU_MIN_A_OFFSET_VALUE))
+ return -EINVAL;
+
+ axis = this_attr->address - ATTR_ACCEL_X_OFFSET;
+ if (INV_MPU6050 == st->chip_type)
+ ch = reg_6050_accel_offset;
+ else
+ ch = reg_6500_accel_offset;
+
+ result = inv_set_offset_reg(st, ch[axis],
+ st->rom_accel_offset[axis] + (data << 1));
+ if (result)
+ goto attr_store_fail;
+ st->input_accel_offset[axis] = data;
+ break;
+ }
+ case ATTR_GYRO_SCALE:
+ result = inv_write_fsr(st, data);
+ break;
+ case ATTR_ACCEL_SCALE:
+ result = inv_write_accel_fs(st, data);
+ break;
+ case ATTR_COMPASS_SCALE:
+ result = st->slave_compass->set_scale(st, data);
+ break;
+ case ATTR_MOTION_LPA_ON:
+ if (INV_MPU6500 == st->chip_type) {
+ if (data)
+ /* enable and put in MPU6500 mode */
+ d = BIT_ACCEL_INTEL_ENABLE
+ | BIT_ACCEL_INTEL_MODE;
+ else
+ d = 0;
+ result = inv_i2c_single_write(st,
+ REG_6500_ACCEL_INTEL_CTRL, d);
+ if (result)
+ goto attr_store_fail;
+ }
+ st->mot_int.mot_on = !!data;
+ st->chip_config.lpa_mode = !!data;
+ break;
+ case ATTR_MOTION_LPA_FREQ:
+ result = inv_lpa_freq(st, data);
+ break;
+ case ATTR_MOTION_LPA_THRESHOLD:
+ if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if (INV_MPU6500 == st->chip_type) {
+ d = (u8)(data >> MPU6500_MOTION_THRESH_SHIFT);
+ data = (d << MPU6500_MOTION_THRESH_SHIFT);
+ } else {
+ d = (u8)(data >> MPU6050_MOTION_THRESH_SHIFT);
+ data = (d << MPU6050_MOTION_THRESH_SHIFT);
+ }
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d);
+ if (result)
+ goto attr_store_fail;
+ st->mot_int.mot_thr = data;
+ break;
+ /* from now on, power is not turned on */
+ case ATTR_SELF_TEST_SAMPLES:
+ if (data > ST_MAX_SAMPLES || data < 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ st->self_test.samples = data;
+ break;
+ case ATTR_SELF_TEST_THRESHOLD:
+ if (data > ST_MAX_THRESHOLD || data < 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ st->self_test.threshold = data;
+ case ATTR_GYRO_ENABLE:
+ st->chip_config.gyro_enable = !!data;
+ break;
+ case ATTR_GYRO_FIFO_ENABLE:
+ st->sensor[SENSOR_GYRO].on = !!data;
+ break;
+ case ATTR_GYRO_RATE:
+ st->sensor[SENSOR_GYRO].rate = data;
+ st->sensor[SENSOR_GYRO].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_GYRO].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_ACCEL_ENABLE:
+ st->chip_config.accel_enable = !!data;
+ break;
+ case ATTR_ACCEL_FIFO_ENABLE:
+ st->sensor[SENSOR_ACCEL].on = !!data;
+ break;
+ case ATTR_ACCEL_RATE:
+ st->sensor[SENSOR_ACCEL].rate = data;
+ st->sensor[SENSOR_ACCEL].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_ACCEL].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_COMPASS_ENABLE:
+ st->sensor[SENSOR_COMPASS].on = !!data;
+ break;
+ case ATTR_COMPASS_RATE:
+ if (data <= 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if ((MSEC_PER_SEC / st->slave_compass->rate_scale) < data)
+ data = MSEC_PER_SEC / st->slave_compass->rate_scale;
+
+ st->sensor[SENSOR_COMPASS].rate = data;
+ st->sensor[SENSOR_COMPASS].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_COMPASS].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_PRESSURE_ENABLE:
+ st->sensor[SENSOR_PRESSURE].on = !!data;
+ break;
+ case ATTR_PRESSURE_RATE:
+ if (data <= 0) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ if ((MSEC_PER_SEC / st->slave_pressure->rate_scale) < data)
+ data = MSEC_PER_SEC / st->slave_pressure->rate_scale;
+
+ st->sensor[SENSOR_PRESSURE].rate = data;
+ st->sensor[SENSOR_PRESSURE].dur = MPU_DEFAULT_DMP_FREQ / data;
+ st->sensor[SENSOR_PRESSURE].dur *= DMP_INTERVAL_INIT;
+ break;
+ case ATTR_POWER_STATE:
+ fake_asleep = !data;
+ break;
+ case ATTR_FIRMWARE_LOADED:
+ result = inv_firmware_loaded(st, data);
+ break;
+ case ATTR_SAMPLING_FREQ:
+ result = inv_fifo_rate_store(st, data);
+ break;
+#ifdef CONFIG_INV_TESTING
+ case ATTR_COMPASS_MATRIX:
+ {
+ char *str;
+ __s8 m[9];
+ d = 0;
+ if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE)
+ return -ENODEV;
+ while ((str = strsep((char **)&buf, ","))) {
+ if (d >= 9) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ result = kstrtoint(str, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ if (data < -1 || data > 1) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ m[d] = data;
+ d++;
+ }
+ if (d < 9) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ memcpy(st->plat_data.secondary_orientation, m, sizeof(m));
+ pr_debug(KERN_INFO
+ "compass_matrix: %d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+ break;
+ }
+ case ATTR_COMPASS_SENS:
+ {
+ char *str;
+ __s8 s[3];
+ d = 0;
+ /* these 2 conditions should never be met, since the
+ 'compass_sens' sysfs entry should be hidden if the compass
+ is not an AKM */
+ if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE)
+ return -ENODEV;
+ if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8972 &&
+ st->plat_data.sec_slave_id != COMPASS_ID_AK8963)
+ return -ENODEV;
+ /* read the input data, expecting 3 comma separated values */
+ while ((str = strsep((char **)&buf, ","))) {
+ if (d >= 3) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ result = kstrtoint(str, 10, &data);
+ if (result)
+ goto attr_store_fail;
+ if (data < 0 || data > 255) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ s[d] = data;
+ d++;
+ }
+ if (d < 3) {
+ result = -EINVAL;
+ goto attr_store_fail;
+ }
+ /* store the new compass sensitivity adjustment */
+ memcpy(st->chip_info.compass_sens, s, sizeof(s));
+ pr_debug(KERN_INFO
+ "compass_sens: %d,%d,%d\n", s[0], s[1], s[2]);
+ break;
+ }
+#endif
+ default:
+ result = -EINVAL;
+ goto attr_store_fail;
+ };
+
+attr_store_fail:
+ if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD)
+ result |= st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_attr_store() - calling this function will store current
+ * non-dmp parameter settings
+ */
+static ssize_t inv_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_master_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int data;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.enable == (!!data)) {
+ result = count;
+ goto end_enable;
+ }
+ if (!!data) {
+ result = st->set_power_state(st, true);
+ if (result)
+ goto end_enable;
+ }
+ result = set_inv_enable(indio_dev, !!data);
+ if (result)
+ goto end_enable;
+ if (!data) {
+ result = st->set_power_state(st, false);
+ if (result)
+ goto end_enable;
+ }
+ result = count;
+
+end_enable:
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+static ssize_t inv_master_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", st->chip_config.enable);
+}
+
+#ifdef CONFIG_INV_TESTING
+static ssize_t inv_test_suspend_resume_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int data;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ if (data)
+ inv_mpu_suspend(dev);
+ else
+ inv_mpu_resume(dev);
+ suspend_state = !!data;
+
+ return count;
+}
+
+static ssize_t inv_test_suspend_resume_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ return sprintf(buf, "%d\n", suspend_state);
+}
+
+/*
+ * inv_reg_write_store() - register write command for testing.
+ * Format: WSRRDD, where RR is the register in hex,
+ * and DD is the data in hex.
+ */
+static ssize_t inv_reg_write_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ u32 result;
+ u8 wreg, wval;
+ int temp;
+ char local_buf[10];
+
+ if ((buf[0] != 'W' && buf[0] != 'w') ||
+ (buf[1] != 'S' && buf[1] != 's'))
+ return -EINVAL;
+ if (strlen(buf) < 6)
+ return -EINVAL;
+
+ strncpy(local_buf, buf, 7);
+ local_buf[6] = 0;
+ result = sscanf(&local_buf[4], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wval = temp;
+ local_buf[4] = 0;
+ sscanf(&local_buf[2], "%x", &temp);
+ if (result == 0)
+ return -EINVAL;
+ wreg = temp;
+
+ result = inv_i2c_single_write(st, wreg, wval);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ u8 *m;
+ int result;
+
+ if (st->chip_config.enable)
+ return -EBUSY;
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ switch (this_attr->address) {
+ case ATTR_DEBUG_ACCEL_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x80, 0xc0, 0xc8, 0xc2};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_01, ARRAY_SIZE(D), m);
+ data_out_control.accel = !!data;
+ break;
+ }
+ case ATTR_DEBUG_GYRO_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x80, 0xc4, 0xcc, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_02, ARRAY_SIZE(D), m);
+ data_out_control.gyro = !!data;
+ break;
+ }
+ case ATTR_DEBUG_COMPASS_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x81, 0xc0, 0xc8, 0xc2};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_03, ARRAY_SIZE(D), m);
+ data_out_control.compass = !!data;
+ break;
+ }
+ case ATTR_DEBUG_PRESSURE_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xb0, 0x81, 0xc4, 0xcc, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_04, ARRAY_SIZE(D), m);
+ data_out_control.pressure = !!data;
+ break;
+ }
+ case ATTR_DEBUG_LPQ_COUNTER:
+ {
+ u8 D[6] = {0xf1, 0xb1, 0x83, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_05, ARRAY_SIZE(D), m);
+ data_out_control.LPQ = !!data;
+ break;
+ }
+ case ATTR_DEBUG_SIXQ_COUNTER:
+ {
+ u8 D[6] = {0xf1, 0xb1, 0x89, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_06, ARRAY_SIZE(D), m);
+ data_out_control.SIXQ = !!data;
+ break;
+ }
+ case ATTR_DEBUG_PEDQ_COUNTER:
+ {
+ u8 D[6] = {0xf2, 0xf2, 0x88, 0xc2, 0xc4, 0xc6};
+ u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0};
+
+ if (data)
+ m = E;
+ else
+ m = D;
+ result = mem_w_key(KEY_TEST_07, ARRAY_SIZE(D), m);
+ data_out_control.PEDQ = !!data;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t inv_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ switch (this_attr->address) {
+ case ATTR_DEBUG_ACCEL_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.accel);
+ case ATTR_DEBUG_GYRO_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.gyro);
+ case ATTR_DEBUG_COMPASS_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.compass);
+ case ATTR_DEBUG_PRESSURE_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.pressure);
+ case ATTR_DEBUG_LPQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.LPQ);
+ case ATTR_DEBUG_SIXQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.SIXQ);
+ case ATTR_DEBUG_PEDQ_COUNTER:
+ return sprintf(buf, "%d\n", data_out_control.PEDQ);
+ default:
+ return -EINVAL;
+ }
+}
+
+#endif /* CONFIG_INV_TESTING */
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+};
+
+/*constant IIO attribute */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+
+/* special sysfs */
+static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL);
+
+/* event based sysfs, needs poll to read */
+static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL);
+static DEVICE_ATTR(event_display_orientation, S_IRUGO,
+ inv_dmp_display_orient_show, NULL);
+static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL);
+static DEVICE_ATTR(event_smd, S_IRUGO, inv_smd_show, NULL);
+static DEVICE_ATTR(event_pedometer, S_IRUGO, inv_ped_show, NULL);
+
+/* master enable method */
+static DEVICE_ATTR(master_enable, S_IRUGO | S_IWUSR, inv_master_enable_show,
+ inv_master_enable_store);
+
+/* special run time sysfs entry, read only */
+static DEVICE_ATTR(flush_batch, S_IRUGO, inv_flush_batch_show, NULL);
+
+/* DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(in_accel_x_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_X_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_accel_y_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Y_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_accel_z_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Z_DMP_BIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_X_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Y_DMP_BIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_dmp_bias, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Z_DMP_BIAS);
+
+static IIO_DEVICE_ATTR(pedometer_int_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_INT_ON);
+static IIO_DEVICE_ATTR(pedometer_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_ON);
+static IIO_DEVICE_ATTR(pedometer_step_thresh, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_STEP_THRESH);
+static IIO_DEVICE_ATTR(pedometer_int_thresh, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_INT_THRESH);
+
+static IIO_DEVICE_ATTR(smd_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_ENABLE);
+static IIO_DEVICE_ATTR(smd_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD);
+static IIO_DEVICE_ATTR(smd_delay_threshold2, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD2);
+
+static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr64_show,
+ inv_attr64_store, ATTR_DMP_PEDOMETER_STEPS);
+static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr64_show,
+ inv_attr64_store, ATTR_DMP_PEDOMETER_TIME);
+static IIO_DEVICE_ATTR(pedometer_counter, S_IRUGO | S_IWUSR, inv_attr64_show,
+ NULL, ATTR_DMP_PEDOMETER_COUNTER);
+
+static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_ON);
+static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_THRESHOLD);
+static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_MIN_COUNT);
+static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_TIME);
+static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+
+/* DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_ON);
+static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_INT_ON);
+static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON);
+static IIO_DEVICE_ATTR(step_indicator_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_STEP_INDICATOR_ON);
+static IIO_DEVICE_ATTR(batchmode_timeout, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_TIMEOUT);
+static IIO_DEVICE_ATTR(batchmode_wake_fifo_full_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL);
+
+static IIO_DEVICE_ATTR(six_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_SIX_Q_ON);
+static IIO_DEVICE_ATTR(six_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_SIX_Q_RATE);
+
+static IIO_DEVICE_ATTR(three_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_LPQ_ON);
+static IIO_DEVICE_ATTR(three_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_LPQ_RATE);
+
+static IIO_DEVICE_ATTR(ped_q_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PED_Q_ON);
+static IIO_DEVICE_ATTR(ped_q_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_PED_Q_RATE);
+
+static IIO_DEVICE_ATTR(step_detector_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_dmp_attr_store, ATTR_DMP_STEP_DETECTOR_ON);
+
+/* non DMP sysfs with power on/off */
+static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_ON);
+static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_FREQ);
+static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_MOTION_LPA_THRESHOLD);
+
+static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_SCALE);
+static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_SCALE);
+static IIO_DEVICE_ATTR(in_magn_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_SCALE);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_X_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_Z_OFFSET);
+
+static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_X_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_Z_OFFSET);
+
+/* non DMP sysfs without power on/off */
+static IIO_DEVICE_ATTR(self_test_samples, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SELF_TEST_SAMPLES);
+static IIO_DEVICE_ATTR(self_test_threshold, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SELF_TEST_THRESHOLD);
+
+static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_ENABLE);
+static IIO_DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_FIFO_ENABLE);
+static IIO_DEVICE_ATTR(gyro_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_GYRO_RATE);
+
+static IIO_DEVICE_ATTR(accel_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_ENABLE);
+static IIO_DEVICE_ATTR(accel_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_FIFO_ENABLE);
+static IIO_DEVICE_ATTR(accel_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_ACCEL_RATE);
+
+static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_ENABLE);
+static IIO_DEVICE_ATTR(compass_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_RATE);
+
+static IIO_DEVICE_ATTR(pressure_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_PRESSURE_ENABLE);
+static IIO_DEVICE_ATTR(pressure_rate, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_PRESSURE_RATE);
+
+static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_POWER_STATE);
+static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_FIRMWARE_LOADED);
+static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_SAMPLING_FREQ);
+
+/* show method only sysfs but with power on/off */
+static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL,
+ ATTR_SELF_TEST);
+
+/* show method only sysfs */
+static IIO_DEVICE_ATTR(in_accel_x_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_X_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_y_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_Y_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_z_calibbias, S_IRUGO, inv_attr_show,
+ NULL, ATTR_ACCEL_Z_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_X_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_Y_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_calibbias, S_IRUGO,
+ inv_attr_show, NULL, ATTR_GYRO_Z_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_self_test_scale, S_IRUGO,
+ inv_attr_show, NULL, ATTR_SELF_TEST_GYRO_SCALE);
+static IIO_DEVICE_ATTR(in_accel_self_test_scale, S_IRUGO,
+ inv_attr_show, NULL, ATTR_SELF_TEST_ACCEL_SCALE);
+
+static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(accel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCEL_MATRIX);
+#ifdef CONFIG_INV_TESTING /* read/write in test mode */
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_MATRIX);
+static IIO_DEVICE_ATTR(compass_sens, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_attr_store, ATTR_COMPASS_SENS);
+#else
+static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_COMPASS_MATRIX);
+#endif
+static IIO_DEVICE_ATTR(secondary_name, S_IRUGO, inv_attr_show, NULL,
+ ATTR_SECONDARY_NAME);
+
+#ifdef CONFIG_INV_TESTING
+static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_reg_write_store, ATTR_REG_WRITE);
+/* smd debug related sysfs */
+static IIO_DEVICE_ATTR(debug_smd_enable_testp1, S_IWUSR, NULL,
+ inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP1);
+static IIO_DEVICE_ATTR(debug_smd_enable_testp2, S_IWUSR, NULL,
+ inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP2);
+static IIO_DEVICE_ATTR(debug_smd_exe_state, S_IRUGO, inv_attr_show,
+ NULL, ATTR_DEBUG_SMD_EXE_STATE);
+static IIO_DEVICE_ATTR(debug_smd_delay_cntr, S_IRUGO, inv_attr_show,
+ NULL, ATTR_DEBUG_SMD_DELAY_CNTR);
+static DEVICE_ATTR(test_suspend_resume, S_IRUGO | S_IWUSR,
+ inv_test_suspend_resume_show, inv_test_suspend_resume_store);
+
+static IIO_DEVICE_ATTR(test_gyro_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_GYRO_COUNTER);
+static IIO_DEVICE_ATTR(test_accel_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_ACCEL_COUNTER);
+static IIO_DEVICE_ATTR(test_compass_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_COMPASS_COUNTER);
+static IIO_DEVICE_ATTR(test_pressure_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_PRESSURE_COUNTER);
+static IIO_DEVICE_ATTR(test_LPQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_LPQ_COUNTER);
+static IIO_DEVICE_ATTR(test_SIXQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_SIXQ_COUNTER);
+static IIO_DEVICE_ATTR(test_PEDQ_counter, S_IRUGO | S_IWUSR, inv_test_show,
+ inv_test_store, ATTR_DEBUG_PEDQ_COUNTER);
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_reg_dump.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_master_enable.attr,
+ &iio_dev_attr_in_anglvel_scale.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_self_test_scale.dev_attr.attr,
+ &iio_dev_attr_self_test_samples.dev_attr.attr,
+ &iio_dev_attr_self_test_threshold.dev_attr.attr,
+ &iio_dev_attr_gyro_enable.dev_attr.attr,
+ &iio_dev_attr_gyro_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_gyro_rate.dev_attr.attr,
+ &iio_dev_attr_power_state.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_self_test.dev_attr.attr,
+ &iio_dev_attr_gyro_matrix.dev_attr.attr,
+ &iio_dev_attr_secondary_name.dev_attr.attr,
+#ifdef CONFIG_INV_TESTING
+ &iio_dev_attr_reg_write.dev_attr.attr,
+ &iio_dev_attr_debug_smd_enable_testp1.dev_attr.attr,
+ &iio_dev_attr_debug_smd_enable_testp2.dev_attr.attr,
+ &iio_dev_attr_debug_smd_exe_state.dev_attr.attr,
+ &iio_dev_attr_debug_smd_delay_cntr.dev_attr.attr,
+ &dev_attr_test_suspend_resume.attr,
+ &iio_dev_attr_test_gyro_counter.dev_attr.attr,
+ &iio_dev_attr_test_accel_counter.dev_attr.attr,
+ &iio_dev_attr_test_compass_counter.dev_attr.attr,
+ &iio_dev_attr_test_pressure_counter.dev_attr.attr,
+ &iio_dev_attr_test_LPQ_counter.dev_attr.attr,
+ &iio_dev_attr_test_SIXQ_counter.dev_attr.attr,
+ &iio_dev_attr_test_PEDQ_counter.dev_attr.attr,
+#endif
+};
+
+static const struct attribute *inv_mpu6xxx_attributes[] = {
+ &dev_attr_event_accel_motion.attr,
+ &dev_attr_event_smd.attr,
+ &dev_attr_event_pedometer.attr,
+ &dev_attr_flush_batch.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_self_test_scale.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_dmp_bias.dev_attr.attr,
+ &iio_dev_attr_pedometer_int_on.dev_attr.attr,
+ &iio_dev_attr_pedometer_on.dev_attr.attr,
+ &iio_dev_attr_pedometer_steps.dev_attr.attr,
+ &iio_dev_attr_pedometer_time.dev_attr.attr,
+ &iio_dev_attr_pedometer_counter.dev_attr.attr,
+ &iio_dev_attr_pedometer_step_thresh.dev_attr.attr,
+ &iio_dev_attr_pedometer_int_thresh.dev_attr.attr,
+ &iio_dev_attr_smd_enable.dev_attr.attr,
+ &iio_dev_attr_smd_threshold.dev_attr.attr,
+ &iio_dev_attr_smd_delay_threshold.dev_attr.attr,
+ &iio_dev_attr_smd_delay_threshold2.dev_attr.attr,
+ &iio_dev_attr_dmp_on.dev_attr.attr,
+ &iio_dev_attr_dmp_int_on.dev_attr.attr,
+ &iio_dev_attr_dmp_event_int_on.dev_attr.attr,
+ &iio_dev_attr_step_indicator_on.dev_attr.attr,
+ &iio_dev_attr_batchmode_timeout.dev_attr.attr,
+ &iio_dev_attr_batchmode_wake_fifo_full_on.dev_attr.attr,
+ &iio_dev_attr_six_axes_q_on.dev_attr.attr,
+ &iio_dev_attr_six_axes_q_rate.dev_attr.attr,
+ &iio_dev_attr_three_axes_q_on.dev_attr.attr,
+ &iio_dev_attr_three_axes_q_rate.dev_attr.attr,
+ &iio_dev_attr_ped_q_on.dev_attr.attr,
+ &iio_dev_attr_ped_q_rate.dev_attr.attr,
+ &iio_dev_attr_step_detector_on.dev_attr.attr,
+ &iio_dev_attr_accel_enable.dev_attr.attr,
+ &iio_dev_attr_accel_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_accel_rate.dev_attr.attr,
+ &iio_dev_attr_firmware_loaded.dev_attr.attr,
+ &iio_dev_attr_accel_matrix.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu6500_attributes[] = {
+ &iio_dev_attr_motion_lpa_on.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_freq.dev_attr.attr,
+ &iio_dev_attr_motion_lpa_threshold.dev_attr.attr,
+};
+
+static const struct attribute *inv_tap_attributes[] = {
+ &dev_attr_event_tap.attr,
+ &iio_dev_attr_tap_on.dev_attr.attr,
+ &iio_dev_attr_tap_threshold.dev_attr.attr,
+ &iio_dev_attr_tap_min_count.dev_attr.attr,
+ &iio_dev_attr_tap_time.dev_attr.attr,
+};
+
+static const struct attribute *inv_display_orient_attributes[] = {
+ &dev_attr_event_display_orientation.attr,
+ &iio_dev_attr_display_orientation_on.dev_attr.attr,
+};
+
+static const struct attribute *inv_compass_attributes[] = {
+ &iio_dev_attr_in_magn_scale.dev_attr.attr,
+ &iio_dev_attr_compass_enable.dev_attr.attr,
+ &iio_dev_attr_compass_rate.dev_attr.attr,
+ &iio_dev_attr_compass_matrix.dev_attr.attr,
+};
+
+static const struct attribute *inv_akxxxx_attributes[] = {
+#ifdef CONFIG_INV_TESTING
+ &iio_dev_attr_compass_sens.dev_attr.attr,
+#endif
+};
+
+static const struct attribute *inv_pressure_attributes[] = {
+ &iio_dev_attr_pressure_enable.dev_attr.attr,
+ &iio_dev_attr_pressure_rate.dev_attr.attr,
+};
+
+static const struct attribute *inv_mpu3050_attributes[] = {
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_accel_enable.dev_attr.attr,
+ &iio_dev_attr_accel_fifo_enable.dev_attr.attr,
+ &iio_dev_attr_accel_matrix.dev_attr.attr,
+};
+
+static struct attribute *inv_attributes[
+ ARRAY_SIZE(inv_gyro_attributes) +
+ ARRAY_SIZE(inv_mpu6xxx_attributes) +
+ ARRAY_SIZE(inv_mpu6500_attributes) +
+ ARRAY_SIZE(inv_compass_attributes) +
+ ARRAY_SIZE(inv_akxxxx_attributes) +
+ ARRAY_SIZE(inv_pressure_attributes) +
+ ARRAY_SIZE(inv_tap_attributes) +
+ ARRAY_SIZE(inv_display_orient_attributes) +
+ 1
+];
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "mpu",
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &inv_attribute_group,
+};
+
+static void inv_setup_func_ptr(struct inv_mpu_state *st)
+{
+ if (st->chip_type == INV_MPU3050) {
+ st->set_power_state = set_power_mpu3050;
+ st->switch_gyro_engine = inv_switch_3050_gyro_engine;
+ st->switch_accel_engine = inv_switch_3050_accel_engine;
+ st->init_config = inv_init_config_mpu3050;
+ st->setup_reg = inv_setup_reg_mpu3050;
+ } else {
+ st->set_power_state = set_power_itg;
+ st->switch_gyro_engine = inv_switch_gyro_engine;
+ st->switch_accel_engine = inv_switch_accel_engine;
+ st->init_config = inv_init_config;
+ st->setup_reg = inv_setup_reg;
+ }
+}
+
+static int inv_detect_6xxx(struct inv_mpu_state *st)
+{
+ int result;
+ u8 d;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &d);
+ if (result)
+ return result;
+ if ((d == MPU6500_ID) || (d == MPU6515_ID)) {
+ st->chip_type = INV_MPU6500;
+ strcpy(st->name, "mpu6500");
+ } else {
+ strcpy(st->name, "mpu6050");
+ }
+
+ return 0;
+}
+
+static int inv_setup_vddio(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[1];
+
+ if (INV_MPU6050 == st->chip_type) {
+ result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data);
+ if (result)
+ return result;
+ data[0] &= ~BIT_I2C_MST_VDDIO;
+ if (st->plat_data.level_shifter)
+ data[0] |= BIT_I2C_MST_VDDIO;
+ /*set up VDDIO register */
+ result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_check_chip_type() - check and setup chip type.
+ */
+static int inv_check_chip_type(struct inv_mpu_state *st,
+ const struct i2c_device_id *id)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ int t_ind;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+ if (!strcmp(id->name, "itg3500")) {
+ st->chip_type = INV_ITG3500;
+ } else if (!strcmp(id->name, "mpu3050")) {
+ st->chip_type = INV_MPU3050;
+ } else if (!strcmp(id->name, "mpu6050")) {
+ st->chip_type = INV_MPU6050;
+ } else if (!strcmp(id->name, "mpu9150")) {
+ st->chip_type = INV_MPU6050;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_AK8975;
+ } else if (!strcmp(id->name, "mpu6500")) {
+ st->chip_type = INV_MPU6500;
+ } else if (!strcmp(id->name, "mpu9250")) {
+ st->chip_type = INV_MPU6500;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_AK8963;
+ } else if (!strcmp(id->name, "mpu6xxx")) {
+ st->chip_type = INV_MPU6050;
+ } else if (!strcmp(id->name, "mpu9350")) {
+ st->chip_type = INV_MPU6500;
+ plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ plat->sec_slave_id = COMPASS_ID_MLX90399;
+ /* block use of MPU9350 in this build
+ since it's not production ready */
+ pr_err("MPU9350 support is not officially available yet.\n");
+ return -EPERM;
+ } else if (!strcmp(id->name, "mpu6515")) {
+ st->chip_type = INV_MPU6500;
+ } else {
+ return -EPERM;
+ }
+ inv_setup_func_ptr(st);
+ st->hw = &hw_info[st->chip_type];
+ reg = &st->reg;
+ st->setup_reg(reg);
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ return result;
+ msleep(POWER_UP_TIME);
+ /* toggle power state */
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+
+ if (!strcmp(id->name, "mpu6xxx")) {
+ /* for MPU6500, reading register need more time */
+ msleep(POWER_UP_TIME);
+ result = inv_detect_6xxx(st);
+ if (result)
+ return result;
+ }
+
+ if ((plat->sec_slave_type != SECONDARY_SLAVE_TYPE_NONE) ||
+ (plat->aux_slave_type != SECONDARY_SLAVE_TYPE_NONE)) {
+ result = inv_setup_vddio(st);
+ if (result)
+ return result;
+ }
+
+ switch (st->chip_type) {
+ case INV_ITG3500:
+ break;
+ case INV_MPU6050:
+ case INV_MPU6500:
+ if (SECONDARY_SLAVE_TYPE_COMPASS == plat->sec_slave_type)
+ conf->has_compass = 1;
+ else
+ conf->has_compass = 0;
+ if (SECONDARY_SLAVE_TYPE_PRESSURE == plat->aux_slave_type)
+ conf->has_pressure = 1;
+ else
+ conf->has_pressure = 0;
+
+ break;
+ case INV_MPU3050:
+ if (SECONDARY_SLAVE_TYPE_ACCEL == plat->sec_slave_type) {
+ if (ACCEL_ID_BMA250 == plat->sec_slave_id)
+ inv_register_mpu3050_slave(st);
+ }
+ break;
+ default:
+ result = st->set_power_state(st, false);
+ return -ENODEV;
+ }
+ if (conf->has_compass && conf->has_pressure &&
+ (COMPASS_ID_MLX90399 == plat->sec_slave_id)) {
+ pr_err("MLX90399 can't share slaves with others\n");
+ return -EINVAL;
+ }
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ result = inv_get_silicon_rev_mpu6050(st);
+ break;
+ case INV_MPU6500:
+ result = inv_get_silicon_rev_mpu6500(st);
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ if (result) {
+ pr_err("read silicon rev error\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+
+ /* turn off the gyro engine after OTP reading */
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, false);
+ if (result)
+ return result;
+
+ if (conf->has_compass) {
+ result = inv_mpu_setup_compass_slave(st);
+ if (result) {
+ pr_err("compass setup failed\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+ }
+ if (conf->has_pressure) {
+ result = inv_mpu_setup_pressure_slave(st);
+ if (result) {
+ pr_err("pressure setup failed\n");
+ st->set_power_state(st, false);
+ return result;
+ }
+ }
+
+ t_ind = 0;
+ memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+ sizeof(inv_gyro_attributes));
+ t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+ if (INV_MPU3050 == st->chip_type && st->slave_accel != NULL) {
+ memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes,
+ sizeof(inv_mpu3050_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu3050_attributes);
+ inv_attributes[t_ind] = NULL;
+ return 0;
+ }
+
+ /* all MPU6xxx based parts */
+ if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) {
+ memcpy(&inv_attributes[t_ind], inv_mpu6xxx_attributes,
+ sizeof(inv_mpu6xxx_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu6xxx_attributes);
+ }
+
+ /* MPU6500 only */
+ if (INV_MPU6500 == st->chip_type) {
+ memcpy(&inv_attributes[t_ind], inv_mpu6500_attributes,
+ sizeof(inv_mpu6500_attributes));
+ t_ind += ARRAY_SIZE(inv_mpu6500_attributes);
+ }
+
+ if (conf->has_compass) {
+ memcpy(&inv_attributes[t_ind], inv_compass_attributes,
+ sizeof(inv_compass_attributes));
+ t_ind += ARRAY_SIZE(inv_compass_attributes);
+
+ /* AKM only */
+ if (st->plat_data.sec_slave_id == COMPASS_ID_AK8975 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK8972 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK09911 ||
+ st->plat_data.sec_slave_id == COMPASS_ID_AK8963) {
+ memcpy(&inv_attributes[t_ind], inv_akxxxx_attributes,
+ sizeof(inv_akxxxx_attributes));
+ t_ind += ARRAY_SIZE(inv_akxxxx_attributes);
+ }
+ }
+
+ if (conf->has_pressure) {
+ memcpy(&inv_attributes[t_ind], inv_pressure_attributes,
+ sizeof(inv_pressure_attributes));
+ t_ind += ARRAY_SIZE(inv_pressure_attributes);
+ }
+
+ inv_attributes[t_ind] = NULL;
+
+ return 0;
+}
+
+/*
+ * inv_create_dmp_sysfs() - create binary sysfs dmp entry.
+ */
+static const struct bin_attribute dmp_firmware = {
+ .attr = {
+ .name = "dmp_firmware",
+ .mode = S_IRUGO | S_IWUSR
+ },
+ .size = DMP_IMAGE_SIZE + 32,
+ .read = inv_dmp_firmware_read,
+ .write = inv_dmp_firmware_write,
+};
+
+static const struct bin_attribute six_q_value = {
+ .attr = {
+ .name = "six_axes_q_value",
+ .mode = S_IWUSR
+ },
+ .size = QUATERNION_BYTES,
+ .read = NULL,
+ .write = inv_six_q_write,
+};
+
+static int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+ int result;
+
+ result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware);
+ if (result)
+ return result;
+ result = sysfs_create_bin_file(&ind->dev.kobj, &six_q_value);
+
+ return result;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+#ifdef CONFIG_DTS_INV_MPU_IIO
+ enable_irq_wake(client->irq);
+#endif
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENOSYS;
+ pr_err("I2c function error\n");
+ goto out_no_free;
+ }
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->sl_handle = client->adapter;
+ st->i2c_addr = client->addr;
+#ifdef CONFIG_DTS_INV_MPU_IIO
+ result = invensense_mpu_parse_dt(&client->dev, &st->plat_data);
+ if (result)
+ goto out_free;
+
+ /*Power on device.*/
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(&client->dev,
+ "power_on failed: %d\n", result);
+ return result;
+ }
+ }
+
+msleep(100);
+#else
+ /* power is turned on inside check chip type */
+ st->plat_data =
+ *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
+#endif
+ result = inv_check_chip_type(st, id);
+ if (result)
+ goto out_free;
+
+ result = st->init_config(indio_dev);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Could not initialize device.\n");
+ goto out_free;
+ }
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ if (!strcmp(id->name, "mpu6xxx"))
+ indio_dev->name = st->name;
+ else
+ indio_dev->name = id->name;
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+ st->irq = client->irq;
+ result = inv_mpu_probe_trigger(indio_dev);
+ if (result) {
+ pr_err("trigger probe fail\n");
+ goto out_remove_ring;
+ }
+
+ /* Tell the i2c counter, we have an IRQ */
+ INV_I2C_SETIRQ(IRQ_MPU, client->irq);
+
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_trigger;
+ }
+
+ if (INV_MPU6050 == st->chip_type ||
+ INV_MPU6500 == st->chip_type) {
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ }
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ mutex_init(&st->suspend_resume_lock);
+ result = st->set_power_state(st, false);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "%s could not be turned off.\n", st->hw->name);
+ goto out_unreg_iio;
+ }
+ inv_init_sensor_struct(st);
+ dev_info(&client->dev, "%s is ready to go!\n",
+ indio_dev->name);
+
+ return 0;
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_trigger:
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ int result;
+
+ reg = &st->reg;
+ mutex_lock(&indio_dev->mlock);
+ dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = st->set_power_state(st, false);
+ if (result)
+ dev_err(&client->adapter->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ kfifo_free(&st->timestamps);
+ iio_device_unregister(indio_dev);
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ inv_mpu_remove_trigger(indio_dev);
+ iio_buffer_unregister(indio_dev);
+ inv_mpu_unconfigure_ring(indio_dev);
+ iio_device_free(indio_dev);
+
+ dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+static int inv_setup_suspend_batchmode(struct iio_dev *indio_dev, bool suspend)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ if (st->chip_config.dmp_on &&
+ st->chip_config.enable &&
+ st->batch.on &&
+ (!st->chip_config.dmp_event_int_on)) {
+ if (!st->batch.wake_fifo_on) {
+ st->batch.overflow_on = suspend;
+ result = inv_i2c_single_write(st,
+ st->reg.user_ctrl, 0);
+ if (result)
+ return result;
+ result = inv_batchmode_setup(st);
+ if (result)
+ return result;
+ result = inv_reset_fifo(indio_dev);
+ if (result)
+ return result;
+ }
+ /* turn off data interrupt in suspend mode;turn on resume */
+ result = inv_set_interrupt_on_gesture_event(st, suspend);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/***************************************************************************
+ * olio_set_lpa_off - turn off low power acc mode
+ *
+ * This function turns off the LPA mode, and resets (not yet! 20150331) the
+ * acc settings to what they were before we went into suspend mode.
+ */
+
+int olio_set_lpa_off (struct inv_mpu_state * st) {
+ u8 off = 0;
+ u8 regstate;
+ int result;
+ int length = 1;
+
+ printk("%s olio_set_lpa_off.\n", st->hw->name);
+
+ /* Set the motion lpa to off */
+
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_INTEL_CTRL, off);
+ if (result) goto i2c_error;
+
+ st->mot_int.mot_on = off;
+ st->chip_config.lpa_mode = off;
+
+ result = inv_i2c_read (st, REG_PWR_MGMT_1, length, &regstate);
+ if (result) goto i2c_error;
+
+ regstate &= (~BIT_SLEEP & ~BIT_CYCLE & ~0x10);
+
+ result = inv_i2c_single_write(st, REG_PWR_MGMT_1, BIT_CYCLE);
+ if (result) goto i2c_error;
+
+ return result;
+
+i2c_error:
+ printk ("%s:%s: Olio: Error, i2c related...\n",
+ __FILE__, __FUNCTION__);
+ return result;
+}
+
+
+/***************************************************************************
+ * olio_set_lpa_on - turn on low power acc mode
+ *
+ * This function turns on the LPA mode. Lots of settings here, all taken
+ * from Invensense docs for the 6515 chip.
+ */
+
+int olio_set_lpa_on (struct inv_mpu_state * st) {
+ int result;
+ u8 regstate;
+ int length = 1;
+
+ int freq;
+ u8 thres;
+
+ /* OLIO_FREQ is 0 -> 3.
+ * thres is 0-255 - 128 seems OK. Steps of 4.
+ */
+
+ printk("%s olio_set_lpa_on.\n", st->hw->name);
+
+ freq = OLIO_DEF_FREQ;
+ thres = OLIO_DEF_THRES;
+
+ /* In PWR_MGMT_1 (0x6B) make CYCLE =0, SLEEP = 0 and STANDBY = 0 */
+
+ result = inv_i2c_read (st, REG_PWR_MGMT_1, length, &regstate);
+ if (result) goto i2c_error;
+
+ regstate &= (~BIT_SLEEP & ~BIT_CYCLE & ~0x10);
+ result = inv_i2c_single_write(st, REG_PWR_MGMT_1, regstate);
+ if (result) goto i2c_error;
+
+ /* In PWR_MGMT_2 (0x6C) set DIS_XA, DIS_YA, DIS_ZA = 0 and DIS_XG, DIS_YG, DIS_ZG = 1 */
+
+ result = inv_i2c_single_write(st, REG_PWR_MGMT_2, 0x07);
+ if (result) goto i2c_error;
+
+ /* In ACCEL_CONFIG 2 (0x1D) set ACCEL_FCHOICE_B = 0 and A_DLPFCFG[2:0]=1(b001) */
+
+ regstate = 0;
+ result = inv_i2c_read (st, REG_6500_ACCEL_CONFIG2, length, &regstate);
+ if (result) goto i2c_error;
+
+ regstate &= 0xF1; /* We're setting last four bits to 0001. Everything else we don't care. */
+ result = inv_i2c_single_write (st, REG_6500_ACCEL_CONFIG2, regstate);
+ if (result) goto i2c_error;
+
+ /* Set motion interrupts only */
+
+ st->mot_int.mot_on = 1;
+ st->chip_config.lpa_mode = 1;
+
+ result = inv_i2c_single_write (st, REG_INT_ENABLE, 0x40);
+ if (result) goto i2c_error;
+
+ /* Enable accel hw intelligence */
+
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_INTEL_CTRL,
+ BIT_ACCEL_INTEL_ENABLE | BIT_ACCEL_INTEL_MODE);
+ if (result) goto i2c_error;
+
+ /* Set the threshold */
+
+ result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, thres);
+ if (result) goto i2c_error;
+
+ st->mot_int.mot_thr = thres;
+
+ /* Set the frequency of acc checks */
+
+ inv_lpa_freq(st, freq);
+
+ /* Set the motion lpa to on */
+
+ result = inv_i2c_single_write(st, REG_PWR_MGMT_1, BIT_CYCLE);
+ if (result) goto i2c_error;
+
+ return 0;
+
+i2c_error:
+ printk ("%s:%s: Olio: Error, i2c related...\n",
+ __FILE__, __FUNCTION__);
+ return result;
+}
+
+
+static int inv_mpu_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ pr_debug("%s inv_mpu_resume\n", st->hw->name);
+ mutex_lock(&indio_dev->mlock);
+
+ result = 0;
+ if (st->chip_config.dmp_on && st->chip_config.enable) {
+ pr_debug("%s dmp_on and enabled\n", st->hw->name);
+ result = st->set_power_state(st, true);
+ result |= inv_read_time_and_ticks(st, true);
+ if (st->ped.int_on)
+ result |= inv_enable_pedometer_interrupt(st, true);
+ if (st->chip_config.display_orient_on)
+ result |= inv_set_display_orient_interrupt_dmp(st,
+ true);
+ result |= inv_setup_suspend_batchmode(indio_dev, false);
+ } else if (st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ }
+
+ /* TODO: OLIO, mfj: Here we need to turn the low-power motion
+ * detection off, since the watch is now awake.
+ */
+ /* result = olio_set_lpa_off (st); */
+
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return result;
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ pr_debug("%s inv_mpu_suspend\n", st->hw->name);
+ mutex_lock(&indio_dev->mlock);
+
+ result = 0;
+ if (st->chip_config.dmp_on && st->chip_config.enable) {
+ pr_debug("%s dmp_on and enabled\n", st->hw->name);
+ /* turn off pedometer interrupt during suspend */
+ if (st->ped.int_on)
+ result |= inv_enable_pedometer_interrupt(st, false);
+ /* turn off orientation interrupt during suspend */
+ if (st->chip_config.display_orient_on)
+ result |= inv_set_display_orient_interrupt_dmp(st,
+ false);
+ /* setup batch mode related during suspend */
+ result = inv_setup_suspend_batchmode(indio_dev, true);
+ /* only in DMP non-batch data mode, turn off the power */
+ if ((!st->batch.on) && (!st->chip_config.smd_enable) &&
+ (!st->ped.on))
+ result |= st->set_power_state(st, false);
+ } else if (st->chip_config.enable) {
+ /* in non DMP case, just turn off the power */
+ result |= st->set_power_state(st, false);
+ }
+
+ /* OLIO, mfj: Here we need to set the low-power motion
+ * detection, for waking the watch up.
+ */
+
+ /* result |= olio_set_lpa_on (st); */
+
+ mutex_unlock(&indio_dev->mlock);
+ mutex_lock(&st->suspend_resume_lock);
+
+ return result;
+}
+static const struct dev_pm_ops inv_mpu_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
+};
+#define INV_MPU_PMOPS (&inv_mpu_pmops)
+#else
+#define INV_MPU_PMOPS NULL
+#endif /* CONFIG_PM */
+
+static const u16 normal_i2c[] = { I2C_CLIENT_END };
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+ {"itg3500", INV_ITG3500},
+ {"mpu3050", INV_MPU3050},
+ {"mpu6050", INV_MPU6050},
+ {"mpu9150", INV_MPU9150},
+ {"mpu6500", INV_MPU6500},
+ {"mpu9250", INV_MPU9250},
+ {"mpu6xxx", INV_MPU6XXX},
+ {"mpu9350", INV_MPU9350},
+ {"mpu6515", INV_MPU6515},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio",
+ .pm = INV_MPU_PMOPS,
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init inv_mpu_init(void)
+{
+ int result = i2c_add_driver(&inv_mpu_driver);
+ if (result) {
+ pr_err("failed\n");
+ return result;
+ }
+ return 0;
+}
+
+static void __exit inv_mpu_exit(void)
+{
+ i2c_del_driver(&inv_mpu_driver);
+}
+
+module_init(inv_mpu_init);
+module_exit(inv_mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("inv-mpu-iio");
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c
new file mode 100644
index 00000000000..276d5bcfea7
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c
@@ -0,0 +1,270 @@
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+#include "inv_mpu_dts.h"
+
+int inv_mpu_power_on(struct mpu_platform_data *pdata)
+{
+ int err ;
+
+ err = regulator_enable(pdata->vdd_ana);
+ err = regulator_enable(pdata->vdd_i2c);
+ pr_debug(KERN_INFO "inv_mpu_power_on call");
+
+ return err ;
+
+}
+
+int inv_mpu_power_off(struct mpu_platform_data *pdata)
+{
+ int err ;
+
+ err = regulator_disable(pdata->vdd_ana);
+ err = regulator_disable(pdata->vdd_i2c);
+ pr_debug(KERN_INFO "inv_mpu_power_off call");
+
+ return err;
+}
+
+int inv_parse_orientation_matrix(struct device *dev, s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix*/
+ rc = of_property_read_u32(np, "axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix*/
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,secondary_type", &name)) {
+ dev_err(dev, "Missing secondary type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "compass")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ } else if (!strcmp(name, "none")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,secondary_name", &name)) {
+ dev_err(dev, "Missing secondary name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "ak8963"))
+ pdata->sec_slave_id = COMPASS_ID_AK8963;
+ else if (!strcmp(name, "ak8975"))
+ pdata->sec_slave_id = COMPASS_ID_AK8975;
+ else if (!strcmp(name, "ak8972"))
+ pdata->sec_slave_id = COMPASS_ID_AK8972;
+ else
+ return -EINVAL;
+ rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary register\n");
+ return rc;
+ }
+ pdata->secondary_i2c_addr = temp_val;
+ rc = inv_parse_secondary_orientation_matrix(dev,
+ pdata->secondary_orientation);
+
+ return rc;
+}
+
+int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,aux_type", &name)) {
+ dev_err(dev, "Missing aux type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "pressure")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE;
+ } else if (!strcmp(name, "none")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,aux_name", &name)) {
+ dev_err(dev, "Missing aux name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "bmp280"))
+ pdata->aux_slave_id = PRESSURE_ID_BMP280;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,aux_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read aux register\n");
+ return rc;
+ }
+ pdata->aux_i2c_addr = temp_val;
+
+ return 0;
+}
+
+int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ pr_debug("Invensense MPU parse_dt started.\n");
+
+ rc = inv_parse_orientation_matrix(dev, pdata->orientation);
+ if (rc)
+ return rc;
+
+ rc = inv_parse_secondary(dev, pdata);
+ if (rc)
+ return rc;
+
+ inv_parse_aux(dev, pdata);
+
+ pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana");
+ if (IS_ERR(pdata->vdd_ana)) {
+ rc = PTR_ERR(pdata->vdd_ana);
+ dev_err(dev, "Regulator get failed vdd_ana-supply rc=%d\n", rc);
+ return rc;
+ }
+ pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c");
+ if (IS_ERR(pdata->vdd_i2c)) {
+ rc = PTR_ERR(pdata->vdd_i2c);
+ dev_err(dev, "Regulator get failed vcc-i2c-supply rc=%d\n", rc);
+ return rc;
+ }
+ pdata->power_on = inv_mpu_power_on;
+ pdata->power_off = inv_mpu_power_off;
+ pr_debug("Invensense MPU parse_dt complete.\n");
+ return rc;
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h
new file mode 100644
index 00000000000..151ac74ea05
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h
@@ -0,0 +1,30 @@
+/*
+* 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_DTS_H_
+#define _INV_MPU_DTS_H_
+
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+
+int inv_mpu_power_on(struct mpu_platform_data *pdata);
+int inv_mpu_power_off(struct mpu_platform_data *pdata);
+int inv_parse_orientation_matrix(struct device *dev, s8 *orient);
+int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient);
+int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata);
+int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata);
+int invensense_mpu_parse_dt(struct device *dev,
+ struct mpu_platform_data *pdata);
+
+#endif /* #ifndef _INV_MPU_DTS_H_ */
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h
new file mode 100644
index 00000000000..d0da84c54af
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h
@@ -0,0 +1,1068 @@
+/*
+* 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/spinlock.h>
+#include <linux/mpu.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+
+#include "dmpKey.h"
+
+/*register and associated bit definition*/
+#define REG_3050_FIFO_EN 0x12
+#define BITS_3050_ACCEL_OUT 0x0E
+
+#define REG_3050_AUX_VDDIO 0x13
+#define BIT_3050_VDDIO 0x04
+
+#define REG_3050_SLAVE_ADDR 0x14
+#define REG_3050_SAMPLE_RATE_DIV 0x15
+#define REG_3050_LPF 0x16
+#define REG_3050_INT_ENABLE 0x17
+#define REG_3050_AUX_BST_ADDR 0x18
+#define REG_3050_INT_STATUS 0x1A
+#define REG_3050_TEMPERATURE 0x1B
+#define REG_3050_RAW_GYRO 0x1D
+#define REG_3050_AUX_XOUT_H 0x23
+#define REG_3050_FIFO_COUNT_H 0x3A
+#define REG_3050_FIFO_R_W 0x3C
+
+#define REG_3050_USER_CTRL 0x3D
+#define BIT_3050_AUX_IF_EN 0x20
+#define BIT_3050_FIFO_RST 0x02
+
+#define REG_3050_PWR_MGMT_1 0x3E
+#define BITS_3050_POWER1 0x30
+#define BITS_3050_POWER2 0x10
+#define BITS_3050_GYRO_STANDBY 0x38
+
+#define REG_3500_OTP 0x0
+
+#define REG_YGOFFS_TC 0x1
+#define BIT_I2C_MST_VDDIO 0x80
+
+#define REG_XA_OFFS_H 0x6
+#define REG_XA_OFFS_L_TC 0x7
+#define REG_PRODUCT_ID 0xC
+#define REG_ST_GCT_X 0xD
+#define REG_XG_OFFS_USRH 0x13
+#define REG_SAMPLE_RATE_DIV 0x19
+#define REG_CONFIG 0x1A
+
+#define REG_GYRO_CONFIG 0x1B
+#define BITS_SELF_TEST_EN 0xE0
+
+#define REG_ACCEL_CONFIG 0x1C
+#define REG_ACCEL_MOT_THR 0x1F
+#define REG_ACCEL_MOT_DUR 0x20
+
+#define REG_FIFO_EN 0x23
+#define BIT_ACCEL_OUT 0x08
+#define BITS_GYRO_OUT 0x70
+
+#define REG_I2C_MST_CTRL 0x24
+#define BIT_WAIT_FOR_ES 0x40
+
+#define REG_I2C_SLV0_ADDR 0x25
+#define REG_I2C_SLV0_REG 0x26
+#define REG_I2C_SLV0_CTRL 0x27
+#define REG_I2C_SLV1_ADDR 0x28
+#define REG_I2C_SLV1_REG 0x29
+#define REG_I2C_SLV1_CTRL 0x2A
+#define REG_I2C_SLV2_ADDR 0x2B
+#define REG_I2C_SLV2_REG 0x2C
+#define REG_I2C_SLV2_CTRL 0x2D
+#define REG_I2C_SLV3_ADDR 0x2E
+#define REG_I2C_SLV3_REG 0x2F
+#define REG_I2C_SLV3_CTRL 0x30
+#define REG_I2C_SLV4_CTRL 0x34
+
+#define INV_MPU_BIT_SLV_EN 0x80
+#define INV_MPU_BIT_BYTE_SW 0x40
+#define INV_MPU_BIT_REG_DIS 0x20
+#define INV_MPU_BIT_I2C_READ 0x80
+
+#define REG_INT_PIN_CFG 0x37
+#define BIT_BYPASS_EN 0x2
+
+#define REG_INT_ENABLE 0x38
+#define BIT_DATA_RDY_EN 0x01
+#define BIT_DMP_INT_EN 0x02
+#define BIT_ZMOT_EN 0x20
+#define BIT_MOT_EN 0x40
+#define BIT_6500_WOM_EN 0x40
+
+#define REG_DMP_INT_STATUS 0x39
+#define BIT_DMP_INT_CI 0x01
+
+#define REG_INT_STATUS 0x3A
+#define BIT_MOT_INT 0x40
+#define BIT_ZMOT_INT 0x20
+
+#define REG_RAW_ACCEL 0x3B
+#define REG_TEMPERATURE 0x41
+#define REG_EXT_SENS_DATA_00 0x49
+#define REG_EXT_SENS_DATA_08 0x51
+
+#define REG_ACCEL_INTEL_STATUS 0x61
+
+#define INV_MPU_REG_I2C_SLV0_DO 0x63
+#define INV_MPU_REG_I2C_SLV1_DO 0x64
+#define INV_MPU_REG_I2C_SLV2_DO 0x65
+#define INV_MPU_REG_I2C_SLV3_DO 0x66
+
+#define REG_I2C_MST_DELAY_CTRL 0x67
+#define BIT_SLV0_DLY_EN 0x01
+#define BIT_SLV1_DLY_EN 0x02
+#define BIT_SLV2_DLY_EN 0x04
+#define BIT_SLV3_DLY_EN 0x08
+
+#define REG_USER_CTRL 0x6A
+#define BIT_FIFO_RST 0x04
+#define BIT_DMP_RST 0x08
+#define BIT_I2C_MST_EN 0x20
+#define BIT_FIFO_EN 0x40
+#define BIT_DMP_EN 0x80
+
+#define REG_PWR_MGMT_1 0x6B
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_CYCLE 0x20
+#define BIT_CLK_MASK 0x7
+
+#define REG_PWR_MGMT_2 0x6C
+#define BIT_PWR_ACCEL_STBY 0x38
+#define BIT_PWR_GYRO_STBY 0x07
+#define BIT_LPA_FREQ 0xC0
+
+#define REG_BANK_SEL 0x6D
+#define REG_MEM_START_ADDR 0x6E
+#define REG_MEM_RW 0x6F
+#define REG_PRGM_STRT_ADDRH 0x70
+#define REG_FIFO_COUNT_H 0x72
+#define REG_FIFO_R_W 0x74
+#define REG_WHOAMI 0x75
+
+#define REG_6500_XG_ST_DATA 0x0
+#define REG_6500_XA_ST_DATA 0xD
+#define REG_6500_XA_OFFS_H 0x77
+#define REG_6500_YA_OFFS_H 0x7A
+#define REG_6500_ZA_OFFS_H 0x7D
+#define REG_6500_ACCEL_CONFIG2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+#define BIT_FIFO_SIZE_1K 0x40
+
+#define REG_6500_LP_ACCEL_ODR 0x1E
+#define REG_6500_ACCEL_WOM_THR 0x1F
+
+#define REG_6500_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_ENABLE 0x80
+#define BIT_ACCEL_INTEL_MODE 0x40
+
+/* data definitions */
+#define DMP_START_ADDR 0x400
+#define DMP_MASK_TAP 0x3f
+#define DMP_MASK_DIS_ORIEN 0xC0
+#define DMP_DIS_ORIEN_SHIFT 6
+
+#define BYTES_FOR_DMP 8
+#define BYTES_FOR_EVENTS 4
+#define QUATERNION_BYTES 16
+#define BYTES_PER_SENSOR 6
+#define MPU3050_FOOTER_SIZE 2
+#define FIFO_COUNT_BYTE 2
+#define FIFO_THRESHOLD 800
+#define FIFO_SIZE 800
+#define HARDWARE_FIFO_SIZE 1024
+#define MAX_READ_SIZE 64
+#define POWER_UP_TIME 100
+#define SENSOR_UP_TIME 30
+#define REG_UP_TIME 5
+#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
+#define MPU_MEM_BANK_SIZE 256
+#define SELF_TEST_GYRO_FULL_SCALE 250
+#define SELF_TEST_ACCEL_FULL_SCALE 8
+#define SELF_TEST_ACCEL_6500_SCALE 2
+
+/* data header defines */
+#define PRESSURE_HDR 0x8000
+#define ACCEL_HDR 0x4000
+#define GYRO_HDR 0x2000
+#define COMPASS_HDR 0x1000
+#define LPQUAT_HDR 0x0800
+#define SIXQUAT_HDR 0x0400
+#define PEDQUAT_HDR 0x0200
+#define STEP_DETECTOR_HDR 0x0100
+#define STEP_INDICATOR_MASK 0xf
+
+#define MAX_BYTES_PER_SAMPLE 80
+#define MAX_HW_FIFO_BYTES (BYTES_PER_SENSOR * 2)
+#define IIO_BUFFER_BYTES 8
+#define HEADERED_NORMAL_BYTES 8
+#define HEADERED_Q_BYTES 16
+
+#define MPU6XXX_MAX_MOTION_THRESH (255*4)
+#define MPU6050_MOTION_THRESH_SHIFT 5
+#define MPU6500_MOTION_THRESH_SHIFT 2
+#define MPU6050_MOTION_DUR_DEFAULT 1
+#define MPU6050_ID 0x68
+#define MPU6050_MAX_MOTION_DUR 255
+#define MPU_TEMP_SHIFT 16
+#define LPA_FREQ_SHIFT 6
+#define COMPASS_RATE_SCALE 10
+#define MAX_GYRO_FS_PARAM 3
+#define MAX_ACCEL_FS_PARAM 3
+#define MAX_LPA_FREQ_PARAM 3
+#define MPU_MAX_A_OFFSET_VALUE 16383
+#define MPU_MIN_A_OFFSET_VALUE -16384
+#define MPU_MAX_G_OFFSET_VALUE 32767
+#define MPU_MIN_G_OFFSET_VALUE -32767
+#define MPU6XXX_MAX_MPU_MEM (256 * 12)
+
+#define INIT_MOT_DUR 128
+#define INIT_MOT_THR 128
+#define INIT_ZMOT_DUR 128
+#define INIT_ZMOT_THR 128
+#define INIT_ST_SAMPLES 200
+#define INIT_ST_MPU6050_SAMPLES 600
+#define INIT_ST_THRESHOLD 50
+#define INIT_PED_INT_THRESH 2
+#define INIT_PED_THRESH 7
+#define ST_THRESHOLD_MULTIPLIER 10
+#define ST_MAX_SAMPLES 500
+#define ST_MAX_THRESHOLD 100
+#define DMP_INTERVAL_INIT (5 * NSEC_PER_MSEC)
+#define DMP_INTERVAL_MIN_ADJ (50 * NSEC_PER_USEC)
+
+/*---- MPU6500 ----*/
+#define MPU6500_ID 0x70 /* unique WHOAMI */
+#define MPU6515_ID 0x74 /* unique WHOAMI */
+#define MPU6500_PRODUCT_REVISION 1
+#define MPU6500_MEM_REV_ADDR 0x16
+#define INV_MPU_REV_MASK 0x0F
+#define MPU6500_REV 2
+#define MPU_DMP_LOAD_START 0x20
+
+/*---- MPU9250 ----*/
+#define MPU9250_ID 0x71 /* unique WHOAMI */
+
+/* ----MPU9350 ----*/
+#define MPU9350_ID 0x72
+
+#define THREE_AXIS 3
+#define GYRO_CONFIG_FSR_SHIFT 3
+#define ACCEL_CONFIG_FSR_SHIFT 3
+#define GYRO_DPS_SCALE 250
+#define MEM_ADDR_PROD_REV 0x6
+#define SOFT_PROD_VER_BYTES 5
+#define CRC_FIRMWARE_SEED 0
+#define SELF_TEST_SUCCESS 1
+#define MS_PER_DMP_TICK 20
+#define DMP_IMAGE_SIZE 2463
+
+/* init parameters */
+#define INIT_FIFO_RATE 50
+#define INIT_DMP_OUTPUT_RATE 25
+#define INIT_DUR_TIME (NSEC_PER_SEC / INIT_FIFO_RATE)
+#define INIT_TAP_THRESHOLD 100
+#define INIT_TAP_TIME 100
+#define INIT_TAP_MIN_COUNT 2
+#define INIT_SAMPLE_DIVIDER 4
+#define MPU_INIT_SMD_DELAY_THLD 3
+#define MPU_INIT_SMD_DELAY2_THLD 1
+#define MPU_INIT_SMD_THLD 1500
+#define MPU_DEFAULT_DMP_FREQ 200
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */
+
+#define BIT_PRFTCH_EN 0x40
+#define BIT_CFG_USER_BANK 0x20
+#define BITS_MEM_SEL 0x1f
+
+#define TIME_STAMP_TOR 5
+#define MAX_CATCH_UP 5
+#define DEFAULT_ACCEL_TRIM 16384
+#define DEFAULT_GYRO_TRIM 131
+#define MAX_FIFO_RATE 1000
+#define MAX_DMP_OUTPUT_RATE 200
+#define MIN_FIFO_RATE 4
+#define ONE_K_HZ 1000
+#define NS_PER_MS_SHIFT 20
+#define END_MARKER 0x0010
+#define EMPTY_MARKER 0x0020
+
+/*tap related defines */
+#define INV_TAP 0x08
+#define INV_NUM_TAP_AXES 3
+
+#define INV_TAP_AXIS_X_POS 0x20
+#define INV_TAP_AXIS_X_NEG 0x10
+#define INV_TAP_AXIS_Y_POS 0x08
+#define INV_TAP_AXIS_Y_NEG 0x04
+#define INV_TAP_AXIS_Z_POS 0x02
+#define INV_TAP_AXIS_Z_NEG 0x01
+#define INV_TAP_ALL_DIRECTIONS 0x3f
+
+#define INV_TAP_AXIS_X 0x1
+#define INV_TAP_AXIS_Y 0x2
+#define INV_TAP_AXIS_Z 0x4
+
+#define INV_TAP_AXIS_ALL \
+ (INV_TAP_AXIS_X | \
+ INV_TAP_AXIS_Y | \
+ INV_TAP_AXIS_Z)
+
+#define INT_SRC_TAP 0x01
+#define INT_SRC_DISPLAY_ORIENT 0x08
+#define INT_SRC_SHAKE 0x10
+
+#define INV_X_AXIS_INDEX 0x00
+#define INV_Y_AXIS_INDEX 0x01
+#define INV_Z_AXIS_INDEX 0x02
+
+#define INV_ELEMENT_1 0x0001
+#define INV_ELEMENT_2 0x0002
+#define INV_ELEMENT_3 0x0004
+#define INV_ELEMENT_4 0x0008
+#define INV_ELEMENT_5 0x0010
+#define INV_ELEMENT_6 0x0020
+#define INV_ELEMENT_7 0x0040
+#define INV_ELEMENT_8 0x0080
+#define INV_ALL 0xFFFF
+#define INV_ELEMENT_MASK 0x00FF
+#define INV_GYRO_ACC_MASK 0x007E
+#define INV_ACCEL_MASK 0x70
+#define INV_GYRO_MASK 0xE
+
+struct inv_mpu_state;
+
+/**
+ * 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.
+ * @accel_config: accel config register
+ * @fifo_count_h: Upper byte of FIFO count.
+ * @fifo_r_w: FIFO register.
+ * @raw_accel Address of first accel register.
+ * @temperature temperature register
+ * @int_enable: Interrupt enable register.
+ * @int_status: Interrupt flags.
+ * @pwr_mgmt_1: Controls chip's power state and clock source.
+ * @pwr_mgmt_2: Controls power state of individual sensors.
+ * @mem_start_addr: Address of first memory read.
+ * @mem_r_w: Access to memory.
+ * @prgm_strt_addrh firmware program start address register
+ */
+struct inv_reg_map_s {
+ u8 sample_rate_div;
+ u8 lpf;
+ u8 bank_sel;
+ u8 user_ctrl;
+ u8 fifo_en;
+ u8 gyro_config;
+ u8 accel_config;
+ u8 fifo_count_h;
+ u8 fifo_r_w;
+ u8 raw_accel;
+ u8 temperature;
+ u8 int_enable;
+ u8 int_status;
+ u8 pwr_mgmt_1;
+ u8 pwr_mgmt_2;
+ u8 mem_start_addr;
+ u8 mem_r_w;
+ u8 prgm_strt_addrh;
+};
+
+/* device enum */
+enum inv_devices {
+ INV_ITG3500,
+ INV_MPU3050,
+ INV_MPU6050,
+ INV_MPU9150,
+ INV_MPU6500,
+ INV_MPU9250,
+ INV_MPU6XXX,
+ INV_MPU9350,
+ INV_MPU6515,
+ INV_NUM_PARTS
+};
+
+/**
+ * struct inv_hw_s - Other important hardware information.
+ * @num_reg: Number of registers on device.
+ * @name: name of the chip
+ */
+struct inv_hw_s {
+ u8 num_reg;
+ u8 *name;
+};
+
+/* enum for sensor */
+enum INV_SENSORS {
+ SENSOR_GYRO = 0,
+ SENSOR_ACCEL,
+ SENSOR_COMPASS,
+ SENSOR_PRESSURE,
+ SENSOR_STEP,
+ SENSOR_PEDQ,
+ SENSOR_SIXQ,
+ SENSOR_LPQ,
+ SENSOR_NUM_MAX,
+ SENSOR_INVALID,
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @ts: this sensors timestamp.
+ * @dur: duration between samples in ns.
+ * @rate: sensor data rate.
+ * @counter: dmp tick counter corresponding to rate.
+ * @on: sensor on/off.
+ * @sample_size: number of bytes for the sensor.
+ * @send_data: function pointer to send data or not.
+ * @set_rate: funcition pointer to set data rate.
+ */
+struct inv_sensor {
+ u64 ts;
+ int dur;
+ int rate;
+ int counter;
+ bool on;
+ u8 sample_size;
+ int (*send_data)(struct inv_mpu_state *st, bool on);
+ int (*set_rate)(struct inv_mpu_state *st);
+};
+
+/**
+ * struct inv_batch - information batchmode.
+ * @on: derived variable for batch mode.
+ * @overflow_on: overflow mode for batchmode.
+ * @wake_fifo_on: overflow for suspend mode.
+ * @counter: counter for batch mode.
+ * @timeout: nominal timeout value for batchmode in milliseconds.
+ * @min_rate: minimum sensor rate that is turned on.
+ */
+struct inv_batch {
+ bool on;
+ bool overflow_on;
+ bool wake_fifo_on;
+ u32 counter;
+ u32 timeout;
+ u32 min_rate;
+};
+
+/**
+ * struct inv_chip_config_s - Cached chip configuration data.
+ * @fsr: Full scale range.
+ * @lpf: Digital low pass filter frequency.
+ * @accel_fs: accel full scale range.
+ * @has_footer: MPU3050 specific work around.
+ * @has_compass: has compass or not.
+ * @has_pressure: has pressure sensor or not.
+ * @enable: master enable to enable output
+ * @accel_enable: enable accel functionality
+ * @gyro_enable: enable gyro functionality
+ * @is_asleep: 1 if chip is powered down.
+ * @dmp_on: dmp is on/off.
+ * @dmp_int_on: dmp interrupt on/off.
+ * @step_indicator_on: step indicate bit added to the sensor or not.
+ * @dmp_event_int_on: dmp event interrupt on/off.
+ * @firmware_loaded: flag indicate firmware loaded or not.
+ * @lpa_mod: low power mode.
+ * @display_orient_on: display orientation on/off.
+ * @normal_compass_measure: discard first compass data after reset.
+ * @normal_pressure_measure: discard first pressure data after reset.
+ * @smd_enable: disable/enable SMD function.
+ * @adjust_time: flag to indicate whether adjust chip clock or not.
+ * @smd_triggered: smd is triggered.
+ * @lpa_freq: low power frequency
+ * @prog_start_addr: firmware program start address.
+ * @fifo_rate: current FIFO update rate.
+ * @bytes_per_datum: number of bytes for 1 sample data.
+ */
+struct inv_chip_config_s {
+ u32 fsr:2;
+ u32 lpf:3;
+ u32 accel_fs:2;
+ u32 has_footer:1;
+ u32 has_compass:1;
+ u32 has_pressure:1;
+ u32 enable:1;
+ u32 accel_enable:1;
+ u32 gyro_enable:1;
+ u32 is_asleep:1;
+ u32 dmp_on:1;
+ u32 dmp_int_on:1;
+ u32 dmp_event_int_on:1;
+ u32 step_indicator_on:1;
+ u32 firmware_loaded:1;
+ u32 lpa_mode:1;
+ u32 display_orient_on:1;
+ u32 normal_compass_measure:1;
+ u32 normal_pressure_measure:1;
+ u32 smd_enable:1;
+ u32 adjust_time:1;
+ u32 smd_triggered:1;
+ u16 lpa_freq;
+ u16 prog_start_addr;
+ u16 fifo_rate;
+ u16 bytes_per_datum;
+};
+
+/**
+ * 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.
+ * @accel_sens_trim: accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+ u8 product_id;
+ u8 product_revision;
+ u8 silicon_revision;
+ u8 software_revision;
+ u8 multi;
+ u8 compass_sens[3];
+ u32 gyro_sens_trim;
+ u32 accel_sens_trim;
+};
+
+/**
+ * struct inv_tap structure to store tap data.
+ * @min_count: minimum taps counted.
+ * @thresh: tap threshold.
+ * @time: tap time.
+ * @on: tap on/off.
+ */
+struct inv_tap {
+ u16 min_count;
+ u16 thresh;
+ u16 time;
+ bool on;
+};
+
+/**
+ * struct accel_mot_int_s structure to store motion interrupt data
+ * @mot_thr: motion threshold.
+ * @mot_dur: motion duration.
+ * @mot_on: flag to indicate motion detection on;
+ */
+struct accel_mot_int {
+ u16 mot_thr;
+ u32 mot_dur;
+ u8 mot_on:1;
+};
+
+/**
+ * struct self_test_setting - self test settables from sysfs
+ * samples: number of samples used in self test.
+ * threshold: threshold fail/pass criterion in self test.
+ * This value is in the percentage multiplied by 100.
+ * So 14% would be 14.
+ */
+struct self_test_setting {
+ u16 samples;
+ u16 threshold;
+};
+
+/**
+ * struct inv_smd significant motion detection structure.
+ * @threshold: accel threshold for motion detection.
+ * @delay: delay time to confirm 2nd motion.
+ * @delay2: delay window parameter.
+ */
+struct inv_smd {
+ u32 threshold;
+ u32 delay;
+ u32 delay2;
+};
+
+/**
+ * struct inv_ped pedometer related data structure.
+ * @step: steps taken.
+ * @time: time taken during the period.
+ * @last_step_time: last time the step is taken.
+ * @step_thresh: step threshold to show steps.
+ * @int_thresh: step threshold to generate interrupt.
+ * @int_on: pedometer interrupt enable/disable.
+ * @on: pedometer on/off.
+ */
+struct inv_ped {
+ u64 step;
+ u64 time;
+ u64 last_step_time;
+ u16 step_thresh;
+ u16 int_thresh;
+ bool int_on;
+ bool on;
+};
+
+struct inv_mpu_slave;
+/**
+ * struct inv_mpu_state - Driver state variables.
+ * @chip_config: Cached attribute information.
+ * @chip_info: Chip information from read-only registers.
+ * @trig; iio trigger.
+ * @tap: tap data structure.
+ * @smd: SMD data structure.
+ * @ped: pedometer data structure.
+ * @batch: batchmode data structure.
+ * @reg: Map of important registers.
+ * @self_test: self test settings.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @time_stamp_lock: spin lock to time stamp.
+ * @suspend_resume_lock: mutex lock for suspend/resume.
+ * @client: i2c client handle.
+ * @plat_data: platform data.
+ * @slave_accel: mpu slave handle for accelerometer(MPU3050 only).
+ * @slave_compass: mpu slave handle for magnetometer.
+ * @slave_pressure: mpu slave handle for pressure sensor.
+ * (*set_power_state)(struct inv_mpu_state *, int on): function ptr
+ * (*switch_gyro_engine)(struct inv_mpu_state *, int on): function ptr
+ * (*switch_accel_engine)(struct inv_mpu_state *, int on): function ptr
+ * (*init_config)(struct iio_dev *indio_dev): function ptr
+ * (*setup_reg)(struct inv_reg_map_s *reg): function ptr
+ * @timestamps: kfifo queue to store time stamp.
+ * @irq: irq number store.
+ * @accel_bias: accel bias store.
+ * @gyro_bias: gyro bias store.
+ * @input_gyro_offset[3]: gyro offset from sysfs.
+ * @input_accel_offset[3]: accel offset from sysfs.
+ * @input_accel_dmp_bias[3]: accel bias for dmp.
+ * @input_gyro_dmp_bias[3];: gyro bias for dmp.
+ * @rom_gyro_bias[3]: gyro bias from sysfs.
+ * @rom_accel_bias[3]: accel bias from sysfs.
+ * @fifo_data[6]: fifo data storage.
+ * @i2c_addr: i2c address.
+ * @sample_divider: sample divider for dmp.
+ * @fifo_divider: fifo divider for dmp.
+ * @display_orient_data:display orient data.
+ * @tap_data: tap data.
+ * @bytes_per_sec: bytes per seconds when in DMP mode.
+ * @left_over[HEADERED_Q_BYTES]: left over bytes storage.
+ * @left_over_size: left over size.
+ * @sensor{SENSOR_NUM_MAX]: sensor individual properties.
+ * @fifo_count: current fifo_count;
+ * @dmp_counter: dmp_counter;
+ * @dmp_ticks: DMP ticks past since the previous read.
+ * @sl_handle: Handle to I2C port.
+ * @irq_dur_ns: duration between each irq.
+ * @ts_counter: time stamp counter.
+ * @dmp_interval: dmp interval. nomial value is 5 ms.
+ * @dmp_interval_accum: dmp interval accumlater.
+ * @diff_accumulater: accumlator for the difference of nominal and actual.
+ * @last_ts: last time stamp.
+ * @step_detector_base_ts: base time stamp for step detector calculation.
+ * @prev_ts: previous time stamp.
+ * @pedometer_step: pedometer steps stored in driver.
+ * @pedometer_time: pedometer time stored in driver.
+ * @last_run_time: last time the post ISR runs.
+ * @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX.
+ * @secondary_name: name for the slave device in the secondary I2C.
+ */
+struct inv_mpu_state {
+#define TIMESTAMP_FIFO_SIZE 64
+ struct inv_chip_config_s chip_config;
+ struct inv_chip_info_s chip_info;
+ struct iio_trigger *trig;
+ struct inv_tap tap;
+ struct inv_smd smd;
+ struct inv_ped ped;
+ struct inv_batch batch;
+ struct inv_reg_map_s reg;
+ struct self_test_setting self_test;
+ const struct inv_hw_s *hw;
+ enum inv_devices chip_type;
+ spinlock_t time_stamp_lock;
+ struct mutex suspend_resume_lock;
+ struct i2c_client *client;
+ struct mpu_platform_data plat_data;
+ struct inv_mpu_slave *slave_accel;
+ struct inv_mpu_slave *slave_compass;
+ struct inv_mpu_slave *slave_pressure;
+ struct accel_mot_int mot_int;
+ int (*set_power_state)(struct inv_mpu_state *, bool on);
+ int (*switch_gyro_engine)(struct inv_mpu_state *, bool on);
+ int (*switch_accel_engine)(struct inv_mpu_state *, bool on);
+ int (*init_config)(struct iio_dev *indio_dev);
+ void (*setup_reg)(struct inv_reg_map_s *reg);
+ DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE);
+ short irq;
+ int accel_bias[3];
+ int gyro_bias[3];
+ s16 input_gyro_offset[3];
+ s16 input_accel_offset[3];
+ int input_accel_dmp_bias[3];
+ int input_gyro_dmp_bias[3];
+ s16 rom_gyro_offset[3];
+ s16 rom_accel_offset[3];
+ u8 fifo_data[6];
+ u8 i2c_addr;
+ u8 sample_divider;
+ u8 fifo_divider;
+ u8 display_orient_data;
+ u8 tap_data;
+ u16 bytes_per_sec;
+ u8 left_over[HEADERED_Q_BYTES];
+ u32 left_over_size;
+ struct inv_sensor sensor[SENSOR_NUM_MAX];
+ u32 fifo_count;
+ u32 dmp_counter;
+ u32 dmp_ticks;
+ void *sl_handle;
+ u32 irq_dur_ns;
+ u32 ts_counter;
+ u32 dmp_interval;
+ s32 dmp_interval_accum;
+ s64 diff_accumulater;
+ u64 last_ts;
+ u64 step_detector_base_ts;
+ u64 prev_ts;
+ u64 last_run_time;
+ u8 name[20];
+ u8 secondary_name[20];
+};
+
+/* produces an unique identifier for each device based on the
+ combination of product version and product revision */
+struct prod_rev_map_t {
+ u16 mpl_product_key;
+ u8 silicon_rev;
+ u16 gyro_trim;
+ u16 accel_trim;
+};
+
+/**
+ * struct inv_mpu_slave - MPU slave structure.
+ * @st_upper: compass self test upper limit.
+ * @st_lower: compass self test lower limit.
+ * @scale: compass scale.
+ * @rate_scale: decide how fast a compass can read.
+ * @min_read_time: minimum time between each reading.
+ * @self_test: self test method of the slave.
+ * @set_scale: set scale of slave
+ * @get_scale: read scale back of the slave.
+ * @suspend: suspend operation.
+ * @resume: resume operation.
+ * @setup: setup chip. initialization.
+ * @combine_data: combine raw data into meaningful data.
+ * @read_data: read external sensor and output
+ * @get_mode: get current chip mode.
+ * @set_lpf: set low pass filter.
+ * @set_fs: set full scale
+ * @prev_ts: last time it is read.
+ */
+struct inv_mpu_slave {
+ const short *st_upper;
+ const short *st_lower;
+ int scale;
+ int rate_scale;
+ int min_read_time;
+ int (*self_test)(struct inv_mpu_state *);
+ int (*set_scale)(struct inv_mpu_state *, int scale);
+ int (*get_scale)(struct inv_mpu_state *, int *val);
+ int (*suspend)(struct inv_mpu_state *);
+ int (*resume)(struct inv_mpu_state *);
+ int (*setup)(struct inv_mpu_state *);
+ int (*combine_data)(u8 *in, short *out);
+ int (*read_data)(struct inv_mpu_state *, short *out);
+ int (*get_mode)(void);
+ int (*set_lpf)(struct inv_mpu_state *, int rate);
+ int (*set_fs)(struct inv_mpu_state *, int fs);
+ u64 prev_ts;
+};
+
+/* 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_ACCEL_X,
+ INV_MPU_SCAN_ACCEL_Y,
+ INV_MPU_SCAN_ACCEL_Z,
+ INV_MPU_SCAN_GYRO_X,
+ INV_MPU_SCAN_GYRO_Y,
+ INV_MPU_SCAN_GYRO_Z,
+ INV_MPU_SCAN_MAGN_X,
+ INV_MPU_SCAN_MAGN_Y,
+ INV_MPU_SCAN_MAGN_Z,
+ INV_MPU_SCAN_TIMESTAMP,
+};
+
+enum inv_filter_e {
+ INV_FILTER_256HZ_NOLPF2 = 0,
+ INV_FILTER_188HZ,
+ INV_FILTER_98HZ,
+ INV_FILTER_42HZ,
+ INV_FILTER_20HZ,
+ INV_FILTER_10HZ,
+ INV_FILTER_5HZ,
+ INV_FILTER_2100HZ_NOLPF,
+ NUM_FILTER
+};
+
+enum inv_slave_mode {
+ INV_MODE_SUSPEND,
+ INV_MODE_NORMAL,
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+ MEM_RAM_BANK_0 = 0,
+ MEM_RAM_BANK_1,
+ MEM_RAM_BANK_2,
+ MEM_RAM_BANK_3,
+ MEM_RAM_BANK_4,
+ MEM_RAM_BANK_5,
+ MEM_RAM_BANK_6,
+ MEM_RAM_BANK_7,
+ MEM_RAM_BANK_8,
+ MEM_RAM_BANK_9,
+ MEM_RAM_BANK_10,
+ MEM_RAM_BANK_11,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = 16
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+ ATTR_DMP_GYRO_X_DMP_BIAS,
+ ATTR_DMP_GYRO_Y_DMP_BIAS,
+ ATTR_DMP_GYRO_Z_DMP_BIAS,
+ ATTR_DMP_ACCEL_X_DMP_BIAS,
+ ATTR_DMP_ACCEL_Y_DMP_BIAS,
+ ATTR_DMP_ACCEL_Z_DMP_BIAS,
+ ATTR_DMP_PED_INT_ON,
+ ATTR_DMP_PED_STEP_THRESH,
+ ATTR_DMP_PED_INT_THRESH,
+ ATTR_DMP_PED_ON,
+ ATTR_DMP_SMD_ENABLE,
+ ATTR_DMP_SMD_THLD,
+ ATTR_DMP_SMD_DELAY_THLD,
+ ATTR_DMP_SMD_DELAY_THLD2,
+ ATTR_DMP_PEDOMETER_STEPS,
+ ATTR_DMP_PEDOMETER_TIME,
+ ATTR_DMP_PEDOMETER_COUNTER,
+ ATTR_DMP_TAP_ON,
+ ATTR_DMP_TAP_THRESHOLD,
+ ATTR_DMP_TAP_MIN_COUNT,
+ ATTR_DMP_TAP_TIME,
+ ATTR_DMP_DISPLAY_ORIENTATION_ON,
+/* *****above this line, are DMP features, power needs on/off */
+/* *****below this line, are DMP features, no power needed */
+ ATTR_DMP_ON,
+ ATTR_DMP_INT_ON,
+ ATTR_DMP_EVENT_INT_ON,
+ ATTR_DMP_STEP_INDICATOR_ON,
+ ATTR_DMP_BATCHMODE_TIMEOUT,
+ ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL,
+ ATTR_DMP_SIX_Q_ON,
+ ATTR_DMP_SIX_Q_RATE,
+ ATTR_DMP_LPQ_ON,
+ ATTR_DMP_LPQ_RATE,
+ ATTR_DMP_PED_Q_ON,
+ ATTR_DMP_PED_Q_RATE,
+ ATTR_DMP_STEP_DETECTOR_ON,
+ ATTR_DMP_STEP_DETECTOR_RATE,
+/* *****above this line, it is all DMP related features */
+/* *****below this line, it is all non-DMP related features */
+ ATTR_GYRO_SCALE,
+ ATTR_ACCEL_SCALE,
+ ATTR_COMPASS_SCALE,
+ ATTR_GYRO_X_OFFSET,
+ ATTR_GYRO_Y_OFFSET,
+ ATTR_GYRO_Z_OFFSET,
+ ATTR_ACCEL_X_OFFSET,
+ ATTR_ACCEL_Y_OFFSET,
+ ATTR_ACCEL_Z_OFFSET,
+ ATTR_MOTION_LPA_ON,
+ ATTR_MOTION_LPA_FREQ,
+ ATTR_MOTION_LPA_THRESHOLD,
+/* *****above this line, it is non-DMP, power needs on/off */
+/* *****below this line, it is non-DMP, no needs to on/off power */
+ ATTR_SELF_TEST_SAMPLES,
+ ATTR_SELF_TEST_THRESHOLD,
+ ATTR_GYRO_ENABLE,
+ ATTR_GYRO_FIFO_ENABLE,
+ ATTR_GYRO_RATE,
+ ATTR_ACCEL_ENABLE,
+ ATTR_ACCEL_FIFO_ENABLE,
+ ATTR_ACCEL_RATE,
+ ATTR_COMPASS_ENABLE,
+ ATTR_COMPASS_RATE,
+ ATTR_PRESSURE_ENABLE,
+ ATTR_PRESSURE_RATE,
+ ATTR_POWER_STATE, /* this is fake sysfs for compatibility */
+ ATTR_FIRMWARE_LOADED,
+ ATTR_SAMPLING_FREQ,
+/* *****below this line, it is attributes only has show methods */
+ ATTR_SELF_TEST, /* this has show-only methods needs power on/off */
+ ATTR_GYRO_X_CALIBBIAS,
+ ATTR_GYRO_Y_CALIBBIAS,
+ ATTR_GYRO_Z_CALIBBIAS,
+ ATTR_ACCEL_X_CALIBBIAS,
+ ATTR_ACCEL_Y_CALIBBIAS,
+ ATTR_ACCEL_Z_CALIBBIAS,
+ ATTR_SELF_TEST_GYRO_SCALE,
+ ATTR_SELF_TEST_ACCEL_SCALE,
+ ATTR_GYRO_MATRIX,
+ ATTR_ACCEL_MATRIX,
+ ATTR_COMPASS_MATRIX,
+ ATTR_SECONDARY_NAME,
+#ifdef CONFIG_INV_TESTING
+ ATTR_COMPASS_SENS,
+ ATTR_I2C_COUNTERS,
+ ATTR_REG_WRITE,
+ ATTR_DEBUG_SMD_ENABLE_TESTP1,
+ ATTR_DEBUG_SMD_ENABLE_TESTP2,
+ ATTR_DEBUG_SMD_EXE_STATE,
+ ATTR_DEBUG_SMD_DELAY_CNTR,
+ ATTR_DEBUG_GYRO_COUNTER,
+ ATTR_DEBUG_ACCEL_COUNTER,
+ ATTR_DEBUG_COMPASS_COUNTER,
+ ATTR_DEBUG_PRESSURE_COUNTER,
+ ATTR_DEBUG_LPQ_COUNTER,
+ ATTR_DEBUG_SIXQ_COUNTER,
+ ATTR_DEBUG_PEDQ_COUNTER,
+#endif
+};
+
+enum inv_accel_fs_e {
+ INV_FS_02G = 0,
+ INV_FS_04G,
+ INV_FS_08G,
+ INV_FS_16G,
+ NUM_ACCEL_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);
+ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos, size_t size);
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+int inv_init_config_mpu3050(struct iio_dev *indio_dev);
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st);
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st);
+int set_3050_bypass(struct inv_mpu_state *st, bool enable);
+int inv_register_mpu3050_slave(struct inv_mpu_state *st);
+void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg);
+int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en);
+int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en);
+int set_power_mpu3050(struct inv_mpu_state *st, bool power_on);
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on);
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on);
+u16 inv_dmp_get_address(u16 key);
+int inv_q30_mult(int a, int b);
+int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold);
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data);
+int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps);
+int inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time);
+int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on);
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data);
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data);
+int inv_hw_self_test(struct inv_mpu_state *st);
+s64 get_time_ns(void);
+int write_be32_key_to_mem(struct inv_mpu_state *st,
+ u32 data, int key);
+int inv_set_accel_bias_dmp(struct inv_mpu_state *st);
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st);
+int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data);
+int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data);
+int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len,
+ u8 const *d);
+int inv_quaternion_on(struct inv_mpu_state *st,
+ struct iio_buffer *ring, bool en);
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en);
+int inv_read_pedometer_counter(struct inv_mpu_state *st);
+int inv_enable_pedometer(struct inv_mpu_state *st, bool en);
+int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps);
+int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time);
+int inv_reset_fifo(struct iio_dev *indio_dev);
+int inv_reset_offset_reg(struct inv_mpu_state *st, bool en);
+int inv_batchmode_setup(struct inv_mpu_state *st);
+void inv_init_sensor_struct(struct inv_mpu_state *st);
+int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume);
+int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data);
+int set_inv_enable(struct iio_dev *indio_dev, bool enable);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+#define mem_w(a, b, c) \
+ mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c)
+#define inv_i2c_read(st, reg, len, data) \
+ inv_i2c_read_base(st, st->i2c_addr, reg, len, data)
+#define inv_i2c_single_write(st, reg, data) \
+ inv_i2c_single_write_base(st, st->i2c_addr, reg, data)
+#define inv_secondary_read(reg, len, data) \
+ inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data)
+#define inv_secondary_write(reg, data) \
+ inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \
+ reg, data)
+#define inv_aux_read(reg, len, data) \
+ inv_i2c_read_base(st, st->plat_data.aux_i2c_addr, reg, len, data)
+#define inv_aux_write(reg, data) \
+ inv_i2c_single_write_base(st, st->plat_data.aux_i2c_addr, \
+ reg, data)
+
+#endif /* #ifndef _INV_MPU_IIO_H_ */
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c
new file mode 100644
index 00000000000..c327c75b1ab
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c
@@ -0,0 +1,2028 @@
+/*
+* 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 <linux/crc32.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_test/inv_counters.h"
+
+/* DMP defines */
+#define DMP_ORIENTATION_TIME 500
+#define DMP_ORIENTATION_ANGLE 60
+#define DMP_DEFAULT_FIFO_RATE 200
+#define DMP_TAP_SCALE (767603923 / 5)
+#define DMP_MULTI_SHIFT 30
+#define DMP_MULTI_TAP_TIME 500
+#define DMP_SHAKE_REJECT_THRESH 100
+#define DMP_SHAKE_REJECT_TIME 10
+#define DMP_SHAKE_REJECT_TIMEOUT 10
+#define DMP_ANGLE_SCALE 15
+#define DMP_PRECISION 1000
+#define DMP_MAX_DIVIDER 4
+#define DMP_MAX_MIN_TAPS 4
+#define DMP_IMAGE_CRC_VALUE 0x972aae92
+
+/*--- Test parameters defaults --- */
+#define DEF_OLDEST_SUPP_PROD_REV 8
+#define DEF_OLDEST_SUPP_SW_REV 2
+
+/* sample rate */
+#define DEF_SELFTEST_SAMPLE_RATE 0
+/* full scale setting dps */
+#define DEF_SELFTEST_GYRO_FS (0 << 3)
+#define DEF_SELFTEST_ACCEL_FS (2 << 3)
+#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
+/* wait time before collecting data */
+#define DEF_GYRO_WAIT_TIME 10
+#define DEF_ST_STABLE_TIME 20
+#define DEF_ST_6500_STABLE_TIME 20
+#define DEF_GYRO_SCALE 131
+#define DEF_ST_PRECISION 1000
+#define DEF_ST_ACCEL_FS_MG 8000UL
+#define DEF_ST_SCALE (1L << 15)
+#define DEF_ST_TRY_TIMES 2
+#define DEF_ST_COMPASS_RESULT_SHIFT 2
+#define DEF_ST_ACCEL_RESULT_SHIFT 1
+#define DEF_ST_OTP0_THRESH 60
+#define DEF_ST_ABS_THRESH 20
+#define DEF_ST_TOR 2
+
+#define X 0
+#define Y 1
+#define Z 2
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5 105
+#define MPU_PRODUCT_KEY_B2_F1 431
+/* accelerometer Hw self test min and max bias shift (mg) */
+#define DEF_ACCEL_ST_SHIFT_MIN 300
+#define DEF_ACCEL_ST_SHIFT_MAX 950
+
+#define DEF_ACCEL_ST_SHIFT_DELTA 500
+#define DEF_GYRO_CT_SHIFT_DELTA 500
+/* gyroscope Coriolis self test min and max bias shift (dps) */
+#define DEF_GYRO_CT_SHIFT_MIN 10
+#define DEF_GYRO_CT_SHIFT_MAX 105
+
+/*---- MPU6500 Self Test Pass/Fail Criteria ----*/
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX 20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL 60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN 225
+#define DEF_ACCEL_ST_AL_MAX 675
+#define DEF_6500_ACCEL_ST_SHIFT_DELTA 500
+#define DEF_6500_GYRO_CT_SHIFT_DELTA 500
+#define DEF_ST_MPU6500_ACCEL_LPF 2
+#define DEF_ST_6500_ACCEL_FS_MG 2000UL
+#define DEF_SELFTEST_6500_ACCEL_FS (0 << 3)
+
+/* Note: The ST_AL values are only used when ST_OTP = 0,
+ * i.e no factory self test values for reference
+ */
+
+/* NOTE: product entries are in chronological order */
+static const struct prod_rev_map_t prod_rev_map[] = {
+ /* prod_ver = 0 */
+ {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384},
+ {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384},
+ /* prod_ver = 1 */
+ {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 2 */
+ {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384},
+ {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 3 */
+ {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 4 */
+ {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192},
+ {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192},
+ /* prod_ver = 5 */
+ {MPL_PROD_KEY(5, 3), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 6 */
+ {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 7 */
+ {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 8 */
+ {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 9 */
+ {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384},
+ /* prod_ver = 10 */
+ {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384}
+};
+
+/*
+* List of product software revisions
+*
+* NOTE :
+* software revision 0 falls back to the old detection method
+* based off the product version and product revision per the
+* table above
+*/
+static const struct prod_rev_map_t sw_rev_map[] = {
+ {0, 0, 0, 0},
+ {1, MPU_SILICON_REV_B1, 131, 8192}, /* rev C */
+ {2, MPU_SILICON_REV_B1, 131, 16384} /* rev D */
+};
+
+static const u16 mpu_6500_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+static const int accel_st_tb[31] = {
+ 340, 351, 363, 375, 388, 401, 414, 428,
+ 443, 458, 473, 489, 506, 523, 541, 559,
+ 578, 597, 617, 638, 660, 682, 705, 729,
+ 753, 779, 805, 832, 860, 889, 919
+};
+
+static const int gyro_6050_st_tb[31] = {
+ 3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486,
+ 4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429,
+ 6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213,
+ 9637, 10080, 10544, 11029, 11537, 12067, 12622
+};
+
+static const int gyro_3500_st_tb[255] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string)
+{
+ int ii;
+ string = kmalloc(len * 2 + 1, GFP_KERNEL);
+ for (ii = 0; ii < len; ii++)
+ sprintf(&string[ii * 2], "%02X", data[ii]);
+ string[len * 2] = 0;
+ return string;
+}
+
+char *wr_pr_debug_end(char *string)
+{
+ kfree(string);
+ return "";
+}
+
+int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = REG_MEM_RW;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (u8 *)buf;
+ msgs[2].len = len + 1;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
+#ifdef CONFIG_DYNAMIC_DEBUG
+ {
+ char *write = 0;
+ pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+ mpu_addr, bank[1], addr[1],
+ wr_pr_debug_begin(data, len, write),
+ wr_pr_debug_end(write),
+ len);
+ }
+#endif
+
+ res = i2c_transfer(st->sl_handle, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ bank[0] = REG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = REG_MEM_RW;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(st->sl_handle, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + 3);
+ INV_I2C_INC_MPUREAD(len);
+#ifdef CONFIG_DYNAMIC_DEBUG
+ {
+ char *read = 0;
+ pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+ mpu_addr, bank[1], addr[1], len,
+ wr_pr_debug_begin(data, len, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+
+ return res;
+}
+
+int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len,
+ u8 const *d)
+{
+ u32 addr;
+ int start, end;
+ int len1, len2;
+ int result = 0;
+
+ if (len > MPU_MEM_BANK_SIZE)
+ return -EINVAL;
+ addr = inv_dmp_get_address(key);
+ if (addr > MPU6XXX_MAX_MPU_MEM)
+ return -EINVAL;
+
+ start = (addr >> 8);
+ end = ((addr + len - 1) >> 8);
+ if (start == end) {
+ result = mpu_memory_write(st, st->i2c_addr, addr, len, d);
+ } else {
+ end <<= 8;
+ len1 = end - addr;
+ len2 = len - len1;
+ result = mpu_memory_write(st, st->i2c_addr, addr, len1, d);
+ result |= mpu_memory_write(st, st->i2c_addr, end, len2,
+ d + len1);
+ }
+
+ return result;
+}
+
+/**
+ * index_of_key()- Inverse lookup of the index of an MPL product key .
+ * @key: the MPL product indentifier also referred to as 'key'.
+ */
+static short index_of_key(u16 key)
+{
+ int i;
+ for (i = 0; i < NUM_OF_PROD_REVS; i++)
+ if (prod_rev_map[i].mpl_product_key == key)
+ return (short)i;
+ return -EINVAL;
+}
+
+int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st)
+{
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ int result;
+ u8 whoami, sw_rev;
+
+ result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami);
+ if (result)
+ return result;
+ if (whoami != MPU6500_ID && whoami != MPU9250_ID &&
+ whoami != MPU9350_ID && whoami != MPU6515_ID)
+ return -EINVAL;
+
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr,
+ MPU6500_MEM_REV_ADDR, 1, &sw_rev);
+ sw_rev &= INV_MPU_REV_MASK;
+ if (result)
+ return result;
+ if (sw_rev != 0)
+ return -EINVAL;
+ /* these values are place holders and not real values */
+ chip_info->product_id = MPU6500_PRODUCT_REVISION;
+ chip_info->product_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->silicon_revision = MPU6500_PRODUCT_REVISION;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM;
+ chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM;
+ chip_info->multi = 1;
+
+ return 0;
+}
+
+int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+ u8 prod_ver = 0x00, prod_rev = 0x00;
+ struct prod_rev_map_t *p_rev;
+ u8 bank =
+ (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+ u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV);
+ u16 key;
+ u8 regs[5];
+ u16 sw_rev;
+ short index;
+ struct inv_chip_info_s *chip_info = &st->chip_info;
+ reg = &st->reg;
+
+ result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver);
+ if (result)
+ return result;
+ prod_ver &= 0xf;
+ /*memory read need more time after power up */
+ msleep(POWER_UP_TIME);
+ result = mpu_memory_read(st, st->i2c_addr, mem_addr, 1, &prod_rev);
+ if (result)
+ return result;
+ prod_rev >>= 2;
+ /* clean the prefetch and cfg user bank bits */
+ result = inv_i2c_single_write(st, reg->bank_sel, 0);
+ if (result)
+ return result;
+ /* get the software-product version, read from XA_OFFS_L */
+ result = inv_i2c_read(st, REG_XA_OFFS_L_TC,
+ SOFT_PROD_VER_BYTES, regs);
+ if (result)
+ return result;
+
+ sw_rev = (regs[4] & 0x01) << 2 | /* 0x0b, bit 0 */
+ (regs[2] & 0x01) << 1 | /* 0x09, bit 0 */
+ (regs[0] & 0x01); /* 0x07, bit 0 */
+ /* if 0, use the product key to determine the type of part */
+ if (sw_rev == 0) {
+ key = MPL_PROD_KEY(prod_ver, prod_rev);
+ if (key == 0)
+ return -EINVAL;
+ index = index_of_key(key);
+ if (index < 0 || index >= NUM_OF_PROD_REVS)
+ return -EINVAL;
+ /* check MPL is compiled for this device */
+ if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1)
+ return -EINVAL;
+ p_rev = (struct prod_rev_map_t *)&prod_rev_map[index];
+ /* if valid, use the software product key */
+ } else if (sw_rev < ARRAY_SIZE(sw_rev_map)) {
+ p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev];
+ } else {
+ return -EINVAL;
+ }
+ chip_info->product_id = prod_ver;
+ chip_info->product_revision = prod_rev;
+ chip_info->silicon_revision = p_rev->silicon_rev;
+ chip_info->software_revision = sw_rev;
+ chip_info->gyro_sens_trim = p_rev->gyro_trim;
+ chip_info->accel_sens_trim = p_rev->accel_trim;
+ if (chip_info->accel_sens_trim == 0)
+ chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM;
+ chip_info->multi = DEFAULT_ACCEL_TRIM / chip_info->accel_sens_trim;
+ if (chip_info->multi != 1)
+ pr_info("multi is %d\n", chip_info->multi);
+ return result;
+}
+
+/**
+ * read_accel_hw_self_test_prod_shift()- read the accelerometer hardware
+ * self-test bias shift calculated
+ * during final production test and
+ * stored in chip non-volatile memory.
+ * @st: main data structure.
+ * @st_prod: A pointer to an array of 3 elements to hold the values
+ * for production hardware self-test bias shifts returned to the
+ * user.
+ * @accel_sens: accel sensitivity.
+ */
+static int read_accel_hw_self_test_prod_shift(struct inv_mpu_state *st,
+ int *st_prod, int *accel_sens)
+{
+ u8 regs[4];
+ u8 shift_code[3];
+ int result, i;
+
+ for (i = 0; i < 3; i++)
+ st_prod[i] = 0;
+
+ result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+ if ((0 == regs[0]) && (0 == regs[1]) &&
+ (0 == regs[2]) && (0 == regs[3]))
+ return -EINVAL;
+ shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4);
+ shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2);
+ shift_code[Z] = ((regs[2] & 0xE0) >> 3) | (regs[3] & 0x03);
+ for (i = 0; i < 3; i++)
+ if (shift_code[i] != 0)
+ st_prod[i] = accel_sens[i] *
+ accel_st_tb[shift_code[i] - 1];
+
+ return 0;
+}
+
+/**
+* inv_check_accel_self_test()- check accel self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int gravity, j, ret_val;
+ int tmp;
+ int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS];
+ int st_shift_ratio[THREE_AXIS];
+ int accel_sens[THREE_AXIS];
+
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+ ret_val = 0;
+ tmp = DEF_ST_SCALE * DEF_ST_PRECISION / DEF_ST_ACCEL_FS_MG;
+ for (j = 0; j < 3; j++)
+ accel_sens[j] = tmp;
+
+ if (MPL_PROD_KEY(st->chip_info.product_id,
+ st->chip_info.product_revision) ==
+ MPU_PRODUCT_KEY_B1_E1_5) {
+ /* half sensitivity Z accelerometer parts */
+ accel_sens[Z] /= 2;
+ } else {
+ /* half sensitivity X, Y, Z accelerometer parts */
+ accel_sens[X] /= st->chip_info.multi;
+ accel_sens[Y] /= st->chip_info.multi;
+ accel_sens[Z] /= st->chip_info.multi;
+ }
+ gravity = accel_sens[Z];
+ ret_val = read_accel_hw_self_test_prod_shift(st, st_shift_prod,
+ accel_sens);
+ if (ret_val)
+ return ret_val;
+
+ for (j = 0; j < 3; j++) {
+ st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]);
+ if (st_shift_prod[j]) {
+ tmp = st_shift_prod[j] / DEF_ST_PRECISION;
+ st_shift_ratio[j] = abs(st_shift_cust[j] / tmp
+ - DEF_ST_PRECISION);
+ if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ } else {
+ if (st_shift_cust[j] <
+ DEF_ACCEL_ST_SHIFT_MIN * gravity)
+ ret_val = 1;
+ if (st_shift_cust[j] >
+ DEF_ACCEL_ST_SHIFT_MAX * gravity)
+ ret_val = 1;
+ }
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_3500_gyro_self_test() check gyro self test. this function returns
+* zero as success. A non-zero return value
+* indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+
+static int inv_check_3500_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int gst[3], ret_val;
+ int gst_otp[3], i;
+ u8 st_code[THREE_AXIS];
+ ret_val = 0;
+
+ for (i = 0; i < 3; i++)
+ gst[i] = st_avg[i] - reg_avg[i];
+ result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code);
+ if (result)
+ return result;
+ gst_otp[0] = 0;
+ gst_otp[1] = 0;
+ gst_otp[2] = 0;
+ for (i = 0; i < 3; i++) {
+ if (st_code[i] != 0)
+ gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1];
+ }
+ /* check self test value passing criterion. Using the DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (gst_otp[i] == 0) {
+ if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH *
+ DEF_ST_PRECISION *
+ DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ } else {
+ if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) >
+ DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val |= (1 << i);
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++) {
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val |= (1 << i);
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6050_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg){
+ int result;
+ int ret_val;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+
+ if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV &&
+ st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV)
+ return 0;
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs);
+ if (result)
+ return result;
+ regs[X] &= 0x1f;
+ regs[Y] &= 0x1f;
+ regs[Z] &= 0x1f;
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0)
+ st_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1];
+ else
+ st_shift_prod[i] = 0;
+ }
+ st_shift_prod[1] = -st_shift_prod[1];
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (st_shift_prod[i]) {
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA)
+ ret_val = 1;
+ } else {
+ if (st_shift_cust[i] < DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MIN * DEF_SELFTEST_GYRO_SENS)
+ ret_val = 1;
+ if (st_shift_cust[i] > DEF_ST_PRECISION *
+ DEF_GYRO_CT_SHIFT_MAX * DEF_SELFTEST_GYRO_SENS)
+ ret_val = 1;
+ }
+ }
+ /* check for absolute value passing criterion. Using DEF_ST_TOR
+ * for certain degree of tolerance */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH *
+ DEF_ST_PRECISION * DEF_GYRO_SCALE)
+ ret_val = 1;
+
+ return ret_val;
+}
+
+/**
+* inv_check_6500_gyro_self_test() - check 6500 gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6500_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ u8 regs[3];
+ int ret_val, result;
+ int otp_value_zero = 0;
+ int st_shift_prod[3], st_shift_cust[3], i;
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_6500_XG_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ if (st_shift_cust[i] < DEF_6500_GYRO_CT_SHIFT_DELTA
+ * st_shift_prod[i])
+ ret_val = 1;
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ if (st_shift_cust[i] < DEF_GYRO_ST_AL *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ if (ret_val == 0) {
+ /* Self Test Pass/Fail Criteria C */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_6500_accel_self_test() - check 6500 accel self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+static int inv_check_6500_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ int ret_val, result;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+ int otp_value_zero = 0;
+
+#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
+ / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
+ / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION)
+
+ ret_val = 0;
+ result = inv_i2c_read(st, REG_6500_XA_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_6500_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ }
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
+ if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
+ st_shift_cust[i] > ACCEL_ST_AL_MAX)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ return ret_val;
+}
+
+/*
+ * inv_do_test() - do the actual test of self testing
+ */
+static int inv_do_test(struct inv_mpu_state *st, int self_test_flag,
+ int *gyro_result, int *accel_result)
+{
+ struct inv_reg_map_s *reg;
+ int result, i, j, packet_size;
+ u8 data[BYTES_PER_SENSOR * 2], d;
+ bool has_accel;
+ int fifo_count, packet_count, ind, s;
+
+ reg = &st->reg;
+ has_accel = (st->chip_type != INV_ITG3500);
+ if (has_accel)
+ packet_size = BYTES_PER_SENSOR * 2;
+ else
+ packet_size = BYTES_PER_SENSOR;
+
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* setup parameters */
+ result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_98HZ);
+ if (result)
+ return result;
+
+ if (INV_MPU6500 == st->chip_type) {
+ /* config accel LPF register for MPU6500 */
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2,
+ DEF_ST_MPU6500_ACCEL_LPF |
+ BIT_FIFO_SIZE_1K);
+ if (result)
+ return result;
+ }
+
+ result = inv_i2c_single_write(st, reg->sample_rate_div,
+ DEF_SELFTEST_SAMPLE_RATE);
+ if (result)
+ return result;
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ result = inv_i2c_single_write(st, reg->gyro_config,
+ self_test_flag | DEF_SELFTEST_GYRO_FS);
+ if (result)
+ return result;
+ if (has_accel) {
+ if (INV_MPU6500 == st->chip_type)
+ d = DEF_SELFTEST_6500_ACCEL_FS;
+ else
+ d = DEF_SELFTEST_ACCEL_FS;
+ d |= self_test_flag;
+ result = inv_i2c_single_write(st, reg->accel_config, d);
+ if (result)
+ return result;
+ }
+ /* wait for the output to get stable */
+ if (self_test_flag) {
+ if (INV_MPU6500 == st->chip_type)
+ msleep(DEF_ST_6500_STABLE_TIME);
+ else
+ msleep(DEF_ST_STABLE_TIME);
+ }
+
+ /* enable FIFO reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO */
+ if (has_accel)
+ d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
+ else
+ d = BITS_GYRO_OUT;
+ for (i = 0; i < THREE_AXIS; i++) {
+ gyro_result[i] = 0;
+ accel_result[i] = 0;
+ }
+ s = 0;
+ while (s < st->self_test.samples) {
+ result = inv_i2c_single_write(st, reg->fifo_en, d);
+ if (result)
+ return result;
+ mdelay(DEF_GYRO_WAIT_TIME);
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ pr_debug("%s self_test fifo_count - %d\n",
+ st->hw->name, fifo_count);
+ packet_count = fifo_count / packet_size;
+ i = 0;
+ while ((i < packet_count) && (s < st->self_test.samples)) {
+ short vals[3];
+ result = inv_i2c_read(st, reg->fifo_r_w,
+ packet_size, data);
+ if (result)
+ return result;
+ ind = 0;
+ if (has_accel) {
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ accel_result[j] += vals[j];
+ }
+ ind += BYTES_PER_SENSOR;
+ pr_debug(
+ "%s self_test accel data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+ }
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ gyro_result[j] += vals[j];
+ }
+ pr_debug("%s self_test gyro data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ s++;
+ i++;
+ }
+ }
+
+ if (has_accel) {
+ for (j = 0; j < THREE_AXIS; j++) {
+ accel_result[j] = accel_result[j] / s;
+ accel_result[j] *= DEF_ST_PRECISION;
+ }
+ }
+ for (j = 0; j < THREE_AXIS; j++) {
+ gyro_result[j] = gyro_result[j] / s;
+ gyro_result[j] *= DEF_ST_PRECISION;
+ }
+
+ return 0;
+}
+
+/*
+ * inv_recover_setting() recover the old settings after everything is done
+ */
+static void inv_recover_setting(struct inv_mpu_state *st)
+{
+ struct inv_reg_map_s *reg;
+ int data;
+
+ reg = &st->reg;
+ inv_i2c_single_write(st, reg->gyro_config,
+ st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT);
+ inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf);
+ data = ONE_K_HZ/st->chip_config.fifo_rate - 1;
+ inv_i2c_single_write(st, reg->sample_rate_div, data);
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ if (INV_ITG3500 != st->chip_type) {
+ inv_i2c_single_write(st, reg->accel_config,
+ (st->chip_config.accel_fs <<
+ ACCEL_CONFIG_FSR_SHIFT));
+ }
+ inv_reset_offset_reg(st, false);
+ st->switch_gyro_engine(st, false);
+ st->switch_accel_engine(st, false);
+ st->set_power_state(st, false);
+}
+
+
+static int inv_power_up_self_test(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = st->set_power_state(st, true);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, true);
+ if (result)
+ return result;
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/*
+ * inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+ int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS];
+ int test_times, i;
+ char compass_result, accel_result, gyro_result;
+
+ result = inv_power_up_self_test(st);
+ if (result)
+ return result;
+ result = inv_reset_offset_reg(st, true);
+ if (result)
+ return result;
+ compass_result = 0;
+ accel_result = 0;
+ gyro_result = 0;
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular,
+ accel_bias_regular);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular[0],
+ accel_bias_regular[1], accel_bias_regular[2]);
+ pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1],
+ gyro_bias_regular[2]);
+
+ for (i = 0; i < 3; i++) {
+ st->gyro_bias[i] = gyro_bias_regular[i];
+ st->accel_bias[i] = accel_bias_regular[i];
+ }
+
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+ accel_bias_st);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ break;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_st - %+d %+d %+d\n",
+ st->hw->name, accel_bias_st[0], accel_bias_st[1],
+ accel_bias_st[2]);
+ pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_st[0], gyro_bias_st[1],
+ gyro_bias_st[2]);
+
+ if (st->chip_type == INV_ITG3500) {
+ gyro_result = !inv_check_3500_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ } else {
+ if (st->chip_config.has_compass)
+ compass_result = !st->slave_compass->self_test(st);
+
+ if (INV_MPU6050 == st->chip_type) {
+ accel_result = !inv_check_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_6050_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ } else if (INV_MPU6500 == st->chip_type) {
+ accel_result = !inv_check_6500_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_6500_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+ }
+ }
+
+test_fail:
+ inv_recover_setting(st);
+
+ return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) |
+ (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
+
+static int inv_load_firmware(struct inv_mpu_state *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+
+ /* first bank start at MPU_DMP_LOAD_START */
+ write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START;
+ memaddr = MPU_DMP_LOAD_START;
+ result = mem_w(memaddr, write_size, data);
+ if (result)
+ return result;
+ size -= write_size;
+ data += write_size;
+
+ /* Write and verify memory */
+ for (bank = 1; size > 0; bank++, size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+
+ result = mem_w(memaddr, write_size, data);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+static int inv_verify_firmware(struct inv_mpu_state *st,
+ u8 *data, int size)
+{
+ int bank, write_size;
+ int result;
+ u16 memaddr;
+ u8 firmware[MPU_MEM_BANK_SIZE];
+
+ /* Write and verify memory */
+ write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START;
+ size -= write_size;
+ data += write_size;
+ for (bank = 1; size > 0; bank++,
+ size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = ((bank << 8) | 0x00);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, firmware);
+ if (result)
+ return result;
+ if (0 != memcmp(firmware, data, write_size))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
+{
+ u8 reg[3];
+
+ if (en) {
+ reg[0] = 0xf4;
+ reg[1] = 0x44;
+ reg[2] = 0xf1;
+
+ } else {
+ reg[0] = 0xf1;
+ reg[1] = 0xf1;
+ reg[2] = 0xf1;
+ }
+
+ return mem_w_key(KEY_CFG_PED_INT, ARRAY_SIZE(reg), reg);
+}
+
+int inv_read_pedometer_counter(struct inv_mpu_state *st)
+{
+ int result;
+ u8 d[4];
+ u32 last_step_counter, curr_counter;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_STPDET_TIMESTAMP), 4, d);
+ if (result)
+ return result;
+ last_step_counter = (u32)be32_to_cpup((__be32 *)(d));
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, d);
+ if (result)
+ return result;
+ curr_counter = (u32)be32_to_cpup((__be32 *)(d));
+ if (0 != last_step_counter)
+ st->ped.last_step_time = get_time_ns() -
+ ((u64)(curr_counter - last_step_counter)) *
+ DMP_INTERVAL_INIT;
+
+ return 0;
+}
+
+int inv_enable_pedometer(struct inv_mpu_state *st, bool en)
+{
+ u8 d[1];
+
+ if (en)
+ d[0] = 0xf1;
+ else
+ d[0] = 0xff;
+
+ return mem_w_key(KEY_CFG_PED_ENABLE, ARRAY_SIZE(d), d);
+}
+
+int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps)
+{
+ u8 d[4];
+ int result;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d);
+ *steps = (u32)be32_to_cpup((__be32 *)(d));
+
+ return result;
+}
+
+int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time)
+{
+ u8 d[4];
+ int result;
+
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d);
+ *time = (u32)be32_to_cpup((__be32 *)(d));
+
+ return result;
+}
+
+int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on)
+{
+ int r;
+ u8 rn[] = {0xf4, 0x41};
+ u8 rf[] = {0xd8, 0xd8};
+
+ if (on)
+ r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rn), rn);
+ else
+ r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rf), rf);
+
+ return r;
+}
+
+static int inv_set_tap_interrupt_dmp(struct inv_mpu_state *st, u8 on)
+{
+ int result;
+ u16 d;
+
+ if (on)
+ d = 192;
+ else
+ d = 128;
+
+ result = inv_write_2bytes(st, KEY_DMP_TAP_GATE, d);
+
+ return result;
+}
+
+/*
+ * inv_set_tap_threshold_dmp():
+ * Sets the tap threshold in the dmp
+ * Simultaneously sets secondary tap threshold to help correct the tap
+ * direction for soft taps.
+ */
+int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold)
+{
+ int result;
+ int sampleDivider;
+ int scaledThreshold;
+ u32 dmpThreshold;
+ u8 sample_div;
+ const u32 accel_sens = (0x20000000 / 0x00010000);
+
+ if (threshold > (1 << 15))
+ return -EINVAL;
+ sample_div = st->sample_divider;
+
+ sampleDivider = (1 + sample_div);
+ /* Scale factor corresponds linearly using
+ * 0 : 0
+ * 25 : 0.0250 g/ms
+ * 50 : 0.0500 g/ms
+ * 100: 1.0000 g/ms
+ * 200: 2.0000 g/ms
+ * 400: 4.0000 g/ms
+ * 800: 8.0000 g/ms
+ */
+ /*multiply by 1000 to avoid floating point 1000/1000*/
+ scaledThreshold = threshold;
+ /* Convert to per sample */
+ scaledThreshold *= sampleDivider;
+
+ /* Scale to DMP 16 bit value */
+ if (accel_sens != 0)
+ dmpThreshold = (u32)(scaledThreshold * accel_sens);
+ else
+ return -EINVAL;
+ dmpThreshold = dmpThreshold / DMP_PRECISION;
+ result = inv_write_2bytes(st, KEY_DMP_TAP_THR_Z, dmpThreshold);
+ if (result)
+ return result;
+ result = inv_write_2bytes(st, KEY_DMP_TAP_PREV_JERK_Z,
+ dmpThreshold * 3 / 4);
+
+ return result;
+}
+
+
+/*
+ * inv_set_min_taps_dmp():
+ * Indicates the minimum number of consecutive taps required
+ * before the DMP will generate an interrupt.
+ */
+int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps)
+{
+ u8 result;
+
+ /* check if any spurious bit other the ones expected are set */
+ if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1))
+ return -EINVAL;
+
+ /* DMP tap count is zero-based. So single-tap is 0.
+ Furthermore, DMP code checks for tap_count > min_taps.
+ So we have to do minus 2 here.
+ For example, if the user expects any single tap will generate an
+ interrupt, (s)he will call inv_set_min_taps_dmp(1).
+ When DMP gets a single tap, tap_count = 0. To get
+ tap_count > min_taps, we have to decrement min_taps by 2 to -1. */
+ result = inv_write_2bytes(st, KEY_DMP_TAP_MIN_TAPS, (u16)(min_taps-2));
+
+ return result;
+}
+
+/*
+ * inv_set_tap_time_dmp():
+ * Determines how long after a tap the DMP requires before
+ * another tap can be registered.
+ */
+int inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAPW_MIN, dmpTime);
+
+ return result;
+}
+
+/*
+ * inv_set_multiple_tap_time_dmp():
+ * Determines how close together consecutive taps must occur
+ * to be considered double/triple taps.
+ */
+static int inv_set_multiple_tap_time_dmp(struct inv_mpu_state *st, u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_NEXT_TAP_THRES, dmpTime);
+
+ return result;
+}
+
+int inv_q30_mult(int a, int b)
+{
+ u64 temp;
+ int result;
+
+ temp = (u64)a * b;
+ result = (int)(temp >> DMP_MULTI_SHIFT);
+
+ return result;
+}
+
+static u16 inv_row_2_scale(const s8 *row)
+{
+ u16 b;
+
+ if (row[0] > 0)
+ b = 0;
+ else if (row[0] < 0)
+ b = 4;
+ else if (row[1] > 0)
+ b = 1;
+ else if (row[1] < 0)
+ b = 5;
+ else if (row[2] > 0)
+ b = 2;
+ else if (row[2] < 0)
+ b = 6;
+ else
+ b = 7;
+
+ return b;
+}
+
+/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar
+* representation.
+* @param[in] mtx Orientation matrix to convert to a scalar.
+* @return Description of orientation matrix. The lowest 2 bits (0 and 1)
+* represent the column the one is on for the
+* first row, with the bit number 2 being the sign. The next 2 bits
+* (3 and 4) represent
+* the column the one is on for the second row with bit number 5 being
+* the sign.
+* The next 2 bits (6 and 7) represent the column the one is on for the
+* third row with
+* bit number 8 being the sign. In binary the identity matrix would therefor
+* be: 010_001_000 or 0x88 in hex.
+*/
+static u16 inv_orientation_matrix_to_scaler(const signed char *mtx)
+{
+
+ u16 scalar;
+ scalar = inv_row_2_scale(mtx);
+ scalar |= inv_row_2_scale(mtx + 3) << 3;
+ scalar |= inv_row_2_scale(mtx + 6) << 6;
+
+ return scalar;
+}
+
+static int inv_gyro_dmp_cal(struct inv_mpu_state *st)
+{
+ int inv_gyro_orient;
+ u8 regs[3];
+ int result;
+
+ u8 tmpD = DINA4C;
+ u8 tmpE = DINACD;
+ u8 tmpF = DINA6C;
+
+ inv_gyro_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ if ((inv_gyro_orient & 3) == 0)
+ regs[0] = tmpD;
+ else if ((inv_gyro_orient & 3) == 1)
+ regs[0] = tmpE;
+ else if ((inv_gyro_orient & 3) == 2)
+ regs[0] = tmpF;
+ if ((inv_gyro_orient & 0x18) == 0)
+ regs[1] = tmpD;
+ else if ((inv_gyro_orient & 0x18) == 0x8)
+ regs[1] = tmpE;
+ else if ((inv_gyro_orient & 0x18) == 0x10)
+ regs[1] = tmpF;
+ if ((inv_gyro_orient & 0xc0) == 0)
+ regs[2] = tmpD;
+ else if ((inv_gyro_orient & 0xc0) == 0x40)
+ regs[2] = tmpE;
+ else if ((inv_gyro_orient & 0xc0) == 0x80)
+ regs[2] = tmpF;
+
+ result = mem_w_key(KEY_FCFG_1, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ if (inv_gyro_orient & 4)
+ regs[0] = DINA36 | 1;
+ else
+ regs[0] = DINA36;
+ if (inv_gyro_orient & 0x20)
+ regs[1] = DINA56 | 1;
+ else
+ regs[1] = DINA56;
+ if (inv_gyro_orient & 0x100)
+ regs[2] = DINA76 | 1;
+ else
+ regs[2] = DINA76;
+ result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+static int inv_accel_dmp_cal(struct inv_mpu_state *st)
+{
+ int inv_accel_orient;
+ int result;
+ u8 regs[3];
+ const u8 tmp[3] = { DINA0C, DINAC9, DINA2C };
+ inv_accel_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ regs[0] = tmp[inv_accel_orient & 3];
+ regs[1] = tmp[(inv_accel_orient >> 3) & 3];
+ regs[2] = tmp[(inv_accel_orient >> 6) & 3];
+ result = mem_w_key(KEY_FCFG_2, ARRAY_SIZE(regs), regs);
+ if (result)
+ return result;
+
+ regs[0] = DINA26;
+ regs[1] = DINA46;
+ regs[2] = DINA66;
+ if (inv_accel_orient & 4)
+ regs[0] |= 1;
+ if (inv_accel_orient & 0x20)
+ regs[1] |= 1;
+ if (inv_accel_orient & 0x100)
+ regs[2] |= 1;
+ result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs);
+
+ return result;
+}
+
+int inv_set_accel_bias_dmp(struct inv_mpu_state *st)
+{
+ int inv_accel_orient, result, i, accel_bias_body[3], out[3];
+ int tmp[] = {1, 1, 1};
+ int mask[] = {4, 0x20, 0x100};
+ int accel_sf = 0x20000000;/* 536870912 */
+ u8 *regs;
+
+ inv_accel_orient =
+ inv_orientation_matrix_to_scaler(st->plat_data.orientation);
+
+ for (i = 0; i < 3; i++)
+ if (inv_accel_orient & mask[i])
+ tmp[i] = -1;
+
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] =
+ st->input_accel_dmp_bias[(inv_accel_orient >>
+ (i * 3)) & 3] * tmp[i];
+ for (i = 0; i < 3; i++)
+ accel_bias_body[i] = inv_q30_mult(accel_sf,
+ accel_bias_body[i]);
+ for (i = 0; i < 3; i++)
+ out[i] = cpu_to_be32p(&accel_bias_body[i]);
+ regs = (u8 *)out;
+ result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs);
+
+ return result;
+}
+
+/*
+ * inv_set_gyro_sf_dmp():
+ * The gyro threshold, in dps, above which taps will be rejected.
+ */
+static int inv_set_gyro_sf_dmp(struct inv_mpu_state *st)
+{
+ int result;
+ u8 sampleDivider;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens,
+ (int)(DMP_TAP_SCALE * (sampleDivider + 1)));
+ result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_thresh_dmp():
+ * The gyro threshold, in dps, above which taps will be rejected.
+ */
+static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_state *st,
+ int thresh)
+{
+ int result;
+ u8 sampleDivider;
+ int thresh_scaled;
+ u32 gyro_sf;
+ const u32 gyro_sens = 0x03e80000;
+
+ sampleDivider = st->sample_divider;
+ gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE *
+ (sampleDivider + 1)));
+ /* We're in units of DPS, convert it back to chip units*/
+ /*split the operation to aviod overflow of integer*/
+ thresh_scaled = gyro_sens / (1L << 16);
+ thresh_scaled = thresh_scaled / thresh;
+ thresh_scaled = gyro_sf / thresh_scaled;
+ result = write_be32_key_to_mem(st, thresh_scaled,
+ KEY_DMP_TAP_SHAKE_REJECT);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_time_dmp():
+ * How long a gyro axis must remain above its threshold
+ * before taps are rejected.
+ */
+static int inv_set_shake_reject_time_dmp(struct inv_mpu_state *st,
+ u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_COUNT_MAX, dmpTime);
+
+ return result;
+}
+
+/*
+ * inv_set_shake_reject_timeout_dmp():
+ * How long the gyros must remain below their threshold,
+ * after taps have been rejected, before taps can be detected again.
+ */
+static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_state *st,
+ u32 time)
+{
+ int result;
+ u16 dmpTime;
+ u8 sampleDivider;
+
+ sampleDivider = st->sample_divider;
+ sampleDivider++;
+
+ /* 60 ms minimum time added */
+ dmpTime = ((time) / sampleDivider);
+ result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_TIMEOUT_MAX, dmpTime);
+
+ return result;
+}
+
+int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on)
+{
+ u8 r;
+ const u8 rn[] = {0xA3};
+ const u8 rf[] = {0xFE};
+
+ if (on)
+ r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rn), rn);
+ else
+ r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rf), rf);
+
+ return r;
+}
+
+/*
+ * inv_enable_tap_dmp() - calling this function will enable/disable tap
+ * function.
+ */
+int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on)
+{
+ int result;
+
+ result = inv_set_tap_interrupt_dmp(st, on);
+ if (result)
+ return result;
+ result = inv_set_tap_threshold_dmp(st, st->tap.thresh);
+ if (result)
+ return result;
+
+ result = inv_set_min_taps_dmp(st, st->tap.min_count);
+ if (result)
+ return result;
+
+ result = inv_set_tap_time_dmp(st, st->tap.time);
+ if (result)
+ return result;
+
+ result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_gyro_sf_dmp(st);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME);
+ if (result)
+ return result;
+
+ result = inv_set_shake_reject_timeout_dmp(st,
+ DMP_SHAKE_REJECT_TIMEOUT);
+ return result;
+}
+
+static int inv_dry_run_dmp(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_DMP_EN);
+ if (result)
+ return result;
+ msleep(400);
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ return result;
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+static void inv_test_reset(struct inv_mpu_state *st)
+{
+ int result, ii;
+ u8 d[0x80];
+
+ if (INV_MPU6500 != st->chip_type)
+ return;
+
+ for (ii = 3; ii < 0x80; ii++) {
+ /* don't read fifo r/w register */
+ if (ii != st->reg.fifo_r_w)
+ inv_i2c_read(st, ii, 1, &d[ii]);
+ }
+ result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, BIT_H_RESET);
+ if (result)
+ return;
+ msleep(POWER_UP_TIME);
+
+ for (ii = 3; ii < 0x80; ii++) {
+ /* don't write certain registers */
+ if ((ii != st->reg.fifo_r_w) &&
+ (ii != st->reg.mem_r_w) &&
+ (ii != st->reg.mem_start_addr) &&
+ (ii != st->reg.fifo_count_h) &&
+ ii != (st->reg.fifo_count_h + 1))
+ result = inv_i2c_single_write(st, ii, d[ii]);
+ }
+}
+
+/*
+ * inv_dmp_firmware_write() - calling this function will load the firmware.
+ * This is the write function of file "dmp_firmware".
+ */
+ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t pos, size_t size)
+{
+ u8 *firmware;
+ int result;
+ struct inv_reg_map_s *reg;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ if (st->chip_config.firmware_loaded)
+ return -EINVAL;
+ if (st->chip_config.enable)
+ return -EBUSY;
+
+ reg = &st->reg;
+ if (DMP_IMAGE_SIZE != size) {
+ pr_err("wrong DMP image size - expected %d, actual %d\n",
+ DMP_IMAGE_SIZE, size);
+ return -EINVAL;
+ }
+
+ firmware = kmalloc(size, GFP_KERNEL);
+ if (!firmware)
+ return -ENOMEM;
+
+ mutex_lock(&indio_dev->mlock);
+
+ memcpy(firmware, buf, size);
+ result = crc32(CRC_FIRMWARE_SEED, firmware, size);
+ if (DMP_IMAGE_CRC_VALUE != result) {
+ pr_err("firmware CRC error - 0x%08x vs 0x%08x\n",
+ result, DMP_IMAGE_CRC_VALUE);
+ result = -EINVAL;
+ goto firmware_write_fail;
+ }
+
+ result = st->set_power_state(st, true);
+ if (result)
+ goto firmware_write_fail;
+ inv_test_reset(st);
+
+ result = inv_load_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_verify_firmware(st, firmware, size);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh,
+ st->chip_config.prog_start_addr >> 8);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1,
+ st->chip_config.prog_start_addr & 0xff);
+ if (result)
+ goto firmware_write_fail;
+
+ result = inv_gyro_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_accel_dmp_cal(st);
+ if (result)
+ goto firmware_write_fail;
+ result = inv_dry_run_dmp(st);
+ if (result)
+ goto firmware_write_fail;
+
+ st->chip_config.firmware_loaded = 1;
+
+firmware_write_fail:
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ kfree(firmware);
+ if (result)
+ return result;
+
+ return size;
+}
+
+ssize_t inv_dmp_firmware_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int bank, write_size, size, data, result;
+ u16 memaddr;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+
+ size = count;
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ data = 0;
+ mutex_lock(&indio_dev->mlock);
+ if (!st->chip_config.enable) {
+ result = st->set_power_state(st, true);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ }
+ for (bank = 0; size > 0; bank++, size -= write_size,
+ data += write_size) {
+ if (size > MPU_MEM_BANK_SIZE)
+ write_size = MPU_MEM_BANK_SIZE;
+ else
+ write_size = size;
+
+ memaddr = (bank << 8);
+ result = mpu_memory_read(st,
+ st->i2c_addr, memaddr, write_size, &buf[data]);
+ if (result) {
+ mutex_unlock(&indio_dev->mlock);
+ return result;
+ }
+ }
+ if (!st->chip_config.enable)
+ result = st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return count;
+}
+
+ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t pos, size_t size)
+{
+ u8 q[QUATERNION_BYTES];
+ struct inv_reg_map_s *reg;
+ struct iio_dev *indio_dev;
+ struct inv_mpu_state *st;
+ int result;
+
+ indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (!st->chip_config.firmware_loaded) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+ if (st->chip_config.enable) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ reg = &st->reg;
+ if (QUATERNION_BYTES != size) {
+ pr_err("wrong quaternion size=%d, should=%d\n", size,
+ QUATERNION_BYTES);
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+
+ memcpy(q, buf, size);
+ result = st->set_power_state(st, true);
+ if (result)
+ goto firmware_write_fail;
+ result = mem_w_key(KEY_DMP_Q0, QUATERNION_BYTES, q);
+
+firmware_write_fail:
+ result |= st->set_power_state(st, false);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return size;
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c
new file mode 100644
index 00000000000..8b0f27cd1a3
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c
@@ -0,0 +1,1868 @@
+/*
+* 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 <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/sysfs.h>
+
+#include "inv_mpu_iio.h"
+
+static u8 fifo_data[HARDWARE_FIFO_SIZE + HEADERED_Q_BYTES];
+static int inv_process_batchmode(struct inv_mpu_state *st);
+
+static int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ iio_push_to_buffers(indio_dev, buf);
+
+ return 0;
+}
+
+static int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr,
+ u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ for (i = 0; i < 3; i++)
+ memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i]));
+ iio_push_to_buffers(indio_dev, buf);
+ memcpy(buf, &t, sizeof(t));
+ iio_push_to_buffers(indio_dev, buf);
+
+ return 0;
+}
+
+static int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t,
+ int *q)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+ iio_push_to_buffers(indio_dev, buf);
+ for (i = 0; i < 2; i++)
+ memcpy(buf + 4 * i, &q[i + 1], sizeof(q[i]));
+ iio_push_to_buffers(indio_dev, buf);
+ memcpy(buf, &t, sizeof(t));
+ iio_push_to_buffers(indio_dev, buf);
+
+ return 0;
+}
+
+static int inv_send_pressure_data(struct inv_mpu_state *st)
+{
+ short sen[3];
+ struct inv_chip_config_s *conf;
+ struct inv_mpu_slave *slave;
+ u64 curr_ts;
+ int result;
+
+ conf = &st->chip_config;
+ slave = st->slave_pressure;
+ if (!st->sensor[SENSOR_PRESSURE].on)
+ return 0;
+ if (conf->dmp_on && conf->dmp_event_int_on)
+ return 0;
+ if (!conf->normal_pressure_measure) {
+ conf->normal_pressure_measure = 1;
+ return 0;
+ }
+ curr_ts = get_time_ns();
+ if (curr_ts - slave->prev_ts > slave->min_read_time) {
+ result = slave->read_data(st, sen);
+ if (!result)
+ inv_push_8bytes_buffer(st, PRESSURE_HDR,
+ st->last_ts, sen);
+ slave->prev_ts = curr_ts;
+ }
+
+ return 0;
+}
+
+static int inv_send_compass_data(struct inv_mpu_state *st)
+{
+ short sen[3];
+ struct inv_chip_config_s *conf;
+ struct inv_mpu_slave *slave;
+ u64 curr_ts;
+ int result;
+
+ conf = &st->chip_config;
+ slave = st->slave_compass;
+ if (!st->sensor[SENSOR_COMPASS].on)
+ return 0;
+ if (conf->dmp_on && conf->dmp_event_int_on)
+ return 0;
+ if (!conf->normal_compass_measure) {
+ conf->normal_compass_measure = 1;
+ return 0;
+ }
+ curr_ts = get_time_ns();
+ if (curr_ts - slave->prev_ts > slave->min_read_time) {
+ result = slave->read_data(st, sen);
+ if (!result)
+ inv_push_8bytes_buffer(st, COMPASS_HDR,
+ st->last_ts, sen);
+ slave->prev_ts = curr_ts;
+ }
+
+ return 0;
+}
+
+static int inv_batchmode_calc(struct inv_mpu_state *st)
+{
+ int b, timeout;
+ int rate_dur;
+
+ rate_dur = MSEC_PER_SEC / st->batch.min_rate;
+ if (st->batch.timeout < rate_dur)
+ st->batch.timeout = rate_dur;
+ b = st->batch.timeout * st->bytes_per_sec;
+ if ((b > (FIFO_SIZE * ONE_K_HZ)) && (!st->batch.overflow_on))
+ timeout = FIFO_SIZE * ONE_K_HZ / st->bytes_per_sec;
+ else
+ timeout = st->batch.timeout;
+
+ st->batch.counter = timeout / 5;
+ if (timeout)
+ st->batch.on = true;
+
+ return 0;
+}
+
+int inv_batchmode_setup(struct inv_mpu_state *st)
+{
+ int r;
+
+ r = inv_write_2bytes(st, KEY_BM_NUMWORD_TOFILL, 0);
+ if (r)
+ return r;
+ r = write_be32_key_to_mem(st, 0, KEY_BM_BATCH_CNTR);
+ if (r)
+ return r;
+
+ if (st->chip_config.dmp_on && (st->batch.timeout > 0) &&
+ (st->chip_config.dmp_event_int_on == 0)) {
+ r = inv_batchmode_calc(st);
+ if (r)
+ return r;
+ }
+
+ if (st->batch.on) {
+ r = write_be32_key_to_mem(st, st->batch.counter,
+ KEY_BM_BATCH_THLD);
+ if (r)
+ return r;
+ }
+ r = inv_write_2bytes(st, KEY_BM_ENABLE, st->batch.on);
+
+ return r;
+}
+
+/**
+ * reset_fifo_mpu3050() - Reset FIFO related registers
+ * @indio_dev: Device driver instance.
+ */
+static int reset_fifo_mpu3050(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result;
+ u8 val, user_ctrl;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ reg = &st->reg;
+
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ user_ctrl &= ~BIT_FIFO_EN;
+ st->chip_config.has_footer = 0;
+ /* reset fifo */
+ val = (BIT_3050_FIFO_RST | user_ctrl);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ if (st->chip_config.dmp_on) {
+ /* enable interrupt when DMP is done */
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DMP_INT_EN);
+ if (result)
+ return result;
+
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN|user_ctrl);
+ if (result)
+ return result;
+ } else {
+ /* enable interrupt */
+ if (st->sensor[SENSOR_ACCEL].on ||
+ st->sensor[SENSOR_GYRO].on) {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | BIT_DATA_RDY_EN);
+ if (result)
+ return result;
+ }
+ /* enable FIFO reading and I2C master interface*/
+ result = inv_i2c_single_write(st, reg->user_ctrl,
+ BIT_FIFO_EN | user_ctrl);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO and FIFO footer*/
+ val = 1;
+ if (st->sensor[SENSOR_ACCEL].on)
+ val |= BITS_3050_ACCEL_OUT;
+ if (st->sensor[SENSOR_GYRO].on)
+ val |= BITS_GYRO_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ return result;
+ }
+
+ return 0;
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config | val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/*
+ * inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+ INV_FILTER_42HZ, INV_FILTER_20HZ,
+ INV_FILTER_10HZ, INV_FILTER_5HZ};
+ int i, h, data, result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ if (INV_MPU3050 == st->chip_type) {
+ if (st->slave_accel != NULL) {
+ result = st->slave_accel->set_lpf(st, rate);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->lpf, data |
+ (st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT));
+ } else {
+ result = inv_i2c_single_write(st, reg->lpf, data);
+ }
+ if (result)
+ return result;
+ st->chip_config.lpf = data;
+
+ return 0;
+}
+
+/*
+ * set_fifo_rate_reg() - Set fifo rate in hardware register
+ */
+static int set_fifo_rate_reg(struct inv_mpu_state *st)
+{
+ u8 data;
+ u16 fifo_rate;
+ int result;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ fifo_rate = st->chip_config.fifo_rate;
+ data = ONE_K_HZ / fifo_rate - 1;
+ result = inv_i2c_single_write(st, reg->sample_rate_div, data);
+ if (result)
+ return result;
+ result = inv_set_lpf(st, fifo_rate);
+ if (result)
+ return result;
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+
+ return 0;
+}
+
+/*
+ * inv_lpa_mode() - store current low power mode settings
+ */
+static int inv_lpa_mode(struct inv_mpu_state *st, int lpa_mode)
+{
+ unsigned long result;
+ u8 d;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d);
+ if (result)
+ return result;
+ if (lpa_mode)
+ d |= BIT_CYCLE;
+ else
+ d &= ~BIT_CYCLE;
+
+ result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d);
+ if (result)
+ return result;
+ if (INV_MPU6500 == st->chip_type) {
+ d = BIT_FIFO_SIZE_1K;
+ if (lpa_mode)
+ d |= BIT_ACCEL_FCHOCIE_B;
+ result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, d);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+static int inv_saturate_secondary_counter(struct inv_mpu_state *st)
+{
+ int result;
+ struct inv_reg_map_s *reg;
+
+#define COUNT_SATURATE_TIME_MS 32
+ reg = &st->reg;
+ /* set sampling to 1KHz */
+ result = inv_i2c_single_write(st, reg->sample_rate_div, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL,
+ BIT_SLV0_DLY_EN);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, BIT_I2C_MST_EN);
+ if (result)
+ return result;
+ msleep(COUNT_SATURATE_TIME_MS);
+
+ return 0;
+}
+static int inv_set_master_delay(struct inv_mpu_state *st)
+{
+ int d, result, rate;
+ u8 delay;
+
+ if ((!st->sensor[SENSOR_COMPASS].on) &&
+ (!st->sensor[SENSOR_PRESSURE].on))
+ return 0;
+
+ delay = 0;
+ d = 0;
+ if (st->sensor[SENSOR_COMPASS].on) {
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ case COMPASS_ID_AK8972:
+ case COMPASS_ID_AK8963:
+ case COMPASS_ID_AK09911:
+ delay = (BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN);
+ break;
+ case COMPASS_ID_MLX90399:
+ delay = (BIT_SLV0_DLY_EN |
+ BIT_SLV1_DLY_EN |
+ BIT_SLV2_DLY_EN |
+ BIT_SLV3_DLY_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ d = max(d, st->slave_compass->rate_scale);
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ /* read fake data when compass is disabled for DMP read */
+ if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on)
+ delay |= BIT_SLV0_DLY_EN;
+ switch (st->plat_data.aux_slave_id) {
+ case PRESSURE_ID_BMP280:
+ delay |= (BIT_SLV2_DLY_EN | BIT_SLV3_DLY_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ d = max(d, st->slave_pressure->rate_scale);
+ }
+
+ d = d * st->chip_config.fifo_rate / ONE_K_HZ;
+ if (st->chip_config.dmp_on) {
+ rate = 0;
+ if (st->sensor[SENSOR_PRESSURE].on)
+ rate = max(rate, st->sensor[SENSOR_PRESSURE].rate);
+ if (st->sensor[SENSOR_COMPASS].on)
+ rate = max(rate, st->sensor[SENSOR_COMPASS].rate);
+ if (rate == 0)
+ return -EINVAL;
+ d = max(d, st->chip_config.fifo_rate / rate);
+ }
+
+ if (d > 0)
+ d -= 1;
+ if (d > 0x1F)
+ d = 0x1F;
+
+ /* I2C_MST_DLY is set to slow down secondary I2C */
+ if (0 == d)
+ delay = 0;
+ if (delay) {
+ result = inv_saturate_secondary_counter(st);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, delay);
+ if (result)
+ return result;
+
+ return inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, d);
+}
+
+/*
+ * reset_fifo_itg() - Reset FIFO related registers.
+ */
+static int reset_fifo_itg(struct iio_dev *indio_dev)
+{
+ struct inv_reg_map_s *reg;
+ int result, i;
+ u8 val, int_word;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ reg = &st->reg;
+ if (st->chip_config.lpa_mode) {
+ result = inv_lpa_mode(st, 0);
+ if (result) {
+ pr_err("reset lpa mode failed\n");
+ return result;
+ }
+ }
+ /* disable interrupt */
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result) {
+ pr_err("int_enable write failed\n");
+ return result;
+ }
+ /* disable the sensor output to FIFO */
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ goto reset_fifo_fail;
+ /* disable fifo reading */
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ if (result)
+ goto reset_fifo_fail;
+ int_word = 0;
+
+ /* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */
+ if (st->mot_int.mot_on)
+ int_word |= BIT_MOT_EN;
+
+ if (st->chip_config.dmp_on) {
+ val = (BIT_FIFO_RST | BIT_DMP_RST);
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ if (st->chip_config.dmp_int_on) {
+ int_word |= BIT_DMP_INT_EN;
+ result = inv_i2c_single_write(st, reg->int_enable,
+ int_word);
+ if (result)
+ return result;
+ }
+ val = (BIT_DMP_EN | BIT_FIFO_EN);
+ if ((st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on) &&
+ (!st->chip_config.dmp_event_int_on))
+ val |= BIT_I2C_MST_EN;
+ result = inv_i2c_single_write(st, reg->user_ctrl, val);
+ if (result)
+ goto reset_fifo_fail;
+ } 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;
+ /* enable interrupt */
+ if (st->sensor[SENSOR_ACCEL].on ||
+ st->sensor[SENSOR_GYRO].on ||
+ st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on)
+ int_word |= BIT_DATA_RDY_EN;
+
+ result = inv_i2c_single_write(st, reg->int_enable, int_word);
+ if (result)
+ return result;
+ /* enable FIFO reading and I2C master interface*/
+ val = BIT_FIFO_EN;
+ if (st->sensor[SENSOR_COMPASS].on ||
+ st->sensor[SENSOR_PRESSURE].on)
+ 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->sensor[SENSOR_GYRO].on)
+ val |= BITS_GYRO_OUT;
+ if (st->sensor[SENSOR_ACCEL].on)
+ val |= BIT_ACCEL_OUT;
+ result = inv_i2c_single_write(st, reg->fifo_en, val);
+ if (result)
+ goto reset_fifo_fail;
+ }
+ st->last_ts = get_time_ns();
+ st->prev_ts = st->last_ts;
+ st->last_run_time = st->last_ts;
+ if (st->sensor[SENSOR_COMPASS].on)
+ st->slave_compass->prev_ts = st->last_ts;
+ if (st->sensor[SENSOR_PRESSURE].on)
+ st->slave_pressure->prev_ts = st->last_ts;
+
+ st->dmp_interval = DMP_INTERVAL_INIT;
+ st->ts_counter = 0;
+ st->diff_accumulater = 0;
+ st->dmp_interval_accum = 0;
+ st->step_detector_base_ts = st->last_ts;
+ st->chip_config.normal_compass_measure = 0;
+ st->chip_config.normal_pressure_measure = 0;
+ st->left_over_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = st->last_ts;
+
+ result = inv_lpa_mode(st, st->chip_config.lpa_mode);
+ if (result)
+ goto reset_fifo_fail;
+
+ return 0;
+
+reset_fifo_fail:
+ if (st->chip_config.dmp_on)
+ val = BIT_DMP_INT_EN;
+ else
+ val = BIT_DATA_RDY_EN;
+ inv_i2c_single_write(st, reg->int_enable, val);
+ pr_err("reset fifo failed\n");
+
+ return result;
+}
+
+/**
+ * inv_clear_kfifo() - clear time stamp fifo
+ * @st: Device driver instance.
+ */
+static void inv_clear_kfifo(struct inv_mpu_state *st)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/*
+ * inv_reset_fifo() - Reset FIFO related registers.
+ */
+int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ inv_clear_kfifo(st);
+ if (INV_MPU3050 == st->chip_type)
+ return reset_fifo_mpu3050(indio_dev);
+ else
+ return reset_fifo_itg(indio_dev);
+}
+
+static int inv_send_gyro_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_GYRO, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_accel_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_ACCL, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_three_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x13};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_3QUAT, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_six_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x13};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_6QUAT, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_ped_q_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_PQUAT, ARRAY_SIZE(rn), r);
+
+ return result;
+}
+
+static int inv_add_step_indicator(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xf3, 0xf3};
+ u8 rf[] = {0xf4, 0x03};
+ int result;
+ u8 *r;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_PEDSTEP_DET, ARRAY_SIZE(rn), r);
+
+ return result;
+}
+
+static int inv_send_compass_dmp_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_CPASS, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_pressure_dmp_data(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x12};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_PRESS, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_send_step_detector(struct inv_mpu_state *st, bool on)
+{
+ u8 rn[] = {0xa3, 0xa3};
+ u8 rf[] = {0xf4, 0x0e};
+ u8 *r;
+ int result;
+
+ if (on)
+ r = rn;
+ else
+ r = rf;
+ result = mem_w_key(KEY_CFG_OUT_STEPDET, ARRAY_SIZE(rf), r);
+
+ return result;
+}
+
+static int inv_set_rate(struct inv_mpu_state *st, int k, int k_ct, int rate)
+{
+ int v, result;
+
+ v = MPU_DEFAULT_DMP_FREQ / rate - 1;
+ result = inv_write_2bytes(st, k, v);
+ if (result)
+ return result;
+ result = inv_write_2bytes(st, k_ct, 0);
+
+ return result;
+}
+
+static int inv_set_gyro_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_GYRO_ODR, KEY_ODR_CNTR_GYRO,
+ st->sensor[SENSOR_GYRO].rate);
+
+ return result;
+}
+
+static int inv_set_accel_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_ACCL_ODR, KEY_ODR_CNTR_ACCL,
+ st->sensor[SENSOR_ACCEL].rate);
+
+ return result;
+}
+
+static int inv_set_compass_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_CPASS_ODR, KEY_ODR_CNTR_CPASS,
+ st->sensor[SENSOR_COMPASS].rate);
+
+ return result;
+}
+
+static int inv_set_pressure_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_PRESS_ODR, KEY_ODR_CNTR_PRESS,
+ st->sensor[SENSOR_PRESSURE].rate);
+
+ return result;
+}
+
+static int inv_set_step_detector(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+
+static int inv_set_lpq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_3QUAT_ODR, KEY_ODR_CNTR_3QUAT,
+ st->sensor[SENSOR_LPQ].rate);
+
+ return result;
+}
+
+static int inv_set_sixq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_6QUAT_ODR, KEY_ODR_CNTR_6QUAT,
+ st->sensor[SENSOR_SIXQ].rate);
+
+ return result;
+}
+
+static int inv_set_pedq_rate(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_set_rate(st, KEY_CFG_PQUAT6_ODR, KEY_ODR_CNTR_PQUAT,
+ st->sensor[SENSOR_PEDQ].rate);
+
+ return result;
+}
+
+
+static int inv_set_dmp_sysfs(struct inv_mpu_state *st)
+{
+ int result, i, s;
+ u8 d[] = {0, 0, 0, 0};
+
+ result = inv_set_interrupt_on_gesture_event(st,
+ st->chip_config.dmp_event_int_on);
+ if (result)
+ return result;
+
+ if (st->chip_config.dmp_event_int_on) {
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ result = st->sensor[i].send_data(st, false);
+ if (result)
+ return result;
+ }
+ } else {
+ s = 0;
+ st->batch.min_rate = MAX_DMP_OUTPUT_RATE;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ result = st->sensor[i].send_data(st, st->sensor[i].on);
+ if (result)
+ return result;
+ if (st->sensor[i].on) {
+ if (0 == st->sensor[i].rate)
+ return -EINVAL;
+ if (st->sensor[i].rate < st->batch.min_rate)
+ st->batch.min_rate = st->sensor[i].rate;
+ s += st->sensor[i].rate *
+ st->sensor[i].sample_size;
+
+ result = st->sensor[i].set_rate(st);
+ if (result)
+ return result;
+ st->sensor[i].counter = MPU_DEFAULT_DMP_FREQ /
+ st->sensor[i].rate;
+ }
+ }
+ st->bytes_per_sec = s;
+ if (st->sensor[SENSOR_STEP].on)
+ result = inv_add_step_indicator(st, true);
+ else
+ result = inv_add_step_indicator(st,
+ st->chip_config.step_indicator_on);
+ if (result)
+ return result;
+ }
+ result = inv_batchmode_setup(st);
+ if (result)
+ return result;
+
+ st->dmp_counter = 0;
+ result = mem_w_key(KEY_DMP_RUN_CNTR, ARRAY_SIZE(d), d);
+ if (result)
+ return result;
+ result = mem_w_key(KEY_D_STPDET_TIMESTAMP, ARRAY_SIZE(d), d);
+
+ return result;
+}
+
+static void inv_get_data_count(struct inv_mpu_state *st)
+{
+ struct inv_chip_config_s *c;
+ int b, i;
+
+ c = &st->chip_config;
+ b = 0;
+ if (st->chip_config.dmp_on) {
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ b += st->sensor[i].sample_size;
+ }
+ } else {
+ if (st->sensor[SENSOR_ACCEL].on)
+ b += BYTES_PER_SENSOR;
+ if (st->sensor[SENSOR_GYRO].on)
+ b += BYTES_PER_SENSOR;
+ }
+ c->bytes_per_datum = b;
+
+ return;
+}
+/*
+ * set_inv_enable() - main enable/disable function.
+ */
+int set_inv_enable(struct iio_dev *indio_dev, bool enable)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ u8 data[2];
+ int result;
+
+ reg = &st->reg;
+ if (enable) {
+ if (st->chip_config.dmp_on &&
+ (!st->chip_config.firmware_loaded))
+ return -EINVAL;
+ st->batch.on = false;
+
+ inv_get_data_count(st);
+ result = inv_set_master_delay(st);
+ if (result)
+ return result;
+
+ result = set_fifo_rate_reg(st);
+ if (result)
+ return result;
+ if (st->chip_config.dmp_on) {
+ result = inv_set_dmp_sysfs(st);
+ if (result)
+ return result;
+ }
+
+ if (st->chip_config.gyro_enable) {
+ result = st->switch_gyro_engine(st, true);
+ if (result)
+ return result;
+ }
+ if (st->chip_config.accel_enable) {
+ result = st->switch_accel_engine(st, true);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_COMPASS].on) {
+ result = st->slave_compass->resume(st);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ result = st->slave_pressure->resume(st);
+ if (result)
+ return result;
+ }
+ result = inv_reset_fifo(indio_dev);
+ if (result)
+ return result;
+ } else {
+ if ((INV_MPU3050 != st->chip_type)
+ && st->chip_config.lpa_mode) {
+ /* if the chip is in low power mode,
+ register write/read could fail */
+ result = inv_lpa_mode(st, 0);
+ if (result)
+ return result;
+ }
+ result = inv_i2c_single_write(st, reg->fifo_en, 0);
+ if (result)
+ return result;
+ if (st->chip_config.dmp_on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ return result;
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ st->fifo_count = be16_to_cpup((__be16 *)(data));
+ if (st->fifo_count) {
+ result = inv_process_batchmode(st);
+ if (result)
+ return result;
+ }
+ }
+ inv_push_marker_to_buffer(st, END_MARKER);
+ /* disable fifo reading */
+ if (INV_MPU3050 != st->chip_type) {
+ result = inv_i2c_single_write(st, reg->int_enable, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, reg->user_ctrl, 0);
+ } else {
+ result = inv_i2c_single_write(st, reg->int_enable,
+ st->plat_data.int_config);
+ }
+ if (result)
+ return result;
+ /* turn off the gyro/accel engine during disable phase */
+ result = st->switch_gyro_engine(st, false);
+ if (result)
+ return result;
+ result = st->switch_accel_engine(st, false);
+ if (result)
+ return result;
+ if (st->sensor[SENSOR_COMPASS].on) {
+ result = st->slave_compass->suspend(st);
+ if (result)
+ return result;
+ }
+ if (st->sensor[SENSOR_PRESSURE].on) {
+ result = st->slave_pressure->suspend(st);
+ if (result)
+ return result;
+ }
+ }
+ st->chip_config.enable = enable;
+
+ return 0;
+}
+
+/*
+ * inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+ u64 ts;
+
+ if (!st->chip_config.dmp_on) {
+ ts = get_time_ns();
+ kfifo_in_spinlocked(&st->timestamps, &ts, 1,
+ &st->time_stamp_lock);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t,
+ int has_footer, u8 *data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int ind, i;
+ short s[THREE_AXIS];
+
+ ind = 0;
+ if (has_footer)
+ ind += 2;
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+
+ inv_push_8bytes_buffer(st, GYRO_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+ if (st->sensor[SENSOR_ACCEL].on) {
+ st->slave_accel->combine_data(&data[ind], s);
+ inv_push_8bytes_buffer(st, ACCEL_HDR, t, s);
+ }
+}
+
+/*
+ * inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for
+ * mpu3050.
+ */
+irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id)
+{
+
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int bytes_per_datum;
+ u8 data[64];
+ int result;
+ short fifo_count, byte_read;
+ s64 timestamp;
+ struct inv_reg_map_s *reg;
+
+ reg = &st->reg;
+ mutex_lock(&st->suspend_resume_lock);
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.dmp_on)
+ bytes_per_datum = HEADERED_NORMAL_BYTES;
+ else
+ bytes_per_datum = (st->sensor[SENSOR_ACCEL].on +
+ st->sensor[SENSOR_GYRO].on) * BYTES_PER_SENSOR;
+ if (st->chip_config.has_footer)
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ else
+ byte_read = bytes_per_datum;
+
+ fifo_count = 0;
+ if (byte_read != 0) {
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ goto end_session;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ if (fifo_count < byte_read)
+ goto end_session;
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ /* Timestamp mismatch. */
+ if (kfifo_len(&st->timestamps) <
+ fifo_count / byte_read)
+ goto flush_fifo;
+ if (kfifo_len(&st->timestamps) >
+ fifo_count / byte_read + TIME_STAMP_TOR) {
+ if (st->chip_config.dmp_on) {
+ result = kfifo_out(&st->timestamps,
+ &timestamp, 1);
+ if (result != 1)
+ goto flush_fifo;
+ } else {
+ goto flush_fifo;
+ }
+ }
+ }
+ while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) {
+ result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data);
+ if (result)
+ goto flush_fifo;
+
+ result = kfifo_out(&st->timestamps, &timestamp, 1);
+ if (result != 1)
+ goto flush_fifo;
+ inv_report_data_3050(indio_dev, timestamp,
+ st->chip_config.has_footer, data);
+ fifo_count -= byte_read;
+ if (st->chip_config.has_footer == 0) {
+ st->chip_config.has_footer = 1;
+ byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE;
+ }
+ }
+
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int inv_report_gyro_accel(struct iio_dev *indio_dev,
+ u8 *data, s64 t)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ short s[THREE_AXIS];
+ int ind;
+ int i;
+
+ ind = 0;
+ if (st->sensor[SENSOR_ACCEL].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ inv_push_8bytes_buffer(st, ACCEL_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ for (i = 0; i < 3; i++)
+ s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2]));
+ inv_push_8bytes_buffer(st, GYRO_HDR, t, s);
+ ind += BYTES_PER_SENSOR;
+ }
+
+ return 0;
+}
+
+static void inv_process_motion(struct inv_mpu_state *st)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result;
+ u8 data[1];
+
+ /* motion interrupt */
+ result = inv_i2c_read(st, REG_INT_STATUS, 1, data);
+ if (result)
+ return;
+
+ if (data[0] & BIT_MOT_INT)
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_accel_motion");
+}
+
+static int inv_get_timestamp(struct inv_mpu_state *st, int count)
+{
+ u32 *dur;
+ u32 thresh;
+ s32 diff, result, counter;
+ u64 ts;
+
+ /* goal of algorithm is to estimate the true frequency of the chip */
+ if (st->chip_config.dmp_on && st->chip_config.dmp_event_int_on)
+ return 0;
+ dur = &st->irq_dur_ns;
+ counter = 1;
+ thresh = min((u32)((*dur) >> 2), (u32)(10 * NSEC_PER_MSEC));
+ while (kfifo_len(&st->timestamps) >= count) {
+ result = kfifo_out(&st->timestamps, &ts, 1);
+ if (result != 1)
+ return -EINVAL;
+ /* first time since reset fifo, just take it */
+ if (!st->ts_counter) {
+ st->last_ts = ts;
+ st->prev_ts = ts;
+ st->ts_counter++;
+ return 0;
+ }
+ diff = (s32)(ts - st->prev_ts);
+ st->prev_ts = ts;
+ if (abs(diff - (*dur)) < thresh) {
+ st->diff_accumulater >>= 1;
+ if (*dur > diff)
+ st->diff_accumulater -= (((*dur) - diff) >> 7);
+ else
+ st->diff_accumulater += ((diff - (*dur)) >> 7);
+ *dur += st->diff_accumulater;
+ }
+ }
+ ts = *dur;
+ ts *= counter;
+ st->last_ts += ts;
+
+ return 0;
+}
+
+static int inv_process_dmp_interrupt(struct inv_mpu_state *st)
+{
+ int r;
+ u8 d[1];
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+
+#define DMP_INT_SMD 0x04
+#define DMP_INT_PED 0x08
+
+ if ((!st->chip_config.smd_enable) &&
+ (!st->ped.int_on) &&
+ (!st->tap.on))
+ return 0;
+
+ r = inv_i2c_read(st, REG_DMP_INT_STATUS, 1, d);
+ if (r)
+ return r;
+ if (d[0] & DMP_INT_SMD) {
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_smd");
+ st->chip_config.smd_enable = false;
+ st->chip_config.smd_triggered = true;
+ }
+ if (d[0] & DMP_INT_PED)
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "event_pedometer");
+
+ return 0;
+}
+
+static int inv_get_shift2(int count)
+{
+ int i;
+
+ if (1 == count)
+ return 13;
+ if (count > 2000)
+ return 2;
+ i = 13;
+ while (count > 0) {
+ count >>= 1;
+ i--;
+ }
+
+ return i;
+}
+
+static void inv_adjust_sensor_ts(struct inv_mpu_state *st, int sensor_ind)
+{
+ s64 diff;
+ int i, rate_adj, s3, delta, total_count;
+
+ if (!st->chip_config.adjust_time)
+ return;
+#define MAX_DIFF 0x7fffffff
+ total_count = st->dmp_ticks;
+ if (0 == total_count)
+ total_count = 1;
+
+ diff = (st->last_ts - st->prev_ts) - (u64)(st->dmp_interval) *
+ total_count;
+ if (diff > MAX_DIFF)
+ diff = MAX_DIFF;
+ if (diff < -MAX_DIFF)
+ diff = -MAX_DIFF;
+ s3 = 4;
+ rate_adj = (int)diff;
+ rate_adj /= total_count;
+ delta = min(abs(rate_adj) >> inv_get_shift2(total_count),
+ DMP_INTERVAL_MIN_ADJ);
+ if (rate_adj < 0)
+ delta = -delta;
+ st->dmp_interval_accum >>= 1;
+ st->dmp_interval_accum += delta;
+ st->dmp_interval += st->dmp_interval_accum;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ if (st->sensor[i].on)
+ st->sensor[i].dur = st->dmp_interval *
+ st->sensor[i].counter;
+
+ st->prev_ts = st->last_ts;
+}
+
+static void inv_reset_ts(struct inv_mpu_state *st, u64 curr_ts)
+{
+ u32 dur, i;
+
+ dur = USEC_PER_SEC / st->bytes_per_sec;
+ dur *= 1024;
+ curr_ts -= ((u64)dur * NSEC_PER_USEC);
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = curr_ts;
+}
+
+static void inv_push_step_indicator(struct inv_mpu_state *st, int sensor_ind,
+ int steps)
+{
+ int dur, i;
+ s16 sen[3];
+ u64 base;
+#define STEP_INDICATOR_HEADER 0x0001
+
+ dur = st->sensor[sensor_ind].dur / steps;
+ base = st->sensor[sensor_ind].ts;
+
+ for (i = 1; i < steps; i++)
+ inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER,
+ base + i * dur, sen);
+}
+
+static int inv_parse_header(u16 hdr)
+{
+ int sensor_ind;
+
+ switch (hdr) {
+ case ACCEL_HDR:
+ sensor_ind = SENSOR_ACCEL;
+ break;
+ case GYRO_HDR:
+ sensor_ind = SENSOR_GYRO;
+ break;
+ case PEDQUAT_HDR:
+ sensor_ind = SENSOR_PEDQ;
+ break;
+ case LPQUAT_HDR:
+ sensor_ind = SENSOR_LPQ;
+ break;
+ case SIXQUAT_HDR:
+ sensor_ind = SENSOR_SIXQ;
+ break;
+ case COMPASS_HDR:
+ sensor_ind = SENSOR_COMPASS;
+ break;
+ case PRESSURE_HDR:
+ sensor_ind = SENSOR_PRESSURE;
+ break;
+ case STEP_DETECTOR_HDR:
+ sensor_ind = SENSOR_STEP;
+ break;
+ default:
+ sensor_ind = SENSOR_INVALID;
+ break;
+ }
+
+ return sensor_ind;
+}
+
+static int inv_process_batchmode(struct inv_mpu_state *st)
+{
+ int i, target_bytes, tmp, res, counter;
+ int sensor_ind, q[3];
+ u8 *dptr, *d;
+ u16 hdr, steps;
+ s16 sen[3];
+ u64 t;
+ bool done_flag;
+
+ if (1024 == st->fifo_count) {
+ inv_reset_ts(st, st->last_ts);
+ st->left_over_size = 0;
+ }
+ d = fifo_data;
+ if (st->left_over_size > 0) {
+ dptr = d + st->left_over_size;
+ memcpy(d, st->left_over, st->left_over_size);
+ } else {
+ dptr = d;
+ }
+ target_bytes = st->fifo_count;
+ while (target_bytes > 0) {
+ if (target_bytes < MAX_READ_SIZE)
+ tmp = target_bytes;
+ else
+ tmp = MAX_READ_SIZE;
+ res = inv_i2c_read(st, st->reg.fifo_r_w, tmp, dptr);
+ if (res < 0)
+ return res;
+ dptr += tmp;
+ target_bytes -= tmp;
+ }
+ dptr = d;
+ done_flag = false;
+ target_bytes = st->fifo_count + st->left_over_size;
+ counter = 0;
+ while ((dptr - d <= target_bytes - HEADERED_NORMAL_BYTES) &&
+ (!done_flag)) {
+ hdr = (u16)be16_to_cpup((__be16 *)(dptr));
+ steps = (hdr & STEP_INDICATOR_MASK);
+ hdr &= (~STEP_INDICATOR_MASK);
+ sensor_ind = inv_parse_header(hdr);
+ /* incomplete packet */
+ if (target_bytes - (dptr - d) <
+ st->sensor[sensor_ind].sample_size) {
+ done_flag = true;
+ continue;
+ }
+ /* error packet */
+ if ((sensor_ind == SENSOR_INVALID) ||
+ (!st->sensor[sensor_ind].on)) {
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (sensor_ind == SENSOR_STEP) {
+ tmp = (int)be32_to_cpup((__be32 *)(dptr + 4));
+ t = st->step_detector_base_ts +
+ (u64)tmp * 5 * NSEC_PER_MSEC;
+ inv_push_8bytes_buffer(st, hdr, t, sen);
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (steps > 1)
+ inv_push_step_indicator(st, sensor_ind, steps);
+ st->sensor[sensor_ind].ts += (u64)st->sensor[sensor_ind].dur;
+ t = st->sensor[sensor_ind].ts;
+ if (sensor_ind == SENSOR_COMPASS) {
+ if (!st->chip_config.normal_compass_measure) {
+ st->chip_config.normal_compass_measure = 1;
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ for (i = 0; i < 6; i++)
+ st->fifo_data[i] = dptr[i + 2];
+ res = st->slave_compass->read_data(st, sen);
+ if (!res)
+ inv_push_8bytes_buffer(st, hdr |
+ (!!steps), t, sen);
+
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (sensor_ind == SENSOR_PRESSURE) {
+ if (!st->chip_config.normal_pressure_measure) {
+ st->chip_config.normal_pressure_measure = 1;
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ for (i = 0; i < 6; i++)
+ st->fifo_data[i] = dptr[i + 2];
+ res = st->slave_pressure->read_data(st, sen);
+ if (!res)
+ inv_push_8bytes_buffer(st, hdr |
+ (!!steps), t, sen);
+
+ dptr += HEADERED_NORMAL_BYTES;
+ continue;
+ }
+ if (st->sensor[sensor_ind].sample_size == HEADERED_Q_BYTES) {
+ for (i = 0; i < 3; i++)
+ q[i] = (int)be32_to_cpup((__be32 *)(dptr + 4
+ + i * 4));
+ inv_push_16bytes_buffer(st, hdr | (!!steps), t, q);
+ } else {
+ for (i = 0; i < 3; i++)
+ sen[i] = (short)be16_to_cpup((__be16 *)(dptr +
+ 2 + i * 2));
+ inv_push_8bytes_buffer(st, hdr | (!!steps), t, sen);
+ }
+ dptr += st->sensor[sensor_ind].sample_size;
+ }
+ inv_adjust_sensor_ts(st, sensor_ind);
+ st->left_over_size = target_bytes - (dptr - d);
+
+ if (st->left_over_size)
+ memcpy(st->left_over, dptr, st->left_over_size);
+
+ return 0;
+}
+
+int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume)
+{
+ int result;
+ u32 counter;
+ u8 data[4];
+
+#define MIN_TICK_READING_TIME NSEC_PER_SEC
+ st->last_ts = get_time_ns();
+ if ((st->last_ts - st->prev_ts < MIN_TICK_READING_TIME) &&
+ (!resume)) {
+ st->chip_config.adjust_time = false;
+ return 0;
+ }
+ result = mpu_memory_read(st, st->i2c_addr,
+ inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, data);
+ if (result)
+ return result;
+
+ counter = be32_to_cpup((__be32 *)(data));
+ if (resume) {
+ st->dmp_counter = counter;
+ st->prev_ts = st->last_ts;
+
+ return 0;
+ }
+ if (counter > st->dmp_counter)
+ st->dmp_ticks = counter - st->dmp_counter;
+ else
+ st->dmp_ticks = 0xffffffff - st->dmp_counter + counter + 1;
+ st->dmp_counter = counter;
+ st->chip_config.adjust_time = true;
+
+ 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_state *st = (struct inv_mpu_state *)dev_id;
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result, bpm;
+ u8 data[MAX_HW_FIFO_BYTES];
+ u16 fifo_count;
+ struct inv_reg_map_s *reg;
+ u64 pts1;
+
+#define DMP_MIN_RUN_TIME (37 * NSEC_PER_MSEC)
+ mutex_lock(&st->suspend_resume_lock);
+ mutex_lock(&indio_dev->mlock);
+ if (st->chip_config.dmp_on) {
+ pts1 = get_time_ns();
+ result = inv_process_dmp_interrupt(st);
+ if (result || st->chip_config.dmp_event_int_on)
+ goto end_session;
+ if (!st->chip_config.smd_triggered) {
+ if (pts1 - st->last_run_time < DMP_MIN_RUN_TIME)
+ goto end_session;
+ else
+ st->last_run_time = pts1;
+ } else {
+ st->chip_config.smd_triggered = false;
+ }
+ }
+
+ if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable))
+ goto end_session;
+
+ reg = &st->reg;
+ if (!(st->sensor[SENSOR_ACCEL].on |
+ st->sensor[SENSOR_GYRO].on |
+ st->sensor[SENSOR_COMPASS].on |
+ st->sensor[SENSOR_PRESSURE].on |
+ st->chip_config.dmp_on |
+ st->mot_int.mot_on))
+ goto end_session;
+ if (st->chip_config.lpa_mode) {
+ result = inv_i2c_read(st, reg->raw_accel,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto end_session;
+ inv_report_gyro_accel(indio_dev, data, get_time_ns());
+ if (st->mot_int.mot_on)
+ inv_process_motion(st);
+
+ goto end_session;
+ }
+
+ if (st->chip_config.dmp_on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ goto end_session;
+ }
+ bpm = st->chip_config.bytes_per_datum;
+ fifo_count = 0;
+ if (bpm) {
+ 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));
+ /* fifo count can't be odd number */
+ if (fifo_count & 1)
+ goto flush_fifo;
+ if (fifo_count == 0)
+ goto end_session;
+ st->fifo_count = fifo_count;
+ }
+
+ if (st->chip_config.dmp_on) {
+ result = inv_process_batchmode(st);
+ } else {
+ if (fifo_count > FIFO_THRESHOLD)
+ goto flush_fifo;
+ if (bpm) {
+ while (fifo_count >= bpm) {
+ result = inv_i2c_read(st, reg->fifo_r_w, bpm,
+ data);
+ if (result)
+ goto flush_fifo;
+ result = inv_get_timestamp(st,
+ fifo_count / bpm);
+ if (result)
+ goto flush_fifo;
+ inv_report_gyro_accel(indio_dev, data,
+ st->last_ts);
+ fifo_count -= bpm;
+ }
+ } else {
+ result = inv_get_timestamp(st, 1);
+ if (result)
+ goto flush_fifo;
+ }
+ inv_send_compass_data(st);
+ inv_send_pressure_data(st);
+ }
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_reset_fifo(indio_dev);
+ inv_clear_kfifo(st);
+ mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->suspend_resume_lock);
+
+ return IRQ_HANDLED;
+}
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ free_irq(st->client->irq, st);
+ iio_kfifo_free(indio_dev->buffer);
+};
+
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ if (st->chip_config.enable) {
+ result = set_inv_enable(indio_dev, false);
+ if (result)
+ return result;
+ result = st->set_power_state(st, false);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+static int inv_check_conflict_sysfs(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ if (st->chip_config.lpa_mode) {
+ /* dmp cannot run with low power mode on */
+ st->chip_config.dmp_on = 0;
+ st->chip_config.gyro_enable = false;
+ st->sensor[SENSOR_GYRO].on = false;
+ st->sensor[SENSOR_COMPASS].on = false;
+ }
+ if (st->sensor[SENSOR_GYRO].on &&
+ (!st->chip_config.gyro_enable)) {
+ st->chip_config.gyro_enable = true;
+ }
+ if (st->sensor[SENSOR_ACCEL].on &&
+ (!st->chip_config.accel_enable)) {
+ st->chip_config.accel_enable = true;
+ }
+
+ return 0;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+ int result;
+
+ result = inv_check_conflict_sysfs(indio_dev);
+ if (result)
+ return result;
+ result = iio_sw_buffer_preenable(indio_dev);
+
+ return result;
+}
+
+void inv_init_sensor_struct(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (i < SENSOR_SIXQ)
+ st->sensor[i].sample_size =
+ HEADERED_NORMAL_BYTES;
+ else
+ st->sensor[i].sample_size = HEADERED_Q_BYTES;
+ if (i == SENSOR_STEP) {
+ st->sensor[i].rate = 1;
+ st->sensor[i].dur = NSEC_PER_SEC;
+ } else {
+ st->sensor[i].rate = INIT_DMP_OUTPUT_RATE;
+ st->sensor[i].dur = NSEC_PER_SEC /
+ INIT_DMP_OUTPUT_RATE;
+ }
+ }
+
+ st->sensor[SENSOR_ACCEL].send_data = inv_send_accel_data;
+ st->sensor[SENSOR_GYRO].send_data = inv_send_gyro_data;
+ st->sensor[SENSOR_COMPASS].send_data = inv_send_compass_dmp_data;
+ st->sensor[SENSOR_PRESSURE].send_data = inv_send_pressure_dmp_data;
+ st->sensor[SENSOR_STEP].send_data = inv_send_step_detector;
+ st->sensor[SENSOR_PEDQ].send_data = inv_send_ped_q_data;
+ st->sensor[SENSOR_SIXQ].send_data = inv_send_six_q_data;
+ st->sensor[SENSOR_LPQ].send_data = inv_send_three_q_data;
+
+ st->sensor[SENSOR_ACCEL].set_rate = inv_set_accel_rate;
+ st->sensor[SENSOR_GYRO].set_rate = inv_set_gyro_rate;
+ st->sensor[SENSOR_COMPASS].set_rate = inv_set_compass_rate;
+ st->sensor[SENSOR_PRESSURE].set_rate = inv_set_pressure_rate;
+ st->sensor[SENSOR_STEP].set_rate = inv_set_step_detector;
+ st->sensor[SENSOR_PEDQ].set_rate = inv_set_pedq_rate;
+ st->sensor[SENSOR_SIXQ].set_rate = inv_set_sixq_rate;
+ st->sensor[SENSOR_LPQ].set_rate = inv_set_lpq_rate;
+}
+
+int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct inv_reg_map_s *reg;
+ u8 data[4];
+ int result;
+
+ reg = &st->reg;
+ if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable))
+ return -EINVAL;
+
+ if (st->batch.on) {
+ result = inv_read_time_and_ticks(st, false);
+ if (result)
+ return result;
+ result = inv_i2c_read(st, reg->fifo_count_h,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ st->fifo_count = be16_to_cpup((__be16 *)(data));
+ if (st->fifo_count) {
+ result = inv_process_batchmode(st);
+ if (result)
+ return result;
+ *has_data = !!st->fifo_count;
+ inv_push_marker_to_buffer(st, END_MARKER);
+ result = write_be32_key_to_mem(st, 0,
+ KEY_BM_BATCH_CNTR);
+ return result;
+ }
+ }
+ inv_push_marker_to_buffer(st, EMPTY_MARKER);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+ .preenable = &inv_preenable,
+ .predisable = &inv_predisable,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *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*/
+ if (INV_MPU3050 == st->chip_type)
+ ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+ inv_read_fifo_mpu3050,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ else
+ ret = request_threaded_irq(st->client->irq, inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
+ if (ret)
+ goto error_iio_sw_rb_free;
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ return 0;
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+
+ return ret;
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c
new file mode 100644
index 00000000000..78b04a6ad86
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c
@@ -0,0 +1,80 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+
+#include "inv_mpu_iio.h"
+
+/*
+ * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state
+ */
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ return 0;
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL)
+ return -ENOMEM;
+ st->trig->dev.parent = &st->client->dev;
+ /* st->trig->private_data = indio_dev; Linux 3.4 --mfj */
+ st->trig->ops = &inv_mpu_trigger_ops;
+ ret = iio_trigger_register(st->trig);
+
+ if (ret) {
+ iio_trigger_free(st->trig);
+ return -EPERM;
+ }
+ indio_dev->trig = st->trig;
+
+ return 0;
+}
+
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_trigger_free(st->trig);
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c
new file mode 100644
index 00000000000..99ae702e67a
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c
@@ -0,0 +1,315 @@
+/*
+* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#define BMA250_CHIP_ID 3
+#define BMA250_RANGE_SET 0
+#define BMA250_BW_SET 4
+
+/* range and bandwidth */
+#define BMA250_RANGE_2G 3
+#define BMA250_RANGE_4G 5
+#define BMA250_RANGE_8G 8
+#define BMA250_RANGE_16G 12
+#define BMA250_RANGE_MAX 4
+#define BMA250_RANGE_MASK 0xF0
+
+#define BMA250_BW_7_81HZ 0x08
+#define BMA250_BW_15_63HZ 0x09
+#define BMA250_BW_31_25HZ 0x0A
+#define BMA250_BW_62_50HZ 0x0B
+#define BMA250_BW_125HZ 0x0C
+#define BMA250_BW_250HZ 0x0D
+#define BMA250_BW_500HZ 0x0E
+#define BMA250_BW_1000HZ 0x0F
+#define BMA250_MAX_BW_SIZE 8
+#define BMA250_BW_REG_MASK 0xE0
+
+/* register definitions */
+#define BMA250_X_AXIS_LSB_REG 0x02
+#define BMA250_RANGE_SEL_REG 0x0F
+#define BMA250_BW_SEL_REG 0x10
+#define BMA250_MODE_CTRL_REG 0x11
+
+/* mode settings */
+#define BMA250_MODE_NORMAL 0
+#define BMA250_MODE_LOWPOWER 1
+#define BMA250_MODE_SUSPEND 2
+#define BMA250_MODE_MAX 3
+#define BMA250_MODE_MASK 0x3F
+#define BMA250_BIT_SUSPEND 0x80
+#define BMA250_BIT_LP 0x40
+
+struct bma_property {
+ int range;
+ int bandwidth;
+ int mode;
+};
+
+static struct bma_property bma_static_property = {
+ .range = BMA250_RANGE_SET,
+ .bandwidth = BMA250_BW_SET,
+ .mode = BMA250_MODE_SUSPEND
+};
+
+static int bma250_set_bandwidth(struct inv_mpu_state *st, u8 bw)
+{
+ int res;
+ u8 data;
+ int bandwidth;
+ switch (bw) {
+ case 0:
+ bandwidth = BMA250_BW_7_81HZ;
+ break;
+ case 1:
+ bandwidth = BMA250_BW_15_63HZ;
+ break;
+ case 2:
+ bandwidth = BMA250_BW_31_25HZ;
+ break;
+ case 3:
+ bandwidth = BMA250_BW_62_50HZ;
+ break;
+ case 4:
+ bandwidth = BMA250_BW_125HZ;
+ break;
+ case 5:
+ bandwidth = BMA250_BW_250HZ;
+ break;
+ case 6:
+ bandwidth = BMA250_BW_500HZ;
+ break;
+ case 7:
+ bandwidth = BMA250_BW_1000HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_BW_REG_MASK;
+ data |= bandwidth;
+ res = inv_secondary_write(BMA250_BW_SEL_REG, data);
+ return res;
+}
+
+static int bma250_set_range(struct inv_mpu_state *st, u8 range)
+{
+ int res;
+ u8 orig, data;
+ switch (range) {
+ case 0:
+ data = BMA250_RANGE_2G;
+ break;
+ case 1:
+ data = BMA250_RANGE_4G;
+ break;
+ case 2:
+ data = BMA250_RANGE_8G;
+ break;
+ case 3:
+ data = BMA250_RANGE_16G;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig);
+ if (res)
+ return res;
+ orig &= BMA250_RANGE_MASK;
+ data |= orig;
+ res = inv_secondary_write(BMA250_RANGE_SEL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.range = range;
+
+ return 0;
+}
+
+static int setup_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[2];
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ /*read secondary i2c ID register */
+ result = inv_secondary_read(0, 1, data);
+ if (result)
+ return result;
+ if (BMA250_CHIP_ID != data[0])
+ return -EINVAL;
+ result = set_3050_bypass(st, false);
+ if (result)
+ return result;
+ /*AUX(accel), slave address is set inside set_3050_bypass*/
+ /* bma250 x axis LSB register address is 2 */
+ result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR,
+ BMA250_X_AXIS_LSB_REG);
+
+ return result;
+}
+
+static int bma250_set_mode(struct inv_mpu_state *st, u8 mode)
+{
+ int res;
+ u8 data;
+
+ res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data);
+ if (res)
+ return res;
+ data &= BMA250_MODE_MASK;
+ switch (mode) {
+ case BMA250_MODE_NORMAL:
+ break;
+ case BMA250_MODE_LOWPOWER:
+ data |= BMA250_BIT_LP;
+ break;
+ case BMA250_MODE_SUSPEND:
+ data |= BMA250_BIT_SUSPEND;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = inv_secondary_write(BMA250_MODE_CTRL_REG, data);
+ if (res)
+ return res;
+ bma_static_property.mode = mode;
+
+ return 0;
+}
+
+static int suspend_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_SUSPEND)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ bma250_set_mode(st, BMA250_MODE_SUSPEND);
+ /* no need to recover to non-bypass mode because we need it now */
+
+ return 0;
+}
+
+static int resume_slave_bma250(struct inv_mpu_state *st)
+{
+ int result;
+ if (bma_static_property.mode == BMA250_MODE_NORMAL)
+ return 0;
+ /*set to bypass mode */
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_mode(st, BMA250_MODE_NORMAL);
+ /* recover bypass mode */
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static int combine_data_slave_bma250(u8 *in, short *out)
+{
+ out[0] = le16_to_cpup((__le16 *)(&in[0]));
+ out[1] = le16_to_cpup((__le16 *)(&in[2]));
+ out[2] = le16_to_cpup((__le16 *)(&in[4]));
+
+ return 0;
+}
+
+static int get_mode_slave_bma250(void)
+{
+ switch (bma_static_property.mode) {
+ case BMA250_MODE_SUSPEND:
+ return INV_MODE_SUSPEND;
+ case BMA250_MODE_NORMAL:
+ return INV_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * set_lpf_bma250() - set lpf value
+ */
+
+static int set_lpf_bma250(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7};
+ const int d[] = {7, 6, 5, 4, 3, 2, 1, 0};
+ int i, h, data, result;
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1))
+ i++;
+ data = d[i];
+
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_bandwidth(st, (u8) data);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+/**
+ * set_fs_bma250() - set range value
+ */
+
+static int set_fs_bma250(struct inv_mpu_state *st, int fs)
+{
+ int result;
+ result = set_3050_bypass(st, true);
+ if (result)
+ return result;
+ result = bma250_set_range(st, (u8) fs);
+ result |= set_3050_bypass(st, false);
+
+ return result ? (-EINVAL) : 0;
+}
+
+static struct inv_mpu_slave slave_bma250 = {
+ .suspend = suspend_slave_bma250,
+ .resume = resume_slave_bma250,
+ .setup = setup_slave_bma250,
+ .combine_data = combine_data_slave_bma250,
+ .get_mode = get_mode_slave_bma250,
+ .set_lpf = set_lpf_bma250,
+ .set_fs = set_fs_bma250
+};
+
+int inv_register_mpu3050_slave(struct inv_mpu_state *st)
+{
+ st->slave_accel = &slave_bma250;
+
+ return 0;
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c
new file mode 100644
index 00000000000..f7d7ff20b17
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c
@@ -0,0 +1,852 @@
+/*
+* 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 "inv_mpu_iio.h"
+/* AKM definitions */
+#define REG_AKM_ID 0x00
+#define REG_AKM_INFO 0x01
+#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
+
+/* AK09911 register definition */
+#define REG_AK09911_DMP_READ 0x10
+#define REG_AK09911_STATUS1 0x10
+#define REG_AK09911_CNTL2 0x31
+#define REG_AK09911_SENSITIVITY 0x60
+
+#define DATA_AKM_ID 0x48
+#define DATA_AKM_MODE_PD 0x00
+#define DATA_AKM_MODE_SM 0x01
+#define DATA_AKM_MODE_ST 0x08
+#define DATA_AKM_MODE_FR 0x0F
+#define DATA_AK09911_MODE_FR 0x1F
+#define DATA_AKM_SELF_TEST 0x40
+#define DATA_AKM_DRDY 0x01
+#define DATA_AKM8963_BIT 0x10
+#define DATA_AKM_STAT_MASK 0x0C
+
+#define DATA_AKM8975_SCALE (9830 * (1L << 15))
+#define DATA_AKM8972_SCALE (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE0 (19661 * (1L << 15))
+#define DATA_AKM8963_SCALE1 (4915 * (1L << 15))
+#define DATA_AK09911_SCALE (19661 * (1L << 15))
+#define DATA_MLX_SCALE (4915 * (1L << 15))
+#define DATA_MLX_SCALE_EMPIRICAL (26214 * (1L << 15))
+
+#define DATA_AKM8963_SCALE_SHIFT 4
+#define DATA_AKM_BYTES_DMP 10
+#define DATA_AKM_BYTES 8
+#define DATA_AKM_MIN_READ_TIME (9 * NSEC_PER_MSEC)
+
+#define DEF_ST_COMPASS_WAIT_MIN (10 * 1000)
+#define DEF_ST_COMPASS_WAIT_MAX (15 * 1000)
+#define DEF_ST_COMPASS_TRY_TIMES 10
+#define DEF_ST_COMPASS_8963_SHIFT 2
+#define X 0
+#define Y 1
+#define Z 2
+
+/* milliseconds between each access */
+#define AKM_RATE_SCALE 10
+#define MLX_RATE_SCALE 50
+
+/* MLX90399 compass definition */
+#define DATA_MLX_CMD_READ_MEASURE 0x4F
+#define DATA_MLX_CMD_SINGLE_MEASURE 0x3F
+#define DATA_MLX_READ_DATA_BYTES 9
+#define DATA_MLX_STATUS_DATA 3
+#define DATA_MLX_MIN_READ_TIME (95 * NSEC_PER_MSEC)
+
+static const short AKM8975_ST_Lower[3] = {-100, -100, -1000};
+static const short AKM8975_ST_Upper[3] = {100, 100, -300};
+
+static const short AKM8972_ST_Lower[3] = {-50, -50, -500};
+static const short AKM8972_ST_Upper[3] = {50, 50, -100};
+
+static const short AKM8963_ST_Lower[3] = {-200, -200, -3200};
+static const short AKM8963_ST_Upper[3] = {200, 200, -800};
+
+/*
+ * inv_setup_compass_akm() - Configure akm series compass.
+ */
+static int inv_setup_compass_akm(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[4];
+ u8 sens, mode, cmd;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+ /* read secondary i2c ID register */
+ result = inv_secondary_read(REG_AKM_ID, 1, data);
+ if (result)
+ return result;
+ if (data[0] != DATA_AKM_ID)
+ return -ENXIO;
+ /* set AKM to Fuse ROM access mode */
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ mode = REG_AK09911_CNTL2;
+ sens = REG_AK09911_SENSITIVITY;
+ cmd = DATA_AK09911_MODE_FR;
+ } else {
+ mode = REG_AKM_MODE;
+ sens = REG_AKM_SENSITIVITY;
+ cmd = DATA_AKM_MODE_FR;
+ }
+
+ result = inv_secondary_write(mode, cmd);
+ if (result)
+ return result;
+ result = inv_secondary_read(sens, THREE_AXIS,
+ st->chip_info.compass_sens);
+ if (result)
+ return result;
+ /* revert to power down mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ if (result)
+ return result;
+ pr_debug("%s senx=%d, seny=%d, senz=%d\n",
+ st->hw->name,
+ st->chip_info.compass_sens[0],
+ st->chip_info.compass_sens[1],
+ st->chip_info.compass_sens[2]);
+ /* restore to non-bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+
+ /* setup master mode and master clock and ES bit */
+ result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (result)
+ return result;
+ /* slave 1 is used for AKM mode change only */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM mode register address */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, mode);
+ if (result)
+ return result;
+ /* output data for slave 1 is fixed, single measure mode */
+ st->slave_compass->scale = 1;
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8975_ST_Upper;
+ st->slave_compass->st_lower = AKM8975_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8972_ST_Upper;
+ st->slave_compass->st_lower = AKM8972_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8963_ST_Upper;
+ st->slave_compass->st_lower = AKM8963_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM |
+ (st->slave_compass->scale << DATA_AKM8963_SCALE_SHIFT);
+ } else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ st->slave_compass->st_upper = AKM8963_ST_Upper;
+ st->slave_compass->st_lower = AKM8963_ST_Lower;
+ data[0] = DATA_AKM_MODE_SM;
+ } else {
+ return -EINVAL;
+ }
+
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, data[0]);
+
+ return result;
+}
+
+static int inv_akm_read_data(struct inv_mpu_state *st, short *o)
+{
+ int result, shift;
+ int i;
+ u8 d[DATA_AKM_BYTES];
+ u8 *sens;
+
+ sens = st->chip_info.compass_sens;
+ result = 0;
+ if (st->chip_config.dmp_on &&
+ (COMPASS_ID_AK09911 != st->plat_data.sec_slave_id)) {
+ for (i = 0; i < 6; i++)
+ d[1 + i] = st->fifo_data[i];
+ } else {
+ result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+ DATA_AKM_BYTES, d);
+ if ((DATA_AKM_DRDY != d[0]) || result)
+ result = -EINVAL;
+ }
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ shift = 7;
+ else
+ shift = 8;
+ for (i = 0; i < 3; i++) {
+ o[i] = (short)((d[i * 2 + 2] << 8) | d[i * 2 + 1]);
+ o[i] = (short)(((int)o[i] * (sens[i] + 128)) >> shift);
+ }
+
+ return result;
+}
+
+static int inv_mlx_read_data(struct inv_mpu_state *st, short *o)
+{
+ int result;
+ int i, z;
+ u8 d[DATA_MLX_READ_DATA_BYTES];
+
+ result = inv_i2c_read(st, REG_EXT_SENS_DATA_00,
+ DATA_MLX_READ_DATA_BYTES, d);
+ if ((!(d[0] & ~DATA_MLX_STATUS_DATA)) && (!result)) {
+ for (i = 0; i < 3; i++)
+ o[i] = (short)((d[i * 2 + 3] << 8) + d[i * 2 + 4]);
+ } else {
+ for (i = 0; i < 3; i++)
+ o[i] = 0;
+ }
+ z = o[2];
+ /* axis sensitivity conversion. Z axis has different sensitiviy from
+ x and y */
+ z *= 26;
+ z /= 15;
+ o[2] = z;
+
+ return 0;
+}
+
+static int inv_check_akm_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ u8 data[6], mode;
+ u8 counter, cntl;
+ short x, y, z;
+ u8 *sens;
+ sens = st->chip_info.compass_sens;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result) {
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+ }
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ mode = REG_AK09911_CNTL2;
+ else
+ mode = REG_AKM_MODE;
+ /* set to power down mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ if (result)
+ goto AKM_fail;
+
+ /* write 1 to ASTC register */
+ result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST);
+ if (result)
+ goto AKM_fail;
+ /* set self test mode */
+ result = inv_secondary_write(mode, DATA_AKM_MODE_ST);
+ if (result)
+ goto AKM_fail;
+ counter = DEF_ST_COMPASS_TRY_TIMES;
+ while (counter > 0) {
+ usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX);
+ result = inv_secondary_read(REG_AKM_STATUS, 1, data);
+ if (result)
+ goto AKM_fail;
+ if ((data[0] & DATA_AKM_DRDY) == 0)
+ counter--;
+ else
+ counter = 0;
+ }
+ if ((data[0] & DATA_AKM_DRDY) == 0) {
+ result = -EINVAL;
+ goto AKM_fail;
+ }
+ result = inv_secondary_read(REG_AKM_MEASURE_DATA,
+ BYTES_PER_SENSOR, data);
+ if (result)
+ goto AKM_fail;
+
+ x = le16_to_cpup((__le16 *)(&data[0]));
+ y = le16_to_cpup((__le16 *)(&data[2]));
+ z = le16_to_cpup((__le16 *)(&data[4]));
+ x = ((x * (sens[0] + 128)) >> 8);
+ y = ((y * (sens[1] + 128)) >> 8);
+ z = ((z * (sens[2] + 128)) >> 8);
+ if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) {
+ result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl);
+ if (result)
+ goto AKM_fail;
+ if (0 == (cntl & DATA_AKM8963_BIT)) {
+ x <<= DEF_ST_COMPASS_8963_SHIFT;
+ y <<= DEF_ST_COMPASS_8963_SHIFT;
+ z <<= DEF_ST_COMPASS_8963_SHIFT;
+ }
+ }
+ result = -EINVAL;
+ if (x > st->slave_compass->st_upper[X] ||
+ x < st->slave_compass->st_lower[X])
+ goto AKM_fail;
+ if (y > st->slave_compass->st_upper[Y] ||
+ y < st->slave_compass->st_lower[Y])
+ goto AKM_fail;
+ if (z > st->slave_compass->st_upper[Z] ||
+ z < st->slave_compass->st_lower[Z])
+ goto AKM_fail;
+ result = 0;
+AKM_fail:
+ /*write 0 to ASTC register */
+ result |= inv_secondary_write(REG_AKM_ST_CTRL, 0);
+ /*set to power down mode */
+ result |= inv_secondary_write(mode, DATA_AKM_MODE_PD);
+ /*restore to non-bypass mode */
+ result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+}
+
+/*
+ * inv_write_akm_scale() - Configure the akm scale range.
+ */
+static int inv_write_akm_scale(struct inv_mpu_state *st, int data)
+{
+ char d, en;
+ int result;
+
+ if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id)
+ return 0;
+ en = !!data;
+ if (st->slave_compass->scale == en)
+ return 0;
+ d = (DATA_AKM_MODE_SM | (en << DATA_AKM8963_SCALE_SHIFT));
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, d);
+ if (result)
+ return result;
+ st->slave_compass->scale = en;
+
+ return 0;
+}
+
+/*
+ * inv_read_akm_scale() - show AKM scale.
+ */
+static int inv_read_akm_scale(struct inv_mpu_state *st, int *scale)
+{
+ if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8975_SCALE;
+ else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id)
+ *scale = DATA_AKM8972_SCALE;
+ else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id)
+ if (st->slave_compass->scale)
+ *scale = DATA_AKM8963_SCALE1;
+ else
+ *scale = DATA_AKM8963_SCALE0;
+ else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id)
+ *scale = DATA_AK09911_SCALE;
+ else
+ return -EINVAL;
+
+ return IIO_VAL_INT;
+}
+
+static int inv_suspend_akm(struct inv_mpu_state *st)
+{
+ int result;
+
+ /* slave 0 is disabled */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (result)
+ return result;
+ /* slave 1 is disabled */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0);
+
+ return result;
+}
+
+static int inv_resume_akm(struct inv_mpu_state *st)
+{
+ int result;
+ u8 reg_addr, bytes;
+
+ /* slave 0 is used to read data from compass */
+ /*read mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.secondary_i2c_addr);
+ if (result)
+ return result;
+ /* AKM status register address is 1 */
+ if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) {
+ if (st->chip_config.dmp_on) {
+ reg_addr = REG_AK09911_DMP_READ;
+ bytes = DATA_AKM_BYTES_DMP;
+ } else {
+ reg_addr = REG_AK09911_STATUS1;
+ bytes = DATA_AKM_BYTES;
+ }
+ } else {
+ if (st->chip_config.dmp_on) {
+ reg_addr = REG_AKM_INFO;
+ bytes = DATA_AKM_BYTES_DMP;
+ } else {
+ reg_addr = REG_AKM_STATUS;
+ bytes = DATA_AKM_BYTES;
+ }
+ }
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, reg_addr);
+ if (result)
+ return result;
+
+ /* slave 0 is enabled, read 10 or 8 bytes from here */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_SLV_EN | bytes);
+ if (result)
+ return result;
+ /* slave 1 is enabled, write byte length is 1 */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ return result;
+}
+
+/*
+ * inv_write_mlx_scale() - Configure the mlx90399 scale range.
+ */
+static int inv_write_mlx_scale(struct inv_mpu_state *st, int data)
+{
+ st->slave_compass->scale = data;
+ return 0;
+}
+
+/*
+ * inv_read_mlx_scale() - show mlx90399 scale.
+ */
+static int inv_read_mlx_scale(struct inv_mpu_state *st, int *scale)
+{
+ *scale = st->slave_compass->scale;
+ return IIO_VAL_INT;
+}
+
+static int inv_i2c_read_mlx(struct inv_mpu_state *st, u16 i2c_addr,
+ u16 length, u8 *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = I2C_M_RD;
+ msgs[0].buf = data;
+ msgs[0].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 1);
+
+ if (res < 1) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+
+ return res;
+}
+
+static int inv_i2c_write_mlx(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 data)
+{
+ u8 tmp[1];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = data;
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 1;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_read_reg_mlx(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u16 *val)
+{
+ u8 tmp[10];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = 0x50;
+ tmp[1] = (reg << 2);
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ }
+ res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp);
+ if (res)
+ return res;
+ *val = ((tmp[1] << 8) | tmp[2]);
+
+ return res;
+}
+
+static int inv_i2c_write_mlx_reg(struct inv_mpu_state *st,
+ u16 i2c_addr, int reg, u16 d)
+{
+ u8 tmp[10];
+ struct i2c_msg msg;
+ int res;
+
+ /* write register command, writing volatile memory */
+ tmp[0] = 0x60;
+ tmp[1] = ((d >> 8) & 0xff);
+ tmp[2] = (d & 0xff);
+ tmp[3] = (reg << 2);
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 4;
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ }
+ /* read status */
+ res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp);
+
+ return res;
+}
+
+static int inv_write_mlx_cmd(struct inv_mpu_state *st, u8 cmd)
+{
+ int result;
+ u8 d[10];
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+ result = inv_i2c_write_mlx(st, addr, cmd);
+ if (result)
+ return result;
+ /* read back status byte */
+ result = inv_i2c_read_mlx(st, addr, 10, d);
+
+ return result;
+}
+
+static int inv_read_mlx_z_axis(struct inv_mpu_state *st, s16 *z)
+{
+ int result;
+ u8 d[10];
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* measure z axis */
+ result = inv_write_mlx_cmd(st, 0x39);
+ if (result)
+ return result;
+ msleep(100);
+ /* read z axis */
+ result = inv_i2c_write_mlx(st, addr, 0x49);
+ if (result)
+ return result;
+ /* read back status byte */
+ result = inv_i2c_read_mlx(st, addr, 10, d);
+ if (result)
+ return result;
+ if ((d[0] & 0x3) == 1)
+ *z = (short)((d[3] << 8) + d[4]);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int inv_write_mlx_reg(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+ u16 r_val;
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* write register 0.
+ set GAIN_SEL as 7;
+ set HALL_CONF as 0xC. */
+ result = inv_i2c_write_mlx_reg(st, addr, 0, 0x7c);
+ if (result)
+ return result;
+ /* write register 2.
+ set resolution is zero for all axes;
+ set DIGI filter as 6.
+ set OSR as 0.
+ set OSR2 as 0. */
+ result = inv_i2c_write_mlx_reg(st, addr, 2, 0x18);
+ if (result)
+ return result;
+ /* read register 1 */
+ result = inv_i2c_read_reg_mlx(st, addr, 1, &r_val);
+ if (result)
+ return result;
+ /* enable temp comp */
+ r_val |= 0x400;
+ result = inv_i2c_write_mlx_reg(st, addr, 1, r_val);
+ /* the value should be kept in the volatile memory */
+
+ return result;
+}
+
+static int inv_check_mlx_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+ s16 meas_ref, meas_coil;
+ u16 diff, r_val;
+
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result) {
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ return result;
+ }
+
+ addr = st->plat_data.secondary_i2c_addr;
+
+ /* fake read to flush the previous data */
+ result = inv_read_mlx_z_axis(st, &meas_ref);
+
+ result = inv_read_mlx_z_axis(st, &meas_ref);
+ if (result)
+ return result;
+
+ /* read register 1 */
+ result = inv_i2c_read_reg_mlx(st, addr, 0, &r_val);
+ if (result)
+ return result;
+ /* enable self test */
+ r_val |= 0x100;
+ result = inv_i2c_write_mlx_reg(st, addr, 0, r_val);
+ if (result)
+ return result;
+ msleep(200);
+ result = inv_read_mlx_z_axis(st, &meas_coil);
+ if (result)
+ return result;
+ result = inv_write_mlx_cmd(st, 0xD0);
+ if (result)
+ return result;
+ result = inv_write_mlx_reg(st);
+ if (result)
+ return result;
+ diff = abs(meas_ref - meas_coil);
+ if (diff < 25 || diff > 300)
+ result = 1;
+
+ /*restore to non-bypass mode */
+ result |= inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+
+ return result;
+}
+
+/*
+ * inv_setup_compass_mlx() - Configure akm series compass.
+ */
+static int inv_setup_compass_mlx(struct inv_mpu_state *st)
+{
+ int result;
+ int addr;
+
+ addr = st->plat_data.secondary_i2c_addr;
+ /* set to bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (result)
+ return result;
+ result = inv_write_mlx_reg(st);
+ if (result)
+ return result;
+
+ /*restore to non-bypass mode */
+ result = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (result)
+ return result;
+
+ /*setup master mode and master clock and ES bit*/
+ result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (result)
+ return result;
+
+ /* slave 0 used to write read measurement command, write mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, addr);
+ if (result)
+ return result;
+ /* ignore the register address, send out data only */
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV0_DO,
+ DATA_MLX_CMD_READ_MEASURE);
+ if (result)
+ return result;
+
+ /* slave 1 used to read status bytes and data of read measurement */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR,
+ INV_MPU_BIT_I2C_READ | addr);
+ if (result)
+ return result;
+ /* slave 2 used to write single measurement command, write mode */
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, addr);
+ if (result)
+ return result;
+ /* ignore the register address, send out data only */
+ result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV2_DO,
+ DATA_MLX_CMD_SINGLE_MEASURE);
+ if (result)
+ return result;
+ /* slave 3 used to read status bytes and data of read measurement */
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR,
+ INV_MPU_BIT_I2C_READ | addr);
+
+ st->slave_compass->scale = DATA_MLX_SCALE;
+
+ return result;
+}
+
+static int inv_suspend_mlx(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0);
+ if (result)
+ return result;
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0);
+
+ return result;
+}
+
+static int inv_resume_mlx(struct inv_mpu_state *st)
+{
+ int result;
+
+ /* enable, ignore register, write 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+ if (result)
+ return result;
+
+ /* enable, ignore register, read 9 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ DATA_MLX_READ_DATA_BYTES);
+ if (result)
+ return result;
+ /* enable, ignore register, write 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+ if (result)
+ return result;
+
+ /* enable, ignore register, read 1 bytes */
+ result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ INV_MPU_BIT_REG_DIS |
+ 1);
+
+ return result;
+}
+
+static struct inv_mpu_slave slave_akm = {
+ .suspend = inv_suspend_akm,
+ .resume = inv_resume_akm,
+ .get_scale = inv_read_akm_scale,
+ .set_scale = inv_write_akm_scale,
+ .self_test = inv_check_akm_self_test,
+ .setup = inv_setup_compass_akm,
+ .read_data = inv_akm_read_data,
+ .rate_scale = AKM_RATE_SCALE,
+ .min_read_time = DATA_AKM_MIN_READ_TIME,
+};
+
+static struct inv_mpu_slave slave_mlx90399 = {
+ .suspend = inv_suspend_mlx,
+ .resume = inv_resume_mlx,
+ .get_scale = inv_read_mlx_scale,
+ .set_scale = inv_write_mlx_scale,
+ .self_test = inv_check_mlx_self_test,
+ .setup = inv_setup_compass_mlx,
+ .read_data = inv_mlx_read_data,
+ .rate_scale = MLX_RATE_SCALE,
+ .min_read_time = DATA_MLX_MIN_READ_TIME,
+};
+
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st)
+{
+ switch (st->plat_data.sec_slave_id) {
+ case COMPASS_ID_AK8975:
+ case COMPASS_ID_AK8972:
+ case COMPASS_ID_AK8963:
+ case COMPASS_ID_AK09911:
+ st->slave_compass = &slave_akm;
+ break;
+ case COMPASS_ID_MLX90399:
+ st->slave_compass = &slave_mlx90399;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return st->slave_compass->setup(st);
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c
new file mode 100644
index 00000000000..7843556c732
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c
@@ -0,0 +1,510 @@
+/*
+* 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 "inv_mpu_iio.h"
+
+/* Constants */
+#define SHIFT_RIGHT_4_POSITION 4
+#define SHIFT_LEFT_2_POSITION 2
+#define SHIFT_LEFT_4_POSITION 4
+#define SHIFT_LEFT_5_POSITION 5
+#define SHIFT_LEFT_8_POSITION 8
+#define SHIFT_LEFT_12_POSITION 12
+#define SHIFT_LEFT_16_POSITION 16
+
+/* Sensor Specific constants */
+#define BMP280_SLEEP_MODE 0x00
+#define BMP280_FORCED_MODE 0x01
+#define BMP280_NORMAL_MODE 0x03
+#define BMP280_SOFT_RESET 0xB6
+
+#define BMP280_DELAYTIME_MS_NONE 0
+#define BMP280_DELAYTIME_MS_5 5
+#define BMP280_DELAYTIME_MS_6 6
+#define BMP280_DELAYTIME_MS_8 8
+#define BMP280_DELAYTIME_MS_12 12
+#define BMP280_DELAYTIME_MS_22 22
+#define BMP280_DELAYTIME_MS_38 38
+
+#define BMP280_OVERSAMPLING_SKIPPED 0x00
+#define BMP280_OVERSAMPLING_1X 0x01
+#define BMP280_OVERSAMPLING_2X 0x02
+#define BMP280_OVERSAMPLING_4X 0x03
+#define BMP280_OVERSAMPLING_8X 0x04
+#define BMP280_OVERSAMPLING_16X 0x05
+
+#define BMP280_ULTRALOWPOWER_MODE 0x00
+#define BMP280_LOWPOWER_MODE 0x01
+#define BMP280_STANDARDRESOLUTION_MODE 0x02
+#define BMP280_HIGHRESOLUTION_MODE 0x03
+#define BMP280_ULTRAHIGHRESOLUTION_MODE 0x04
+
+#define BMP280_ULTRALOWPOWER_OSRS_P BMP280_OVERSAMPLING_1X
+#define BMP280_ULTRALOWPOWER_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_LOWPOWER_OSRS_P BMP280_OVERSAMPLING_2X
+#define BMP280_LOWPOWER_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_STANDARDRESOLUTION_OSRS_P BMP280_OVERSAMPLING_4X
+#define BMP280_STANDARDRESOLUTION_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_HIGHRESOLUTION_OSRS_P BMP280_OVERSAMPLING_8X
+#define BMP280_HIGHRESOLUTION_OSRS_T BMP280_OVERSAMPLING_1X
+
+#define BMP280_ULTRAHIGHRESOLUTION_OSRS_P BMP280_OVERSAMPLING_16X
+#define BMP280_ULTRAHIGHRESOLUTION_OSRS_T BMP280_OVERSAMPLING_2X
+
+#define BMP280_FILTERCOEFF_OFF 0x00
+#define BMP280_FILTERCOEFF_2 0x01
+#define BMP280_FILTERCOEFF_4 0x02
+#define BMP280_FILTERCOEFF_8 0x03
+#define BMP280_FILTERCOEFF_16 0x04
+
+/*calibration parameters */
+#define BMP280_DIG_T1_LSB_REG 0x88
+#define BMP280_DIG_T1_MSB_REG 0x89
+#define BMP280_DIG_T2_LSB_REG 0x8A
+#define BMP280_DIG_T2_MSB_REG 0x8B
+#define BMP280_DIG_T3_LSB_REG 0x8C
+#define BMP280_DIG_T3_MSB_REG 0x8D
+#define BMP280_DIG_P1_LSB_REG 0x8E
+#define BMP280_DIG_P1_MSB_REG 0x8F
+#define BMP280_DIG_P2_LSB_REG 0x90
+#define BMP280_DIG_P2_MSB_REG 0x91
+#define BMP280_DIG_P3_LSB_REG 0x92
+#define BMP280_DIG_P3_MSB_REG 0x93
+#define BMP280_DIG_P4_LSB_REG 0x94
+#define BMP280_DIG_P4_MSB_REG 0x95
+#define BMP280_DIG_P5_LSB_REG 0x96
+#define BMP280_DIG_P5_MSB_REG 0x97
+#define BMP280_DIG_P6_LSB_REG 0x98
+#define BMP280_DIG_P6_MSB_REG 0x99
+#define BMP280_DIG_P7_LSB_REG 0x9A
+#define BMP280_DIG_P7_MSB_REG 0x9B
+#define BMP280_DIG_P8_LSB_REG 0x9C
+#define BMP280_DIG_P8_MSB_REG 0x9D
+#define BMP280_DIG_P9_LSB_REG 0x9E
+#define BMP280_DIG_P9_MSB_REG 0x9F
+
+#define BMP280_CHIPID_REG 0xD0 /*Chip ID Register */
+#define BMP280_RESET_REG 0xE0 /*Softreset Register */
+#define BMP280_STATUS_REG 0xF3 /*Status Register */
+#define BMP280_CTRLMEAS_REG 0xF4 /*Ctrl Measure Register */
+#define BMP280_CONFIG_REG 0xF5 /*Configuration Register */
+#define BMP280_PRESSURE_MSB_REG 0xF7 /*Pressure MSB Register */
+#define BMP280_PRESSURE_LSB_REG 0xF8 /*Pressure LSB Register */
+#define BMP280_PRESSURE_XLSB_REG 0xF9 /*Pressure XLSB Register */
+#define BMP280_TEMPERATURE_MSB_REG 0xFA /*Temperature MSB Reg */
+#define BMP280_TEMPERATURE_LSB_REG 0xFB /*Temperature LSB Reg */
+#define BMP280_TEMPERATURE_XLSB_REG 0xFC /*Temperature XLSB Reg */
+
+/* Status Register */
+#define BMP280_STATUS_REG_MEASURING__POS 3
+#define BMP280_STATUS_REG_MEASURING__MSK 0x08
+#define BMP280_STATUS_REG_MEASURING__LEN 1
+#define BMP280_STATUS_REG_MEASURING__REG BMP280_STATUS_REG
+
+#define BMP280_STATUS_REG_IMUPDATE__POS 0
+#define BMP280_STATUS_REG_IMUPDATE__MSK 0x01
+#define BMP280_STATUS_REG_IMUPDATE__LEN 1
+#define BMP280_STATUS_REG_IMUPDATE__REG BMP280_STATUS_REG
+
+/* Control Measurement Register */
+#define BMP280_CTRLMEAS_REG_OSRST__POS 5
+#define BMP280_CTRLMEAS_REG_OSRST__MSK 0xE0
+#define BMP280_CTRLMEAS_REG_OSRST__LEN 3
+#define BMP280_CTRLMEAS_REG_OSRST__REG BMP280_CTRLMEAS_REG
+
+#define BMP280_CTRLMEAS_REG_OSRSP__POS 2
+#define BMP280_CTRLMEAS_REG_OSRSP__MSK 0x1C
+#define BMP280_CTRLMEAS_REG_OSRSP__LEN 3
+#define BMP280_CTRLMEAS_REG_OSRSP__REG BMP280_CTRLMEAS_REG
+
+#define BMP280_CTRLMEAS_REG_MODE__POS 0
+#define BMP280_CTRLMEAS_REG_MODE__MSK 0x03
+#define BMP280_CTRLMEAS_REG_MODE__LEN 2
+#define BMP280_CTRLMEAS_REG_MODE__REG BMP280_CTRLMEAS_REG
+
+/* Configuation Register */
+#define BMP280_CONFIG_REG_TSB__POS 5
+#define BMP280_CONFIG_REG_TSB__MSK 0xE0
+#define BMP280_CONFIG_REG_TSB__LEN 3
+#define BMP280_CONFIG_REG_TSB__REG BMP280_CONFIG_REG
+
+#define BMP280_CONFIG_REG_FILTER__POS 2
+#define BMP280_CONFIG_REG_FILTER__MSK 0x1C
+#define BMP280_CONFIG_REG_FILTER__LEN 3
+#define BMP280_CONFIG_REG_FILTER__REG BMP280_CONFIG_REG
+
+#define BMP280_CONFIG_REG_SPI3WEN__POS 0
+#define BMP280_CONFIG_REG_SPI3WEN__MSK 0x01
+#define BMP280_CONFIG_REG_SPI3WEN__LEN 1
+#define BMP280_CONFIG_REG_SPI3WEN__REG BMP280_CONFIG_REG
+
+/* Data Register */
+#define BMP280_PRESSURE_XLSB_REG_DATA__POS 4
+#define BMP280_PRESSURE_XLSB_REG_DATA__MSK 0xF0
+#define BMP280_PRESSURE_XLSB_REG_DATA__LEN 4
+#define BMP280_PRESSURE_XLSB_REG_DATA__REG BMP280_PRESSURE_XLSB_REG
+
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__POS 4
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__MSK 0xF0
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__LEN 4
+#define BMP280_TEMPERATURE_XLSB_REG_DATA__REG BMP280_TEMPERATURE_XLSB_REG
+
+#define BMP280_RATE_SCALE 34
+#define DATA_BMP280_MIN_READ_TIME (32 * NSEC_PER_MSEC)
+#define BMP280_DATA_BYTES 6
+#define FAKE_DATA_NUM_BYTES 10
+
+/** this structure holds all device specific calibration parameters */
+struct bmp280_calibration_param_t {
+ u32 dig_T1;
+ s32 dig_T2;
+ s32 dig_T3;
+ u32 dig_P1;
+ s32 dig_P2;
+ s32 dig_P3;
+ s32 dig_P4;
+ s32 dig_P5;
+ s32 dig_P6;
+ s32 dig_P7;
+ s32 dig_P8;
+ s32 dig_P9;
+
+ s32 t_fine;
+};
+/** BMP280 image registers data structure */
+struct bmp280_t {
+ struct bmp280_calibration_param_t cal_param;
+
+ u8 chip_id;
+ u8 dev_addr;
+
+ u8 waittime;
+
+ u8 osrs_t;
+ u8 osrs_p;
+};
+static struct bmp280_t bmp280;
+
+static int bmp280_get_calib_param(struct inv_mpu_state *st)
+{
+ u8 d[24];
+ int r;
+
+ r = inv_aux_read(BMP280_DIG_T1_LSB_REG, 24, d);
+ if (r)
+ return r;
+
+ bmp280.cal_param.dig_T1 = (u16)((((u16)((u8)d[1])) <<
+ SHIFT_LEFT_8_POSITION) | d[0]);
+ bmp280.cal_param.dig_T2 = (s16)((((s16)((s8)d[3])) <<
+ SHIFT_LEFT_8_POSITION) | d[2]);
+ bmp280.cal_param.dig_T3 = (s16)((((s16)((s8)d[5])) <<
+ SHIFT_LEFT_8_POSITION) | d[4]);
+ bmp280.cal_param.dig_P1 = (u16)((((u16)((u8)d[7])) <<
+ SHIFT_LEFT_8_POSITION) | d[6]);
+ bmp280.cal_param.dig_P2 = (s16)((((s16)((s8)d[9])) <<
+ SHIFT_LEFT_8_POSITION) | d[8]);
+ bmp280.cal_param.dig_P3 = (s16)((((s16)((s8)d[11])) <<
+ SHIFT_LEFT_8_POSITION) | d[10]);
+ bmp280.cal_param.dig_P4 = (s16)((((s16)((s8)d[13])) <<
+ SHIFT_LEFT_8_POSITION) | d[12]);
+ bmp280.cal_param.dig_P5 = (s16)((((s16)((s8)d[15])) <<
+ SHIFT_LEFT_8_POSITION) | d[14]);
+ bmp280.cal_param.dig_P6 = (s16)((((s16)((s8)d[17])) <<
+ SHIFT_LEFT_8_POSITION) | d[16]);
+ bmp280.cal_param.dig_P7 = (s16)((((s16)((s8)d[19])) <<
+ SHIFT_LEFT_8_POSITION) | d[18]);
+ bmp280.cal_param.dig_P8 = (s16)((((s16)((s8)d[21])) <<
+ SHIFT_LEFT_8_POSITION) | d[20]);
+ bmp280.cal_param.dig_P9 = (s16)((((s16)((s8)d[23])) <<
+ SHIFT_LEFT_8_POSITION) | d[22]);
+
+ return 0;
+}
+
+static int inv_setup_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+ u8 d[10];
+
+ /* set to bypass mode */
+ r = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config | BIT_BYPASS_EN);
+ if (r)
+ return r;
+ /* issue soft reset */
+ r = inv_aux_write(BMP280_RESET_REG, BMP280_SOFT_RESET);
+ if (r)
+ return r;
+ msleep(100);
+ r = inv_aux_read(BMP280_CHIPID_REG, 1, d);
+ if (r)
+ return r;
+ /* set pressure as ultra high resolution */
+ bmp280.osrs_t = BMP280_ULTRAHIGHRESOLUTION_OSRS_T;
+ bmp280.osrs_p = BMP280_ULTRAHIGHRESOLUTION_OSRS_P;
+
+ /* set IIR filter as 4 */
+ r = inv_aux_write(BMP280_CONFIG_REG_FILTER__REG,
+ BMP280_FILTERCOEFF_16 << SHIFT_LEFT_2_POSITION);
+ if (r)
+ return r;
+ r = bmp280_get_calib_param(st);
+ if (r)
+ return r;
+
+ /*restore to non-bypass mode */
+ r = inv_i2c_single_write(st, REG_INT_PIN_CFG,
+ st->plat_data.int_config);
+ if (r)
+ return r;
+
+ /* setup master mode and master clock and ES bit */
+ r = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES);
+ if (r)
+ return r;
+ /*slave 3 is used for pressure mode change only*/
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR,
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* pressure sensor mode register address */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_REG, BMP280_CTRLMEAS_REG);
+ if (r)
+ return r;
+ d[0] = (bmp280.osrs_t << SHIFT_LEFT_5_POSITION) +
+ (bmp280.osrs_p << SHIFT_LEFT_2_POSITION) +
+ BMP280_FORCED_MODE;
+ r = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV3_DO, d[0]);
+
+ return r;
+}
+
+static int inv_check_bmp280_self_test(struct inv_mpu_state *st)
+{
+ return 0;
+}
+static int inv_write_bmp280_scale(struct inv_mpu_state *st, int data)
+{
+ return 0;
+}
+static int inv_read_bmp280_scale(struct inv_mpu_state *st, int *scale)
+{
+ return 0;
+}
+
+static int inv_resume_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+
+ if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) {
+ /* if compass is disabled, read fake data for DMP */
+ /*read mode */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* read calibration data as the fake data */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_REG,
+ BMP280_DIG_T1_LSB_REG);
+ if (r)
+ return r;
+ /* slave 0 is enabled, read 10 bytes from here */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL,
+ INV_MPU_BIT_SLV_EN |
+ FAKE_DATA_NUM_BYTES);
+ }
+
+ /* slave 2 is used to read data from pressure sensor */
+ /*read mode */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR,
+ INV_MPU_BIT_I2C_READ |
+ st->plat_data.aux_i2c_addr);
+ if (r)
+ return r;
+ /* start from pressure sensor */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_REG,
+ BMP280_PRESSURE_MSB_REG);
+ if (r)
+ return r;
+
+ /* slave 2 is enabled, read 6 bytes from here */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL,
+ INV_MPU_BIT_SLV_EN | BMP280_DATA_BYTES);
+ if (r)
+ return r;
+ /* slave 3 is enabled, write byte length is 1 */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ return r;
+}
+
+static int inv_suspend_bmp280(struct inv_mpu_state *st)
+{
+ int r;
+
+ if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) {
+ /* slave 0 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0);
+ if (r)
+ return r;
+ }
+
+ /* slave 2 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0);
+ if (r)
+ return r;
+ /* slave 3 is disabled */
+ r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0);
+
+ return r;
+}
+
+static s32 bmp280_compensate_T_int32(s32 adc_t)
+{
+ s32 v_x1_u32r = 0;
+ s32 v_x2_u32r = 0;
+ s32 temperature = 0;
+
+ v_x1_u32r = ((((adc_t >> 3) - ((s32)
+ bmp280.cal_param.dig_T1 << 1))) *
+ ((s32)bmp280.cal_param.dig_T2)) >> 11;
+ v_x2_u32r = (((((adc_t >> 4) -
+ ((s32)bmp280.cal_param.dig_T1)) * ((adc_t >> 4) -
+ ((s32)bmp280.cal_param.dig_T1))) >> 12) *
+ ((s32)bmp280.cal_param.dig_T3)) >> 14;
+ bmp280.cal_param.t_fine = v_x1_u32r + v_x2_u32r;
+ temperature = (bmp280.cal_param.t_fine * 5 + 128) >> 8;
+
+ return temperature;
+}
+
+
+static u32 bmp280_compensate_P_int32(s32 adc_p)
+{
+ s32 v_x1_u32r = 0;
+ s32 v_x2_u32r = 0;
+ u32 pressure = 0;
+
+ v_x1_u32r = (((s32)bmp280.cal_param.t_fine) >> 1) -
+ (s32)64000;
+ v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) *
+ ((s32)bmp280.cal_param.dig_P6);
+ v_x2_u32r = v_x2_u32r + ((v_x1_u32r *
+ ((s32)bmp280.cal_param.dig_P5)) << 1);
+ v_x2_u32r = (v_x2_u32r >> 2) +
+ (((s32)bmp280.cal_param.dig_P4) << 16);
+ v_x1_u32r = (((bmp280.cal_param.dig_P3 * (((v_x1_u32r >> 2) *
+ (v_x1_u32r >> 2)) >> 13)) >> 3) +
+ ((((s32)bmp280.cal_param.dig_P2) *
+ v_x1_u32r) >> 1)) >> 18;
+ v_x1_u32r = ((((32768+v_x1_u32r)) *
+ ((s32)bmp280.cal_param.dig_P1)) >> 15);
+ /* Avoid exception caused by division by zero */
+ if (v_x1_u32r == 0)
+ return 0;
+ pressure = (((u32)(((s32)1048576) - adc_p) -
+ (v_x2_u32r >> 12))) * 3125;
+ if (pressure < 0x80000000)
+ pressure = (pressure << 1) / ((u32)v_x1_u32r);
+ else
+ pressure = (pressure / (u32)v_x1_u32r) * 2;
+ v_x1_u32r = (((s32)bmp280.cal_param.dig_P9) *
+ ((s32)(((pressure >> 3) * (pressure >> 3)) >> 13)))
+ >> 12;
+ v_x2_u32r = (((s32)(pressure >> 2)) *
+ ((s32)bmp280.cal_param.dig_P8)) >> 13;
+ pressure = (u32)((s32)pressure +
+ ((v_x1_u32r + v_x2_u32r + bmp280.cal_param.dig_P7) >> 4));
+
+ return pressure;
+}
+
+static int inv_bmp280_read_data(struct inv_mpu_state *st, short *o)
+{
+ int r, i;
+ u8 d[BMP280_DATA_BYTES], reg_addr;
+ s32 upressure, utemperature;
+
+ if (st->chip_config.dmp_on) {
+ for (i = 0; i < 6; i++)
+ d[i] = st->fifo_data[i];
+ } else {
+ if (st->sensor[SENSOR_COMPASS].on)
+ reg_addr = REG_EXT_SENS_DATA_08;
+ else
+ reg_addr = REG_EXT_SENS_DATA_00;
+ r = inv_i2c_read(st, reg_addr, BMP280_DATA_BYTES, d);
+ if (r)
+ return r;
+ }
+ /* pressure */
+ upressure = (s32)((((s32)(d[0]))
+ << SHIFT_LEFT_12_POSITION) | (((u32)(d[1]))
+ << SHIFT_LEFT_4_POSITION) | ((u32)d[2] >>
+ SHIFT_RIGHT_4_POSITION));
+
+ /* Temperature */
+ utemperature = (s32)(((
+ (s32) (d[3])) << SHIFT_LEFT_12_POSITION) |
+ (((u32)(d[4])) << SHIFT_LEFT_4_POSITION)
+ | ((u32)d[5] >> SHIFT_RIGHT_4_POSITION));
+
+ bmp280_compensate_T_int32(utemperature);
+ r = bmp280_compensate_P_int32(upressure);
+ o[0] = 0;
+ o[1] = (r >> 16);
+ o[2] = (r & 0xffff);
+
+ return 0;
+}
+
+static struct inv_mpu_slave slave_bmp280 = {
+ .suspend = inv_suspend_bmp280,
+ .resume = inv_resume_bmp280,
+ .get_scale = inv_read_bmp280_scale,
+ .set_scale = inv_write_bmp280_scale,
+ .self_test = inv_check_bmp280_self_test,
+ .setup = inv_setup_bmp280,
+ .read_data = inv_bmp280_read_data,
+ .rate_scale = BMP280_RATE_SCALE,
+ .min_read_time = DATA_BMP280_MIN_READ_TIME,
+};
+
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st)
+{
+ switch (st->plat_data.aux_slave_id) {
+ case PRESSURE_ID_BMP280:
+ st->slave_pressure = &slave_bmp280;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return st->slave_pressure->setup(st);
+}
+
diff --git a/drivers/staging/iio/imu/inv_mpu/olio_specials.h b/drivers/staging/iio/imu/inv_mpu/olio_specials.h
new file mode 100644
index 00000000000..e882c3eea86
--- /dev/null
+++ b/drivers/staging/iio/imu/inv_mpu/olio_specials.h
@@ -0,0 +1,17 @@
+/* olio_specials.h - header specific to Olio Devices */
+
+/*
+ * Description:
+ * ============
+ * This file holds a couple of #defines needed by the Olio H1 to set the
+ * low power states the way we want them.
+ */
+
+/*
+ * Modification History:
+ * =====================
+ * 01a, 20150331, mfj Created
+ */
+
+#define OLIO_DEF_FREQ 0x03
+#define OLIO_DEF_THRES 0xE0 /* 224 */
diff --git a/drivers/staging/iio/inv_test/Kconfig b/drivers/staging/iio/inv_test/Kconfig
new file mode 100644
index 00000000000..86c30bd8a63
--- /dev/null
+++ b/drivers/staging/iio/inv_test/Kconfig
@@ -0,0 +1,13 @@
+#
+# Kconfig for Invensense IIO testing hooks
+#
+
+config INV_TESTING
+ boolean "Invensense IIO testing hooks"
+ depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
+ default n
+ help
+ This flag enables display of additional testing information from the
+ Invensense IIO drivers.
+ It also enables the I2C counters facility to perform IO profiling.
+ Some additional sysfs entries will appear when this flag is enabled.
diff --git a/drivers/staging/iio/inv_test/Makefile b/drivers/staging/iio/inv_test/Makefile
new file mode 100644
index 00000000000..4f0edd3de90
--- /dev/null
+++ b/drivers/staging/iio/inv_test/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense IIO testing hooks.
+#
+
+obj-$(CONFIG_INV_TESTING) += inv_counters.o
+
diff --git a/drivers/staging/iio/inv_test/inv_counters.c b/drivers/staging/iio/inv_test/inv_counters.c
new file mode 100644
index 00000000000..3b26ca97284
--- /dev/null
+++ b/drivers/staging/iio/inv_test/inv_counters.c
@@ -0,0 +1,154 @@
+/*
+ * @file inv_counters.c
+ * @brief Exports i2c read write counts through sysfs
+ *
+ * @version 0.1
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+ uint32_t i2c_tempreads;
+ uint32_t i2c_mpureads;
+ uint32_t i2c_mpuwrites;
+ uint32_t i2c_accelreads;
+ uint32_t i2c_accelwrites;
+ uint32_t i2c_compassreads;
+ uint32_t i2c_compasswrites;
+ uint32_t i2c_compassirq;
+ uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+static ssize_t i2c_counters_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+ jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+ mpu_irq ? kstat_irqs(mpu_irq) : 0,
+ Counters.i2c_tempreads,
+ Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+ accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+ Counters.i2c_accelreads, Counters.i2c_accelwrites,
+ compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+ Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+ switch (type) {
+ case IRQ_MPU:
+ mpu_irq = irq;
+ break;
+ case IRQ_ACCEL:
+ accel_irq = irq;
+ break;
+ case IRQ_COMPASS:
+ compass_irq = irq;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
+
+void inv_iio_counters_tempread(int count)
+{
+ Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+ Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+ Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+ Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+ Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+ Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+ Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+ Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+ Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+ __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+ __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+ .name = "inv_counters",
+ .owner = THIS_MODULE,
+ .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+ memset(&Counters, 0, sizeof(Counters));
+
+ return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+ class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/staging/iio/inv_test/inv_counters.h b/drivers/staging/iio/inv_test/inv_counters.h
new file mode 100644
index 00000000000..d60dac9d97b
--- /dev/null
+++ b/drivers/staging/iio/inv_test/inv_counters.h
@@ -0,0 +1,72 @@
+/*
+ * @file inv_counters.h
+ * @brief Debug file to keep track of various counters for the InvenSense
+ * sensor drivers.
+ *
+ * @version 0.1
+ */
+
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_INV_TESTING
+
+enum irqtype {
+ IRQ_MPU,
+ IRQ_ACCEL,
+ IRQ_COMPASS
+};
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c
index 217339dd7a9..67f48dd8dca 100644
--- a/drivers/usb/phy/phy-ulpi.c
+++ b/drivers/usb/phy/phy-ulpi.c
@@ -47,6 +47,8 @@ struct ulpi_info {
static struct ulpi_info ulpi_ids[] = {
ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
+ ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
+ ULPI_INFO(ULPI_ID(0x0451, 0x1508), "TI TUSB1211"),
};
static int ulpi_set_otg_flags(struct usb_phy *phy)
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index af8f97ebbe0..92f8d32b63e 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -16,7 +16,13 @@ config PANEL_TFP410
help
Driver for TFP410 DPI-to-DVI chip. The driver uses i2c to read EDID
information from the monitor.
-
+
+config PANEL_ILI_9342
+ tristate "ili9342 display controller"
+ depends on OMAP2_DSS_DPI && SPI
+ help
+ LCD Display controller used on the Olio H1
+
config PANEL_LGPHILIPS_LB035Q02
tristate "LG.Philips LB035Q02 LCD Panel"
depends on OMAP2_DSS_DPI && SPI
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index abc0d9dae56..3fb5d3ac750 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o
obj-$(CONFIG_PANEL_TFP410) += panel-tfp410.o
+obj-$(CONFIG_PANEL_ILI_9342) += panel-ili9342.o
obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
diff --git a/drivers/video/omap2/displays/panel-ili9342.c b/drivers/video/omap2/displays/panel-ili9342.c
new file mode 100644
index 00000000000..40a9713aa0d
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-ili9342.c
@@ -0,0 +1,598 @@
+#define DEBUG
+/*
+ * Driver for ili9342 display driver
+ *
+ * Copyright (C) 2014,2015 Olio Devices Inc.
+ * Author: Evan Wilson <evan@oliodevices.com>
+ * Author: Mattis Fjallstrom <mattis@oliodevices.com>
+ *
+ * Adapted from panel-generic-dpi.c, panel-nec-nl8048hl11-01b.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/omapdss.h>
+
+#define OLIODEBUG
+#ifdef OLIODEBUG
+#define oliodebug(...) printk ( __VA_ARGS__ )
+#else
+#define oliodebug(...)
+#endif
+
+struct panel_config {
+ struct omap_video_timings timings;
+
+ int power_on_delay;
+ int power_off_delay;
+
+ /*
+ * Used to match device to panel configuration
+ * when use generic panel driver
+ */
+ const char *name;
+};
+
+/* Panel configurations */
+static struct panel_config ili9342_panels[] = {
+ /* Olio H1 panel */
+ {
+ {
+ .x_res = 320,
+ .y_res = 240,
+
+ .pixel_clock = 5333,
+
+ .hsw = 10,
+ .hfp = 10,
+ .hbp = 10,
+
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ },
+ .name = "olio_h1_panel",
+ },
+};
+
+struct panel_drv_data {
+
+ struct omap_dss_device *dssdev;
+
+ struct panel_config *panel_config;
+
+ struct mutex lock;
+};
+
+static int ili9342_panel_power_on(struct omap_dss_device *dssdev)
+{
+ int r;
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+ struct panel_config *panel_config = drv_data->panel_config;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
+ omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings);
+ omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines);
+
+ r = omapdss_dpi_display_enable(dssdev);
+ if (r)
+ goto err0;
+
+ /* wait couple of vsyncs until enabling the LCD */
+ if (panel_config->power_on_delay)
+ msleep(panel_config->power_on_delay);
+
+ if (dssdev->platform_enable) {
+ r = dssdev->platform_enable(dssdev);
+ if (r)
+ goto err1;
+ }
+
+ return 0;
+err1:
+ omapdss_dpi_display_disable(dssdev);
+err0:
+ return r;
+}
+
+static void ili9342_panel_power_off(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+ struct panel_config *panel_config = drv_data->panel_config;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ /* wait couple of vsyncs after disabling the LCD */
+ if (panel_config->power_off_delay)
+ msleep(panel_config->power_off_delay);
+
+ omapdss_dpi_display_disable(dssdev);
+}
+
+static int ili9342_panel_probe(struct omap_dss_device *dssdev)
+{
+ struct panel_config *panel_config = NULL;
+ struct panel_drv_data *drv_data = NULL;
+ int i;
+
+ dev_dbg(&dssdev->dev, "probe\n");
+
+ if (!dssdev || !dssdev->name)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ili9342_panels); i++) {
+ if (strcmp(dssdev->name, ili9342_panels[i].name) == 0) {
+ panel_config = &ili9342_panels[i];
+ break;
+ }
+ }
+
+ if (!panel_config) {
+ dev_err(&dssdev->dev, "Could not find %s in ili9342 panel configs\n", dssdev->name);
+ return -EINVAL;
+ }
+
+ dssdev->panel.timings = panel_config->timings;
+
+ drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ drv_data->dssdev = dssdev;
+ drv_data->panel_config = panel_config;
+
+ mutex_init(&drv_data->lock);
+
+ dev_set_drvdata(&dssdev->dev, drv_data);
+
+ return 0;
+}
+
+static void __exit ili9342_panel_remove(struct omap_dss_device *dssdev)
+{
+ dev_dbg(&dssdev->dev, "remove\n");
+
+ dev_set_drvdata(&dssdev->dev, NULL);
+}
+
+static int ili9342_panel_enable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&drv_data->lock);
+
+ r = ili9342_panel_power_on(dssdev);
+ if (r)
+ goto err;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+err:
+ mutex_unlock(&drv_data->lock);
+
+ return r;
+}
+
+static void ili9342_panel_disable(struct omap_dss_device *dssdev)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&drv_data->lock);
+
+ ili9342_panel_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ mutex_unlock(&drv_data->lock);
+}
+
+static void ili9342_panel_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&drv_data->lock);
+
+ omapdss_dpi_set_timings(dssdev, timings);
+
+ dssdev->panel.timings = *timings;
+
+ mutex_unlock(&drv_data->lock);
+}
+
+static void ili9342_panel_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&drv_data->lock);
+
+ *timings = dssdev->panel.timings;
+
+ mutex_unlock(&drv_data->lock);
+}
+
+static int ili9342_panel_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&drv_data->lock);
+
+ r = dpi_check_timings(dssdev, timings);
+
+ mutex_unlock(&drv_data->lock);
+
+ return r;
+}
+
+static int ili9342_get_recommended_bpp(struct omap_dss_device *dssdev) {
+ return 24;
+}
+
+/***************************************************************************
+ * suspend & resume
+ *
+ * For now, this is all handled by the SPI driver (see below).
+ * Leaving this functions here should we change our minds.
+ */
+
+static int ili9342_disp_suspend (struct device * dev) {
+ ili9342_panel_disable((struct omap_dss_device *) dev);
+ return 0;
+}
+
+static int ili9342_disp_resume (struct device * dev) {
+ ili9342_panel_enable((struct omap_dss_device *) dev);
+ return 0;
+}
+
+static struct dev_pm_ops ili9342_disp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ili9342_disp_suspend, ili9342_disp_resume)
+};
+
+
+static struct omap_dss_driver ili9342_driver = {
+ .probe = ili9342_panel_probe,
+ .remove = __exit_p(ili9342_panel_remove),
+
+ .enable = ili9342_panel_enable,
+ .disable = ili9342_panel_disable,
+
+ .set_timings = ili9342_panel_set_timings,
+ .get_timings = ili9342_panel_get_timings,
+ .check_timings = ili9342_panel_check_timings,
+
+ .get_recommended_bpp = ili9342_get_recommended_bpp,
+
+ .driver = {
+ .name = "ili9342_panel",
+ .owner = THIS_MODULE,
+ .pm = &ili9342_disp_pm_ops,
+ },
+};
+
+/* ====================================================================== */
+
+/* Here follows the SPI driver - it's required by the panel, but the
+ * coupling is rather weak. It registers another driver.
+ */
+
+/* ====================================================================== */
+
+static struct regulator *spi_regulator;
+
+static int ili9342_spi_write(struct spi_device *spi, bool cmd, unsigned char val) {
+ unsigned short buf;
+ struct spi_message m;
+ struct spi_transfer t = {
+ .tx_buf = &buf,
+ .len = 2,
+ .bits_per_word = 9,
+ };
+ int r;
+
+ if(cmd) {
+ buf = 0;
+ } else {
+ buf = 1 << 8;
+ }
+ buf |= val;
+
+ dev_dbg(&spi->dev, "SPI sync: %x", buf);
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ r = spi_sync(spi, &m);
+ if(r < 0) {
+ dev_err(&spi->dev, "SPI sync failed.");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ili9342_write_cmd(struct spi_device *spi, unsigned char val) {
+ return ili9342_spi_write(spi, 1, val);
+}
+
+static int ili9342_write_data(struct spi_device *spi, unsigned char val) {
+ return ili9342_spi_write(spi, 0, val);
+}
+
+static inline void ili9342_init_seq(struct spi_device *spi) {
+ ili9342_write_cmd(spi, 0xC8);
+ ili9342_write_data(spi, 0xFF);
+ ili9342_write_data(spi, 0x93);
+ ili9342_write_data(spi, 0x42);
+
+ ili9342_write_cmd(spi, 0xB0);
+ ili9342_write_data(spi, 0xE0);
+
+// Timing settings
+
+// ili9342_write_cmd(spi, 0xB5);
+// ili9342_write_data(spi, 0x04);
+// ili9342_write_data(spi, 0x01);
+// ili9342_write_data(spi, 0x0a);
+// ili9342_write_data(spi, 0x14);
+
+ //ili9342_write_cmd(spi, 0xB1);
+ //ili9342_write_data(spi, 0x00);
+ //ili9342_write_data(spi, 0x10);
+
+ ili9342_write_cmd(spi, 0x0B);
+ ili9342_write_data(spi, 0x20);
+
+ ili9342_write_cmd(spi, 0xF6);
+ ili9342_write_data(spi, 0x01);
+ ili9342_write_data(spi, 0x00);
+ ili9342_write_data(spi, 0x06);
+
+
+ ili9342_write_cmd(spi, 0xB4);
+ ili9342_write_data(spi, 0x00);
+
+ ili9342_write_cmd(spi, 0xC0);
+ ili9342_write_data(spi, 0x16);
+ ili9342_write_data(spi, 0x11);
+
+ ili9342_write_cmd(spi, 0xC1);
+ ili9342_write_data(spi, 0x01);
+
+ ili9342_write_cmd(spi, 0xC5);
+ ili9342_write_data(spi, 0xDB);
+
+// RGB color mode
+
+ //ili9342_write_cmd(spi, 0x36);
+ //ili9342_write_data(spi, 0x08);
+
+ ili9342_write_cmd(spi, 0xB6);
+ ili9342_write_data(spi, 0x0A);
+ ili9342_write_data(spi, 0x00);
+ ili9342_write_data(spi, 0x1D);
+ ili9342_write_data(spi, 0x04);
+// GAMMA settings
+ //ili9342_write_cmd(spi, 0x26);
+ //ili9342_write_data(spi, 0x04);
+
+ ili9342_write_cmd(spi, 0xE0);
+ ili9342_write_data(spi, 0x00); //63
+ ili9342_write_data(spi, 0x1b); //62
+ ili9342_write_data(spi, 0x22); //61
+ ili9342_write_data(spi, 0x05); //59
+ ili9342_write_data(spi, 0x13); //57
+ ili9342_write_data(spi, 0x07); //50
+ ili9342_write_data(spi, 0x4c); //43
+ ili9342_write_data(spi, 0xa7); //27
+ ili9342_write_data(spi, 0x5f); //20
+ ili9342_write_data(spi, 0x05); //13
+ ili9342_write_data(spi, 0x0b); //06
+ ili9342_write_data(spi, 0x09); //04
+ ili9342_write_data(spi, 0x32); //02
+ ili9342_write_data(spi, 0x36); //01
+ ili9342_write_data(spi, 0x0F); //00
+
+ ili9342_write_cmd(spi, 0xE1);
+ ili9342_write_data(spi, 0x00); //00
+ ili9342_write_data(spi, 0x0c); //01
+ ili9342_write_data(spi, 0x0d); //02
+ ili9342_write_data(spi, 0x05); //04
+ ili9342_write_data(spi, 0x11); //06
+ ili9342_write_data(spi, 0x06); //13
+ ili9342_write_data(spi, 0x30); //20
+ ili9342_write_data(spi, 0x58); //27
+ ili9342_write_data(spi, 0x44); //43
+ ili9342_write_data(spi, 0x08); //50
+ ili9342_write_data(spi, 0x14); //57
+ ili9342_write_data(spi, 0x0c); //59
+ ili9342_write_data(spi, 0x1e); //61
+ ili9342_write_data(spi, 0x24); //62
+ ili9342_write_data(spi, 0x0F); //63
+
+ ili9342_write_cmd(spi, 0x35);
+ ili9342_write_data(spi, 0x00);
+
+ ili9342_write_cmd(spi, 0x3A);
+ ili9342_write_data(spi, 0x66);
+
+ ili9342_write_cmd(spi, 0x11);
+ msleep(120);
+ ili9342_write_cmd(spi, 0x29);
+}
+
+
+static inline void init_ili9342_hw (struct spi_device *spi) {
+ struct omap_dss_device *panel = spi->dev.platform_data;
+
+ gpio_set_value(panel->reset_gpio, 1);
+ mdelay(1);
+ gpio_set_value(panel->reset_gpio, 0);
+ mdelay(50);
+ gpio_set_value(panel->reset_gpio, 1);
+ mdelay(120);
+
+ ili9342_init_seq(spi);
+}
+
+static inline int init_ili9342_spi(struct spi_device *spi) {
+ struct omap_dss_device *panel = spi->dev.platform_data;
+ int reset_gpio;
+
+ if(!panel->reset_gpio) {
+ dev_err(&spi->dev, "platform data requires reset\n");
+ return -EINVAL;
+ }
+
+ reset_gpio = panel->reset_gpio;
+
+ if (!panel) {
+ dev_err(&spi->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if(gpio_request_one(reset_gpio, GPIOF_OUT_INIT_LOW, "ili9342-reset")) {
+ dev_err(&spi->dev, "Could not request reset gpio %d", panel->reset_gpio);
+ return -EINVAL;
+ }
+
+ if(gpio_export(reset_gpio, 0)) {
+ dev_err(&spi->dev, "Could not export reset gpio %d", panel->reset_gpio);
+ return -EINVAL;
+ }
+
+ init_ili9342_hw (spi);
+
+ return 0;
+}
+
+static int ili9342_spi_probe(struct spi_device *spi)
+{
+ int err;
+ int ret;
+
+ spi_regulator = devm_regulator_get(&spi->dev, "vdd");
+
+ if (IS_ERR(spi_regulator)) {
+ dev_err(&spi->dev, "regulator get failed\n");
+ err = PTR_ERR(spi_regulator);
+ spi_regulator = NULL;
+ return err;
+ }
+
+ ret = regulator_enable(spi_regulator);
+
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret);
+ return ret;
+ }
+
+ init_ili9342_spi(spi);
+
+ return omap_dss_register_driver(&ili9342_driver);
+}
+
+static int ili9342_spi_remove(struct spi_device *spi)
+{
+ oliodebug ("OLIO %s:%s Removing SPI\n", __FILE__, __FUNCTION__);
+
+ omap_dss_unregister_driver(&ili9342_driver);
+ return 0;
+}
+
+static int ili9342_suspend(struct device *dev) {
+ int ret;
+
+ oliodebug ("OLIO %s:%s Suspending SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev));
+
+ ret = regulator_disable(spi_regulator);
+ if (ret) {
+ dev_err(dev, "Failed to disable vdd:%d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int ili9342_resume(struct device *dev) {
+ int ret;
+
+ /* HACK WARNING: We need an spi_device here. If, as can be assumed,
+ * the device pointer passed in points to a device in an spi_device,
+ * it's the first device in the spi_device struct. In other words,
+ * it's address is the same as the spi_device and a cast should be OK.
+ */
+
+ /* IF, otoh, that's an incorrect assumption ... then this will lead
+ * to horrible crashes. But oh well, you can't win 'em all.
+ */
+
+ struct spi_device * spi = (struct spi_device *) dev;
+
+ oliodebug ("OLIO %s:%s Resuming SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev));
+
+ ret = regulator_enable(spi_regulator);
+
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret);
+ return ret;
+ }
+
+ ili9342_init_seq(spi); /* init_ili9342_hw (spi); */
+
+ return 0;
+}
+
+
+static struct dev_pm_ops ili9342_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ili9342_suspend, ili9342_resume)
+};
+
+
+static struct spi_driver ili9342_spi_driver = {
+ .probe = ili9342_spi_probe,
+ .remove = ili9342_spi_remove,
+ .driver = {
+ .name = "ili9342-spi",
+ .owner = THIS_MODULE,
+ .pm = &ili9342_pm_ops,
+ },
+};
+
+module_spi_driver(ili9342_spi_driver);
+
+MODULE_AUTHOR("Evan Wilson <evan@oliodevice.com");
+MODULE_DESCRIPTION("ili9342 Display Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 7464de4c93f..18a9429d686 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -393,7 +393,7 @@ static int check_fb_res_bounds(struct fb_var_screeninfo *var)
var->xres_virtual = var->xres;
if (var->yres_virtual == 0)
- var->yres_virtual = var->yres;
+ var->yres_virtual = 2 * var->yres;
if (var->xres_virtual < xres_min || var->yres_virtual < yres_min)
return -EINVAL;
@@ -1571,7 +1571,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
DBG("adjusting fb mem size for VRFB, %u -> %lu\n",
w * h * bytespp, size);
} else {
- size = w * h * bytespp;
+ size = 2 * w * h * bytespp;
}
}
@@ -1882,7 +1882,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
}
var->xres_virtual = var->xres;
- var->yres_virtual = var->yres;
+ var->yres_virtual = 2 * var->yres;
if (!var->bits_per_pixel) {
switch (omapfb_get_recommended_bpp(fbdev, display)) {
@@ -1903,7 +1903,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
var->xres = 320;
var->yres = 240;
var->xres_virtual = var->xres;
- var->yres_virtual = var->yres;
+ var->yres_virtual = 2 * var->yres;
if (!var->bits_per_pixel)
var->bits_per_pixel = 16;
}
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index 99e379b7439..bc74c3f4c86 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -15,35 +15,16 @@
#include <linux/types.h>
-/* For key_map array */
-#define MXT_NUM_GPIO 4
-
-/* Orient */
-#define MXT_NORMAL 0x0
-#define MXT_DIAGONAL 0x1
-#define MXT_HORIZONTAL_FLIP 0x2
-#define MXT_ROTATED_90_COUNTER 0x3
-#define MXT_VERTICAL_FLIP 0x4
-#define MXT_ROTATED_90 0x5
-#define MXT_ROTATED_180 0x6
-#define MXT_DIAGONAL_COUNTER 0x7
-
/* The platform data for the Atmel maXTouch touchscreen driver */
struct mxt_platform_data {
- const u8 *config;
- size_t config_length;
-
- unsigned int x_line;
- unsigned int y_line;
- unsigned int x_size;
- unsigned int y_size;
- unsigned int blen;
- unsigned int threshold;
- unsigned int voltage;
- unsigned char orient;
unsigned long irqflags;
- bool is_tp;
- const unsigned int key_map[MXT_NUM_GPIO];
+ u8 t19_num_keys;
+ const unsigned int *t19_keymap;
+ int t15_num_keys;
+ const unsigned int *t15_keymap;
+ unsigned long gpio_reset;
+ const char *cfg_name;
+ const char *input_name;
};
#endif /* __LINUX_ATMEL_MXT_TS_H */
diff --git a/include/linux/mpu.h b/include/linux/mpu.h
new file mode 100644
index 00000000000..cda715bcfa4
--- /dev/null
+++ b/include/linux/mpu.h
@@ -0,0 +1,111 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#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,
+ COMPASS_ID_MLX90399,
+ COMPASS_ID_AK09911,
+
+ PRESSURE_ID_BMP085,
+ PRESSURE_ID_BMP280,
+};
+
+#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
+ * @key: key for MPL library.
+ *
+ * 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];
+ enum secondary_slave_type aux_slave_type;
+ enum ext_slave_id aux_slave_id;
+ __u16 aux_i2c_addr;
+
+#ifdef CONFIG_DTS_INV_MPU_IIO
+ int (*power_on)(struct mpu_platform_data *);
+ int (*power_off)(struct mpu_platform_data *);
+ struct regulator *vdd_ana;
+ struct regulator *vdd_i2c;
+#endif
+};
+
+#endif /* __MPU_H_ */
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
index 211ff67e8b0..95fc482cef3 100644
--- a/include/linux/mtd/bbm.h
+++ b/include/linux/mtd/bbm.h
@@ -93,8 +93,6 @@ struct nand_bbt_descr {
#define NAND_BBT_CREATE_EMPTY 0x00000400
/* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000800
-/* Scan block empty during good / bad block scan */
-#define NAND_BBT_SCANEMPTY 0x00001000
/* Write bbt if neccecary */
#define NAND_BBT_WRITE 0x00002000
/* Read and write back block contents when writing bbt */
diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h
index 6bf9ef43ddb..3e9dd6676b9 100644
--- a/include/linux/platform_data/mtd-nand-omap2.h
+++ b/include/linux/platform_data/mtd-nand-omap2.h
@@ -1,6 +1,4 @@
/*
- * arch/arm/plat-omap/include/mach/nand.h
- *
* Copyright (C) 2006 Micron Technology Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -23,13 +21,16 @@ enum nand_io {
};
enum omap_ecc {
- /* 1-bit ecc: stored at end of spare area */
- OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */
- OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */
- /* 1-bit ecc: stored at beginning of spare area as romcode */
- OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */
- OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */
- OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */
+ /* 1-bit ECC calculation by GPMC, Error detection by Software */
+ OMAP_ECC_HAM1_CODE_HW = 0,
+ /* 4-bit ECC calculation by GPMC, Error detection by Software */
+ OMAP_ECC_BCH4_CODE_HW_DETECTION_SW,
+ /* 4-bit ECC calculation by GPMC, Error detection by ELM */
+ OMAP_ECC_BCH4_CODE_HW,
+ /* 8-bit ECC calculation by GPMC, Error detection by Software */
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
+ /* 8-bit ECC calculation by GPMC, Error detection by ELM */
+ OMAP_ECC_BCH8_CODE_HW,
};
struct gpmc_nand_regs {
@@ -63,5 +64,6 @@ struct omap_nand_platform_data {
/* for passing the partitions */
struct device_node *of_node;
+ struct device_node *elm_of_node;
};
#endif
diff --git a/include/linux/platform_data/mtd-onenand-omap2.h b/include/linux/platform_data/mtd-onenand-omap2.h
index e9a9fb188f9..56ff0e6f5ad 100644
--- a/include/linux/platform_data/mtd-onenand-omap2.h
+++ b/include/linux/platform_data/mtd-onenand-omap2.h
@@ -1,6 +1,4 @@
/*
- * arch/arm/plat-omap/include/mach/onenand.h
- *
* Copyright (C) 2006 Nokia Corporation
* Author: Juha Yrjola
*
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 8780bd2a272..7a175b4b241 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -269,7 +269,10 @@ struct tty_struct {
void *driver_data;
struct list_head tty_files;
-#define N_TTY_BUF_SIZE 4096
+ /* default was 4096 - at one place this is used in a short, so
+ * can't be big.
+ */
+#define N_TTY_BUF_SIZE 8192
unsigned char closing:1;
unsigned short minimum_to_wake;
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 66967ba6f75..12f74fb3de7 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -32,11 +32,12 @@ static struct snd_soc_dai_driver dmic_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
- .channels_max = 8,
+ .channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S16_LE,
+ .sig_bits = 24
},
};
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 77157ed6efd..66d98e26a24 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -26,6 +26,14 @@ config SND_OMAP_SOC_MCPDM
config SND_OMAP_SOC_HDMI
tristate
+config SND_OMAP_SOC_OMAP3_H1
+ tristate "SoC Audio support for OMAP3 H1 Board"
+ depends on SND_OMAP_SOC && MACH_OMAP3_H1
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_DMIC
+ help
+ Say Y if you want to add support for SoC audio on the Olio H1 board.
+
config SND_OMAP_SOC_N810
tristate "SoC Audio support for Nokia N810"
depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 7272bf4da6d..15d3e0105ef 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -21,6 +21,7 @@ snd-soc-omap-c55-objs := omap-c55audio.o
snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
snd-soc-omap-twl4030-objs := omap-twl4030.o
snd-soc-omap3pandora-objs := omap3pandora.o
+snd-soc-omap3h1-objs := omap3h1.o
snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
@@ -33,4 +34,5 @@ obj-$(CONFIG_SND_OMAP_SOC_C55) += snd-soc-omap-c55.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_H1) += snd-soc-omap3h1.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
diff --git a/sound/soc/omap/omap3h1.c b/sound/soc/omap/omap3h1.c
new file mode 100644
index 00000000000..179c53d4655
--- /dev/null
+++ b/sound/soc/omap/omap3h1.c
@@ -0,0 +1,147 @@
+/*
+ * omap3h1.c -- SoC audio for OMAP3 H1
+ *
+ * Author: Evan Wilson <evan@oliodevices.com>
+ *
+ * Adapted from omap3pandora
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#define DEBUG
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include "omap-mcbsp.h"
+
+static int omap3h1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ //unsigned int fmt;
+ int ret = 0;
+ unsigned int freq;
+
+ //freq = 256 * params_rate(params);
+ // We are triggering from the 96 MHz FSCK
+ pr_info("ASoc OMAP3H1: setting system clock to: %d", freq);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK,
+ 96000000, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set DMIC cpu system clock\n");
+ return ret;
+ }
+
+ // Set the divider so that our clock rate is ~3KHz, this is about 48KHz sampling frequency on the mic
+ // (32-bit/channel, 2 channels, 48 Khz sampling)
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 32);
+ if (ret < 0) {
+ pr_err("ASoc OMAP3H1: can't set SRG clock divider\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops omap3h1_ops = {
+ .hw_params = omap3h1_hw_params,
+};
+
+//static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+// SND_SOC_DAPM_MIC("Digital Mic", NULL),
+//};
+//
+//static const struct snd_soc_dapm_route dmic_audio_map[] = {
+// {"DMic", NULL, "Digital Mic"},
+//};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3h1_dai = {
+ .name = "DMIC",
+ .stream_name = "DMIC Capture",
+ .cpu_dai_name = "omap-mcbsp.3",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "omap-pcm-audio",
+ .codec_name = "dmic-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &omap3h1_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3h1 = {
+ .name = "omap3h1",
+ .owner = THIS_MODULE,
+ .dai_link = &omap3h1_dai,
+ .num_links = 1,
+
+// .dapm_widgets = dmic_dapm_widgets,
+// .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+// .dapm_routes = dmic_audio_map,
+// .num_dapm_routes = ARRAY_SIZE(dmic_audio_map),
+};
+
+static struct platform_device *omap3h1_snd_device;
+
+static int __init omap3h1_soc_init(void)
+{
+ int ret;
+
+ if (!(machine_is_omap3_h1()))
+ return -ENODEV;
+ pr_info("OMAP3 H1 SoC init\n");
+
+ omap3h1_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!omap3h1_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap3h1_snd_device, &snd_soc_omap3h1);
+
+ ret = platform_device_add(omap3h1_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(omap3h1_snd_device);
+
+ return ret;
+}
+
+static void __exit omap3h1_soc_exit(void)
+{
+ platform_device_unregister(omap3h1_snd_device);
+}
+
+module_init(omap3h1_soc_init);
+module_exit(omap3h1_soc_exit);
+
+MODULE_AUTHOR("Evan Wilson <evan@oliodevices.com");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 H1");
+MODULE_LICENSE("GPL");