diff options
| author | mattis fjallstrom <mattis@acm.org> | 2015-01-05 10:10:59 -0800 | 
|---|---|---|
| committer | mattis fjallstrom <mattis@acm.org> | 2015-01-05 10:10:59 -0800 | 
| commit | c4a8cecab6597381b0e82f7369e432d6a14530b7 (patch) | |
| tree | 4248f562509bb8890b603d004f408131b4ef5034 | |
| parent | 9197dc43519131ccd3241af1f04351468d989fe9 (diff) | |
| parent | 04c22768e7ed9962fdb3e458228213385d34d1f2 (diff) | |
| download | olio-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
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 = ®s[0]; +	unsigned char *stat2 = ®s[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 = ®s[0]; +	unsigned char *stat2 = ®s[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 *)(®s[0])); +	param->m_gain.y = le16_to_cpup((__le16 *)(®s[2])); +	param->m_gain.z = le16_to_cpup((__le16 *)(®s[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/ |