summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattis fjallstrom <mattis@acm.org>2015-01-05 10:10:59 -0800
committermattis fjallstrom <mattis@acm.org>2015-01-05 10:10:59 -0800
commitc4a8cecab6597381b0e82f7369e432d6a14530b7 (patch)
tree4248f562509bb8890b603d004f408131b4ef5034
parent9197dc43519131ccd3241af1f04351468d989fe9 (diff)
parent04c22768e7ed9962fdb3e458228213385d34d1f2 (diff)
downloadolio-linux-3.10-c4a8cecab6597381b0e82f7369e432d6a14530b7.tar.xz
olio-linux-3.10-c4a8cecab6597381b0e82f7369e432d6a14530b7.zip
Merge branch 'android-3.10-bringup' of ssh://internal.oliodevices.com:29418/kernel/omap into android-3.10-bringup
-rw-r--r--arch/arm/configs/omap3_h1_defconfig4
-rw-r--r--arch/arm/mach-omap2/board-omap3h1.c7
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/imu/Kconfig2
-rw-r--r--drivers/iio/imu/Makefile1
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/Kconfig (renamed from drivers/staging/iio/imu/Kconfig)15
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/Makefile (renamed from drivers/staging/iio/imu/inv_mpu/Makefile)19
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/README (renamed from drivers/staging/iio/imu/inv_mpu/README)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/dmpDefaultMPU6050.c (renamed from drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c)59
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/dmpKey.h (renamed from drivers/staging/iio/imu/inv_mpu/dmpKey.h)6
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/dmpmap.h (renamed from drivers/staging/iio/imu/inv_mpu/dmpmap.h)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_mpu3050_iio.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_mpu_core.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c)426
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_mpu_dts.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_mpu_dts.h (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h)0
-rw-r--r--drivers/iio/imu/inv_mpu6515/inv_mpu_iio.h (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h)18
-rw-r--r--drivers/iio/imu/inv_mpu6515/inv_mpu_misc.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c)21
-rw-r--r--drivers/iio/imu/inv_mpu6515/inv_mpu_ring.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c)149
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_mpu_trigger.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c)14
-rw-r--r--drivers/iio/imu/inv_mpu6515/inv_slave_bma250.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_slave_compass.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c)32
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_slave_pressure.c (renamed from drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c)60
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_test/Kconfig (renamed from drivers/staging/iio/inv_test/Kconfig)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_test/Makefile (renamed from drivers/staging/iio/inv_test/Makefile)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_test/inv_counters.c (renamed from drivers/staging/iio/inv_test/inv_counters.c)0
-rwxr-xr-x[-rw-r--r--]drivers/iio/imu/inv_mpu6515/inv_test/inv_counters.h (renamed from drivers/staging/iio/inv_test/inv_counters.h)0
-rw-r--r--drivers/iio/industrialio-core.c5
-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/staging/iio/Kconfig2
-rw-r--r--drivers/staging/iio/imu/Makefile4
49 files changed, 4979 insertions, 306 deletions
diff --git a/arch/arm/configs/omap3_h1_defconfig b/arch/arm/configs/omap3_h1_defconfig
index 3c291309575..3ff7277be7f 100644
--- a/arch/arm/configs/omap3_h1_defconfig
+++ b/arch/arm/configs/omap3_h1_defconfig
@@ -1285,11 +1285,11 @@ CONFIG_INPUT_MOUSE=y
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
-CONFIG_TOUCHSCREEN_ATMXT=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_ATMEL_MXT=m
# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
# CONFIG_TOUCHSCREEN_BU21013 is not set
# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
diff --git a/arch/arm/mach-omap2/board-omap3h1.c b/arch/arm/mach-omap2/board-omap3h1.c
index 5c22de9d30f..9364dc4e709 100644
--- a/arch/arm/mach-omap2/board-omap3h1.c
+++ b/arch/arm/mach-omap2/board-omap3h1.c
@@ -217,6 +217,7 @@ static struct regulator_init_data tps65910_1v8 = {
.valid_modes_mask = REGULATOR_MODE_NORMAL,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = true,
+ .apply_uV = true,
},
};
@@ -270,7 +271,7 @@ static struct tps65910_board omap3h1_tps65910_pdata = {
.tps65910_pmic_init_data[TPS65910_REG_VAUX1] = &tps65910_dummy,
.tps65910_pmic_init_data[TPS65910_REG_VAUX2] = &tps65910_3v3,
.tps65910_pmic_init_data[TPS65910_REG_VAUX33] = &tps65910_3v3,
- .tps65910_pmic_init_data[TPS65910_REG_VMMC] = &tps65910_dummy,
+ .tps65910_pmic_init_data[TPS65910_REG_VMMC] = &tps65910_3v3,
};
static struct i2c_board_info __initdata omap3h1_i2c1_board_info[] = {
@@ -293,6 +294,9 @@ static struct i2c_board_info __initdata omap3h1_i2c1_board_info[] = {
I2C_BOARD_INFO("mXT224", 0x4a),
.platform_data = &mxt_data,
},
+ {
+ I2C_BOARD_INFO("bq274xx", 0x55),
+ },
};
static int __init omap3_h1_i2c_init(void)
@@ -301,6 +305,7 @@ static int __init omap3_h1_i2c_init(void)
omap3h1_i2c1_board_info[3].irq = gpio_to_irq(ATMEL_MXT_GPIO);
omap_register_i2c_bus(1, 400, omap3h1_i2c1_board_info, ARRAY_SIZE(omap3h1_i2c1_board_info));
+ //omap_register_i2c_bus(3, 400, omap3h1_i2c3_board_info, ARRAY_SIZE(omap3h1_i2c3_board_info));
return 0;
}
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b2f963be399..03cd178e262 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
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 4f40a10cb74..d7835a16243 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/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/Makefile
index f2f56ceaed2..2fb21dd4a54 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/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/staging/iio/imu/Kconfig b/drivers/iio/imu/inv_mpu6515/Kconfig
index 91aca5b2aac..18ccddb1f4e 100644..100755
--- a/drivers/staging/iio/imu/Kconfig
+++ b/drivers/iio/imu/inv_mpu6515/Kconfig
@@ -1,9 +1,3 @@
-
-#
-# IIO imu drivers configuration
-#
-menu "Inertial measurement units"
-
#
# inv-mpu-iio driver for Invensense MPU devices and combos
#
@@ -36,4 +30,11 @@ config DTS_INV_MPU_IIO
This enables Invensense MPU devices to use Device Tree Structure.
DTS must be enabled in the system.
-endmenu
+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/staging/iio/imu/inv_mpu/Makefile b/drivers/iio/imu/inv_mpu6515/Makefile
index 31b765b34d0..6e3c2475889 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/Makefile
+++ b/drivers/iio/imu/inv_mpu6515/Makefile
@@ -13,6 +13,16 @@ 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
@@ -21,17 +31,26 @@ 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/staging/iio/imu/inv_mpu/README b/drivers/iio/imu/inv_mpu6515/README
index 0963954a844..0963954a844 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/README
+++ b/drivers/iio/imu/inv_mpu6515/README
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c b/drivers/iio/imu/inv_mpu6515/dmpDefaultMPU6050.c
index 81e63960f39..892e62dc14f 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c
+++ b/drivers/iio/imu/inv_mpu6515/dmpDefaultMPU6050.c
@@ -16,34 +16,34 @@
#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 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)
@@ -215,6 +215,8 @@
#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},
@@ -345,6 +347,7 @@ static const struct tKeyLabel dmpTConfig[] = {
{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},
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpKey.h b/drivers/iio/imu/inv_mpu6515/dmpKey.h
index f6173b3c0f8..20b28478785 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/dmpKey.h
+++ b/drivers/iio/imu/inv_mpu6515/dmpKey.h
@@ -216,9 +216,9 @@
#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_MTX_22 + 1)
+#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)
@@ -392,7 +392,7 @@
#define KEY_DMP_9Q3 (KEY_DMP_9Q2 + 1)
/* Test key */
-#define KEY_TEST_01 (KEY_9AXIS_ACCURACY + 1)
+#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)
diff --git a/drivers/staging/iio/imu/inv_mpu/dmpmap.h b/drivers/iio/imu/inv_mpu6515/dmpmap.h
index 92936372224..92936372224 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/dmpmap.h
+++ b/drivers/iio/imu/inv_mpu6515/dmpmap.h
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c b/drivers/iio/imu/inv_mpu6515/inv_mpu3050_iio.c
index 1fc5ff98097..1fc5ff98097 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu3050_iio.c
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6515/inv_mpu_core.c
index 8efac054bb2..6f5a131dac5 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_core.c
@@ -39,7 +39,7 @@
s64 get_time_ns(void)
{
struct timespec ts;
- ktime_get_ts(&ts);
+ ts = CURRENT_TIME;
return timespec_to_ns(&ts);
}
@@ -342,6 +342,7 @@ static int inv_init_config(struct iio_dev *indio_dev)
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,
@@ -667,29 +668,119 @@ static ssize_t _dmp_attr_store(struct device *dev,
if (st->chip_config.enable)
return -EBUSY;
- if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) {
- if (!st->chip_config.firmware_loaded)
+ 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;
- result = st->set_power_state(st, true);
- if (result)
- return result;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_store_fail;
st->ped.on = !!data;
break;
}
@@ -697,7 +788,7 @@ static ssize_t _dmp_attr_store(struct device *dev,
{
result = inv_write_2bytes(st, KEY_D_PEDSTD_SB, data);
if (result)
- goto dmp_attr_store_fail;
+ goto dmp_mem_store_fail;
st->ped.step_thresh = data;
break;
}
@@ -705,158 +796,95 @@ static ssize_t _dmp_attr_store(struct device *dev,
{
result = inv_write_2bytes(st, KEY_D_PEDSTD_SB2, data);
if (result)
- goto dmp_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_store_fail;
result = write_be32_key_to_mem(st, data << 16,
KEY_SMD_ACCEL_THLD);
if (result)
- goto dmp_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_store_fail;
}
result = inv_set_tap_threshold_dmp(st, data);
if (result)
- goto dmp_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_store_fail;
}
result = inv_set_min_taps_dmp(st, data);
if (result)
- goto dmp_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_store_fail;
}
result = inv_set_tap_time_dmp(st, data);
if (result)
- goto dmp_attr_store_fail;
+ 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_attr_store_fail;
+ goto dmp_mem_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;
+ goto dmp_mem_store_fail;
result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d);
if (result)
- goto dmp_attr_store_fail;
+ goto dmp_mem_store_fail;
}
break;
case ATTR_DEBUG_SMD_ENABLE_TESTP2:
@@ -864,21 +892,20 @@ static ssize_t _dmp_attr_store(struct device *dev,
u8 d[] = {0x42};
result = st->set_power_state(st, true);
if (result)
- goto dmp_attr_store_fail;
+ goto dmp_mem_store_fail;
result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d);
if (result)
- goto dmp_attr_store_fail;
+ goto dmp_mem_store_fail;
}
break;
#endif
default:
result = -EINVAL;
- goto dmp_attr_store_fail;
+ goto dmp_mem_store_fail;
}
-dmp_attr_store_fail:
- if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON)
- result |= st->set_power_state(st, false);
+dmp_mem_store_fail:
+ result |= st->set_power_state(st, false);
if (result)
return result;
@@ -886,17 +913,16 @@ dmp_attr_store_fail:
}
/*
- * inv_dmp_attr_store() - calling this function will store current
- * dmp parameter settings
+ * inv_dmp_mem_store() - calling this function will store DMP memory data
*/
-static ssize_t inv_dmp_attr_store(struct device *dev,
+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_attr_store(dev, attr, buf, count);
+ result = _dmp_mem_store(dev, attr, buf, count);
mutex_unlock(&indio_dev->mlock);
return result;
@@ -926,7 +952,7 @@ static ssize_t inv_attr64_show(struct device *dev,
break;
case ATTR_DMP_PEDOMETER_TIME:
result = inv_get_pedometer_time(st, &ped);
- tmp = st->ped.time + ped;
+ tmp = (u64)st->ped.time + ((u64)ped) * MS_PER_PED_TICKS;
break;
case ATTR_DMP_PEDOMETER_COUNTER:
tmp = st->ped.last_step_time;
@@ -2002,22 +2028,22 @@ 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);
+ 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_attr_store, ATTR_DMP_PED_ON);
+ 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_attr_store, ATTR_DMP_PED_STEP_THRESH);
+ 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_attr_store, ATTR_DMP_PED_INT_THRESH);
+ 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_attr_store, ATTR_DMP_SMD_ENABLE);
+ 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_attr_store, ATTR_DMP_SMD_THLD);
+ 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_attr_store, ATTR_DMP_SMD_DELAY_THLD);
+ 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_attr_store, ATTR_DMP_SMD_DELAY_THLD2);
+ 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);
@@ -2027,15 +2053,15 @@ 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);
+ 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_attr_store, ATTR_DMP_TAP_THRESHOLD);
+ 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_attr_store, ATTR_DMP_TAP_MIN_COUNT);
+ 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_attr_store, ATTR_DMP_TAP_TIME);
+ 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_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON);
+ 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,
@@ -2422,7 +2448,7 @@ static int inv_setup_vddio(struct inv_mpu_state *st)
* 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)
+ const struct i2c_device_id *id, bool reset_needed)
{
struct inv_reg_map_s *reg;
int result;
@@ -2467,11 +2493,14 @@ static int inv_check_chip_type(struct inv_mpu_state *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);
+
+ 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)
@@ -2667,6 +2696,14 @@ static int inv_mpu_probe(struct i2c_client *client,
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
@@ -2676,7 +2713,11 @@ static int inv_mpu_probe(struct i2c_client *client,
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;
@@ -2699,15 +2740,20 @@ static int inv_mpu_probe(struct i2c_client *client,
"power_on failed: %d\n", result);
return result;
}
- }
+ msleep(POWER_UP_TIME);
-msleep(100);
+ /*
+ * 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);
+ result = inv_check_chip_type(st, id, reset_needed);
if (result)
goto out_free;
@@ -2790,7 +2836,11 @@ out_remove_ring:
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;
@@ -2835,8 +2885,11 @@ static int inv_mpu_remove(struct i2c_client *client)
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;
@@ -2846,46 +2899,101 @@ 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->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;
+ 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);
@@ -2893,24 +3001,37 @@ static int inv_mpu_resume(struct device *dev)
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);
- mutex_unlock(&st->suspend_resume_lock);
+ /* 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);
- mutex_lock(&indio_dev->mlock);
result = 0;
if (st->chip_config.dmp_on && st->chip_config.enable) {
@@ -2923,22 +3044,33 @@ static int inv_mpu_suspend(struct device *dev)
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 */
- mutex_unlock(&indio_dev->mlock);
- mutex_lock(&st->suspend_resume_lock);
-
- return result;
+ return 0;
}
+
static const struct dev_pm_ops inv_mpu_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
+ .suspend = inv_mpu_suspend,
+ .resume = inv_mpu_resume,
};
#define INV_MPU_PMOPS (&inv_mpu_pmops)
#else
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c b/drivers/iio/imu/inv_mpu6515/inv_mpu_dts.c
index 276d5bcfea7..276d5bcfea7 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_dts.c
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h b/drivers/iio/imu/inv_mpu6515/inv_mpu_dts.h
index 151ac74ea05..151ac74ea05 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_dts.h
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6515/inv_mpu_iio.h
index ed41456f56e..701d4db78ed 100644
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_iio.h
@@ -97,6 +97,7 @@
#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
@@ -120,6 +121,7 @@
#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
@@ -181,6 +183,10 @@
#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
@@ -189,12 +195,12 @@
#define MPU3050_FOOTER_SIZE 2
#define FIFO_COUNT_BYTE 2
#define FIFO_THRESHOLD 800
-#define FIFO_SIZE 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 5
+#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
@@ -206,6 +212,7 @@
#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
@@ -244,7 +251,7 @@
#define INIT_ST_MPU6050_SAMPLES 600
#define INIT_ST_THRESHOLD 50
#define INIT_PED_INT_THRESH 2
-#define INIT_PED_THRESH 7
+#define INIT_PED_THRESH 9
#define ST_THRESHOLD_MULTIPLIER 10
#define ST_MAX_SAMPLES 500
#define ST_MAX_THRESHOLD 100
@@ -275,7 +282,7 @@
#define CRC_FIRMWARE_SEED 0
#define SELF_TEST_SUCCESS 1
#define MS_PER_DMP_TICK 20
-#define DMP_IMAGE_SIZE 2463
+#define DMP_IMAGE_SIZE 2943
/* init parameters */
#define INIT_FIFO_RATE 50
@@ -301,6 +308,7 @@
#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
@@ -678,6 +686,7 @@ struct inv_mpu_slave;
* @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.
@@ -742,6 +751,7 @@ struct inv_mpu_state {
void *sl_handle;
u32 irq_dur_ns;
u32 ts_counter;
+ bool suspend_state;
u32 dmp_interval;
s32 dmp_interval_accum;
s64 diff_accumulater;
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c b/drivers/iio/imu/inv_mpu6515/inv_mpu_misc.c
index 70f239a3575..73b883679c9 100644
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_misc.c
@@ -45,7 +45,7 @@
#define DMP_PRECISION 1000
#define DMP_MAX_DIVIDER 4
#define DMP_MAX_MIN_TAPS 4
-#define DMP_IMAGE_CRC_VALUE 0x972aae92
+#define DMP_IMAGE_CRC_VALUE 0xa7e2110d
/*--- Test parameters defaults --- */
#define DEF_OLDEST_SUPP_PROD_REV 8
@@ -1250,6 +1250,17 @@ static int inv_verify_firmware(struct inv_mpu_state *st,
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];
@@ -1297,11 +1308,13 @@ int inv_enable_pedometer(struct inv_mpu_state *st, bool en)
{
u8 d[1];
- if (en)
+ if (en) {
+ inv_set_step_buffer_time(st);
+ inv_set_step_threshold(st);
d[0] = 0xf1;
- else
+ } else {
d[0] = 0xff;
-
+ }
return mem_w_key(KEY_CFG_PED_ENABLE, ARRAY_SIZE(d), d);
}
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6515/inv_mpu_ring.c
index 7e15b4c80e9..9c3d0912df4 100644
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_ring.c
@@ -43,8 +43,11 @@ static int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr)
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;
}
@@ -58,10 +61,17 @@ static int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr,
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;
}
@@ -74,12 +84,24 @@ static int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t,
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;
}
@@ -135,7 +157,9 @@ static int inv_send_compass_data(struct inv_mpu_state *st)
curr_ts = get_time_ns();
if (curr_ts - slave->prev_ts > slave->min_read_time) {
result = slave->read_data(st, sen);
- if (!result)
+ 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;
@@ -328,12 +352,8 @@ static int set_fifo_rate_reg(struct inv_mpu_state *st)
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;
+ return result;
}
/*
@@ -1383,40 +1403,29 @@ static void inv_push_step_indicator(struct inv_mpu_state *st, int sensor_ind,
static int inv_parse_header(u16 hdr)
{
- int sensor_ind;
-
switch (hdr) {
case ACCEL_HDR:
- sensor_ind = SENSOR_ACCEL;
- break;
+ return SENSOR_ACCEL;
case GYRO_HDR:
- sensor_ind = SENSOR_GYRO;
- break;
+ return SENSOR_GYRO;
case PEDQUAT_HDR:
- sensor_ind = SENSOR_PEDQ;
- break;
+ return SENSOR_PEDQ;
case LPQUAT_HDR:
- sensor_ind = SENSOR_LPQ;
- break;
+ return SENSOR_LPQ;
case SIXQUAT_HDR:
- sensor_ind = SENSOR_SIXQ;
- break;
+ return SENSOR_SIXQ;
case COMPASS_HDR:
- sensor_ind = SENSOR_COMPASS;
- break;
+ case COMPASS_HDR_2:
+ return SENSOR_COMPASS;
case PRESSURE_HDR:
- sensor_ind = SENSOR_PRESSURE;
- break;
+ return SENSOR_PRESSURE;
case STEP_DETECTOR_HDR:
- sensor_ind = SENSOR_STEP;
- break;
+ return SENSOR_STEP;
default:
- sensor_ind = SENSOR_INVALID;
- break;
+ return SENSOR_INVALID;
}
-
- return sensor_ind;
}
+#define FEATURE_IKR_PANIC 1
static int inv_process_batchmode(struct inv_mpu_state *st)
{
@@ -1428,12 +1437,30 @@ static int inv_process_batchmode(struct inv_mpu_state *st)
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 {
@@ -1461,18 +1488,18 @@ static int inv_process_batchmode(struct inv_mpu_state *st)
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;
}
+ /* 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 +
@@ -1488,32 +1515,30 @@ static int inv_process_batchmode(struct inv_mpu_state *st)
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 |
+ } 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;
- 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 |
+ } 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;
}
@@ -1533,8 +1558,15 @@ static int inv_process_batchmode(struct inv_mpu_state *st)
inv_adjust_sensor_ts(st, sensor_ind);
st->left_over_size = target_bytes - (dptr - d);
- if (st->left_over_size)
+ 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;
}
@@ -1589,7 +1621,8 @@ irqreturn_t inv_read_fifo(int irq, void *dev_id)
u64 pts1;
#define DMP_MIN_RUN_TIME (37 * NSEC_PER_MSEC)
- mutex_lock(&st->suspend_resume_lock);
+ if (st->suspend_state)
+ return IRQ_HANDLED;
mutex_lock(&indio_dev->mlock);
if (st->chip_config.dmp_on) {
pts1 = get_time_ns();
@@ -1679,7 +1712,6 @@ irqreturn_t inv_read_fifo(int irq, void *dev_id)
}
end_session:
mutex_unlock(&indio_dev->mlock);
- mutex_unlock(&st->suspend_resume_lock);
return IRQ_HANDLED;
flush_fifo:
@@ -1687,7 +1719,6 @@ flush_fifo:
inv_reset_fifo(indio_dev);
inv_clear_kfifo(st);
mutex_unlock(&indio_dev->mlock);
- mutex_unlock(&st->suspend_resume_lock);
return IRQ_HANDLED;
}
@@ -1794,7 +1825,7 @@ 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];
+ u8 data[FIFO_COUNT_BYTE];
int result;
reg = &st->reg;
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6515/inv_mpu_trigger.c
index 88b71ae4da1..ad67c089cc3 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_mpu_trigger.c
@@ -51,18 +51,28 @@ 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;
@@ -75,6 +85,10 @@ 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/staging/iio/imu/inv_mpu/inv_slave_bma250.c b/drivers/iio/imu/inv_mpu6515/inv_slave_bma250.c
index 99ae702e67a..99ae702e67a 100644
--- a/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_slave_bma250.c
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c b/drivers/iio/imu/inv_mpu6515/inv_slave_compass.c
index f7d7ff20b17..68b76d3a8e0 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_slave_compass.c
@@ -32,7 +32,7 @@
#define REG_AKM8963_CNTL1 0x0A
/* AK09911 register definition */
-#define REG_AK09911_DMP_READ 0x10
+#define REG_AK09911_DMP_READ 0x3
#define REG_AK09911_STATUS1 0x10
#define REG_AK09911_CNTL2 0x31
#define REG_AK09911_SENSITIVITY 0x60
@@ -57,8 +57,8 @@
#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_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)
@@ -187,19 +187,18 @@ static int inv_akm_read_data(struct inv_mpu_state *st, short *o)
{
int result, shift;
int i;
- u8 d[DATA_AKM_BYTES];
+ u8 d[DATA_AKM_99_BYTES_DMP - 1];
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)) {
+ 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_BYTES, d);
- if ((DATA_AKM_DRDY != d[0]) || result)
+ 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)
@@ -207,7 +206,7 @@ static int inv_akm_read_data(struct inv_mpu_state *st, short *o)
else
shift = 8;
for (i = 0; i < 3; i++) {
- o[i] = (short)((d[i * 2 + 2] << 8) | d[i * 2 + 1]);
+ o[i] = (short)((d[i * 2 + 1] << 8) | d[i * 2 + 2]);
o[i] = (short)(((int)o[i] * (sens[i] + 128)) >> shift);
}
@@ -405,27 +404,30 @@ static int inv_resume_akm(struct inv_mpu_state *st)
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;
+ bytes = DATA_AKM_99_BYTES_DMP;
} else {
reg_addr = REG_AK09911_STATUS1;
- bytes = DATA_AKM_BYTES;
+ bytes = DATA_AKM_99_BYTES_DMP - 1;
}
} else {
if (st->chip_config.dmp_on) {
reg_addr = REG_AKM_INFO;
- bytes = DATA_AKM_BYTES_DMP;
+ bytes = DATA_AKM_89_BYTES_DMP;
} else {
reg_addr = REG_AKM_STATUS;
- bytes = DATA_AKM_BYTES;
+ 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 */
+ /* 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_SLV_EN | bytes);
+ 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 */
diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c b/drivers/iio/imu/inv_mpu6515/inv_slave_pressure.c
index 7843556c732..24d80810449 100644..100755
--- a/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_slave_pressure.c
@@ -171,9 +171,9 @@
#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 BMP280_RATE_SCALE 35
#define DATA_BMP280_MIN_READ_TIME (32 * NSEC_PER_MSEC)
-#define BMP280_DATA_BYTES 6
+#define BMP280_DATA_BYTES_9911 6
#define FAKE_DATA_NUM_BYTES 10
/** this structure holds all device specific calibration parameters */
@@ -318,26 +318,35 @@ static int inv_read_bmp280_scale(struct inv_mpu_state *st, int *scale)
static int inv_resume_bmp280(struct inv_mpu_state *st)
{
int r;
+ u8 bytes, start;
- 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,
+ 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,
+ 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,
+ 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,
@@ -346,14 +355,13 @@ static int inv_resume_bmp280(struct inv_mpu_state *st)
if (r)
return r;
/* start from pressure sensor */
- r = inv_i2c_single_write(st, REG_I2C_SLV2_REG,
- BMP280_PRESSURE_MSB_REG);
+ r = inv_i2c_single_write(st, REG_I2C_SLV2_REG, start);
if (r)
return r;
- /* slave 2 is enabled, read 6 bytes from here */
+ /* 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 | BMP280_DATA_BYTES);
+ INV_MPU_BIT_SLV_EN | bytes);
if (r)
return r;
/* slave 3 is enabled, write byte length is 1 */
@@ -447,18 +455,22 @@ static u32 bmp280_compensate_P_int32(s32 adc_p)
static int inv_bmp280_read_data(struct inv_mpu_state *st, short *o)
{
int r, i;
- u8 d[BMP280_DATA_BYTES], reg_addr;
+ 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)
- reg_addr = REG_EXT_SENS_DATA_08;
- 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, d);
+ }
+ r = inv_i2c_read(st, reg_addr, BMP280_DATA_BYTES_9911, d);
if (r)
return r;
}
diff --git a/drivers/staging/iio/inv_test/Kconfig b/drivers/iio/imu/inv_mpu6515/inv_test/Kconfig
index 86c30bd8a63..86c30bd8a63 100644..100755
--- a/drivers/staging/iio/inv_test/Kconfig
+++ b/drivers/iio/imu/inv_mpu6515/inv_test/Kconfig
diff --git a/drivers/staging/iio/inv_test/Makefile b/drivers/iio/imu/inv_mpu6515/inv_test/Makefile
index 4f0edd3de90..4f0edd3de90 100644..100755
--- a/drivers/staging/iio/inv_test/Makefile
+++ b/drivers/iio/imu/inv_mpu6515/inv_test/Makefile
diff --git a/drivers/staging/iio/inv_test/inv_counters.c b/drivers/iio/imu/inv_mpu6515/inv_test/inv_counters.c
index 3b26ca97284..3b26ca97284 100644..100755
--- a/drivers/staging/iio/inv_test/inv_counters.c
+++ b/drivers/iio/imu/inv_mpu6515/inv_test/inv_counters.c
diff --git a/drivers/staging/iio/inv_test/inv_counters.h b/drivers/iio/imu/inv_mpu6515/inv_test/inv_counters.h
index d60dac9d97b..d60dac9d97b 100644..100755
--- a/drivers/staging/iio/inv_test/inv_counters.h
+++ b/drivers/iio/imu/inv_mpu6515/inv_test/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/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/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 258de770af3..db4d6dc0324 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -16,8 +16,6 @@ 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/Kconfig"
-source "drivers/staging/iio/inv_test/Kconfig"
config IIO_DUMMY_EVGEN
tristate
diff --git a/drivers/staging/iio/imu/Makefile b/drivers/staging/iio/imu/Makefile
deleted file mode 100644
index 0ed92302560..00000000000
--- a/drivers/staging/iio/imu/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-#
-# Makefile for Inertial Measurement Units
-#
-obj-y += inv_mpu/