MA Lite 8.1.4a

Module:
* 8.1.1-simple-test1a

Supported Kernel Versions:
* 3.18
* 4.4
* 4.9

Supported TDK-InvenSense Sensors:
* IAM20680
This commit is contained in:
Eiji Iwatsuki 2018-04-17 11:56:16 -07:00
commit a71da78c6d
22 changed files with 8083 additions and 0 deletions

View file

@ -0,0 +1,63 @@
#
# inv-mpu-iio driver for Invensense MPU devices
#
config INV_MPU_IIO
tristate
select IIO_BUFFER
select IIO_KFIFO_BUF
select IIO_TRIGGER
select CRC32
choice
prompt "Chip name"
depends on INV_MPU_IIO
config INV_MPU_IIO_ICM20648
bool "ICM20648/ICM20948"
help
Select this if you are using a ICM20648/ICM20948 chip.
config INV_MPU_IIO_ICM20608D
bool "ICM20608D/ICM20609/ICM20689"
help
Select this if you are using a ICM20608D/ICM20609/ICM20689 chip.
config INV_MPU_IIO_ICM20602
bool "ICM20602"
help
Select this if you are using a ICM20602 chip.
config INV_MPU_IIO_ICM20690
bool "ICM20690"
help
Select this if you are using a ICM20690 chip.
config INV_MPU_IIO_IAM20680
bool "IAM20680"
help
Select this if you are using a IAM20680 chip.
endchoice
config INV_MPU_IIO_I2C
tristate "Invensense ICM20xxx devices (I2C)"
depends on I2C && !INV_MPU6050_IIO
select INV_MPU_IIO
default n
help
This driver supports Invensense ICM20xxx devices over I2C.
This driver can be built as a module. The module will be called
inv-mpu-iio-i2c.
config INV_MPU_IIO_SPI
tristate "Invensense ICM20xxx devices (SPI)"
depends on SPI_MASTER && !INV_MPU6050_IIO
select INV_MPU_IIO
default n
help
This driver supports Invensense ICM20xxx devices over SPI.
This driver can be built as a module. The module will be called
inv-mpu-iio-spi.
source "drivers/iio/imu/inv_mpu/inv_test/Kconfig"

View file

@ -0,0 +1,61 @@
#
# Makefile for Invensense inv-mpu-iio device.
#
obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
inv-mpu-iio-objs += inv_mpu_common.o
inv-mpu-iio-objs += inv_mpu_ring.o
inv-mpu-iio-objs += inv_mpu_timestamp.o
inv-mpu-iio-objs += inv_mpu_dts.o
# chip support
ifeq ($(CONFIG_INV_MPU_IIO_ICM20648), y)
inv-mpu-iio-objs += icm20648/inv_mpu_init.o
inv-mpu-iio-objs += icm20648/inv_mpu_core.o
inv-mpu-iio-objs += icm20648/inv_mpu_parsing.o
inv-mpu-iio-objs += icm20648/inv_mpu_setup.o
inv-mpu-iio-objs += icm20648/inv_mpu_dmp_fifo.o
inv-mpu-iio-objs += icm20648/inv_slave_compass.o
inv-mpu-iio-objs += icm20648/inv_slave_pressure.o
inv-mpu-iio-objs += icm20648/inv_slave_als.o
inv-mpu-iio-objs += icm20648/inv_mpu_load_dmp.o
inv-mpu-iio-objs += icm20648/inv_mpu_selftest.o
inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
else ifeq ($(CONFIG_INV_MPU_IIO_ICM20690), y)
inv-mpu-iio-objs += icm20690/inv_mpu_init_20690.o
inv-mpu-iio-objs += icm20690/inv_mpu_core_20690.o
inv-mpu-iio-objs += icm20690/inv_mpu_parsing_20690.o
inv-mpu-iio-objs += icm20690/inv_mpu_setup_20690.o
inv-mpu-iio-objs += icm20690/inv_mpu_selftest_20690.o
inv-mpu-iio-objs += icm20690/inv_slave_compass.o
else ifeq ($(CONFIG_INV_MPU_IIO_ICM20602), y)
inv-mpu-iio-objs += icm20602/inv_mpu_init_20602.o
inv-mpu-iio-objs += icm20602/inv_mpu_core_20602.o
inv-mpu-iio-objs += icm20602/inv_mpu_parsing_20602.o
inv-mpu-iio-objs += icm20602/inv_mpu_setup_20602.o
inv-mpu-iio-objs += icm20602/inv_mpu_selftest_20602.o
else ifeq ($(CONFIG_INV_MPU_IIO_ICM20608D), y)
inv-mpu-iio-objs += icm20608d/inv_mpu_init_20608.o
inv-mpu-iio-objs += icm20608d/inv_mpu_core_20608.o
inv-mpu-iio-objs += icm20608d/inv_mpu_parsing_20608.o
inv-mpu-iio-objs += icm20608d/inv_mpu_setup_20608D.o
inv-mpu-iio-objs += icm20608d/inv_mpu_dmp_fifo.o
inv-mpu-iio-objs += icm20608d/inv_mpu_load_dmp.o
inv-mpu-iio-objs += icm20608d/inv_mpu_selftest_20608.o
inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
else ifeq ($(CONFIG_INV_MPU_IIO_IAM20680), y)
inv-mpu-iio-objs += iam20680/inv_mpu_init_20680.o
inv-mpu-iio-objs += iam20680/inv_mpu_core_20680.o
inv-mpu-iio-objs += iam20680/inv_mpu_parsing_20680.o
inv-mpu-iio-objs += iam20680/inv_mpu_setup_20680.o
inv-mpu-iio-objs += iam20680/inv_mpu_selftest_20680.o
endif
# Bus support
obj-$(CONFIG_INV_MPU_IIO_I2C) += inv-mpu-iio-i2c.o
inv-mpu-iio-i2c-objs := inv_mpu_i2c.o
obj-$(CONFIG_INV_MPU_IIO_SPI) += inv-mpu-iio-spi.o
inv-mpu-iio-spi-objs := inv_mpu_spi.o
obj-y += inv_test/

View file

@ -0,0 +1,117 @@
Kernel driver inv-mpu-iio
Author: InvenSense, Inc.
Table of Contents
=================
- Description
- Integrating the Driver in the Linux Kernel
- Dts file
- Communicating with the Driver in Userspace
Description
===========
This document describes how to install the Invensense device driver into a
Linux kernel. The supported chips are listed in Kconfig and user selects an
appropriate one from .e.g. menuconfig.
Integrating the Driver in the Linux Kernel
==========================================
Please add the files as follows (kernel 3.10):
- Copy mpu.h to <kernel_root>/include/linux/iio/imu/
- Copy inv_mpu folder under <kernel_root>/drivers/iio/imu/
In order to see the driver in menuconfig when building the kernel, please
make modifications as shown below:
add "source "drivers/iio/imu/inv_mpu/Kconfig""
in <kernel_root>/drivers/iio/imu/Kconfig
add "obj-y += inv_mpu/"
in <kernel_root>/drivers/iio/imu/Makefile
Dts file
========
In order to recognize the Invensense device on the I2C/SPI bus, dts(or dtsi)
file must be modified.
Example)
ICM20648 + AK09911/BMP280/APDS9930 on AUX I2C
i2c@f9968000 {
/* Invensense */
mpu6515_acc@68 {
compatible = "inven,icm20648";
reg = <0x68>;
interrupt-parent = <&msmgpio>;
interrupts = <73 0x2>;
inven,vdd_ana-supply = <&pm8941_l17>;
inven,vcc_i2c-supply = <&pm8941_lvs1>;
inven,gpio_int1 = <&msmgpio 73 0x00>;
fs_range = <0x00>;
/* mount matrix */
axis_map_x = <1>;
axis_map_y = <0>;
axis_map_z = <2>;
negate_x = <0>;
negate_y = <0>;
negate_z = <1>;
poll_interval = <200>;
min_interval = <5>;
inven,secondary_reg = <0x0c>;
/* If no compass sensor,
* replace "compass" with "none"
*/
inven,secondary_type = "compass";
inven,secondary_name = "ak09911";
inven,secondary_axis_map_x = <1>;
inven,secondary_axis_map_y = <0>;
inven,secondary_axis_map_z = <2>;
inven,secondary_negate_x = <1>;
inven,secondary_negate_y = <1>;
inven,secondary_negate_z = <1>;
/* If no pressure sensor,
* replace "pressure" with "none"
*/
inven,aux_type = "pressure";
inven,aux_name = "bmp280";
inven,aux_reg = <0x76>;
/* If no ALS sensor
* replace "als" with "none"
*/
inven,read_only_slave_type = "als";
inven,read_only_slave_name = "apds9930";
inven,read_only_slave_reg = <0x39>;
};
};
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:
(I2C) /sys/devices/*.i2c/i2c-*/*-*/iio:device*
(SPI) /sys/devices/*.spi/spi_master/spi*/spi*.*/iio:device*
Group and Owner for all entries should be updated to system/system at
boot time to allow userspace to access properly.
License
=======
Copyright (C) 2018 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.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,229 @@
/*
* Copyright (C) 2017-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _INV_MPU_IIO_REG_20680_H_
#define _INV_MPU_IIO_REG_20680_H_
/* Uncomment when HAL does not support the algorithm library
* for calibration and sensor fusion not to expose unused
* sysfs entries */
#define SUPPORT_ONLY_BASIC_FEATURES
/* Uncomment to read data registers for sensor data
* instead of FIFO */
//#define SENSOR_DATA_FROM_REGISTERS
/*register and associated bit definition*/
#define REG_XA_OFFS_H 0x77
#define REG_YA_OFFS_H 0x7A
#define REG_ZA_OFFS_H 0x7D
#define REG_XG_OFFS_USR_H 0x13
#define REG_YG_OFFS_USR_H 0x15
#define REG_ZG_OFFS_USR_H 0x17
#define REG_SAMPLE_RATE_DIV 0x19
#define REG_CONFIG 0x1A
#define EXT_SYNC_SET 8
#define REG_GYRO_CONFIG 0x1B
#define BITS_SELF_TEST_EN 0xE0
#define SHIFT_GYRO_FS_SEL 0x03
#define REG_ACCEL_CONFIG 0x1C
#define SHIFT_ACCEL_FS 0x03
#define REG_LP_MODE_CTRL 0x1E
#define BIT_GYRO_CYCLE_EN 0x80
#define REG_ACCEL_WOM_THR 0x1F
#define REG_ACCEL_WOM_X_THR 0x20
#define REG_ACCEL_WOM_Y_THR 0x21
#define REG_ACCEL_WOM_Z_THR 0x22
#define REG_ACCEL_MOT_THR 0x1F
#define REG_ACCEL_MOT_DUR 0x20
#define REG_ACCEL_CONFIG_2 0x1D
#define BIT_ACCEL_FCHOCIE_B 0x08
#define REG_FIFO_EN 0x23
#define BITS_GYRO_FIFO_EN 0x70
#define BIT_ACCEL_FIFO_EN 0x08
#define REG_FSYNC_INT 0x36
#define BIT_FSYNC_INT 0x80
#define REG_INT_PIN_CFG 0x37
#define REG_INT_ENABLE 0x38
#define BIT_WOM_X_INT_EN 0x80
#define BIT_WOM_Y_INT_EN 0x40
#define BIT_WOM_Z_INT_EN 0x20
#define BIT_WOM_ALL_INT_EN 0xE0
#define BIT_FSYNC_INT_EN 0x8
#define BIT_DATA_RDY_EN 0x1
#define REG_INT_STATUS 0x3A
#define BIT_WOM_X_INT 0x80
#define BIT_WOM_Y_INT 0x40
#define BIT_WOM_Z_INT 0x20
#define REG_RAW_ACCEL 0x3B
#define REG_RAW_TEMP 0x41
#define REG_RAW_GYRO 0x43
#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_CTRL 0x69
#define BIT_ACCEL_INTEL_EN 0x80
#define BIT_ACCEL_INTEL_MODE 0x40
#define REG_USER_CTRL 0x6A
#define BIT_COND_RST 0x01
#define BIT_FIFO_RST 0x04
#define BIT_FIFO_EN 0x40
#define REG_PWR_MGMT_1 0x6B
#define BIT_H_RESET 0x80
#define BIT_SLEEP 0x40
#define BIT_LP_EN 0x20
#define BIT_CLK_PLL 0x01
#define BIT_CLK_MASK 0x07
#define REG_PWR_MGMT_2 0x6C
#define BIT_PWR_ACCEL_STBY 0x38
#define BIT_PWR_GYRO_STBY 0x07
#define BIT_PWR_ALL_OFF 0x3F
#define BIT_FIFO_LP_EN 0x80
#define REG_MEM_BANK_SEL 0x6D
#define REG_MEM_START_ADDR 0x6E
#define REG_MEM_R_W 0x6F
#define REG_FIFO_COUNT_H 0x72
#define REG_FIFO_R_W 0x74
#define REG_WHO_AM_I 0x75
#define REG_6500_XG_ST_DATA 0x50
#define REG_6500_XA_ST_DATA 0xD
#define REG_6500_XA_OFFS_H 0x77
#define REG_6500_YA_OFFS_H 0x7A
#define REG_6500_ZA_OFFS_H 0x7D
#define REG_6500_ACCEL_CONFIG2 0x1D
#define BIT_ACCEL_FCHOCIE_B 0x08
#define BIT_FIFO_SIZE_1K 0x40
#define REG_LP_MODE_CFG 0x1E
#define REG_6500_LP_ACCEL_ODR 0x1E
#define REG_6500_ACCEL_WOM_THR 0x1F
/* data output control reg 2 */
#define ACCEL_ACCURACY_SET 0x4000
#define GYRO_ACCURACY_SET 0x2000
#define CPASS_ACCURACY_SET 0x1000
/* data definitions */
#define ACCEL_COVARIANCE 0
#define BYTES_PER_SENSOR 6
#define BYTES_FOR_TEMP 2
#define FIFO_COUNT_BYTE 2
#define HARDWARE_FIFO_SIZE 512
#define FIFO_SIZE (HARDWARE_FIFO_SIZE * 7 / 8)
#define POWER_UP_TIME 100
#define REG_UP_TIME_USEC 100
#define LEFT_OVER_BYTES 128
#define IIO_BUFFER_BYTES 8
#define BASE_SAMPLE_RATE 1000
#define DRY_RUN_TIME 50
#define INV_IAM20680_GYRO_START_TIME 35
#define INV_IAM20680_ACCEL_START_TIME 30
#define MODE_1K_INIT_SAMPLE 5
#define FIRST_SAMPLE_BUF_MS 30
#ifdef BIAS_CONFIDENCE_HIGH
#define DEFAULT_ACCURACY 3
#else
#define DEFAULT_ACCURACY 1
#endif
/* temperature */
#define TEMP_SENSITIVITY 32680 // 326.8 LSB/degC * 100
#define TEMP_OFFSET 2500 // 25 degC * 100
/* enum for sensor */
enum INV_SENSORS {
SENSOR_ACCEL = 0,
SENSOR_TEMP,
SENSOR_GYRO,
SENSOR_COMPASS,
SENSOR_NUM_MAX,
SENSOR_INVALID,
};
enum inv_filter_e {
INV_FILTER_256HZ_NOLPF2 = 0,
INV_FILTER_188HZ,
INV_FILTER_98HZ,
INV_FILTER_42HZ,
INV_FILTER_20HZ,
INV_FILTER_10HZ,
INV_FILTER_5HZ,
INV_FILTER_2100HZ_NOLPF,
NUM_FILTER
};
#define MPU_DEFAULT_DMP_FREQ 200
#define PEDOMETER_FREQ (MPU_DEFAULT_DMP_FREQ >> 2)
#define SENSOR_FUSION_MIN_RATE 100
#define GESTURE_ACCEL_RATE 50
#define ESI_GYRO_RATE 1000
#define MAX_FIFO_PACKET_READ 6
#define MAX_BATCH_FIFO_SIZE 896
#define MIN_MST_ODR_CONFIG 4
#define MAX_MST_ODR_CONFIG 5
/* initial rate is important. For non-DMP mode, it is set as 4 1000/256*/
#define MPU_INIT_SENSOR_RATE 4
#define MAX_MST_NON_COMPASS_ODR_CONFIG 7
#define THREE_AXES 3
#define NINE_ELEM (THREE_AXES * THREE_AXES)
#define MPU_TEMP_SHIFT 16
#define DMP_DIVIDER (BASE_SAMPLE_RATE / MPU_DEFAULT_DMP_FREQ)
#define DEFAULT_BATCH_RATE 400
#define DEFAULT_BATCH_TIME (MSEC_PER_SEC / DEFAULT_BATCH_RATE)
#define TEMPERATURE_SCALE 3340827L
#define TEMPERATURE_OFFSET 1376256L
#define SECONDARY_INIT_WAIT 100
#define MPU_SOFT_REV_ADDR 0x86
#define MPU_SOFT_REV_MASK 0xf
#define SW_REV_LP_EN_MODE 4
/* data limit definitions */
#define MIN_FIFO_RATE 4
#define MAX_FIFO_RATE MPU_DEFAULT_DMP_FREQ
#define MAX_MPU_MEM 8192
#define MAX_PRS_RATE 281
enum inv_devices {
ICM20608D,
ICM20690,
ICM20602,
IAM20680,
INV_NUM_PARTS,
};
#endif

View file

@ -0,0 +1,254 @@
/*
* Copyright (C) 2017-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include "../inv_mpu_iio.h"
static int inv_calc_gyro_sf(s8 pll)
{
int a, r;
int value, t;
t = 102870L + 81L * pll;
a = (1L << 30) / t;
r = (1L << 30) - a * t;
value = a * 797 * DMP_DIVIDER;
value += (s64) ((a * 1011387LL * DMP_DIVIDER) >> 20);
value += r * 797L * DMP_DIVIDER / t;
value += (s32) ((s64) ((r * 1011387LL * DMP_DIVIDER) >> 20)) / t;
value <<= 1;
return value;
}
static int inv_read_timebase(struct inv_mpu_state *st)
{
inv_plat_single_write(st, REG_CONFIG, 3);
st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
st->eng_info[ENGINE_ACCEL].base_time_1k = NSEC_PER_SEC;
/* talor expansion to calculate base time unit */
st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
st->eng_info[ENGINE_GYRO].base_time_1k = NSEC_PER_SEC;
st->eng_info[ENGINE_I2C].base_time = NSEC_PER_SEC;
st->eng_info[ENGINE_I2C].base_time_1k = NSEC_PER_SEC;
st->eng_info[ENGINE_ACCEL].orig_rate = BASE_SAMPLE_RATE;
st->eng_info[ENGINE_GYRO].orig_rate = BASE_SAMPLE_RATE;
st->eng_info[ENGINE_I2C].orig_rate = BASE_SAMPLE_RATE;
st->gyro_sf = inv_calc_gyro_sf(0);
return 0;
}
int inv_set_gyro_sf(struct inv_mpu_state *st)
{
int result;
result = inv_plat_single_write(st, REG_GYRO_CONFIG,
st->chip_config.fsr << SHIFT_GYRO_FS_SEL);
return result;
}
int inv_set_accel_sf(struct inv_mpu_state *st)
{
int result;
result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
st->chip_config.accel_fs << SHIFT_ACCEL_FS);
return result;
}
// dummy for 20602
int inv_set_accel_intel(struct inv_mpu_state *st)
{
return 0;
}
static void inv_init_sensor_struct(struct inv_mpu_state *st)
{
int i;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
st->sensor[SENSOR_ACCEL].sample_size = BYTES_PER_SENSOR;
st->sensor[SENSOR_TEMP].sample_size = BYTES_FOR_TEMP;
st->sensor[SENSOR_GYRO].sample_size = BYTES_PER_SENSOR;
st->sensor_l[SENSOR_L_SIXQ].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_PEDQ].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_SIXQ_WAKE].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_PEDQ_WAKE].base = SENSOR_GYRO;
st->sensor[SENSOR_ACCEL].a_en = true;
st->sensor[SENSOR_GYRO].a_en = false;
st->sensor[SENSOR_ACCEL].g_en = false;
st->sensor[SENSOR_GYRO].g_en = true;
st->sensor[SENSOR_ACCEL].c_en = false;
st->sensor[SENSOR_GYRO].c_en = false;
st->sensor[SENSOR_ACCEL].p_en = false;
st->sensor[SENSOR_GYRO].p_en = false;
st->sensor[SENSOR_ACCEL].engine_base = ENGINE_ACCEL;
st->sensor[SENSOR_GYRO].engine_base = ENGINE_GYRO;
st->sensor_l[SENSOR_L_ACCEL].base = SENSOR_ACCEL;
st->sensor_l[SENSOR_L_GESTURE_ACCEL].base = SENSOR_ACCEL;
st->sensor_l[SENSOR_L_GYRO].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_GYRO_CAL].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_EIS_GYRO].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_ACCEL_WAKE].base = SENSOR_ACCEL;
st->sensor_l[SENSOR_L_GYRO_WAKE].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].base = SENSOR_GYRO;
st->sensor_l[SENSOR_L_ACCEL].header = ACCEL_HDR;
st->sensor_l[SENSOR_L_GESTURE_ACCEL].header = ACCEL_HDR;
st->sensor_l[SENSOR_L_GYRO].header = GYRO_HDR;
st->sensor_l[SENSOR_L_GYRO_CAL].header = GYRO_CALIB_HDR;
st->sensor_l[SENSOR_L_EIS_GYRO].header = EIS_GYRO_HDR;
st->sensor_l[SENSOR_L_SIXQ].header = SIXQUAT_HDR;
st->sensor_l[SENSOR_L_THREEQ].header = LPQ_HDR;
st->sensor_l[SENSOR_L_NINEQ].header = NINEQUAT_HDR;
st->sensor_l[SENSOR_L_PEDQ].header = PEDQUAT_HDR;
st->sensor_l[SENSOR_L_ACCEL_WAKE].header = ACCEL_WAKE_HDR;
st->sensor_l[SENSOR_L_GYRO_WAKE].header = GYRO_WAKE_HDR;
st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].header = GYRO_CALIB_WAKE_HDR;
st->sensor_l[SENSOR_L_MAG_WAKE].header = COMPASS_WAKE_HDR;
st->sensor_l[SENSOR_L_MAG_CAL_WAKE].header = COMPASS_CALIB_WAKE_HDR;
st->sensor_l[SENSOR_L_SIXQ_WAKE].header = SIXQUAT_WAKE_HDR;
st->sensor_l[SENSOR_L_NINEQ_WAKE].header = NINEQUAT_WAKE_HDR;
st->sensor_l[SENSOR_L_PEDQ_WAKE].header = PEDQUAT_WAKE_HDR;
st->sensor_l[SENSOR_L_ACCEL].wake_on = false;
st->sensor_l[SENSOR_L_GYRO].wake_on = false;
st->sensor_l[SENSOR_L_GYRO_CAL].wake_on = false;
st->sensor_l[SENSOR_L_MAG].wake_on = false;
st->sensor_l[SENSOR_L_MAG_CAL].wake_on = false;
st->sensor_l[SENSOR_L_EIS_GYRO].wake_on = false;
st->sensor_l[SENSOR_L_SIXQ].wake_on = false;
st->sensor_l[SENSOR_L_NINEQ].wake_on = false;
st->sensor_l[SENSOR_L_PEDQ].wake_on = false;
st->sensor_l[SENSOR_L_ACCEL_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_GYRO_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_MAG_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_SIXQ_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_NINEQ_WAKE].wake_on = true;
st->sensor_l[SENSOR_L_PEDQ_WAKE].wake_on = true;
}
static int inv_init_config(struct inv_mpu_state *st)
{
int res, i;
st->batch.overflow_on = 0;
st->chip_config.fsr = MPU_INIT_GYRO_SCALE;
st->chip_config.accel_fs = MPU_INIT_ACCEL_SCALE;
st->ped.int_thresh = MPU_INIT_PED_INT_THRESH;
st->ped.step_thresh = MPU_INIT_PED_STEP_THRESH;
st->chip_config.low_power_gyro_on = 1;
st->eis.count_precision = NSEC_PER_MSEC;
st->firmware = 0;
st->fifo_count_mode = BYTE_MODE;
st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
inv_init_sensor_struct(st);
res = inv_read_timebase(st);
if (res)
return res;
res = inv_set_gyro_sf(st);
if (res)
return res;
res = inv_set_accel_sf(st);
if (res)
return res;
res = inv_set_accel_intel(st);
if (res)
return res;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].ts = 0;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].previous_ts = 0;
return res;
}
int inv_mpu_initialize(struct inv_mpu_state *st)
{
u8 v;
int result;
struct inv_chip_config_s *conf;
struct mpu_platform_data *plat;
conf = &st->chip_config;
plat = &st->plat_data;
/* verify whoami */
result = inv_plat_read(st, REG_WHO_AM_I, 1, &v);
if (result)
return result;
pr_info("whoami= %x\n", v);
if (v == 0x00 || v == 0xff)
return -ENODEV;
/* reset to make sure previous state are not there */
result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
if (result)
return result;
usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
msleep(100);
/* toggle power state */
result = inv_set_power(st, false);
if (result)
return result;
result = inv_set_power(st, true);
if (result)
return result;
result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
if (result)
return result;
result = inv_init_config(st);
if (result)
return result;
result = mem_r(MPU_SOFT_REV_ADDR, 1, &v);
pr_info("sw_rev=%x, res=%d\n", v, result);
if (result)
return result;
st->chip_config.lp_en_mode_off = 0;
pr_info("%s: Mask %X, v = %X, lp mode = %d\n", __func__,
MPU_SOFT_REV_MASK, v, st->chip_config.lp_en_mode_off);
result = inv_set_power(st, false);
pr_info("%s: initialize result is %d....\n", __func__, result);
return 0;
}

View file

@ -0,0 +1,388 @@
/*
* Copyright (C) 2017-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.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/math64.h>
#include "../inv_mpu_iio.h"
static char iden[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
static int inv_process_gyro(struct inv_mpu_state *st, u8 *d, u64 t)
{
s16 raw[3];
s32 calib[3];
int i;
#define BIAS_UNIT 2859
for (i = 0; i < 3; i++)
raw[i] = be16_to_cpup((__be16 *) (d + i * 2));
for (i = 0; i < 3; i++)
calib[i] = (raw[i] << 15);
inv_push_gyro_data(st, raw, calib, t);
return 0;
}
static int inv_check_fsync(struct inv_mpu_state *st, u8 fsync_status)
{
u8 data[1];
if (!st->chip_config.eis_enable)
return 0;
inv_plat_read(st, REG_FSYNC_INT, 1, data);
if (data[0] & BIT_FSYNC_INT) {
pr_debug("fsync\n");
st->eis.eis_triggered = true;
st->eis.fsync_delay = 1;
st->eis.prev_state = 1;
st->eis.frame_count++;
st->eis.eis_frame = true;
}
st->header_count--;
return 0;
}
static int inv_push_sensor(struct inv_mpu_state *st, int ind, u64 t, u8 *d)
{
#ifdef ACCEL_BIAS_TEST
s16 acc[3], avg[3];
#endif
switch (ind) {
case SENSOR_ACCEL:
inv_convert_and_push_8bytes(st, ind, d, t, iden);
#ifdef ACCEL_BIAS_TEST
acc[0] = be16_to_cpup((__be16 *) (d));
acc[1] = be16_to_cpup((__be16 *) (d + 2));
acc[2] = be16_to_cpup((__be16 *) (d + 4));
if(inv_get_3axis_average(acc, avg, 0)){
pr_debug("accel 200 samples average = %5d, %5d, %5d\n", avg[0], avg[1], avg[2]);
}
#endif
break;
case SENSOR_TEMP:
inv_check_fsync(st, d[1]);
break;
case SENSOR_GYRO:
inv_process_gyro(st, d, t);
break;
default:
break;
}
return 0;
}
static int inv_push_20680_data(struct inv_mpu_state *st, u8 *d)
{
u8 *dptr;
int i;
dptr = d;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
inv_get_dmp_ts(st, i);
if (st->sensor[i].send && (!st->ts_algo.first_sample)) {
st->sensor[i].sample_calib++;
inv_push_sensor(st, i, st->sensor[i].ts, dptr);
}
dptr += st->sensor[i].sample_size;
}
}
if (st->ts_algo.first_sample)
st->ts_algo.first_sample--;
st->header_count--;
return 0;
}
static int inv_process_20680_data(struct inv_mpu_state *st)
{
int total_bytes, tmp, res, fifo_count, pk_size, i;
u8 *dptr, *d;
u8 data[14];
bool done_flag;
u8 v;
#ifdef SENSOR_DATA_FROM_REGISTERS
u8 reg;
int len;
#endif
if(st->gesture_only_on && (!st->batch.timeout)) {
res = inv_plat_read(st, REG_INT_STATUS, 1, data);
if (res)
return res;
pr_debug("ges cnt=%d, statu=%x\n",
st->gesture_int_count, data[0]);
if (data[0] & (BIT_WOM_ALL_INT_EN)) {
if (!st->gesture_int_count) {
inv_switch_power_in_lp(st, true);
res = inv_plat_single_write(st, REG_INT_ENABLE,
BIT_WOM_ALL_INT_EN | BIT_DATA_RDY_EN);
if (res)
return res;
v = 0;
if (st->chip_config.gyro_enable)
v |= BITS_GYRO_FIFO_EN;
if (st->chip_config.accel_enable)
v |= BIT_ACCEL_FIFO_EN;
res = inv_plat_single_write(st, REG_FIFO_EN, v);
if (res)
return res;
/* First time wake up from WOM.
We don't need data in the FIFO */
res = inv_reset_fifo(st, true);
if (res)
return res;
res = inv_switch_power_in_lp(st, false);
st->gesture_int_count = WOM_DELAY_THRESHOLD;
return res;
}
st->gesture_int_count = WOM_DELAY_THRESHOLD;
} else {
if (!st->gesture_int_count) {
inv_switch_power_in_lp(st, true);
res = inv_plat_single_write(st, REG_FIFO_EN, 0);
res = inv_plat_single_write(st, REG_INT_ENABLE,
BIT_WOM_ALL_INT_EN);
inv_switch_power_in_lp(st, false);
return res;
}
st->gesture_int_count--;
}
}
fifo_count = inv_get_last_run_time_non_dmp_record_mode(st);
pr_debug("fifc= %d\n", fifo_count);
if (!fifo_count) {
pr_debug("REG_FIFO_COUNT_H size is 0\n");
return 0;
}
pk_size = st->batch.pk_size;
if (!pk_size)
return -EINVAL;
fifo_count *= st->batch.pk_size;
st->fifo_count = fifo_count;
d = st->fifo_data_store;
dptr = d;
total_bytes = fifo_count;
#ifdef SENSOR_DATA_FROM_REGISTERS
len = 0;
if (st->sensor[SENSOR_GYRO].on) {
reg = REG_RAW_GYRO;
len += BYTES_PER_SENSOR;
if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
len += BYTES_FOR_TEMP;
}
if (st->sensor[SENSOR_TEMP].on) {
reg = REG_RAW_TEMP;
len += BYTES_FOR_TEMP;
}
if (st->sensor[SENSOR_ACCEL].on) {
reg = REG_RAW_ACCEL;
len += BYTES_PER_SENSOR;
}
if (len == 0) {
pr_debug("No sensor is enabled\n");
return 0;
}
/* read data registers */
res = inv_plat_read(st, reg, len, data);
if (res < 0) {
pr_err("read data registers is failed\n");
return res;
}
/* copy sensor data to buffer as FIFO data format */
tmp = 0;
if (st->sensor[SENSOR_ACCEL].on) {
for (i = 0; i < BYTES_PER_SENSOR; i++)
dptr[i] = data[tmp + i];
dptr += BYTES_PER_SENSOR;
tmp += BYTES_PER_SENSOR;
}
if (st->sensor[SENSOR_TEMP].on) {
for (i = 0; i < BYTES_FOR_TEMP; i++)
dptr[i] = data[tmp + i];
dptr += BYTES_FOR_TEMP;
tmp += BYTES_FOR_TEMP;
}
if (st->sensor[SENSOR_GYRO].on) {
if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
tmp += BYTES_FOR_TEMP;
for (i = 0; i < BYTES_PER_SENSOR; i++)
dptr[i] = data[tmp + i];
}
#else
while (total_bytes > 0) {
if (total_bytes < pk_size * MAX_FIFO_PACKET_READ)
tmp = total_bytes;
else
tmp = pk_size * MAX_FIFO_PACKET_READ;
res = inv_plat_read(st, REG_FIFO_R_W, tmp, dptr);
if (res < 0) {
pr_err("read REG_FIFO_R_W is failed\n");
return res;
}
pr_debug("inside: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[0], dptr[1], dptr[2],
dptr[3], dptr[4], dptr[5], dptr[6], dptr[7]);
pr_debug("insid2: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[8], dptr[9], dptr[10],
dptr[11], dptr[12], dptr[13], dptr[14], dptr[15]);
dptr += tmp;
total_bytes -= tmp;
}
#endif /* SENSOR_DATA_FROM_REGISTERS */
dptr = d;
pr_debug("dd: %x, %x, %x, %x, %x, %x, %x, %x\n", d[0], d[1], d[2],
d[3], d[4], d[5], d[6], d[7]);
pr_debug("dd2: %x, %x, %x, %x, %x, %x, %x, %x\n", d[8], d[9], d[10],
d[11], d[12], d[13], d[14], d[15]);
total_bytes = fifo_count;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
st->sensor[i].count = total_bytes / pk_size;
}
}
st->header_count = 0;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on)
st->header_count = max(st->header_count,
st->sensor[i].count);
}
st->ts_algo.calib_counter++;
inv_bound_timestamp(st);
dptr = d;
done_flag = false;
while (!done_flag) {
pr_debug("total%d, pk=%d\n", total_bytes, pk_size);
if (total_bytes >= pk_size) {
res = inv_push_20680_data(st, dptr);
if (res)
return res;
total_bytes -= pk_size;
dptr += pk_size;
} else {
done_flag = true;
}
}
return 0;
}
/*
* inv_read_fifo() - Transfer data from FIFO to ring buffer.
*/
irqreturn_t inv_read_fifo(int irq, void *dev_id)
{
struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
struct iio_dev *indio_dev = iio_priv_to_dev(st);
int result;
result = wait_event_interruptible_timeout(st->wait_queue,
st->resume_state, msecs_to_jiffies(300));
if (result <= 0)
return IRQ_HANDLED;
mutex_lock(&indio_dev->mlock);
st->wake_sensor_received = false;
result = inv_process_20680_data(st);
if (result)
goto err_reset_fifo;
mutex_unlock(&indio_dev->mlock);
if (st->wake_sensor_received)
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
#else
__pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
#endif
return IRQ_HANDLED;
err_reset_fifo:
if ((!st->chip_config.gyro_enable) &&
(!st->chip_config.accel_enable) &&
(!st->chip_config.slave_enable) &&
(!st->chip_config.pressure_enable)) {
inv_switch_power_in_lp(st, false);
mutex_unlock(&indio_dev->mlock);
return IRQ_HANDLED;
}
pr_err("error to reset fifo\n");
inv_switch_power_in_lp(st, true);
inv_reset_fifo(st, true);
inv_switch_power_in_lp(st, false);
mutex_unlock(&indio_dev->mlock);
return IRQ_HANDLED;
}
int inv_flush_batch_data(struct iio_dev *indio_dev, int data)
{
struct inv_mpu_state *st = iio_priv(indio_dev);
#ifndef SENSOR_DATA_FROM_REGISTERS
if (st->chip_config.gyro_enable ||
st->chip_config.accel_enable ||
st->chip_config.slave_enable ||
st->chip_config.pressure_enable) {
st->wake_sensor_received = 0;
inv_process_20680_data(st);
if (st->wake_sensor_received)
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
#else
__pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
#endif
inv_switch_power_in_lp(st, false);
}
#endif /* SENSOR_DATA_FROM_REGISTERS */
inv_push_marker_to_buffer(st, END_MARKER, data);
return 0;
}

View file

@ -0,0 +1,752 @@
/*
* Copyright (C) 2017-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include "../inv_mpu_iio.h"
/* register settings */
#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
/* wait time before collecting data */
#define MAX_PACKETS 20
#define SELFTEST_WAIT_TIME (MAX_PACKETS * 10)
#define DEF_ST_STABLE_TIME 20
#define DEF_GYRO_SCALE 131
#define DEF_ST_PRECISION 1000
#define DEF_ST_ACCEL_FS_MG 2000UL
#define DEF_ST_SCALE 32768
#define DEF_ST_TRY_TIMES 2
#define DEF_ST_ACCEL_RESULT_SHIFT 1
#define DEF_ST_SAMPLES 200
#define DEF_ACCEL_ST_SHIFT_DELTA_MIN 500
#define DEF_ACCEL_ST_SHIFT_DELTA_MAX 1500
#define DEF_GYRO_CT_SHIFT_DELTA 500
#define SENSOR_UP_TIME 30
#define REG_UP_TIME 2
#define DEF_ST_ACCEL_FS_MG 2000UL
#define DEF_ACCEL_ST_SHIFT_DELTA 500
#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
/ DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
/ DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
#define THREE_AXIS 3
#define DEF_ST_MPU6500_ACCEL_LPF 2
#define DEF_SELFTEST_SAMPLE_RATE 0 /* 1000Hz */
#define DEF_SELFTEST_SAMPLE_RATE_LP 3 /* 250Hz */
#define DEF_SELFTEST_SAMPLE_RATE_ACC_LP 10 /* 250Hz LPOSC_CLKSEL */
#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
#define DEF_SELFTEST_6500_ACCEL_FS (0 << 3)
#define DEF_SELFTEST_GYRO_FS (0 << 3)
#define DEF_ST_6500_STABLE_TIME 20
#define BIT_ACCEL_OUT 0x08
#define BITS_GYRO_OUT 0x70
#define THREE_AXIS 3
#define DEF_GYRO_WAIT_TIME 10
#define DEF_GYRO_WAIT_TIME_LP 50
/* Gyro Offset Max Value (dps) */
#define DEF_GYRO_OFFSET_MAX 20
/* Gyro Self Test Absolute Limits ST_AL (dps) */
#define DEF_GYRO_ST_AL 60
/* Accel Self Test Absolute Limits ST_AL (mg) */
#define DEF_ACCEL_ST_AL_MIN 225
#define DEF_ACCEL_ST_AL_MAX 675
struct recover_regs {
u8 int_enable; /* REG_INT_ENABLE */
u8 fifo_en; /* REG_FIFO_EN */
u8 user_ctrl; /* REG_USER_CTRL */
u8 config; /* REG_CONFIG */
u8 gyro_config; /* REG_GYRO_CONFIG */
u8 accel_config; /* REG_ACCEL_CONFIG */
u8 accel_config_2; /* REG_ACCEL_CONFIG_2 */
u8 smplrt_div; /* REG_SAMPLE_RATE_DIV */
u8 lp_mode; /* REG_LP_MODE_CTRL */
u8 pwr_mgmt_1; /* REG_PWR_MGMT_1 */
u8 pwr_mgmt_2; /* REG_PWR_MGMT_2 */
};
static struct recover_regs saved_regs;
static const u16 mpu_st_tb[256] = {
2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
30903, 31212, 31524, 31839, 32157, 32479, 32804
};
static void inv_show_saved_setting(struct inv_mpu_state *st)
{
pr_debug(" REG_INT_ENABLE : 0x%02X\n", saved_regs.int_enable);
pr_debug(" REG_FIFO_EN : 0x%02X\n", saved_regs.fifo_en);
pr_debug(" REG_USER_CTRL : 0x%02X\n", saved_regs.user_ctrl);
pr_debug(" REG_CONFIG : 0x%02X\n", saved_regs.config);
pr_debug(" REG_GYRO_CONFIG : 0x%02X\n", saved_regs.gyro_config);
pr_debug(" REG_ACCEL_CONFIG : 0x%02X\n", saved_regs.accel_config);
pr_debug(" REG_ACCEL_CONFIG_2 : 0x%02X\n", saved_regs.accel_config_2);
pr_debug(" REG_SAMPLE_RATE_DIV : 0x%02X\n", saved_regs.smplrt_div);
pr_debug(" REG_LP_MODE_CTRL : 0x%02X\n", saved_regs.lp_mode);
pr_debug(" REG_PWR_MGMT_1 : 0x%02X\n", saved_regs.pwr_mgmt_1);
pr_debug(" REG_PWR_MGMT_2 : 0x%02X\n", saved_regs.pwr_mgmt_2);
}
static int inv_save_setting(struct inv_mpu_state *st)
{
int result;
result = inv_plat_read(st, REG_PWR_MGMT_1, 1,
&saved_regs.pwr_mgmt_1);
if (result)
return result;
/* wake up */
result = inv_plat_single_write(st, REG_PWR_MGMT_1,
(saved_regs.pwr_mgmt_1 & ~BIT_SLEEP));
if (result)
return result;
result = inv_plat_read(st, REG_INT_ENABLE, 1,
&saved_regs.int_enable);
if (result)
return result;
result = inv_plat_read(st, REG_FIFO_EN, 1,
&saved_regs.fifo_en);
if (result)
return result;
result = inv_plat_read(st, REG_USER_CTRL, 1,
&saved_regs.user_ctrl);
if (result)
return result;
result = inv_plat_read(st, REG_CONFIG, 1,
&saved_regs.config);
if (result)
return result;
result = inv_plat_read(st, REG_GYRO_CONFIG, 1,
&saved_regs.gyro_config);
if (result)
return result;
result = inv_plat_read(st, REG_ACCEL_CONFIG, 1,
&saved_regs.accel_config);
if (result)
return result;
result = inv_plat_read(st, REG_ACCEL_CONFIG_2, 1,
&saved_regs.accel_config_2);
if (result)
return result;
result = inv_plat_read(st, REG_SAMPLE_RATE_DIV, 1,
&saved_regs.smplrt_div);
if (result)
return result;
result = inv_plat_read(st, REG_LP_MODE_CTRL, 1,
&saved_regs.lp_mode);
if (result)
return result;
result = inv_plat_read(st, REG_PWR_MGMT_2, 1,
&saved_regs.pwr_mgmt_2);
if (result)
return result;
inv_show_saved_setting(st);
return result;
}
static int inv_recover_setting(struct inv_mpu_state *st)
{
int result;
/* Stop sensors */
result = inv_plat_single_write(st, REG_PWR_MGMT_2,
BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY);
if (result)
return result;
/* Restore sensor configurations */
result = inv_plat_single_write(st, REG_INT_ENABLE,
saved_regs.int_enable);
if (result)
return result;
result = inv_plat_single_write(st, REG_FIFO_EN,
saved_regs.fifo_en);
if (result)
return result;
result = inv_plat_single_write(st, REG_USER_CTRL,
saved_regs.user_ctrl);
if (result)
return result;
result = inv_plat_single_write(st, REG_CONFIG,
saved_regs.config);
if (result)
return result;
result = inv_plat_single_write(st, REG_GYRO_CONFIG,
saved_regs.gyro_config);
if (result)
return result;
result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
saved_regs.accel_config);
if (result)
return result;
result = inv_plat_single_write(st, REG_ACCEL_CONFIG_2,
saved_regs.accel_config_2);
if (result)
return result;
result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
saved_regs.smplrt_div);
if (result)
return result;
result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
saved_regs.lp_mode);
if (result)
return result;
result = inv_plat_single_write(st, REG_PWR_MGMT_1,
saved_regs.pwr_mgmt_1);
if (result)
return result;
result = inv_plat_single_write(st, REG_PWR_MGMT_2,
saved_regs.pwr_mgmt_2);
if (result)
return result;
return result;
}
int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask)
{
u8 data, mgmt_1;
int result;
if (BIT_PWR_GYRO_STBY == mask) {
result = inv_plat_read(st, REG_PWR_MGMT_1, 1, &mgmt_1);
if (result)
return result;
mgmt_1 &= ~BIT_CLK_MASK;
}
if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
if (result)
return result;
}
result = inv_plat_read(st, REG_PWR_MGMT_2, 1, &data);
if (result)
return result;
if (en)
data &= (~mask);
else
data |= mask;
data |= BIT_FIFO_LP_EN;
result = inv_plat_single_write(st, REG_PWR_MGMT_2, data);
if (result)
return result;
if ((BIT_PWR_GYRO_STBY == mask) && en) {
/* only gyro on needs sensor up time */
msleep(SENSOR_UP_TIME);
/* after gyro is on & stable, switch internal clock to PLL */
mgmt_1 |= BIT_CLK_PLL;
result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
if (result)
return result;
}
if ((BIT_PWR_ACCEL_STBY == mask) && en)
msleep(REG_UP_TIME);
return 0;
}
int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val)
{
int result;
u8 d;
d = ((val >> 8) & 0xff);
result = inv_plat_single_write(st, reg, d);
if (result)
return result;
d = (val & 0xff);
result = inv_plat_single_write(st, reg + 1, d);
return result;
}
/**
* inv_check_gyro_self_test() - check gyro self test. this function
* returns zero as success. A non-zero return
* value indicates failure in self test.
* @*st: main data structure.
* @*reg_avg: average value of normal test.
* @*st_avg: average value of self test
*/
int inv_check_gyro_self_test(struct inv_mpu_state *st,
int *reg_avg, int *st_avg) {
u8 regs[3];
int ret_val, result;
int otp_value_zero = 0;
int st_shift_prod[3], st_shift_cust[3], i;
ret_val = 0;
result = inv_plat_read(st, REG_6500_XG_ST_DATA, 3, regs);
if (result)
return result;
pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
st->hw->name, regs[0], regs[1], regs[2]);
for (i = 0; i < 3; i++) {
if (regs[i] != 0) {
st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
} else {
st_shift_prod[i] = 0;
otp_value_zero = 1;
}
}
pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
st->hw->name, st_shift_prod[0], st_shift_prod[1],
st_shift_prod[2]);
for (i = 0; i < 3; i++) {
st_shift_cust[i] = st_avg[i] - reg_avg[i];
if (!otp_value_zero) {
/* Self Test Pass/Fail Criteria A */
if (st_shift_cust[i] < DEF_GYRO_CT_SHIFT_DELTA
* st_shift_prod[i])
ret_val = 1;
} else {
/* Self Test Pass/Fail Criteria B */
if (st_shift_cust[i] < DEF_GYRO_ST_AL *
DEF_SELFTEST_GYRO_SENS *
DEF_ST_PRECISION)
ret_val = 1;
}
}
pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
st->hw->name, st_shift_cust[0], st_shift_cust[1],
st_shift_cust[2]);
if (ret_val == 0) {
/* Self Test Pass/Fail Criteria C */
for (i = 0; i < 3; i++)
if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
DEF_SELFTEST_GYRO_SENS *
DEF_ST_PRECISION)
ret_val = 1;
}
return ret_val;
}
/**
* inv_check_accel_self_test() - check 6500 accel self test. this function
* returns zero as success. A non-zero return
* value indicates failure in self test.
* @*st: main data structure.
* @*reg_avg: average value of normal test.
* @*st_avg: average value of self test
*/
int inv_check_accel_self_test(struct inv_mpu_state *st,
int *reg_avg, int *st_avg) {
int ret_val, result;
int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
u8 regs[3];
int otp_value_zero = 0;
ret_val = 0;
result = inv_plat_read(st, REG_6500_XA_ST_DATA, 3, regs);
if (result)
return result;
pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
st->hw->name, regs[0], regs[1], regs[2]);
for (i = 0; i < 3; i++) {
if (regs[i] != 0) {
st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
} else {
st_shift_prod[i] = 0;
otp_value_zero = 1;
}
}
pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
st->hw->name, st_shift_prod[0], st_shift_prod[1],
st_shift_prod[2]);
if (!otp_value_zero) {
/* Self Test Pass/Fail Criteria A */
for (i = 0; i < 3; i++) {
st_shift_cust[i] = st_avg[i] - reg_avg[i];
st_shift_ratio[i] = abs(st_shift_cust[i] /
st_shift_prod[i] - DEF_ST_PRECISION);
if (st_shift_ratio[i] > DEF_ACCEL_ST_SHIFT_DELTA)
ret_val = 1;
}
} else {
/* Self Test Pass/Fail Criteria B */
for (i = 0; i < 3; i++) {
st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
st_shift_cust[i] > ACCEL_ST_AL_MAX)
ret_val = 1;
}
}
pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
st->hw->name, st_shift_cust[0], st_shift_cust[1],
st_shift_cust[2]);
return ret_val;
}
/*
* inv_do_test() - do the actual test of self testing
*/
int inv_do_test(struct inv_mpu_state *st, int self_test_flag,
int *gyro_result, int *accel_result, int lp_mode)
{
int result, i, j, packet_size;
u8 data[BYTES_PER_SENSOR * 2], d, dd;
int fifo_count, packet_count, ind, s;
packet_size = BYTES_PER_SENSOR * 2;
/* disable interrupt */
result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
if (result)
return result;
/* disable the sensor output to FIFO */
result = inv_plat_single_write(st, REG_FIFO_EN, 0);
if (result)
return result;
/* disable fifo reading */
result = inv_plat_single_write(st, REG_USER_CTRL, 0);
if (result)
return result;
/* clear FIFO */
result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
if (result)
return result;
/* setup parameters */
result = inv_plat_single_write(st, REG_CONFIG, INV_FILTER_98HZ);
if (result)
return result;
/* gyro lp mode */
if (lp_mode == 1)
d = BIT_GYRO_CYCLE_EN;
else if (lp_mode == 2)
d = DEF_SELFTEST_SAMPLE_RATE_ACC_LP;
else
d = 0;
result = inv_plat_single_write(st, REG_LP_MODE_CTRL, d);
if (result)
return result;
/* config accel LPF register */
if (lp_mode == 2)
d = BIT_ACCEL_FCHOCIE_B;
else
d = DEF_ST_MPU6500_ACCEL_LPF;
result = inv_plat_single_write(st, REG_6500_ACCEL_CONFIG2, d);
if (result)
return result;
if (lp_mode) {
result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
DEF_SELFTEST_SAMPLE_RATE_LP);
} else {
result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
DEF_SELFTEST_SAMPLE_RATE);
}
if (result)
return result;
/* wait for the sampling rate change to stabilize */
mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
result = inv_plat_single_write(st, REG_GYRO_CONFIG,
self_test_flag | DEF_SELFTEST_GYRO_FS);
if (result)
return result;
d = DEF_SELFTEST_6500_ACCEL_FS;
d |= self_test_flag;
result = inv_plat_single_write(st, REG_ACCEL_CONFIG, d);
if (result)
return result;
/* wait for the output to get stable */
msleep(DEF_ST_6500_STABLE_TIME);
/* enable FIFO reading */
result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
if (result)
return result;
/* enable sensor output to FIFO */
d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
for (i = 0; i < THREE_AXIS; i++) {
gyro_result[i] = 0;
accel_result[i] = 0;
}
s = 0;
while (s < 200 /*st->self_test.samples*/) {
/* Stop FIFO */
result = inv_plat_single_write(st, REG_USER_CTRL, 0);
if (result)
return result;
/* clear FIFO */
result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
if (result)
return result;
/* enable FIFO reading */
result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
if (result)
return result;
/* accel lp mode */
dd = BIT_CLK_PLL;
if (lp_mode == 2)
dd |= BIT_LP_EN;
else
dd &= ~BIT_LP_EN;
result = inv_plat_single_write(st, REG_PWR_MGMT_1, dd);
if (result)
return result;
result = inv_plat_single_write(st, REG_FIFO_EN, d);
if (result)
return result;
if (lp_mode)
mdelay(DEF_GYRO_WAIT_TIME_LP);
else
mdelay(DEF_GYRO_WAIT_TIME);
result = inv_plat_single_write(st, REG_FIFO_EN, 0);
if (result)
return result;
result = inv_plat_read(st, REG_FIFO_COUNT_H,
FIFO_COUNT_BYTE, data);
if (result)
return result;
fifo_count = be16_to_cpup((__be16 *)(&data[0]));
pr_debug("%s self_test fifo_count - %d\n",
st->hw->name, fifo_count);
packet_count = fifo_count / packet_size;
i = 0;
while ((i < packet_count) && (s < 200 /*st->self_test.samples*/)) {
short vals[3];
result = inv_plat_read(st, REG_FIFO_R_W,
packet_size, data);
if (result)
return result;
ind = 0;
for (j = 0; j < THREE_AXIS; j++) {
vals[j] = (short)be16_to_cpup(
(__be16 *)(&data[ind + 2 * j]));
accel_result[j] += vals[j];
}
ind += BYTES_PER_SENSOR;
pr_debug(
"%s self_test accel data - %d %+d %+d %+d",
st->hw->name, s, vals[0], vals[1], vals[2]);
for (j = 0; j < THREE_AXIS; j++) {
vals[j] = (short)be16_to_cpup(
(__be16 *)(&data[ind + 2 * j]));
gyro_result[j] += vals[j];
}
pr_debug("%s self_test gyro data - %d %+d %+d %+d",
st->hw->name, s, vals[0], vals[1], vals[2]);
s++;
i++;
}
}
for (j = 0; j < THREE_AXIS; j++) {
accel_result[j] = accel_result[j] / s;
accel_result[j] *= DEF_ST_PRECISION;
}
for (j = 0; j < THREE_AXIS; j++) {
gyro_result[j] = gyro_result[j] / s;
gyro_result[j] *= DEF_ST_PRECISION;
}
return 0;
}
int inv_power_up_self_test(struct inv_mpu_state *st)
{
int result;
result = inv_switch_power_in_lp(st, true);
/* make sure no interrupts */
result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
if (result)
return result;
if (result)
return result;
result = inv_switch_engine(st, true, BIT_PWR_ACCEL_STBY);
if (result)
return result;
result = inv_switch_engine(st, true, BIT_PWR_GYRO_STBY);
if (result)
return result;
return 0;
}
/*
* inv_hw_self_test() - main function to do hardware self test
*/
int inv_hw_self_test(struct inv_mpu_state *st)
{
int result;
int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS];
#if 0
int gyro_bias_regular_lp[THREE_AXIS];
int accel_bias_regular_lp[THREE_AXIS];
int dummy_bias_regular[THREE_AXIS];
#endif
int test_times, i;
char accel_result, gyro_result;
result = inv_save_setting(st);
if (result)
return result;
result = inv_power_up_self_test(st);
if (result)
return result;
accel_result = 0;
gyro_result = 0;
test_times = DEF_ST_TRY_TIMES;
while (test_times > 0) {
result = inv_do_test(st, 0, gyro_bias_regular,
accel_bias_regular, 0);
if (result == -EAGAIN)
test_times--;
else
test_times = 0;
}
if (result)
goto test_fail;
pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n",
st->hw->name, accel_bias_regular[0],
accel_bias_regular[1], accel_bias_regular[2]);
pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n",
st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1],
gyro_bias_regular[2]);
test_times = DEF_ST_TRY_TIMES;
while (test_times > 0) {
result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
accel_bias_st, 0);
if (result == -EAGAIN)
test_times--;
else
break;
}
if (result)
goto test_fail;
pr_debug("%s self_test accel bias_st - %+d %+d %+d\n",
st->hw->name, accel_bias_st[0], accel_bias_st[1],
accel_bias_st[2]);
pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n",
st->hw->name, gyro_bias_st[0], gyro_bias_st[1],
gyro_bias_st[2]);
#if 0
/* lp gyro mode */
test_times = DEF_ST_TRY_TIMES;
while (test_times > 0) {
result = inv_do_test(st, 0, gyro_bias_regular_lp,
dummy_bias_regular, 1);
if (result == -EAGAIN)
test_times--;
else
test_times = 0;
}
if (result)
goto test_fail;
pr_debug("%s self_test gyro bias_regular lp - %+d %+d %+d\n",
st->hw->name, gyro_bias_regular_lp[0], gyro_bias_regular_lp[1],
gyro_bias_regular_lp[2]);
/* lp accel mode */
test_times = DEF_ST_TRY_TIMES;
while (test_times > 0) {
result = inv_do_test(st, 0, dummy_bias_regular,
accel_bias_regular_lp, 2);
if (result == -EAGAIN)
test_times--;
else
test_times = 0;
}
if (result)
goto test_fail;
pr_debug("%s self_test accel bias_regular lp - %+d %+d %+d\n",
st->hw->name, accel_bias_regular_lp[0],
accel_bias_regular_lp[1], accel_bias_regular_lp[2]);
#endif
/* copy bias */
for (i = 0; i < 3; i++) {
/* gyro : LN bias as LN is default mode */
st->gyro_st_bias[i] = gyro_bias_regular[i] / DEF_ST_PRECISION;
/* accel : LN bias as LN is default mode */
st->accel_st_bias[i] = accel_bias_regular[i] / DEF_ST_PRECISION;
}
/* Check is done on continuous mode data */
accel_result = !inv_check_accel_self_test(st,
accel_bias_regular, accel_bias_st);
gyro_result = !inv_check_gyro_self_test(st,
gyro_bias_regular, gyro_bias_st);
test_fail:
inv_recover_setting(st);
return (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
}

View file

@ -0,0 +1,423 @@
/*
* Copyright (C) 2017-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include "../inv_mpu_iio.h"
/* set LN mode for gyro regardless of conditions */
#define USE_GYRO_LN_MODE
static int inv_calc_engine_dur(struct inv_engine_info *ei)
{
if (!ei->running_rate)
return -EINVAL;
ei->dur = ei->base_time / ei->orig_rate;
ei->dur *= ei->divider;
return 0;
}
static int inv_turn_on_fifo(struct inv_mpu_state *st)
{
u8 int_en, fifo_en, mode, user;
int r;
r = inv_plat_single_write(st, REG_FIFO_EN, 0);
if (r)
return r;
r = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
if (r)
return r;
fifo_en = 0;
int_en = 0;
if (st->gesture_only_on && (!st->batch.timeout)) {
st->gesture_int_count = WOM_DELAY_THRESHOLD;
int_en |= BIT_WOM_ALL_INT_EN;
}
if (st->batch.timeout) {
if(!st->batch.fifo_wm_th)
int_en = BIT_DATA_RDY_EN;
} else {
int_en = BIT_DATA_RDY_EN;
if (st->chip_config.eis_enable)
int_en |= BIT_FSYNC_INT_EN;
}
if (st->sensor[SENSOR_GYRO].on)
fifo_en |= BITS_GYRO_FIFO_EN;
if (st->sensor[SENSOR_ACCEL].on)
fifo_en |= BIT_ACCEL_FIFO_EN;
r = inv_plat_single_write(st, REG_FIFO_EN, fifo_en);
if (r)
return r;
r = inv_plat_single_write(st, REG_INT_ENABLE, int_en);
if (r)
return r;
if (st->gesture_only_on && (!st->batch.timeout)) {
mode = BIT_ACCEL_INTEL_EN | BIT_ACCEL_INTEL_MODE;
} else {
mode = 0;
}
r = inv_plat_single_write(st, REG_ACCEL_INTEL_CTRL, mode);
#ifdef SENSOR_DATA_FROM_REGISTERS
user = 0;
#else
user = BIT_FIFO_EN;
#endif
r = inv_plat_single_write(st, REG_USER_CTRL, user | st->i2c_dis);
return r;
}
/*
* inv_reset_fifo() - Reset FIFO related registers.
*/
int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off)
{
int r, i;
struct inv_timestamp_algo *ts_algo = &st->ts_algo;
int dur_ms;
r = inv_turn_on_fifo(st);
if (r)
return r;
ts_algo->last_run_time = get_time_ns();
ts_algo->reset_ts = ts_algo->last_run_time;
if (st->mode_1k_on)
ts_algo->first_sample = MODE_1K_INIT_SAMPLE;
else
ts_algo->first_sample = 1;
dur_ms = st->smplrt_div + 1;
if ((ts_algo->first_sample * dur_ms) < FIRST_SAMPLE_BUF_MS)
ts_algo->first_sample = FIRST_SAMPLE_BUF_MS / dur_ms;
if (ts_algo->first_sample == 0)
ts_algo->first_sample = 1;
st->last_temp_comp_time = ts_algo->last_run_time;
st->left_over_size = 0;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
st->sensor[i].calib_flag = 0;
st->sensor[i].sample_calib = 0;
st->sensor[i].time_calib = ts_algo->last_run_time;
}
ts_algo->calib_counter = 0;
return 0;
}
static int inv_turn_on_engine(struct inv_mpu_state *st)
{
u8 v, w;
int r;
unsigned int wait_ms;
if (st->chip_config.gyro_enable | st->chip_config.accel_enable) {
w = 0;
if (!st->chip_config.gyro_enable)
w |= BIT_PWR_GYRO_STBY;
if (!st->chip_config.accel_enable)
w |= BIT_PWR_ACCEL_STBY;
} else if (st->chip_config.compass_enable) {
w = BIT_PWR_GYRO_STBY;
} else {
w = (BIT_PWR_GYRO_STBY | BIT_PWR_ACCEL_STBY);
}
r = inv_plat_read(st, REG_PWR_MGMT_2, 1, &v);
if (r)
return r;
r = inv_plat_single_write(st, REG_PWR_MGMT_2, w);
if (r)
return r;
wait_ms = 0;
if (st->chip_config.gyro_enable
&& (v & BIT_PWR_GYRO_STBY)) {
wait_ms = INV_IAM20680_GYRO_START_TIME;
}
if (st->chip_config.accel_enable
&& (v & BIT_PWR_ACCEL_STBY)) {
if (INV_IAM20680_ACCEL_START_TIME > wait_ms)
wait_ms = INV_IAM20680_ACCEL_START_TIME;
}
if (wait_ms)
msleep(wait_ms);
if (st->chip_config.has_compass) {
if (st->chip_config.compass_enable)
r = st->slave_compass->resume(st);
else
r = st->slave_compass->suspend(st);
if (r)
return r;
}
return 0;
}
static int inv_setup_dmp_rate(struct inv_mpu_state *st)
{
int i;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
st->cntl |= st->sensor[i].output;
st->sensor[i].dur =
st->eng_info[st->sensor[i].engine_base].dur;
st->sensor[i].div = 1;
}
}
return 0;
}
/*
* inv_set_lpf() - set low pass filer based on fifo rate.
*/
static int inv_set_lpf(struct inv_mpu_state *st, int rate)
{
const short hz[] = {188, 98, 42, 20, 10, 5};
const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
INV_FILTER_42HZ, INV_FILTER_20HZ,
INV_FILTER_10HZ, INV_FILTER_5HZ};
int i, h, data, result;
#ifdef USE_GYRO_LN_MODE
if (1) {
#else
if (st->chip_config.eis_enable || st->ois.en || st->mode_1k_on) {
#endif
h = (rate >> 1);
i = 0;
while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
i++;
data = d[i];
data |= EXT_SYNC_SET;
result = inv_plat_single_write(st, REG_CONFIG, data);
if (result)
return result;
st->chip_config.lpf = data;
result = inv_plat_single_write(st, REG_LP_MODE_CTRL, 0);
} else {
result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
BIT_GYRO_CYCLE_EN);
if (result)
return result;
data = 0;
result = inv_plat_single_write(st, REG_CONFIG, data | 3);
}
return result;
}
static int inv_set_div(struct inv_mpu_state *st, int a_d, int g_d)
{
int result, div;
if (st->chip_config.gyro_enable)
div = g_d;
else
div = a_d;
if (st->chip_config.eis_enable)
div = 0;
st->smplrt_div = div;
pr_debug("div= %d\n", div);
result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV, div);
return result;
}
// 20680 does not support batching
static int inv_set_batch(struct inv_mpu_state *st)
{
st->batch.fifo_wm_th = 0;
return 0;
}
static int inv_set_rate(struct inv_mpu_state *st)
{
int g_d, a_d, result, i;
result = inv_setup_dmp_rate(st);
if (result)
return result;
g_d = st->eng_info[ENGINE_GYRO].divider - 1;
a_d = st->eng_info[ENGINE_ACCEL].divider - 1;
result = inv_set_div(st, a_d, g_d);
if (result)
return result;
result = inv_set_lpf(st, st->eng_info[ENGINE_GYRO].running_rate);
if (result)
return result;
// set ADLPF at this point not to change after accel is enabled
result = inv_set_accel_config2(st, false);
st->batch.pk_size = 0;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on)
st->batch.pk_size += st->sensor[i].sample_size;
}
inv_set_batch(st);
return result;
}
static int inv_determine_engine(struct inv_mpu_state *st)
{
int i;
bool a_en, g_en;
int accel_rate, gyro_rate;
a_en = false;
g_en = false;
gyro_rate = MPU_INIT_SENSOR_RATE;
accel_rate = MPU_INIT_SENSOR_RATE;
/* loop the streaming sensors to see which engine needs to be turned on
*/
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
a_en |= st->sensor[i].a_en;
g_en |= st->sensor[i].g_en;
}
}
if (st->chip_config.eis_enable) {
g_en = true;
st->eis.frame_count = 0;
st->eis.fsync_delay = 0;
st->eis.gyro_counter = 0;
st->eis.voting_count = 0;
st->eis.voting_count_sub = 0;
gyro_rate = BASE_SAMPLE_RATE;
} else {
st->eis.eis_triggered = false;
st->eis.prev_state = false;
}
accel_rate = st->sensor[SENSOR_ACCEL].rate;
gyro_rate = max(gyro_rate, st->sensor[SENSOR_GYRO].rate);
st->ts_algo.clock_base = ENGINE_ACCEL;
if (g_en) {
/* gyro engine needs to be fastest */
if (a_en)
gyro_rate = max(gyro_rate, accel_rate);
accel_rate = gyro_rate;
st->ts_algo.clock_base = ENGINE_GYRO;
} else if (a_en) {
/* accel engine needs to be fastest if gyro engine is off */
gyro_rate = accel_rate;
st->ts_algo.clock_base = ENGINE_ACCEL;
}
st->eng_info[ENGINE_GYRO].running_rate = gyro_rate;
st->eng_info[ENGINE_ACCEL].running_rate = accel_rate;
if ((gyro_rate >= BASE_SAMPLE_RATE) ||
(accel_rate >= BASE_SAMPLE_RATE))
st->mode_1k_on = true;
else
st->mode_1k_on = false;
/* engine divider for pressure and compass is set later */
if (st->chip_config.eis_enable || st->mode_1k_on) {
st->eng_info[ENGINE_GYRO].divider = 1;
st->eng_info[ENGINE_ACCEL].divider = 1;
// need to update rate and div for 1khz mode
for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ ) {
if (st->sensor_l[i].on) {
st->sensor_l[i].counter = 0;
if (st->sensor_l[i].rate)
st->sensor_l[i].div =
BASE_SAMPLE_RATE
/ st->sensor_l[i].rate;
else
st->sensor_l[i].div = 0xffff;
}
}
} else {
st->eng_info[ENGINE_GYRO].divider = BASE_SAMPLE_RATE /
st->eng_info[ENGINE_GYRO].running_rate;
st->eng_info[ENGINE_ACCEL].divider = BASE_SAMPLE_RATE /
st->eng_info[ENGINE_ACCEL].running_rate;
}
for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ )
st->sensor_l[i].counter = 0;
inv_calc_engine_dur(&st->eng_info[ENGINE_GYRO]);
inv_calc_engine_dur(&st->eng_info[ENGINE_ACCEL]);
pr_debug("gen: %d aen: %d grate: %d arate: %d\n",
g_en, a_en, gyro_rate, accel_rate);
st->chip_config.gyro_enable = g_en;
st->chip_config.accel_enable = a_en;
return 0;
}
/*
* set_inv_enable() - enable function.
*/
int set_inv_enable(struct iio_dev *indio_dev)
{
int result;
struct inv_mpu_state *st = iio_priv(indio_dev);
result = inv_switch_power_in_lp(st, true);
if (result)
return result;
inv_stop_interrupt(st);
inv_determine_engine(st);
result = inv_set_rate(st);
if (result) {
pr_err("inv_set_rate error\n");
return result;
}
result = inv_turn_on_engine(st);
if (result) {
pr_err("inv_turn_on_engine error\n");
return result;
}
result = inv_reset_fifo(st, false);
if (result)
return result;
result = inv_switch_power_in_lp(st, false);
if ((!st->chip_config.gyro_enable) &&
(!st->chip_config.accel_enable)) {
inv_set_power(st, false);
return 0;
}
return result;
}
/* dummy function for 20608D */
int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
{
return 0;
}
int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf)
{
return 0;
}
int inv_firmware_load(struct inv_mpu_state *st)
{
return 0;
}

View file

@ -0,0 +1,988 @@
/*
* Copyright (C) 2012-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include "inv_mpu_iio.h"
#ifdef CONFIG_RTC_INTF_ALARM
#include <linux/android_alarm.h>
#endif
#include <linux/export.h>
#ifdef CONFIG_RTC_INTF_ALARM
s64 get_time_ns(void)
{
struct timespec ts;
/* get_monotonic_boottime(&ts); */
/* Workaround for some platform on which monotonic clock and
* Android SystemClock has a gap.
* Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
* get_monotonic_boottime() for these platform
*/
ts = ktime_to_timespec(alarm_get_elapsed_realtime());
return timespec_to_ns(&ts);
}
#else
s64 get_time_ns(void)
{
struct timespec ts;
get_monotonic_boottime(&ts);
/* Workaround for some platform on which monotonic clock and
* Android SystemClock has a gap.
* Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
* get_monotonic_boottime() for these platform
*/
return timespec_to_ns(&ts);
}
#endif
#ifdef ACCEL_BIAS_TEST
int inv_get_3axis_average(s16 src[], s16 dst[], s16 reset)
{
#define BUFFER_SIZE 200
static s16 buffer[BUFFER_SIZE][3];
static s16 current_position = 0;
static s16 ready = 0;
int sum[3]= {0,};
int i;
if(reset){
current_position = 0;
ready = 0;
}
buffer[current_position][0] = src[0];
buffer[current_position][1] = src[1];
buffer[current_position][2] = src[2];
current_position++;
if(current_position == BUFFER_SIZE){
ready = 1;
current_position = 0;
}
if(ready){
for(i = 0 ; i < BUFFER_SIZE ; i++){
sum[0] += buffer[i][0];
sum[1] += buffer[i][1];
sum[2] += buffer[i][2];
}
dst[0] = sum[0]/BUFFER_SIZE;
dst[1] = sum[1]/BUFFER_SIZE;
dst[2] = sum[2]/BUFFER_SIZE;
return 1;
}
return 0;
}
#endif
int inv_q30_mult(int a, int b)
{
#define DMP_MULTI_SHIFT 30
u64 temp;
int result;
temp = ((u64)a) * b;
result = (int)(temp >> DMP_MULTI_SHIFT);
return result;
}
#if defined(CONFIG_INV_MPU_IIO_ICM20648) || \
defined(CONFIG_INV_MPU_IIO_ICM20690)
/* inv_read_secondary(): set secondary registers for reading.
The chip must be set as bank 3 before calling.
*/
int inv_read_secondary(struct inv_mpu_state *st, int ind, int addr,
int reg, int len)
{
int result;
result = inv_plat_single_write(st, st->slv_reg[ind].addr,
INV_MPU_BIT_I2C_READ | addr);
if (result)
return result;
result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
if (result)
return result;
result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
INV_MPU_BIT_SLV_EN | len);
return result;
}
int inv_execute_read_secondary(struct inv_mpu_state *st, int ind, int addr,
int reg, int len, u8 *d)
{
int result;
inv_set_bank(st, BANK_SEL_3);
result = inv_read_secondary(st, ind, addr, reg, len);
if (result)
return result;
inv_set_bank(st, BANK_SEL_0);
result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
BIT_I2C_MST_EN);
msleep(SECONDARY_INIT_WAIT);
result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
if (result)
return result;
result = inv_plat_read(st, REG_EXT_SLV_SENS_DATA_00, len, d);
return result;
}
/* inv_write_secondary(): set secondary registers for writing.
The chip must be set as bank 3 before calling.
*/
int inv_write_secondary(struct inv_mpu_state *st, int ind, int addr,
int reg, int v)
{
int result;
result = inv_plat_single_write(st, st->slv_reg[ind].addr, addr);
if (result)
return result;
result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
if (result)
return result;
result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
INV_MPU_BIT_SLV_EN | 1);
result = inv_plat_single_write(st, st->slv_reg[ind].d0, v);
return result;
}
int inv_execute_write_secondary(struct inv_mpu_state *st, int ind, int addr,
int reg, int v)
{
int result;
inv_set_bank(st, BANK_SEL_3);
result = inv_write_secondary(st, ind, addr, reg, v);
if (result)
return result;
inv_set_bank(st, BANK_SEL_0);
result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
BIT_I2C_MST_EN);
msleep(SECONDARY_INIT_WAIT);
result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
return result;
}
int inv_set_bank(struct inv_mpu_state *st, u8 bank)
{
#ifdef CONFIG_INV_MPU_IIO_ICM20648
int r;
r = inv_plat_single_write(st, REG_BANK_SEL, bank);
return r;
#else
return 0;
#endif
}
#endif
#ifdef CONFIG_INV_MPU_IIO_ICM20648
/**
* inv_write_cntl() - Write control word to designated address.
* @st: Device driver instance.
* @wd: control word.
* @en: enable/disable.
* @cntl: control address to be written.
*/
int inv_write_cntl(struct inv_mpu_state *st, u16 wd, bool en, int cntl)
{
int result;
u8 reg[2], d_out[2];
result = mem_r(cntl, 2, d_out);
if (result)
return result;
reg[0] = ((wd >> 8) & 0xff);
reg[1] = (wd & 0xff);
if (!en) {
d_out[0] &= ~reg[0];
d_out[1] &= ~reg[1];
} else {
d_out[0] |= reg[0];
d_out[1] |= reg[1];
}
result = mem_w(cntl, 2, d_out);
return result;
}
#endif
int inv_set_power(struct inv_mpu_state *st, bool power_on)
{
u8 d;
int r;
if ((!power_on) == st->chip_config.is_asleep)
return 0;
d = BIT_CLK_PLL;
if (!power_on)
d |= BIT_SLEEP;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, d);
if (r)
return r;
if (power_on)
usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
st->chip_config.is_asleep = !power_on;
return 0;
}
EXPORT_SYMBOL_GPL(inv_set_power);
int inv_stop_interrupt(struct inv_mpu_state *st)
{
int res;
#if defined(CONFIG_INV_MPU_IIO_ICM20648)
/* disable_irq_wake alone should work already. However,
it might need system configuration change. From driver side,
we will disable IRQ altogether for non-wakeup sensors. */
res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
if (res)
return res;
res = inv_plat_read(st, REG_INT_ENABLE_2, 1, &st->int_en_2);
if (res)
return res;
res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
if (res)
return res;
res = inv_plat_single_write(st, REG_INT_ENABLE_2, 0);
if (res)
return res;
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
if (res)
return res;
res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
if (res)
return res;
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
|| defined(CONFIG_INV_MPU_IIO_ICM20690) \
|| defined(CONFIG_INV_MPU_IIO_IAM20680)
res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
if (res)
return res;
res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
if (res)
return res;
#endif
return 0;
}
int inv_reenable_interrupt(struct inv_mpu_state *st)
{
int res = 0;
#if defined(CONFIG_INV_MPU_IIO_ICM20648)
res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
if (res)
return res;
res = inv_plat_single_write(st, REG_INT_ENABLE_2, st->int_en_2);
if (res)
return res;
#elif defined(CONFIG_INV_MPU_IIO_ICM20608D)
res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
if (res)
return res;
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
|| defined(CONFIG_INV_MPU_IIO_ICM20690) \
|| defined(CONFIG_INV_MPU_IIO_IAM20680)
res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
if (res)
return res;
#endif
return res;
}
static int inv_lp_en_off_mode(struct inv_mpu_state *st, bool on)
{
int r;
if (!st->chip_config.is_asleep)
return 0;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_CLK_PLL);
st->chip_config.is_asleep = 0;
return r;
}
#ifdef CONFIG_INV_MPU_IIO_ICM20648
static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
{
int r = 0;
u8 w;
if ((!st->chip_config.is_asleep) &&
((!on) == st->chip_config.lp_en_set))
return 0;
w = BIT_CLK_PLL;
if ((!on) && (!st->eis.eis_triggered))
w |= BIT_LP_EN;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
st->chip_config.is_asleep = 0;
st->chip_config.lp_en_set = (!on);
return r;
}
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
|| defined(CONFIG_INV_MPU_IIO_ICM20690) \
|| defined(CONFIG_INV_MPU_IIO_IAM20680)
int inv_set_accel_config2(struct inv_mpu_state *st, bool cycle_mode)
{
int cycle_freq[] = {275, 192, 111, 59};
int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
int i, r, rate;
u8 v;
v = 0;
#ifdef CONFIG_INV_MPU_IIO_ICM20690
v |= BIT_FIFO_SIZE_1K;
#endif
if (cycle_mode) {
rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
i = ARRAY_SIZE(cycle_freq) - 1;
while (i > 0) {
if (rate < cycle_freq[i]) {
break;
}
i--;
}
r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v |
(i << 4) | 7);
if (r)
return r;
} else {
rate = (st->eng_info[ENGINE_ACCEL].running_rate >> 1);
for (i = 1; i < ARRAY_SIZE(cont_freq); i++) {
if (rate >= cont_freq[i])
break;
}
if (i > 6)
i = 6;
r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v | i);
if (r)
return r;
}
return 0;
}
static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
{
int r = 0;
u8 w;
bool cond_check;
if ((!st->chip_config.is_asleep) &&
((!on) == st->chip_config.lp_en_set))
return 0;
cond_check = (!on) && st->cycle_on;
w = BIT_CLK_PLL;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
if (cond_check) {
w |= BIT_LP_EN;
inv_set_accel_config2(st, true);
st->chip_config.lp_en_set = true;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
} else {
inv_set_accel_config2(st, false);
#ifdef CONFIG_INV_MPU_IIO_ICM20690
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w | BIT_SLEEP);
if (r)
return r;
#endif
st->chip_config.lp_en_set = false;
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
msleep(10);
}
st->chip_config.is_asleep = 0;
return r;
}
#endif
#ifdef CONFIG_INV_MPU_IIO_ICM20608D
static int inv_set_accel_config2(struct inv_mpu_state *st)
{
int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
int dec2_cfg = 0;
int i, r, rate;
rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
i = 0;
if (!st->chip_config.eis_enable){
while ((rate < cont_freq[i]) && (i < ARRAY_SIZE(cont_freq) - 1))
i++;
dec2_cfg = 2<<4; //4x
}
r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, i | dec2_cfg);
if (r)
return r;
return 0;
}
static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
{
int r = 0;
u8 w;
w = BIT_CLK_PLL;
if ((!on) && (!st->chip_config.eis_enable))
w |= BIT_LP_EN;
inv_set_accel_config2(st);
r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
return r;
}
#endif
int inv_switch_power_in_lp(struct inv_mpu_state *st, bool on)
{
int r;
if (st->chip_config.lp_en_mode_off)
r = inv_lp_en_off_mode(st, on);
else
r = inv_lp_en_on_mode(st, on);
return r;
}
EXPORT_SYMBOL_GPL(inv_switch_power_in_lp);
int write_be16_to_mem(struct inv_mpu_state *st, u16 data, int addr)
{
u8 d[2];
d[0] = (data >> 8) & 0xff;
d[1] = data & 0xff;
return mem_w(addr, sizeof(d), d);
}
int write_be32_to_mem(struct inv_mpu_state *st, u32 data, int addr)
{
cpu_to_be32s(&data);
return mem_w(addr, sizeof(data), (u8 *)&data);
}
int read_be16_from_mem(struct inv_mpu_state *st, u16 *o, int addr)
{
int result;
u8 d[2];
result = mem_r(addr, 2, (u8 *) &d);
*o = d[0] << 8 | d[1];
return result;
}
int read_be32_from_mem(struct inv_mpu_state *st, u32 *o, int addr)
{
int result;
u32 d = 0;
result = mem_r(addr, 4, (u8 *) &d);
*o = be32_to_cpup((__be32 *)(&d));
return result;
}
int be32_to_int(u8 *d)
{
return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
}
u32 inv_get_cntr_diff(u32 curr_counter, u32 prev)
{
u32 diff;
if (curr_counter > prev)
diff = curr_counter - prev;
else
diff = 0xffffffff - prev + curr_counter + 1;
return diff;
}
int inv_write_2bytes(struct inv_mpu_state *st, int addr, int data)
{
u8 d[2];
if (data < 0 || data > USHRT_MAX)
return -EINVAL;
d[0] = (u8) ((data >> 8) & 0xff);
d[1] = (u8) (data & 0xff);
return mem_w(addr, ARRAY_SIZE(d), d);
}
int inv_process_eis(struct inv_mpu_state *st, u16 delay)
{
int tmp1, tmp2, tmp3;
switch (st->eis.voting_state) {
case 0:
st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
st->eis.voting_count = 1;
st->eis.voting_count_sub = 0;
st->eis.voting_state = 1;
break;
case 1:
if (abs(st->eis.gyro_counter_s[0] -
st->eis.gyro_counter) <= 1) {
st->eis.voting_count++;
} else {
st->eis.gyro_counter_s[2] = st->eis.gyro_counter;
st->eis.voting_count_sub++;
st->eis.voting_state = 2;
}
if (st->eis.voting_count > 5)
st->eis.voting_state = 3;
break;
case 2:
tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
tmp2 = abs(st->eis.gyro_counter_s[2] - st->eis.gyro_counter);
if ((tmp1 < tmp2) && (tmp1 <= 1))
st->eis.voting_count++;
else
st->eis.voting_count_sub++;
if (st->eis.voting_count > 5) {
st->eis.voting_state = 3;
st->eis.voting_count = 0;
st->eis.voting_count_sub = 0;
}
if (st->eis.voting_count_sub > 5) {
st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
st->eis.voting_state = 1;
st->eis.voting_count = 1;
st->eis.voting_count_sub = 0;
}
break;
case 3:
tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
if (tmp1 == 1) {
st->eis.gyro_counter_s[1] = st->eis.gyro_counter;
st->eis.fsync_delay_s[1] = delay - st->eis.fsync_delay;
st->eis.voting_state = 4;
st->eis.voting_count_sub = 1;
st->eis.voting_count = 1;
}
break;
case 4:
if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
tmp1 = delay - st->eis.fsync_delay;
tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
if (tmp2 < 3) {
st->eis.voting_count++;
} else {
st->eis.fsync_delay_s[2] = tmp1;
st->eis.voting_count_sub = 1;
st->eis.voting_state = 5;
}
if (st->eis.voting_count > 5) {
st->eis.voting_count = 1;
st->eis.voting_state = 6;
}
}
break;
case 5:
if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
tmp1 = delay - st->eis.fsync_delay;
tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
if ((tmp2 < tmp3) && (tmp2 < 3))
st->eis.voting_count++;
else
st->eis.voting_count_sub++;
if ((st->eis.voting_count > 5) &&
(st->eis.voting_count_sub
< st->eis.voting_count)) {
st->eis.voting_state = 6;
st->eis.voting_count = 1;
} else if (st->eis.voting_count_sub > 5) {
st->eis.fsync_delay_s[0] = tmp1;
st->eis.voting_state = 4;
st->eis.voting_count = 1;
}
}
break;
case 6:
if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
tmp1 = delay - st->eis.fsync_delay;
tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
if (tmp2 < 3) {
st->eis.voting_count++;
} else {
st->eis.fsync_delay_s[2] = tmp1;
st->eis.voting_count_sub = 1;
st->eis.voting_count = 1;
st->eis.voting_state = 7;
}
if (st->eis.voting_count > 5)
st->eis.voting_state = 8;
}
break;
case 7:
if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
tmp1 = delay - st->eis.fsync_delay;
tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
if ((tmp2 < tmp3) && (tmp2 < 3))
st->eis.voting_count++;
else
st->eis.voting_count_sub++;
if ((st->eis.voting_count > 5) &&
(st->eis.voting_count_sub
< st->eis.voting_count)) {
st->eis.voting_state = 8;
} else if (st->eis.voting_count_sub > 5) {
st->eis.fsync_delay_s[1] = tmp1;
st->eis.voting_state = 6;
st->eis.voting_count = 1;
}
}
break;
default:
break;
}
pr_debug("de= %d gc= %d\n", delay, st->eis.gyro_counter);
st->eis.fsync_delay = delay;
st->eis.gyro_counter = 0;
pr_debug("state=%d g1= %d d1= %d g2= %d d2= %d\n",
st->eis.voting_state,
st->eis.gyro_counter_s[0],
st->eis.fsync_delay_s[0],
st->eis.gyro_counter_s[1],
st->eis.fsync_delay_s[1]);
return 0;
}
int inv_rate_convert(struct inv_mpu_state *st, int ind, int data)
{
int t, out, out1, out2;
int base_freq;
if (data <= MPU_DEFAULT_DMP_FREQ)
base_freq = MPU_DEFAULT_DMP_FREQ;
else
base_freq = BASE_SAMPLE_RATE;
t = base_freq / data;
if (!t)
t = 1;
out1 = base_freq / (t + 1);
out2 = base_freq / t;
if ((data - out1) * INV_ODR_BUFFER_MULTI < data)
out = out1;
else
out = out2;
return out;
}
static void inv_check_wake_non_wake(struct inv_mpu_state *st,
enum SENSOR_L wake, enum SENSOR_L non_wake)
{
int tmp_rate;
if (!st->sensor_l[wake].on && !st->sensor_l[non_wake].on)
return;
tmp_rate = MPU_INIT_SENSOR_RATE;
if (st->sensor_l[wake].on)
tmp_rate = st->sensor_l[wake].rate;
if (st->sensor_l[non_wake].on)
tmp_rate = max(tmp_rate, st->sensor_l[non_wake].rate);
st->sensor_l[wake].rate = tmp_rate;
st->sensor_l[non_wake].rate = tmp_rate;
}
static void inv_check_wake_non_wake_divider(struct inv_mpu_state *st,
enum SENSOR_L wake, enum SENSOR_L non_wake)
{
if (st->sensor_l[wake].on && st->sensor_l[non_wake].on)
st->sensor_l[non_wake].div = 0xffff;
}
#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
|| defined(CONFIG_INV_MPU_IIO_ICM20690) \
|| defined(CONFIG_INV_MPU_IIO_IAM20680)
int inv_check_sensor_on(struct inv_mpu_state *st)
{
int i, max_rate;
enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
SENSOR_L_MAG_WAKE};
enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
SENSOR_L_MAG};
st->sensor_l[SENSOR_L_GESTURE_ACCEL].rate = GESTURE_ACCEL_RATE;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].on = false;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
if ((st->step_detector_l_on
|| st->step_detector_wake_l_on
|| st->step_counter_l_on
|| st->step_counter_wake_l_on
|| st->chip_config.pick_up_enable
|| st->chip_config.tilt_enable)
&& (!st->sensor_l[SENSOR_L_ACCEL].on)
&& (!st->sensor_l[SENSOR_L_ACCEL_WAKE].on))
st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = true;
else
st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = false;
st->chip_config.wake_on = false;
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on && st->sensor_l[i].rate) {
st->sensor[st->sensor_l[i].base].on = true;
st->chip_config.wake_on |= st->sensor_l[i].wake_on;
}
}
if (st->sensor_l[SENSOR_L_GESTURE_ACCEL].on &&
(!st->sensor[SENSOR_GYRO].on) &&
(!st->sensor[SENSOR_COMPASS].on))
st->gesture_only_on = true;
else
st->gesture_only_on = false;
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on) {
st->sensor[st->sensor_l[i].base].rate =
max(st->sensor[st->sensor_l[i].base].rate,
st->sensor_l[i].rate);
}
}
max_rate = MPU_INIT_SENSOR_RATE;
if (st->chip_config.eis_enable) {
max_rate = ESI_GYRO_RATE;
st->sensor_l[SENSOR_L_EIS_GYRO].rate = ESI_GYRO_RATE;
}
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
max_rate = max(max_rate, st->sensor[i].rate);
}
}
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
st->sensor[i].rate = max_rate;
}
}
for (i = 0; i < ARRAY_SIZE(wake); i++)
inv_check_wake_non_wake(st, wake[i], non_wake[i]);
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on) {
if (st->sensor_l[i].rate)
st->sensor_l[i].div =
st->sensor[st->sensor_l[i].base].rate
/ st->sensor_l[i].rate;
else
st->sensor_l[i].div = 0xffff;
pr_debug("sensor= %d, div= %d\n",
i, st->sensor_l[i].div);
}
}
for (i = 0; i < ARRAY_SIZE(wake); i++)
inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
if (st->step_detector_wake_l_on ||
st->step_counter_wake_l_on ||
st->chip_config.pick_up_enable ||
st->chip_config.tilt_enable)
st->chip_config.wake_on = true;
return 0;
}
#else
static void inv_do_check_sensor_on(struct inv_mpu_state *st,
enum SENSOR_L *wake,
enum SENSOR_L *non_wake, int sensor_size)
{
int i;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].on = false;
for (i = 0; i < SENSOR_NUM_MAX; i++)
st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
st->chip_config.wake_on = false;
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on && st->sensor_l[i].rate) {
st->sensor[st->sensor_l[i].base].on = true;
st->chip_config.wake_on |= st->sensor_l[i].wake_on;
}
}
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on) {
st->sensor[st->sensor_l[i].base].rate =
max(st->sensor[st->sensor_l[i].base].rate,
st->sensor_l[i].rate);
}
}
for (i = 0; i < sensor_size; i++)
inv_check_wake_non_wake(st, wake[i], non_wake[i]);
for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
if (st->sensor_l[i].on) {
if (st->sensor_l[i].rate)
st->sensor_l[i].div =
st->sensor[st->sensor_l[i].base].rate
/ st->sensor_l[i].rate;
else
st->sensor_l[i].div = 0xffff;
}
}
for (i = 0; i < sensor_size; i++)
inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
if (st->step_detector_wake_l_on ||
st->step_counter_wake_l_on ||
st->chip_config.pick_up_enable ||
st->chip_config.tilt_enable ||
st->smd.on)
st->chip_config.wake_on = true;
}
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
int inv_check_sensor_on(struct inv_mpu_state *st)
{
enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
SENSOR_L_GYRO_CAL_WAKE};
enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
SENSOR_L_SIXQ, SENSOR_L_PEDQ,
SENSOR_L_GYRO_CAL};
inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
return 0;
}
#endif
#if defined(CONFIG_INV_MPU_IIO_ICM20648)
int inv_check_sensor_on(struct inv_mpu_state *st)
{
enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
SENSOR_L_MAG_WAKE, SENSOR_L_ALS_WAKE,
SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
SENSOR_L_NINEQ_WAKE, SENSOR_L_GEOMAG_WAKE,
SENSOR_L_PRESSURE_WAKE,
SENSOR_L_GYRO_CAL_WAKE,
SENSOR_L_MAG_CAL_WAKE};
enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
SENSOR_L_MAG, SENSOR_L_ALS,
SENSOR_L_SIXQ, SENSOR_L_PEDQ,
SENSOR_L_NINEQ, SENSOR_L_GEOMAG,
SENSOR_L_PRESSURE,
SENSOR_L_GYRO_CAL,
SENSOR_L_MAG_CAL};
inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
int inv_mpu_suspend(struct iio_dev *indio_dev)
{
struct inv_mpu_state *st = iio_priv(indio_dev);
/* add code according to different request Start */
dev_info(st->dev, "%s suspend\n", st->hw->name);
mutex_lock(&indio_dev->mlock);
st->resume_state = false;
if (st->chip_config.wake_on) {
enable_irq_wake(st->irq);
} else {
inv_stop_interrupt(st);
}
mutex_unlock(&indio_dev->mlock);
return 0;
}
EXPORT_SYMBOL_GPL(inv_mpu_suspend);
/*
* inv_mpu_complete(): complete method for this driver.
* This method can be modified according to the request of different
* customers. It basically undo everything suspend is doing
* and recover the chip to what it was before suspend. We use complete to
* make sure that alarm clock resume is finished. If we use resume, the
* alarm clock may not resume yet and get incorrect clock reading.
*/
void inv_mpu_complete(struct iio_dev *indio_dev)
{
struct inv_mpu_state *st = iio_priv(indio_dev);
dev_info(st->dev, "%s resume\n", st->hw->name);
if (st->resume_state)
return;
mutex_lock(&indio_dev->mlock);
if (!st->chip_config.wake_on) {
inv_reenable_interrupt(st);
} else {
disable_irq_wake(st->irq);
}
/* resume state is used to synchronize read_fifo such that it won't
proceed unless resume is finished. */
st->resume_state = true;
/* resume flag is indicating that current clock reading is from resume,
it has up to 1 second drift and should do proper processing */
st->ts_algo.resume_flag = true;
mutex_unlock(&indio_dev->mlock);
wake_up_interruptible(&st->wait_queue);
return;
}
EXPORT_SYMBOL_GPL(inv_mpu_complete);
#endif

View file

@ -0,0 +1,343 @@
/*
* Copyright (C) 2012-2017 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/device.h>
#include <linux/err.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/export.h>
#include <linux/iio/imu/mpu.h>
#include "inv_mpu_dts.h"
#include "inv_mpu_iio.h"
#ifdef CONFIG_OF
static int inv_mpu_power_on(struct mpu_platform_data *pdata)
{
int err;
if (!IS_ERR(pdata->vdd_ana)) {
err = regulator_enable(pdata->vdd_ana);
if (err)
return err;
}
if (!IS_ERR(pdata->vdd_i2c)) {
err = regulator_enable(pdata->vdd_i2c);
if (err)
goto error_disable_vdd_ana;
}
return 0;
error_disable_vdd_ana:
regulator_disable(pdata->vdd_ana);
return err;
}
static int inv_mpu_power_off(struct mpu_platform_data *pdata)
{
if (!IS_ERR(pdata->vdd_ana))
regulator_disable(pdata->vdd_ana);
if (!IS_ERR(pdata->vdd_i2c))
regulator_disable(pdata->vdd_i2c);
return 0;
}
static int inv_parse_orientation_matrix(struct device *dev, s8 *orient)
{
int rc, i;
struct device_node *np = dev->of_node;
u32 temp_val, temp_val2;
for (i = 0; i < 9; i++)
orient[i] = 0;
/* parsing axis x orientation matrix */
rc = of_property_read_u32(np, "axis_map_x", &temp_val);
if (rc) {
dev_err(dev, "Unable to read axis_map_x\n");
return rc;
}
rc = of_property_read_u32(np, "negate_x", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read negate_x\n");
return rc;
}
if (temp_val2)
orient[temp_val] = -1;
else
orient[temp_val] = 1;
/* parsing axis y orientation matrix */
rc = of_property_read_u32(np, "axis_map_y", &temp_val);
if (rc) {
dev_err(dev, "Unable to read axis_map_y\n");
return rc;
}
rc = of_property_read_u32(np, "negate_y", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read negate_y\n");
return rc;
}
if (temp_val2)
orient[3 + temp_val] = -1;
else
orient[3 + temp_val] = 1;
/* parsing axis z orientation matrix */
rc = of_property_read_u32(np, "axis_map_z", &temp_val);
if (rc) {
dev_err(dev, "Unable to read axis_map_z\n");
return rc;
}
rc = of_property_read_u32(np, "negate_z", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read negate_z\n");
return rc;
}
if (temp_val2)
orient[6 + temp_val] = -1;
else
orient[6 + temp_val] = 1;
return 0;
}
static int inv_parse_secondary_orientation_matrix(struct device *dev,
s8 *orient)
{
int rc, i;
struct device_node *np = dev->of_node;
u32 temp_val, temp_val2;
for (i = 0; i < 9; i++)
orient[i] = 0;
/* parsing axis x orientation matrix */
rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val);
if (rc) {
dev_err(dev, "Unable to read secondary axis_map_x\n");
return rc;
}
rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read secondary negate_x\n");
return rc;
}
if (temp_val2)
orient[temp_val] = -1;
else
orient[temp_val] = 1;
/* parsing axis y orientation matrix */
rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val);
if (rc) {
dev_err(dev, "Unable to read secondary axis_map_y\n");
return rc;
}
rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read secondary negate_y\n");
return rc;
}
if (temp_val2)
orient[3 + temp_val] = -1;
else
orient[3 + temp_val] = 1;
/* parsing axis z orientation matrix */
rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val);
if (rc) {
dev_err(dev, "Unable to read secondary axis_map_z\n");
return rc;
}
rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2);
if (rc) {
dev_err(dev, "Unable to read secondary negate_z\n");
return rc;
}
if (temp_val2)
orient[6 + temp_val] = -1;
else
orient[6 + temp_val] = 1;
return 0;
}
static int inv_parse_secondary(struct device *dev,
struct mpu_platform_data *pdata)
{
int rc;
struct device_node *np = dev->of_node;
u32 temp_val;
const char *name;
if (of_property_read_string(np, "inven,secondary_type", &name)) {
dev_err(dev, "Missing secondary type.\n");
return -EINVAL;
}
if (!strcmp(name, "compass")) {
pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
} else if (!strcmp(name, "none")) {
pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE;
return 0;
} else {
return -EINVAL;
}
if (of_property_read_string(np, "inven,secondary_name", &name)) {
dev_err(dev, "Missing secondary name.\n");
return -EINVAL;
}
if (!strcmp(name, "ak8963"))
pdata->sec_slave_id = COMPASS_ID_AK8963;
else if (!strcmp(name, "ak8975"))
pdata->sec_slave_id = COMPASS_ID_AK8975;
else if (!strcmp(name, "ak8972"))
pdata->sec_slave_id = COMPASS_ID_AK8972;
else if (!strcmp(name, "ak09911"))
pdata->sec_slave_id = COMPASS_ID_AK09911;
else if (!strcmp(name, "ak09912"))
pdata->sec_slave_id = COMPASS_ID_AK09912;
else if (!strcmp(name, "ak09916"))
pdata->sec_slave_id = COMPASS_ID_AK09916;
else
return -EINVAL;
rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val);
if (rc) {
dev_err(dev, "Unable to read secondary register\n");
return rc;
}
pdata->secondary_i2c_addr = temp_val;
rc = inv_parse_secondary_orientation_matrix(dev,
pdata->
secondary_orientation);
return rc;
}
static int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata)
{
int rc;
struct device_node *np = dev->of_node;
u32 temp_val;
const char *name;
if (of_property_read_string(np, "inven,aux_type", &name)) {
dev_err(dev, "Missing aux type.\n");
return -EINVAL;
}
if (!strcmp(name, "pressure")) {
pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE;
} else if (!strcmp(name, "none")) {
pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE;
return 0;
} else {
return -EINVAL;
}
if (of_property_read_string(np, "inven,aux_name", &name)) {
dev_err(dev, "Missing aux name.\n");
return -EINVAL;
}
if (!strcmp(name, "bmp280"))
pdata->aux_slave_id = PRESSURE_ID_BMP280;
else
return -EINVAL;
rc = of_property_read_u32(np, "inven,aux_reg", &temp_val);
if (rc) {
dev_err(dev, "Unable to read aux register\n");
return rc;
}
pdata->aux_i2c_addr = temp_val;
return 0;
}
static int inv_parse_readonly_secondary(struct device *dev,
struct mpu_platform_data *pdata)
{
int rc;
struct device_node *np = dev->of_node;
u32 temp_val;
const char *name;
if (of_property_read_string(np, "inven,read_only_slave_type", &name)) {
dev_err(dev, "Missing read only slave type type.\n");
return -EINVAL;
}
if (!strcmp(name, "als")) {
pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_ALS;
} else if (!strcmp(name, "none")) {
pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_NONE;
return 0;
} else {
return -EINVAL;
}
if (of_property_read_string(np, "inven,read_only_slave_name", &name)) {
dev_err(dev, "Missing read only slave type name.\n");
return -EINVAL;
}
if (!strcmp(name, "apds9930"))
pdata->read_only_slave_id = ALS_ID_APDS_9930;
else
return -EINVAL;
rc = of_property_read_u32(np, "inven,read_only_slave_reg", &temp_val);
if (rc) {
dev_err(dev, "Unable to read read only slave reg register\n");
return rc;
}
pdata->read_only_i2c_addr = temp_val;
return 0;
}
int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata)
{
int rc;
rc = inv_parse_orientation_matrix(dev, pdata->orientation);
if (rc)
return rc;
rc = inv_parse_secondary(dev, pdata);
if (rc)
return rc;
inv_parse_aux(dev, pdata);
inv_parse_readonly_secondary(dev, pdata);
pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana");
if (IS_ERR(pdata->vdd_ana)) {
rc = PTR_ERR(pdata->vdd_ana);
dev_warn(dev, "regulator get failed vdd_ana-supply rc=%d\n", rc);
}
pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c");
if (IS_ERR(pdata->vdd_i2c)) {
rc = PTR_ERR(pdata->vdd_i2c);
dev_warn(dev, "regulator get failed vcc-i2c-supply rc=%d\n", rc);
}
pdata->power_on = inv_mpu_power_on;
pdata->power_off = inv_mpu_power_off;
dev_dbg(dev, "parse dt complete\n");
return 0;
}
EXPORT_SYMBOL_GPL(invensense_mpu_parse_dt);
#endif /* CONFIG_OF */

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2012-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _INV_MPU_DTS_H_
#define _INV_MPU_DTS_H_
#include <linux/kernel.h>
#include <linux/iio/imu/mpu.h>
#ifdef CONFIG_OF
int invensense_mpu_parse_dt(struct device *dev,
struct mpu_platform_data *pdata);
#endif
#endif /* #ifndef _INV_MPU_DTS_H_ */

View file

@ -0,0 +1,553 @@
/*
* Copyright (C) 2012-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include "inv_mpu_iio.h"
#include "inv_mpu_dts.h"
#define CONFIG_DYNAMIC_DEBUG_I2C 0
/**
* inv_i2c_read_base() - Read one or more bytes from the device registers.
* @st: Device driver instance.
* @i2c_addr: i2c address of device.
* @reg: First device register to be read from.
* @length: Number of bytes to read.
* @data: Data read from device.
* NOTE:This is not re-implementation of i2c_smbus_read because i2c
* address could be specified in this case. We could have two different
* i2c address due to secondary i2c interface.
*/
int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
u8 reg, u16 length, u8 *data)
{
struct i2c_msg msgs[2];
int res;
if (!data)
return -EINVAL;
msgs[0].addr = i2c_addr;
msgs[0].flags = 0; /* write */
msgs[0].buf = &reg;
msgs[0].len = 1;
msgs[1].addr = i2c_addr;
msgs[1].flags = I2C_M_RD;
msgs[1].buf = data;
msgs[1].len = length;
res = i2c_transfer(st->sl_handle, msgs, 2);
if (res < 2) {
if (res >= 0)
res = -EIO;
} else
res = 0;
INV_I2C_INC_MPUWRITE(3);
INV_I2C_INC_MPUREAD(length);
return res;
}
/**
* inv_i2c_single_write_base() - Write a byte to a device register.
* @st: Device driver instance.
* @i2c_addr: I2C address of the device.
* @reg: Device register to be written to.
* @data: Byte to write to device.
* NOTE:This is not re-implementation of i2c_smbus_write because i2c
* address could be specified in this case. We could have two different
* i2c address due to secondary i2c interface.
*/
int inv_i2c_single_write_base(struct inv_mpu_state *st,
u16 i2c_addr, u8 reg, u8 data)
{
u8 tmp[2];
struct i2c_msg msg;
int res;
tmp[0] = reg;
tmp[1] = data;
msg.addr = i2c_addr;
msg.flags = 0; /* write */
msg.buf = tmp;
msg.len = 2;
INV_I2C_INC_MPUWRITE(3);
res = i2c_transfer(st->sl_handle, &msg, 1);
if (res < 1) {
if (res == 0)
res = -EIO;
return res;
} else
return 0;
}
static int inv_i2c_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
{
return inv_i2c_single_write_base(st, st->i2c_addr, reg, data);
}
static int inv_i2c_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
{
return inv_i2c_read_base(st, st->i2c_addr, reg, len, data);
}
static int _memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 const *data)
{
u8 bank[2];
u8 addr[2];
u8 buf[513];
struct i2c_msg msgs[3];
int res;
if (!data || !st)
return -EINVAL;
if (len >= (sizeof(buf) - 1))
return -ENOMEM;
bank[0] = REG_MEM_BANK_SEL;
bank[1] = mem_addr >> 8;
addr[0] = REG_MEM_START_ADDR;
addr[1] = mem_addr & 0xFF;
buf[0] = REG_MEM_R_W;
memcpy(buf + 1, data, len);
/* write message */
msgs[0].addr = mpu_addr;
msgs[0].flags = 0;
msgs[0].buf = bank;
msgs[0].len = sizeof(bank);
msgs[1].addr = mpu_addr;
msgs[1].flags = 0;
msgs[1].buf = addr;
msgs[1].len = sizeof(addr);
msgs[2].addr = mpu_addr;
msgs[2].flags = 0;
msgs[2].buf = (u8 *) buf;
msgs[2].len = len + 1;
INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
#if CONFIG_DYNAMIC_DEBUG_I2C
{
char *write = 0;
pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
mpu_addr, bank[1], addr[1],
wr_pr_debug_begin(data, len, write),
wr_pr_debug_end(write), len);
}
#endif
res = i2c_transfer(st->sl_handle, msgs, 3);
if (res != 3) {
if (res >= 0)
res = -EIO;
return res;
} else {
return 0;
}
}
static int inv_i2c_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 const *data)
{
int r, i, j;
#define DMP_MEM_CMP_SIZE 16
u8 w[DMP_MEM_CMP_SIZE];
bool retry;
j = 0;
retry = true;
while ((j < 3) && retry) {
retry = false;
r = _memory_write(st, mpu_addr, mem_addr, len, data);
if (len < DMP_MEM_CMP_SIZE) {
r = mem_r(mem_addr, len, w);
for (i = 0; i < len; i++) {
if (data[i] != w[i]) {
pr_debug
("error write=%x, len=%d,data=%x, w=%x, i=%d\n",
mem_addr, len, data[i], w[i], i);
retry = true;
}
}
}
j++;
}
return r;
}
static int inv_i2c_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 *data)
{
u8 bank[2];
u8 addr[2];
u8 buf;
struct i2c_msg msgs[4];
int res;
if (!data || !st)
return -EINVAL;
bank[0] = REG_MEM_BANK_SEL;
bank[1] = mem_addr >> 8;
addr[0] = REG_MEM_START_ADDR;
addr[1] = mem_addr & 0xFF;
buf = REG_MEM_R_W;
/* write message */
msgs[0].addr = mpu_addr;
msgs[0].flags = 0;
msgs[0].buf = bank;
msgs[0].len = sizeof(bank);
msgs[1].addr = mpu_addr;
msgs[1].flags = 0;
msgs[1].buf = addr;
msgs[1].len = sizeof(addr);
msgs[2].addr = mpu_addr;
msgs[2].flags = 0;
msgs[2].buf = &buf;
msgs[2].len = 1;
msgs[3].addr = mpu_addr;
msgs[3].flags = I2C_M_RD;
msgs[3].buf = data;
msgs[3].len = len;
res = i2c_transfer(st->sl_handle, msgs, 4);
if (res != 4) {
if (res >= 0)
res = -EIO;
} else
res = 0;
INV_I2C_INC_MPUWRITE(3 + 3 + 3);
INV_I2C_INC_MPUREAD(len);
#if CONFIG_DYNAMIC_DEBUG_I2C
{
char *read = 0;
pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
mpu_addr, bank[1], addr[1], len,
wr_pr_debug_begin(data, len, read),
wr_pr_debug_end(read));
}
#endif
return res;
}
/*
* inv_mpu_probe() - probe function.
*/
static int inv_mpu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct inv_mpu_state *st;
struct iio_dev *indio_dev;
int result;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
result = -ENOSYS;
pr_err("I2c function error\n");
goto out_no_free;
}
#ifdef KERNEL_VERSION_4_X
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#else
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#endif
st = iio_priv(indio_dev);
st->client = client;
st->sl_handle = client->adapter;
st->i2c_addr = client->addr;
st->write = inv_i2c_single_write;
st->read = inv_i2c_read;
st->mem_write = inv_i2c_mem_write;
st->mem_read = inv_i2c_mem_read;
st->dev = &client->dev;
st->bus_type = BUS_I2C;
#ifdef CONFIG_OF
result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
if (result)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
/* Power on device */
if (st->plat_data.power_on) {
result = st->plat_data.power_on(&st->plat_data);
if (result < 0) {
dev_err(st->dev, "power_on failed: %d\n", result);
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
}
pr_info("%s: power on here.\n", __func__);
}
pr_info("%s: power on.\n", __func__);
msleep(100);
#else
if (dev_get_platdata(st->dev) == NULL)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
#endif
/* power is turned on inside check chip type */
result = inv_check_chip_type(indio_dev, id->name);
if (result)
#ifdef KERNEL_VERSION_4_X
return -ENODEV;
#else
goto out_free;
#endif
/* Make state variables available to all _show and _store functions. */
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = st->dev;
indio_dev->name = id->name;
st->irq = client->irq;
result = inv_mpu_configure_ring(indio_dev);
if (result) {
pr_err("configure ring buffer fail\n");
goto out_free;
}
#ifdef KERNEL_VERSION_4_X
INV_I2C_SETIRQ(IRQ_MPU, st->irq);
result = devm_iio_device_register(st->dev, indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_unreg_ring;
}
#else
result = iio_buffer_register(indio_dev, indio_dev->channels,
indio_dev->num_channels);
if (result) {
pr_err("ring buffer register fail\n");
goto out_unreg_ring;
}
INV_I2C_SETIRQ(IRQ_MPU, client->irq);
result = iio_device_register(indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_remove_ring;
}
#endif
result = inv_create_dmp_sysfs(indio_dev);
if (result) {
pr_err("create dmp sysfs failed\n");
goto out_unreg_iio;
}
init_waitqueue_head(&st->wait_queue);
st->resume_state = true;
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
#else
wakeup_source_init(&st->wake_lock, "inv_mpu");
#endif
dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
indio_dev->name, INVENSENSE_DRIVER_VERSION);
#ifdef SENSOR_DATA_FROM_REGISTERS
pr_info("Data read from registers\n");
#else
pr_info("Data read from FIFO\n");
#endif
return 0;
#ifdef KERNEL_VERSION_4_X
out_unreg_iio:
devm_iio_device_unregister(st->dev, indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
devm_iio_device_free(st->dev, indio_dev);
out_no_free:
#else
out_unreg_iio:
iio_device_unregister(indio_dev);
out_remove_ring:
iio_buffer_unregister(indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
iio_device_free(indio_dev);
out_no_free:
#endif
dev_err(st->dev, "%s failed %d\n", __func__, result);
return -EIO;
}
static void inv_mpu_shutdown(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu_state *st = iio_priv(indio_dev);
int result;
mutex_lock(&indio_dev->mlock);
inv_switch_power_in_lp(st, true);
dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
/* reset to make sure previous state are not there */
result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
if (result)
dev_err(st->dev, "Failed to reset %s\n",
st->hw->name);
msleep(POWER_UP_TIME);
/* turn off power to ensure gyro engine is off */
result = inv_set_power(st, false);
if (result)
dev_err(st->dev, "Failed to turn off %s\n",
st->hw->name);
inv_switch_power_in_lp(st, false);
mutex_unlock(&indio_dev->mlock);
}
/*
* inv_mpu_remove() - remove function.
*/
static int inv_mpu_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu_state *st = iio_priv(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_unregister(st->dev, indio_dev);
#else
iio_device_unregister(indio_dev);
iio_buffer_unregister(indio_dev);
#endif
inv_mpu_unconfigure_ring(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_free(st->dev, indio_dev);
#else
iio_device_free(indio_dev);
#endif
dev_info(st->dev, "inv-mpu-iio module removed.\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int inv_mpu_i2c_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
return inv_mpu_suspend(indio_dev);
}
static void inv_mpu_i2c_complete(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
inv_mpu_complete(indio_dev);
}
#endif
static const struct dev_pm_ops inv_mpu_i2c_pmops = {
#ifdef CONFIG_PM_SLEEP
.suspend = inv_mpu_i2c_suspend,
.complete = inv_mpu_i2c_complete,
#endif
};
/* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id inv_mpu_id[] = {
#ifdef CONFIG_INV_MPU_IIO_ICM20648
{"icm20645", ICM20645},
{"icm10340", ICM10340},
{"icm20648", ICM20648},
#else
{"icm20608d", ICM20608D},
{"icm20690", ICM20690},
{"icm20602", ICM20602},
{"iam20680", IAM20680},
#endif
{}
};
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.shutdown = inv_mpu_shutdown,
.id_table = inv_mpu_id,
.driver = {
.owner = THIS_MODULE,
.name = "inv-mpu-iio-i2c",
.pm = &inv_mpu_i2c_pmops,
},
};
module_i2c_driver(inv_mpu_driver);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense I2C device driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,620 @@
/*
* Copyright (C) 2012-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/slab.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/math64.h>
#include <linux/miscdevice.h>
#include "inv_mpu_iio.h"
static void inv_push_timestamp(struct iio_dev *indio_dev, u64 t)
{
u8 buf[IIO_BUFFER_BYTES];
struct inv_mpu_state *st;
st = iio_priv(indio_dev);
if (st->poke_mode_on)
memcpy(buf, &st->poke_ts, sizeof(t));
else
memcpy(buf, &t, sizeof(t));
iio_push_to_buffers(indio_dev, buf);
}
int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr, int data)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
u8 buf[IIO_BUFFER_BYTES];
memcpy(buf, &hdr, sizeof(hdr));
memcpy(&buf[4], &data, sizeof(data));
iio_push_to_buffers(indio_dev, buf);
return 0;
}
static int inv_calc_precision(struct inv_mpu_state *st)
{
int diff;
int init;
if (st->eis.voting_state != 8)
return 0;
diff = abs(st->eis.fsync_delay_s[1] - st->eis.fsync_delay_s[0]);
init = 0;
if (diff)
init = st->sensor[SENSOR_GYRO].dur / diff;
if (abs(init - NSEC_PER_USEC) < (NSEC_PER_USEC >> 3))
st->eis.count_precision = init;
else
st->eis.voting_state = 0;
pr_debug("dur= %d prc= %d\n", st->sensor[SENSOR_GYRO].dur,
st->eis.count_precision);
return 0;
}
static s64 calc_frame_ave(struct inv_mpu_state *st, int delay)
{
s64 ts;
ts = st->eis.current_timestamp - delay;
#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
ts -= st->ts_algo.gyro_ts_shift;
#endif
pr_debug("shift= %d ts = %lld\n", st->ts_algo.gyro_ts_shift, ts);
return ts;
}
static void inv_push_eis_ring(struct inv_mpu_state *st, int *q, bool sync,
s64 t)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
struct inv_eis *eis = &st->eis;
u8 buf[IIO_BUFFER_BYTES];
int tmp, ii;
buf[0] = (EIS_GYRO_HDR & 0xff);
buf[1] = (EIS_GYRO_HDR >> 8);
memcpy(buf + 4, &q[0], sizeof(q[0]));
iio_push_to_buffers(indio_dev, buf);
for (ii = 0; ii < 2; ii++)
memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
iio_push_to_buffers(indio_dev, buf);
tmp = eis->frame_count;
if (sync)
tmp |= 0x80000000;
memcpy(buf, &tmp, sizeof(tmp));
iio_push_to_buffers(indio_dev, buf);
inv_push_timestamp(indio_dev, t);
}
static int inv_do_interpolation_gyro(struct inv_mpu_state *st, int *prev,
s64 prev_t, int *curr, s64 curr_t, s64 t, bool trigger)
{
int i;
int out[3];
#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
prev_t -= st->ts_algo.gyro_ts_shift;
prev_t += MPU_4X_TS_GYRO_SHIFT;
curr_t -= st->ts_algo.gyro_ts_shift;
curr_t += MPU_4X_TS_GYRO_SHIFT;
#endif
if ((t > prev_t) && (t < curr_t)) {
for (i = 0; i < 3; i++)
out[i] = (int)div_s64((s64)(curr[i] - prev[i]) *
(s64)(t - prev_t), curr_t - prev_t) + prev[i];
} else if (t < prev_t) {
for (i = 0; i < 3; i++)
out[i] = prev[i];
} else {
for (i = 0; i < 3; i++)
out[i] = curr[i];
}
pr_debug("prev= %lld t = %lld curr= %lld\n", prev_t, t, curr_t);
pr_debug("prev = %d, %d, %d\n", prev[0], prev[1], prev[2]);
pr_debug("curr = %d, %d, %d\n", curr[0], curr[1], curr[2]);
pr_debug("out = %d, %d, %d\n", out[0], out[1], out[2]);
inv_push_eis_ring(st, out, trigger, t);
return 0;
}
#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
static void inv_handle_triggered_eis(struct inv_mpu_state *st)
{
struct inv_eis *eis = &st->eis;
int delay;
if (st->eis.eis_frame) {
inv_calc_precision(st);
delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
eis->fsync_timestamp = calc_frame_ave(st, delay);
inv_do_interpolation_gyro(st,
st->eis.prev_gyro, st->eis.prev_timestamp,
st->eis.current_gyro, st->eis.current_timestamp,
eis->fsync_timestamp, true);
pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
eis->fsync_timestamp, eis->current_timestamp, delay);
inv_push_eis_ring(st, st->eis.current_gyro, false,
st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ MPU_4X_TS_GYRO_SHIFT);
eis->last_fsync_timestamp = eis->fsync_timestamp;
} else {
pr_debug("cur= %lld\n", st->eis.current_timestamp);
inv_push_eis_ring(st, st->eis.current_gyro, false,
st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ MPU_4X_TS_GYRO_SHIFT);
}
}
#else
static void inv_handle_triggered_eis(struct inv_mpu_state *st)
{
struct inv_eis *eis = &st->eis;
int delay;
if ((st->eis.eis_frame && (st->eis.fsync_delay != 5)) ||
(st->eis.eis_frame && (st->eis.fsync_delay == 5) &&
(!st->eis.current_sync))
) {
inv_calc_precision(st);
delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
eis->fsync_timestamp = calc_frame_ave(st, delay);
inv_do_interpolation_gyro(st,
st->eis.prev_gyro, st->eis.prev_timestamp,
st->eis.current_gyro, st->eis.current_timestamp,
eis->fsync_timestamp, true);
pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
eis->fsync_timestamp, eis->current_timestamp, delay);
inv_push_eis_ring(st, st->eis.current_gyro, false,
st->eis.current_timestamp);
eis->last_fsync_timestamp = eis->fsync_timestamp;
st->eis.eis_frame = false;
} else {
st->eis.current_sync = false;
pr_debug("cur= %lld\n", st->eis.current_timestamp);
inv_push_eis_ring(st, st->eis.current_gyro, false,
st->eis.current_timestamp);
}
}
#endif
static void inv_push_eis_buffer(struct inv_mpu_state *st, u64 t, int *q)
{
int ii;
if (st->eis.eis_triggered) {
for (ii = 0; ii < 3; ii++)
st->eis.prev_gyro[ii] = st->eis.current_gyro[ii];
st->eis.prev_timestamp = st->eis.current_timestamp;
for (ii = 0; ii < 3; ii++)
st->eis.current_gyro[ii] = q[ii];
st->eis.current_timestamp = t;
inv_handle_triggered_eis(st);
} else {
for (ii = 0; ii < 3; ii++)
st->eis.current_gyro[ii] = q[ii];
st->eis.current_timestamp = t;
}
}
static int inv_push_16bytes_final(struct inv_mpu_state *st, int j,
s32 *q, u64 t, s16 accur)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
u8 buf[IIO_BUFFER_BYTES];
int ii;
memcpy(buf, &st->sensor_l[j].header, sizeof(st->sensor_l[j].header));
memcpy(buf + 2, &accur, sizeof(accur));
memcpy(buf + 4, &q[0], sizeof(q[0]));
iio_push_to_buffers(indio_dev, buf);
for (ii = 0; ii < 2; ii++)
memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
iio_push_to_buffers(indio_dev, buf);
inv_push_timestamp(indio_dev, t);
st->sensor_l[j].counter = 0;
if (st->sensor_l[j].wake_on)
st->wake_sensor_received = true;
return 0;
}
int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 sensor,
u64 t, int *q, s16 accur)
{
int j;
for (j = 0; j < SENSOR_L_NUM_MAX; j++) {
if (st->sensor_l[j].on && (st->sensor_l[j].base == sensor)) {
st->sensor_l[j].counter++;
if ((st->sensor_l[j].div != 0xffff) &&
(st->sensor_l[j].counter >=
st->sensor_l[j].div)) {
pr_debug(
"Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
j, sensor,
st->sensor_l[j].header,
st->sensor_l[j].div,
t, q[0], q[1], q[2]);
inv_push_16bytes_final(st, j, q, t, accur);
}
}
}
return 0;
}
void inv_convert_and_push_16bytes(struct inv_mpu_state *st, u16 hdr,
u8 *d, u64 t, s8 *m)
{
int i, j;
s32 in[3], out[3];
for (i = 0; i < 3; i++)
in[i] = be32_to_int(d + i * 4);
/* multiply with orientation matrix can be optimized like this */
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
if (m[i * 3 + j])
out[i] = in[j] * m[i * 3 + j];
inv_push_16bytes_buffer(st, hdr, t, out, 0);
}
void inv_convert_and_push_8bytes(struct inv_mpu_state *st, u16 hdr,
u8 *d, u64 t, s8 *m)
{
int i, j;
s16 in[3], out[3];
for (i = 0; i < 3; i++)
in[i] = be16_to_cpup((__be16 *) (d + i * 2));
/* multiply with orientation matrix can be optimized like this */
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
if (m[i * 3 + j])
out[i] = in[j] * m[i * 3 + j];
inv_push_8bytes_buffer(st, hdr, t, out);
}
int inv_push_special_8bytes_buffer(struct inv_mpu_state *st,
u16 hdr, u64 t, s16 *d)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
u8 buf[IIO_BUFFER_BYTES];
int j;
memcpy(buf, &hdr, sizeof(hdr));
memcpy(&buf[2], &d[0], sizeof(d[0]));
for (j = 0; j < 2; j++)
memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
iio_push_to_buffers(indio_dev, buf);
inv_push_timestamp(indio_dev, t);
return 0;
}
static int inv_s16_gyro_push(struct inv_mpu_state *st, int i, s16 *raw, u64 t)
{
if (st->sensor_l[i].on) {
st->sensor_l[i].counter++;
if ((st->sensor_l[i].div != 0xffff) &&
(st->sensor_l[i].counter >= st->sensor_l[i].div)) {
inv_push_special_8bytes_buffer(st,
st->sensor_l[i].header, t, raw);
st->sensor_l[i].counter = 0;
if (st->sensor_l[i].wake_on)
st->wake_sensor_received = true;
}
}
return 0;
}
static int inv_s32_gyro_push(struct inv_mpu_state *st, int i, s32 *calib, u64 t)
{
if (st->sensor_l[i].on) {
st->sensor_l[i].counter++;
if ((st->sensor_l[i].div != 0xffff) &&
(st->sensor_l[i].counter >= st->sensor_l[i].div)) {
inv_push_16bytes_final(st, i, calib, t, 0);
st->sensor_l[i].counter = 0;
if (st->sensor_l[i].wake_on)
st->wake_sensor_received = true;
}
}
return 0;
}
int inv_push_gyro_data(struct inv_mpu_state *st, s16 *raw, s32 *calib, u64 t)
{
int gyro_data[] = {SENSOR_L_GYRO, SENSOR_L_GYRO_WAKE};
int calib_data[] = {SENSOR_L_GYRO_CAL, SENSOR_L_GYRO_CAL_WAKE};
int i;
if (st->sensor_l[SENSOR_L_EIS_GYRO].on)
inv_push_eis_buffer(st, t, calib);
for (i = 0; i < 2; i++)
inv_s16_gyro_push(st, gyro_data[i], raw, t);
for (i = 0; i < 2; i++)
inv_s32_gyro_push(st, calib_data[i], calib, t);
return 0;
}
int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 sensor, u64 t, s16 *d)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
u8 buf[IIO_BUFFER_BYTES];
int ii, j;
if ((sensor == STEP_DETECTOR_HDR) ||
(sensor == STEP_DETECTOR_WAKE_HDR)) {
memcpy(buf, &sensor, sizeof(sensor));
memcpy(&buf[2], &d[0], sizeof(d[0]));
for (j = 0; j < 2; j++)
memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
iio_push_to_buffers(indio_dev, buf);
inv_push_timestamp(indio_dev, t);
if (sensor == STEP_DETECTOR_WAKE_HDR)
st->wake_sensor_received = true;
return 0;
}
for (ii = 0; ii < SENSOR_L_NUM_MAX; ii++) {
if (st->sensor_l[ii].on &&
(st->sensor_l[ii].base == sensor) &&
(st->sensor_l[ii].div != 0xffff)) {
st->sensor_l[ii].counter++;
if (st->sensor_l[ii].counter >= st->sensor_l[ii].div) {
pr_debug(
"Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
ii, sensor, st->sensor_l[ii].header,
st->sensor_l[ii].div, t, d[0], d[1], d[2]);
memcpy(buf, &st->sensor_l[ii].header,
sizeof(st->sensor_l[ii].header));
memcpy(&buf[2], &d[0], sizeof(d[0]));
for (j = 0; j < 2; j++)
memcpy(&buf[4 + j * 2], &d[j + 1],
sizeof(d[j]));
iio_push_to_buffers(indio_dev, buf);
inv_push_timestamp(indio_dev, t);
st->sensor_l[ii].counter = 0;
if (st->sensor_l[ii].wake_on)
st->wake_sensor_received = true;
}
}
}
return 0;
}
#ifdef CONFIG_INV_MPU_IIO_ICM20648
/* Implemented activity to string function for BAC test */
#define TILT_DETECTED 0x1000
#define NONE 0x00
#define DRIVE 0x01
#define WALK 0x02
#define RUN 0x04
#define BIKE 0x08
#define TILT 0x10
#define STILL 0x20
#define DRIVE_WALK (DRIVE | WALK)
#define DRIVE_RUN (DRIVE | RUN)
char *act_string(s16 data)
{
data &= (~TILT);
switch (data) {
case NONE:
return "None";
case DRIVE:
return "Drive";
case WALK:
return "Walk";
case RUN:
return "Run";
case BIKE:
return "Bike";
case STILL:
return "Still";
case DRIVE_WALK:
return "drive and walk";
case DRIVE_RUN:
return "drive and run";
default:
return "Unknown";
}
return "Unknown";
}
char *inv_tilt_check(s16 data)
{
if (data & TILT)
return "Tilt";
else
return "None";
}
int inv_push_8bytes_kf(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
u8 buf[IIO_BUFFER_BYTES];
int i;
if (st->chip_config.activity_on) {
memcpy(buf, &hdr, sizeof(hdr));
for (i = 0; i < 3; i++)
memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i]));
kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
memcpy(buf, &t, sizeof(t));
kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
st->activity_size += IIO_BUFFER_BYTES * 2;
}
if (st->chip_config.tilt_enable) {
pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]",
d[0], d[0] & 0x00FF,
inv_tilt_check(d[0] & 0x00FF),
(d[0] & 0xFF00) >> 8, inv_tilt_check((d[0] & 0xFF00) >> 8));
sysfs_notify(&indio_dev->dev.kobj, NULL, "poll_tilt");
}
pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]", d[0], d[0] & 0x00FF,
act_string(d[0] & 0x00FF),
(d[0] & 0xFF00) >> 8, act_string((d[0] & 0xFF00) >> 8));
read_be32_from_mem(st, &st->bac_drive_conf, BAC_DRIVE_CONFIDENCE);
read_be32_from_mem(st, &st->bac_walk_conf, BAC_WALK_CONFIDENCE);
read_be32_from_mem(st, &st->bac_smd_conf, BAC_SMD_CONFIDENCE);
read_be32_from_mem(st, &st->bac_bike_conf, BAC_BIKE_CONFIDENCE);
read_be32_from_mem(st, &st->bac_still_conf, BAC_STILL_CONFIDENCE);
read_be32_from_mem(st, &st->bac_run_conf, BAC_RUN_CONFIDENCE);
return 0;
}
#endif
int inv_send_steps(struct inv_mpu_state *st, int step, u64 ts)
{
s16 s[3];
s[0] = 0;
s[1] = (s16) (step & 0xffff);
s[2] = (s16) ((step >> 16) & 0xffff);
if (st->step_counter_l_on)
inv_push_special_8bytes_buffer(st, STEP_COUNTER_HDR, ts, s);
if (st->step_counter_wake_l_on) {
inv_push_special_8bytes_buffer(st, STEP_COUNTER_WAKE_HDR,
ts, s);
st->wake_sensor_received = true;
}
return 0;
}
void inv_push_step_indicator(struct inv_mpu_state *st, u64 t)
{
s16 sen[3];
#define STEP_INDICATOR_HEADER 0x0001
sen[0] = 0;
sen[1] = 0;
sen[2] = 0;
inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER, t, sen);
}
/*
* inv_irq_handler() - Cache a timestamp at each data ready interrupt.
*/
static irqreturn_t inv_irq_handler(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
{
struct inv_mpu_state *st = iio_priv(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_free_irq(st->dev, st->irq, st);
devm_iio_kfifo_free(st->dev, indio_dev->buffer);
#else
free_irq(st->irq, st);
iio_kfifo_free(indio_dev->buffer);
#endif
};
EXPORT_SYMBOL_GPL(inv_mpu_unconfigure_ring);
#ifndef KERNEL_VERSION_4_X
static int inv_predisable(struct iio_dev *indio_dev)
{
return 0;
}
static int inv_preenable(struct iio_dev *indio_dev)
{
return 0;
}
static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
.preenable = &inv_preenable,
.predisable = &inv_predisable,
};
#endif
int inv_mpu_configure_ring(struct iio_dev *indio_dev)
{
int ret;
struct inv_mpu_state *st = iio_priv(indio_dev);
struct iio_buffer *ring;
#ifdef KERNEL_VERSION_4_X
ring = devm_iio_kfifo_allocate(st->dev);
if (!ring)
return -ENOMEM;
ring->scan_timestamp = true;
iio_device_attach_buffer(indio_dev, ring);
ret = devm_request_threaded_irq(st->dev,
st->irq,
inv_irq_handler,
inv_read_fifo,
IRQF_TRIGGER_RISING | IRQF_SHARED,
"inv_irq",
st);
if (ret) {
devm_iio_kfifo_free(st->dev, ring);
return ret;
}
// this mode does not use ops
indio_dev->modes = INDIO_ALL_BUFFER_MODES;
return ret;
#else
ring = iio_kfifo_allocate(indio_dev);
if (!ring)
return -ENOMEM;
indio_dev->buffer = ring;
/* setup ring buffer */
ring->scan_timestamp = true;
indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
ret = request_threaded_irq(st->irq,
inv_irq_handler,
inv_read_fifo,
IRQF_TRIGGER_RISING | IRQF_SHARED,
"inv_irq",
st);
if (ret)
goto error_iio_sw_rb_free;
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
return 0;
error_iio_sw_rb_free:
iio_kfifo_free(indio_dev->buffer);
return ret;
#endif
}
EXPORT_SYMBOL_GPL(inv_mpu_configure_ring);

View file

@ -0,0 +1,407 @@
/*
* Copyright (C) 2012-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include "inv_mpu_iio.h"
#include "inv_mpu_dts.h"
#define INV_SPI_READ 0x80
static int inv_spi_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
{
struct spi_message msg;
int res;
u8 d[2];
struct spi_transfer xfers = {
.tx_buf = d,
.bits_per_word = 8,
.len = 2,
};
pr_debug("reg_write: reg=0x%x data=0x%x\n", reg, data);
d[0] = reg;
d[1] = data;
spi_message_init(&msg);
spi_message_add_tail(&xfers, &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
return res;
}
static int inv_spi_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
{
struct spi_message msg;
int res;
u8 d[1];
struct spi_transfer xfers[] = {
{
.tx_buf = d,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = data,
.bits_per_word = 8,
.len = len,
}
};
if (!data)
return -EINVAL;
d[0] = (reg | INV_SPI_READ);
spi_message_init(&msg);
spi_message_add_tail(&xfers[0], &msg);
spi_message_add_tail(&xfers[1], &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
if (len ==1)
pr_debug("reg_read: reg=0x%x length=%d data=0x%x\n",
reg, len, data[0]);
else
pr_debug("reg_read: reg=0x%x length=%d d0=0x%x d1=0x%x\n",
reg, len, data[0], data[1]);
return res;
}
static int inv_spi_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 const *data)
{
struct spi_message msg;
u8 buf[258];
int res;
struct spi_transfer xfers = {
.tx_buf = buf,
.bits_per_word = 8,
.len = len + 1,
};
if (!data || !st)
return -EINVAL;
if (len > (sizeof(buf) - 1))
return -ENOMEM;
inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
buf[0] = REG_MEM_R_W;
memcpy(buf + 1, data, len);
spi_message_init(&msg);
spi_message_add_tail(&xfers, &msg);
res = spi_sync(to_spi_device(st->dev), &msg);
return res;
}
static int inv_spi_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
u32 len, u8 *data)
{
int res;
if (!data || !st)
return -EINVAL;
if (len > 256)
return -EINVAL;
res = inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
res = inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
res = inv_plat_read(st, REG_MEM_R_W, len, data);
return res;
}
/*
* inv_mpu_probe() - probe function.
*/
static int inv_mpu_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct inv_mpu_state *st;
struct iio_dev *indio_dev;
int result;
#ifdef KERNEL_VERSION_4_X
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#else
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL) {
pr_err("memory allocation failed\n");
result = -ENOMEM;
goto out_no_free;
}
#endif
st = iio_priv(indio_dev);
st->write = inv_spi_single_write;
st->read = inv_spi_read;
st->mem_write = inv_spi_mem_write;
st->mem_read = inv_spi_mem_read;
st->dev = &spi->dev;
st->irq = spi->irq;
#if !defined(CONFIG_INV_MPU_IIO_ICM20602) \
&& !defined(CONFIG_INV_MPU_IIO_IAM20680)
st->i2c_dis = BIT_I2C_IF_DIS;
#endif
st->bus_type = BUS_SPI;
spi_set_drvdata(spi, indio_dev);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = id->name;
#ifdef CONFIG_OF
result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
if (result)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
/* Power on device */
if (st->plat_data.power_on) {
result = st->plat_data.power_on(&st->plat_data);
if (result < 0) {
dev_err(st->dev, "power_on failed: %d\n", result);
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
}
pr_info("%s: power on here.\n", __func__);
}
pr_info("%s: power on.\n", __func__);
msleep(100);
#else
if (dev_get_platdata(st->dev) == NULL)
# ifdef KERNEL_VERSION_4_X
return -ENODEV;
# else
goto out_free;
# endif
st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
#endif
/* power is turned on inside check chip type */
result = inv_check_chip_type(indio_dev, id->name);
if (result)
#ifdef KERNEL_VERSION_4_X
return -ENODEV;
#else
goto out_free;
#endif
result = inv_mpu_configure_ring(indio_dev);
if (result) {
pr_err("configure ring buffer fail\n");
goto out_free;
}
#ifdef KERNEL_VERSION_4_X
result = devm_iio_device_register(st->dev, indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_unreg_ring;
}
#else
result = iio_buffer_register(indio_dev, indio_dev->channels,
indio_dev->num_channels);
if (result) {
pr_err("ring buffer register fail\n");
goto out_unreg_ring;
}
result = iio_device_register(indio_dev);
if (result) {
pr_err("IIO device register fail\n");
goto out_remove_ring;
}
#endif
result = inv_create_dmp_sysfs(indio_dev);
if (result) {
pr_err("create dmp sysfs failed\n");
goto out_unreg_iio;
}
init_waitqueue_head(&st->wait_queue);
st->resume_state = true;
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
#else
wakeup_source_init(&st->wake_lock, "inv_mpu");
#endif
dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
indio_dev->name, INVENSENSE_DRIVER_VERSION);
#ifdef SENSOR_DATA_FROM_REGISTERS
pr_info("Data read from registers\n");
#else
pr_info("Data read from FIFO\n");
#endif
return 0;
#ifdef KERNEL_VERSION_4_X
out_unreg_iio:
devm_iio_device_unregister(st->dev, indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
devm_iio_device_free(st->dev, indio_dev);
out_no_free:
#else
out_unreg_iio:
iio_device_unregister(indio_dev);
out_remove_ring:
iio_buffer_unregister(indio_dev);
out_unreg_ring:
inv_mpu_unconfigure_ring(indio_dev);
out_free:
iio_device_free(indio_dev);
out_no_free:
#endif
dev_err(st->dev, "%s failed %d\n", __func__, result);
return -EIO;
}
static void inv_mpu_shutdown(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct inv_mpu_state *st = iio_priv(indio_dev);
int result;
mutex_lock(&indio_dev->mlock);
inv_switch_power_in_lp(st, true);
dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
/* reset to make sure previous state are not there */
result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
if (result)
dev_err(st->dev, "Failed to reset %s\n",
st->hw->name);
msleep(POWER_UP_TIME);
/* turn off power to ensure gyro engine is off */
result = inv_set_power(st, false);
if (result)
dev_err(st->dev, "Failed to turn off %s\n",
st->hw->name);
inv_switch_power_in_lp(st, false);
mutex_unlock(&indio_dev->mlock);
}
/*
* inv_mpu_remove() - remove function.
*/
static int inv_mpu_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct inv_mpu_state *st = iio_priv(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_unregister(st->dev, indio_dev);
#else
iio_device_unregister(indio_dev);
iio_buffer_unregister(indio_dev);
#endif
inv_mpu_unconfigure_ring(indio_dev);
#ifdef KERNEL_VERSION_4_X
devm_iio_device_free(st->dev, indio_dev);
#else
iio_device_free(indio_dev);
#endif
dev_info(st->dev, "inv-mpu-iio module removed.\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int inv_mpu_spi_suspend(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
return inv_mpu_suspend(indio_dev);
}
static void inv_mpu_spi_complete(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
inv_mpu_complete(indio_dev);
}
#endif
static const struct dev_pm_ops inv_mpu_spi_pmops = {
#ifdef CONFIG_PM_SLEEP
.suspend = inv_mpu_spi_suspend,
.complete = inv_mpu_spi_complete,
#endif
};
/* device id table is used to identify what device can be
* supported by this driver
*/
static const struct spi_device_id inv_mpu_id[] = {
#ifdef CONFIG_INV_MPU_IIO_ICM20648
{"icm20645", ICM20645},
{"icm10340", ICM10340},
{"icm20648", ICM20648},
#else
{"icm20608d", ICM20608D},
{"icm20690", ICM20690},
{"icm20602", ICM20602},
{"iam20680", IAM20680},
#endif
{}
};
MODULE_DEVICE_TABLE(spi, inv_mpu_id);
static struct spi_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.shutdown = inv_mpu_shutdown,
.id_table = inv_mpu_id,
.driver = {
.owner = THIS_MODULE,
.name = "inv-mpu-iio-spi",
.pm = &inv_mpu_spi_pmops,
},
};
module_spi_driver(inv_mpu_driver);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense SPI device driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,280 @@
/*
* Copyright (C) 2012-2018 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "inv_mpu: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.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/math64.h>
#include "inv_mpu_iio.h"
#define INV_TIME_CALIB_THRESHOLD_1 2
#define MIN_DELAY (3 * NSEC_PER_MSEC)
#define JITTER_THRESH ( 1 * NSEC_PER_MSEC)
int inv_update_dmp_ts(struct inv_mpu_state *st, int ind)
{
int i;
u32 counter;
u64 ts;
enum INV_ENGINE en_ind;
struct inv_timestamp_algo *ts_algo = &st->ts_algo;
u32 base_time;
u64 cal_period;
if (st->mode_1k_on)
cal_period = (NSEC_PER_SEC >> 2);
else
cal_period = 2 * NSEC_PER_SEC;
ts = ts_algo->last_run_time - st->sensor[ind].time_calib;
counter = st->sensor[ind].sample_calib;
en_ind = st->sensor[ind].engine_base;
if (en_ind != ts_algo->clock_base)
return 0;
/* we average over 2 seconds period to do the timestamp calculation */
if (ts < cal_period)
return 0;
/* this is the first time we do timestamp averaging, return */
/* after resume from suspend, the clock of linux has up to 1 seconds
drift. We should start from the resume clock instead of using clock
before resume */
if ((!st->sensor[ind].calib_flag) || ts_algo->resume_flag) {
st->sensor[ind].sample_calib = 0;
st->sensor[ind].time_calib = ts_algo->last_run_time;
st->sensor[ind].calib_flag = 1;
ts_algo->resume_flag = false;
return 0;
}
/* if the sample number in current FIFO is not zero and between now and
last update time is more than 2 seconds, we do calculation */
if ((counter > 0) &&
(ts_algo->last_run_time - st->eng_info[en_ind].last_update_time >
cal_period)) {
/* duration for each sensor */
st->sensor[ind].dur = (u32) div_u64(ts, counter);
/* engine duration derived from each sensor */
if (st->sensor[ind].div)
st->eng_info[en_ind].dur = st->sensor[ind].dur /
st->sensor[ind].div;
else
pr_err("sensor %d divider zero!\n", ind);
/* update base time for each sensor */
if (st->eng_info[en_ind].divider) {
base_time = (st->eng_info[en_ind].dur /
st->eng_info[en_ind].divider) *
st->eng_info[en_ind].orig_rate;
if (st->mode_1k_on)
st->eng_info[en_ind].base_time_1k = base_time;
else
st->eng_info[en_ind].base_time = base_time;
} else {
pr_err("engine %d divider zero!\n", en_ind);
}
st->eng_info[en_ind].last_update_time = ts_algo->last_run_time;
/* update all the sensors duration based on the same engine */
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on &&
(st->sensor[i].engine_base == en_ind))
st->sensor[i].dur = st->sensor[i].div *
st->eng_info[en_ind].dur;
}
}
st->sensor[ind].sample_calib = 0;
st->sensor[ind].time_calib = ts_algo->last_run_time;
return 0;
}
/**
* int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
* This is the function to get last run time in non dmp and record mode.
* This function will update the last_run_time, which is important parameter
* in overall timestamp algorithm.
* return value: this function returns fifo count value.
*/
int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
{
long long t_pre, t_post, dur;
int fifo_count;
#ifndef SENSOR_DATA_FROM_REGISTERS
int res;
u8 data[2];
#endif
t_pre = get_time_ns();
#ifndef SENSOR_DATA_FROM_REGISTERS
res = inv_plat_read(st, REG_FIFO_COUNT_H, FIFO_COUNT_BYTE, data);
if (res) {
pr_info("read REG_FIFO_COUNT_H failed= %d\n", res);
return 0;
}
#endif
t_post = get_time_ns();
#ifdef SENSOR_DATA_FROM_REGISTERS
if (st->fifo_count_mode == BYTE_MODE)
fifo_count = st->batch.pk_size;
else
fifo_count = 1;
#else
fifo_count = be16_to_cpup((__be16 *) (data));
#endif
pr_debug("fifc=%d\n", fifo_count);
if (!fifo_count)
return 0;
if (st->special_mag_mode && (fifo_count == 2)) {
pr_debug("special trigger\n");
fifo_count = 1;
}
/* In non DMP mode, either gyro or accel duration is the duration
for each sample */
if (st->chip_config.gyro_enable)
dur = st->eng_info[ENGINE_GYRO].dur;
else
dur = st->eng_info[ENGINE_ACCEL].dur;
if (st->fifo_count_mode == BYTE_MODE) {
fifo_count /= st->batch.pk_size;
}
/* In record mode, each number in fifo_count is 1 record or 1 sample */
st->ts_algo.last_run_time += dur * fifo_count;
if (st->ts_algo.last_run_time < t_pre)
st->ts_algo.last_run_time = t_pre;
if (st->ts_algo.last_run_time > t_post)
st->ts_algo.last_run_time = t_post;
return fifo_count;
}
int inv_get_dmp_ts(struct inv_mpu_state *st, int i)
{
u64 current_time;
int expected_lower_duration, expected_upper_duration;
current_time = get_time_ns();
st->sensor[i].ts += st->sensor[i].dur + st->sensor[i].ts_adj;
if (st->sensor[i].ts < st->sensor[i].previous_ts)
st->sensor[i].ts = st->sensor[i].previous_ts + st->sensor[i].dur;
//hifi sensor limits ts jitter to +/- 2%
expected_upper_duration = st->eng_info[st->sensor[i].engine_base].divider * 1020000;
expected_lower_duration = st->eng_info[st->sensor[i].engine_base].divider * 980000;
#if defined(CONFIG_INV_MPU_IIO_ICM20602) || defined(CONFIG_INV_MPU_IIO_ICM20690) || defined(CONFIG_INV_MPU_IIO_IAM20680)
if (st->sensor[i].ts < st->sensor[i].previous_ts + expected_lower_duration)
st->sensor[i].ts = st->sensor[i].previous_ts + expected_lower_duration;
if (st->sensor[i].ts > st->sensor[i].previous_ts + expected_upper_duration)
st->sensor[i].ts = st->sensor[i].previous_ts + expected_upper_duration;
#endif
if (st->sensor[i].ts > current_time )
st->sensor[i].ts = current_time;
st->sensor[i].previous_ts = st->sensor[i].ts;
pr_debug("ts=%lld, reset=%lld\n", st->sensor[i].ts, st->ts_algo.reset_ts);
if (st->sensor[i].ts < st->ts_algo.reset_ts) {
pr_debug("less than reset\n");
st->sensor[i].send = false;
} else {
st->sensor[i].send = true;
}
if (st->header_count == 1)
inv_update_dmp_ts(st, i);
return 0;
}
static void process_sensor_bounding(struct inv_mpu_state *st, int i)
{
s64 elaps_time, thresh1, thresh2;
struct inv_timestamp_algo *ts_algo = &st->ts_algo;
u32 dur;
elaps_time = ((u64) (st->sensor[i].dur)) * st->sensor[i].count;
thresh1 = ts_algo->last_run_time - elaps_time;
dur = max(st->sensor[i].dur, (int)MIN_DELAY);
thresh2 = thresh1 - dur;
if (thresh1 < 0)
thresh1 = 0;
if (thresh2 < 0)
thresh2 = 0;
st->sensor[i].ts_adj = 0;
if ((ts_algo->calib_counter >= INV_TIME_CALIB_THRESHOLD_1) &&
(!ts_algo->resume_flag)) {
if (st->sensor[i].ts < thresh2)
st->sensor[i].ts_adj = thresh2 - st->sensor[i].ts;
} else if ((ts_algo->calib_counter >=
INV_TIME_CALIB_THRESHOLD_1) && ts_algo->resume_flag) {
if (st->sensor[i].ts < thresh2)
st->sensor[i].ts = ts_algo->last_run_time -
elaps_time - JITTER_THRESH;
} else {
st->sensor[i].ts = ts_algo->last_run_time - elaps_time -
JITTER_THRESH;
st->sensor[i].previous_ts = st->sensor[i].ts;
}
if (st->sensor[i].ts > thresh1)
st->sensor[i].ts_adj = thresh1 - st->sensor[i].ts;
pr_debug("cali=%d\n", st->ts_algo.calib_counter);
pr_debug("adj= %lld\n", st->sensor[i].ts_adj);
pr_debug("dur= %d count= %d last= %lld\n", st->sensor[i].dur,
st->sensor[i].count, ts_algo->last_run_time);
if (st->sensor[i].ts_adj && (st->sensor[i].count > 1))
st->sensor[i].ts_adj = div_s64(st->sensor[i].ts_adj,
st->sensor[i].count);
}
/* inv_bound_timestamp (struct inv_mpu_state *st)
The purpose this function is to give a generic bound to each
sensor timestamp. The timestamp cannot exceed current time.
The timestamp cannot backwards one sample time either, otherwise, there
would be another sample in between. Using this principle, we can bound
the sensor samples */
int inv_bound_timestamp(struct inv_mpu_state *st)
{
int i;
struct inv_timestamp_algo *ts_algo = &st->ts_algo;
for (i = 0; i < SENSOR_NUM_MAX; i++) {
if (st->sensor[i].on) {
if (st->sensor[i].count) {
process_sensor_bounding(st, i);
} else if (ts_algo->calib_counter <
INV_TIME_CALIB_THRESHOLD_1) {
st->sensor[i].ts = ts_algo->reset_ts;
st->sensor[i].previous_ts = st->sensor[i].ts;
}
}
}
return 0;
}

View file

@ -0,0 +1,13 @@
#
# Kconfig for Invensense IIO testing hooks
#
config INV_TESTING
boolean "Invensense IIO testing hooks"
depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
default n
help
This flag enables display of additional testing information from the
Invensense IIO drivers
It also enables the I2C counters facility to perform IO profiling.
Some additional sysfs entries will appear when this flag is enabled.

View file

@ -0,0 +1,6 @@
#
# Makefile for Invensense IIO testing hooks.
#
obj-$(CONFIG_INV_TESTING) += inv_counters.o

View file

@ -0,0 +1,159 @@
/*
* Copyright (C) 2012-2017 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/device.h>
#include <linux/miscdevice.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/kdev_t.h>
#include <linux/string.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include "inv_counters.h"
static int mpu_irq;
static int accel_irq;
static int compass_irq;
struct inv_counters {
uint32_t i2c_tempreads;
uint32_t i2c_mpureads;
uint32_t i2c_mpuwrites;
uint32_t i2c_accelreads;
uint32_t i2c_accelwrites;
uint32_t i2c_compassreads;
uint32_t i2c_compasswrites;
uint32_t i2c_compassirq;
uint32_t i2c_accelirq;
};
static struct inv_counters Counters;
static ssize_t i2c_counters_show(struct class *cls,
struct class_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE,
"%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
mpu_irq ? kstat_irqs(mpu_irq) : 0,
Counters.i2c_tempreads,
Counters.i2c_mpureads, Counters.i2c_mpuwrites,
accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
Counters.i2c_accelreads, Counters.i2c_accelwrites,
compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
Counters.i2c_compassreads, Counters.i2c_compasswrites);
}
void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
{
switch (type) {
case IRQ_MPU:
mpu_irq = irq;
break;
case IRQ_ACCEL:
accel_irq = irq;
break;
case IRQ_COMPASS:
compass_irq = irq;
break;
}
}
EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
void inv_iio_counters_tempread(int count)
{
Counters.i2c_tempreads += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
void inv_iio_counters_mpuread(int count)
{
Counters.i2c_mpureads += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
void inv_iio_counters_mpuwrite(int count)
{
Counters.i2c_mpuwrites += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
void inv_iio_counters_accelread(int count)
{
Counters.i2c_accelreads += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
void inv_iio_counters_accelwrite(int count)
{
Counters.i2c_accelwrites += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
void inv_iio_counters_compassread(int count)
{
Counters.i2c_compassreads += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
void inv_iio_counters_compasswrite(int count)
{
Counters.i2c_compasswrites += count;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
void inv_iio_counters_compassirq(void)
{
Counters.i2c_compassirq++;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
void inv_iio_counters_accelirq(void)
{
Counters.i2c_accelirq++;
}
EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
static struct class_attribute inv_class_attr[] = {
__ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
__ATTR_NULL
};
static struct class inv_counters_class = {
.name = "inv_counters",
.owner = THIS_MODULE,
.class_attrs = (struct class_attribute *) &inv_class_attr
};
static int __init inv_counters_init(void)
{
memset(&Counters, 0, sizeof(Counters));
return class_register(&inv_counters_class);
}
static void __exit inv_counters_exit(void)
{
class_unregister(&inv_counters_class);
}
module_init(inv_counters_init);
module_exit(inv_counters_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GESL");
MODULE_DESCRIPTION("inv_counters debug support");

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2012-2017 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_COUNTERS_H_
#define _INV_COUNTERS_H_
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#ifdef CONFIG_INV_TESTING
enum irqtype {
IRQ_MPU,
IRQ_ACCEL,
IRQ_COMPASS
};
#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
void inv_iio_counters_mpuread(int count);
void inv_iio_counters_mpuwrite(int count);
void inv_iio_counters_accelread(int count);
void inv_iio_counters_accelwrite(int count);
void inv_iio_counters_compassread(int count);
void inv_iio_counters_compasswrite(int count);
void inv_iio_counters_tempread(int count);
void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
void inv_iio_counters_compassirq(void);
void inv_iio_counters_accelirq(void);
#else
#define INV_I2C_INC_MPUREAD(x)
#define INV_I2C_INC_MPUWRITE(x)
#define INV_I2C_INC_ACCELREAD(x)
#define INV_I2C_INC_ACCELWRITE(x)
#define INV_I2C_INC_COMPASSREAD(x)
#define INV_I2C_INC_COMPASSWRITE(x)
#define INV_I2C_INC_TEMPREAD(x)
#define INV_I2C_SETIRQ(type, irq)
#define INV_I2C_INC_COMPASSIRQ()
#define INV_I2C_INC_ACCELIRQ()
#endif /* CONFIG_INV_TESTING */
#endif /* _INV_COUNTERS_H_ */

124
include/linux/iio/imu/mpu.h Normal file
View file

@ -0,0 +1,124 @@
/*
* Copyright (C) 2012-2017 InvenSense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __MPU_H_
#define __MPU_H_
#ifdef __KERNEL__
#include <linux/types.h>
#include <linux/ioctl.h>
#endif
enum secondary_slave_type {
SECONDARY_SLAVE_TYPE_NONE,
SECONDARY_SLAVE_TYPE_ACCEL,
SECONDARY_SLAVE_TYPE_COMPASS,
SECONDARY_SLAVE_TYPE_PRESSURE,
SECONDARY_SLAVE_TYPE_ALS,
SECONDARY_SLAVE_TYPE_TYPES
};
enum ext_slave_id {
ID_INVALID = 0,
GYRO_ID_MPU3050,
GYRO_ID_MPU6050A2,
GYRO_ID_MPU6050B1,
GYRO_ID_MPU6050B1_NO_ACCEL,
GYRO_ID_ITG3500,
ACCEL_ID_LIS331,
ACCEL_ID_LSM303DLX,
ACCEL_ID_LIS3DH,
ACCEL_ID_KXSD9,
ACCEL_ID_KXTF9,
ACCEL_ID_BMA150,
ACCEL_ID_BMA222,
ACCEL_ID_BMA250,
ACCEL_ID_ADXL34X,
ACCEL_ID_MMA8450,
ACCEL_ID_MMA845X,
ACCEL_ID_MPU6050,
COMPASS_ID_AK8963,
COMPASS_ID_AK8975,
COMPASS_ID_AK8972,
COMPASS_ID_AMI30X,
COMPASS_ID_AMI306,
COMPASS_ID_YAS529,
COMPASS_ID_YAS530,
COMPASS_ID_HMC5883,
COMPASS_ID_LSM303DLH,
COMPASS_ID_LSM303DLM,
COMPASS_ID_MMC314X,
COMPASS_ID_HSCDTD002B,
COMPASS_ID_HSCDTD004A,
COMPASS_ID_MLX90399,
COMPASS_ID_AK09911,
COMPASS_ID_AK09912,
COMPASS_ID_AK09916,
PRESSURE_ID_BMP085,
PRESSURE_ID_BMP280,
ALS_ID_APDS_9900,
ALS_ID_APDS_9930,
ALS_ID_TSL_2772,
};
#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
/**
* struct mpu_platform_data - Platform data for the mpu driver
* @int_config: Bits [7:3] of the int config register.
* @level_shifter: 0: VLogic, 1: VDD
* @orientation: Orientation matrix of the gyroscope
* @sec_slave_type: secondary slave device type, can be compass, accel, etc
* @sec_slave_id: id of the secondary slave device
* @secondary_i2c_address: secondary device's i2c address
* @secondary_orientation: secondary device's orientation matrix
* @aux_slave_type: auxiliary slave. Another slave device type
* @aux_slave_id: auxiliary slave ID.
* @aux_i2c_addr: auxiliary device I2C address.
* @read_only_slave_type: read only slave type.
* @read_only_slave_id: read only slave device ID.
* @read_only_i2c_addr: read only slave device address.
*
* Contains platform specific information on how to configure the MPU3050 to
* work on this platform. The orientation matricies are 3x3 rotation matricies
* that are applied to the data to rotate from the mounting orientation to the
* platform orientation. The values must be one of 0, 1, or -1 and each row and
* column should have exactly 1 non-zero value.
*/
struct mpu_platform_data {
__u8 int_config;
__u8 level_shifter;
__s8 orientation[9];
enum secondary_slave_type sec_slave_type;
enum ext_slave_id sec_slave_id;
__u16 secondary_i2c_addr;
__s8 secondary_orientation[9];
enum secondary_slave_type aux_slave_type;
enum ext_slave_id aux_slave_id;
__u16 aux_i2c_addr;
enum secondary_slave_type read_only_slave_type;
enum ext_slave_id read_only_slave_id;
__u16 read_only_i2c_addr;
#ifdef CONFIG_OF
int (*power_on)(struct mpu_platform_data *);
int (*power_off)(struct mpu_platform_data *);
struct regulator *vdd_ana;
struct regulator *vdd_i2c;
#endif
};
#endif /* __MPU_H_ */