diff options
Diffstat (limited to 'drivers/misc/inv_mpu')
61 files changed, 25836 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig new file mode 100644 index 00000000000..4c231b576f6 --- /dev/null +++ b/drivers/misc/inv_mpu/Kconfig | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | config MPU_SENSORS_TIMERIRQ | ||
| 2 | tristate "MPU Timer IRQ" | ||
| 3 | help | ||
| 4 | If you say yes here you get access to the timerirq device handle which | ||
| 5 | can be used to select on. This can be used instead of IRQ's, sleeping, | ||
| 6 | or timer threads. Reading from this device returns the same type of | ||
| 7 | information as reading from the MPU and slave IRQ's. | ||
| 8 | |||
| 9 | menuconfig INV_SENSORS | ||
| 10 | tristate "Motion Processing Unit" | ||
| 11 | depends on I2C | ||
| 12 | default y | ||
| 13 | |||
| 14 | if INV_SENSORS | ||
| 15 | |||
| 16 | choice | ||
| 17 | tristate "MPU Master" | ||
| 18 | depends on I2C && INV_SENSORS | ||
| 19 | default MPU_SENSORS_MPU3050 | ||
| 20 | |||
| 21 | config MPU_SENSORS_MPU3050 | ||
| 22 | tristate "MPU3050" | ||
| 23 | depends on I2C | ||
| 24 | select MPU_SENSORS_MPU3050_GYRO | ||
| 25 | help | ||
| 26 | If you say yes here you get support for the MPU3050 Gyroscope driver | ||
| 27 | This driver can also be built as a module. If so, the module | ||
| 28 | will be called mpu3050. | ||
| 29 | |||
| 30 | config MPU_SENSORS_MPU6050A2 | ||
| 31 | tristate "MPU6050A2" | ||
| 32 | depends on I2C | ||
| 33 | select MPU_SENSORS_MPU6050_GYRO | ||
| 34 | help | ||
| 35 | If you say yes here you get support for the MPU6050A2 Gyroscope driver | ||
| 36 | This driver can also be built as a module. If so, the module | ||
| 37 | will be called mpu6050a2. | ||
| 38 | |||
| 39 | config MPU_SENSORS_MPU6050B1 | ||
| 40 | tristate "MPU6050B1" | ||
| 41 | depends on I2C | ||
| 42 | select MPU_SENSORS_MPU6050_GYRO | ||
| 43 | help | ||
| 44 | If you say yes here you get support for the MPU6050 Gyroscope driver | ||
| 45 | This driver can also be built as a module. If so, the module | ||
| 46 | will be called mpu6050b1. | ||
| 47 | |||
| 48 | endchoice | ||
| 49 | |||
| 50 | config MPU_SENSORS_MPU3050_GYRO | ||
| 51 | bool "MPU3050 built in gyroscope" | ||
| 52 | depends on MPU_SENSORS_MPU3050 | ||
| 53 | |||
| 54 | config MPU_SENSORS_MPU6050_GYRO | ||
| 55 | bool "MPU6050 built in gyroscope" | ||
| 56 | depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 57 | |||
| 58 | source "drivers/misc/inv_mpu/accel/Kconfig" | ||
| 59 | source "drivers/misc/inv_mpu/compass/Kconfig" | ||
| 60 | source "drivers/misc/inv_mpu/pressure/Kconfig" | ||
| 61 | |||
| 62 | config MPU_USERSPACE_DEBUG | ||
| 63 | bool "MPU Userspace debugging ioctls" | ||
| 64 | help | ||
| 65 | Allows the ioctls MPU_SET_MPU_PLATFORM_DATA and | ||
| 66 | MPU_SET_EXT_SLAVE_PLATFORM_DATA, which allows userspace applications | ||
| 67 | to override the slave address and orientation. This is dangerous | ||
| 68 | and could cause the devices not to work. | ||
| 69 | |||
| 70 | endif #INV_SENSORS | ||
diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile new file mode 100644 index 00000000000..f10c6844a08 --- /dev/null +++ b/drivers/misc/inv_mpu/Makefile | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | |||
| 2 | # Kernel makefile for motions sensors | ||
| 3 | # | ||
| 4 | # | ||
| 5 | |||
| 6 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 7 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
| 8 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
| 9 | |||
| 10 | obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o | ||
| 11 | obj-$(CONFIG_INV_SENSORS) += mpuirq.o slaveirq.o | ||
| 12 | |||
| 13 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050/ | ||
| 14 | obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050/ | ||
| 15 | |||
| 16 | obj-y += accel/ | ||
| 17 | obj-y += compass/ | ||
| 18 | obj-y += pressure/ | ||
diff --git a/drivers/misc/inv_mpu/accel/Kconfig b/drivers/misc/inv_mpu/accel/Kconfig new file mode 100644 index 00000000000..4e280bd876b --- /dev/null +++ b/drivers/misc/inv_mpu/accel/Kconfig | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | menuconfig INV_SENSORS_ACCELEROMETERS | ||
| 2 | bool "Accelerometer Slave Sensors" | ||
| 3 | default y | ||
| 4 | ---help--- | ||
| 5 | Say Y here to get to see options for device drivers for various | ||
| 6 | accelerometrs for integration with the MPU3050 or MPU6050 driver. | ||
| 7 | This option alone does not add any kernel code. | ||
| 8 | |||
| 9 | If you say N, all options in this submenu will be skipped and disabled. | ||
| 10 | |||
| 11 | if INV_SENSORS_ACCELEROMETERS | ||
| 12 | |||
| 13 | config MPU_SENSORS_ADXL34X | ||
| 14 | tristate "ADI adxl34x" | ||
| 15 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 16 | help | ||
| 17 | This enables support for the ADI adxl345 or adxl346 accelerometers. | ||
| 18 | This support is for integration with the MPU3050 gyroscope device | ||
| 19 | driver. Only one accelerometer can be registered at a time. | ||
| 20 | Specifying more that one accelerometer in the board file will result | ||
| 21 | in runtime errors. | ||
| 22 | |||
| 23 | config MPU_SENSORS_BMA222 | ||
| 24 | tristate "Bosch BMA222" | ||
| 25 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 26 | help | ||
| 27 | This enables support for the Bosch BMA222 accelerometer | ||
| 28 | This support is for integration with the MPU3050 gyroscope device | ||
| 29 | driver. Only one accelerometer can be registered at a time. | ||
| 30 | Specifying more that one accelerometer in the board file will result | ||
| 31 | in runtime errors. | ||
| 32 | |||
| 33 | config MPU_SENSORS_BMA150 | ||
| 34 | tristate "Bosch BMA150" | ||
| 35 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 36 | help | ||
| 37 | This enables support for the Bosch BMA150 accelerometer | ||
| 38 | This support is for integration with the MPU3050 gyroscope device | ||
| 39 | driver. Only one accelerometer can be registered at a time. | ||
| 40 | Specifying more that one accelerometer in the board file will result | ||
| 41 | in runtime errors. | ||
| 42 | |||
| 43 | config MPU_SENSORS_BMA250 | ||
| 44 | tristate "Bosch BMA250" | ||
| 45 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 46 | help | ||
| 47 | This enables support for the Bosch BMA250 accelerometer | ||
| 48 | This support is for integration with the MPU3050 gyroscope device | ||
| 49 | driver. Only one accelerometer can be registered at a time. | ||
| 50 | Specifying more that one accelerometer in the board file will result | ||
| 51 | in runtime errors. | ||
| 52 | |||
| 53 | config MPU_SENSORS_KXSD9 | ||
| 54 | tristate "Kionix KXSD9" | ||
| 55 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 56 | help | ||
| 57 | This enables support for the Kionix KXSD9 accelerometer | ||
| 58 | This support is for integration with the MPU3050 gyroscope device | ||
| 59 | driver. Only one accelerometer can be registered at a time. | ||
| 60 | Specifying more that one accelerometer in the board file will result | ||
| 61 | in runtime errors. | ||
| 62 | |||
| 63 | config MPU_SENSORS_KXTF9 | ||
| 64 | tristate "Kionix KXTF9" | ||
| 65 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 66 | help | ||
| 67 | This enables support for the Kionix KXFT9 accelerometer | ||
| 68 | This support is for integration with the MPU3050 gyroscope device | ||
| 69 | driver. Only one accelerometer can be registered at a time. | ||
| 70 | Specifying more that one accelerometer in the board file will result | ||
| 71 | in runtime errors. | ||
| 72 | |||
| 73 | config MPU_SENSORS_LIS331DLH | ||
| 74 | tristate "ST lis331dlh" | ||
| 75 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 76 | help | ||
| 77 | This enables support for the ST lis331dlh accelerometer | ||
| 78 | This support is for integration with the MPU3050 gyroscope device | ||
| 79 | driver. Only one accelerometer can be registered at a time. | ||
| 80 | Specifying more that one accelerometer in the board file will result | ||
| 81 | in runtime errors. | ||
| 82 | |||
| 83 | config MPU_SENSORS_LIS3DH | ||
| 84 | tristate "ST lis3dh" | ||
| 85 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 86 | help | ||
| 87 | This enables support for the ST lis3dh accelerometer | ||
| 88 | This support is for integration with the MPU3050 gyroscope device | ||
| 89 | driver. Only one accelerometer can be registered at a time. | ||
| 90 | Specifying more that one accelerometer in the board file will result | ||
| 91 | in runtime errors. | ||
| 92 | |||
| 93 | config MPU_SENSORS_LSM303DLX_A | ||
| 94 | tristate "ST lsm303dlx" | ||
| 95 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 96 | help | ||
| 97 | This enables support for the ST lsm303dlh and lsm303dlm accelerometers | ||
| 98 | This support is for integration with the MPU3050 gyroscope device | ||
| 99 | driver. Only one accelerometer can be registered at a time. | ||
| 100 | Specifying more that one accelerometer in the board file will result | ||
| 101 | in runtime errors. | ||
| 102 | |||
| 103 | config MPU_SENSORS_MMA8450 | ||
| 104 | tristate "Freescale mma8450" | ||
| 105 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 106 | help | ||
| 107 | This enables support for the Freescale mma8450 accelerometer | ||
| 108 | This support is for integration with the MPU3050 gyroscope device | ||
| 109 | driver. Only one accelerometer can be registered at a time. | ||
| 110 | Specifying more that one accelerometer in the board file will result | ||
| 111 | in runtime errors. | ||
| 112 | |||
| 113 | config MPU_SENSORS_MMA845X | ||
| 114 | tristate "Freescale mma8451/8452/8453" | ||
| 115 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 116 | help | ||
| 117 | This enables support for the Freescale mma8451/8452/8453 accelerometer | ||
| 118 | This support is for integration with the MPU3050 gyroscope device | ||
| 119 | driver. Only one accelerometer can be registered at a time. | ||
| 120 | Specifying more that one accelerometer in the board file will result | ||
| 121 | in runtime errors. | ||
| 122 | |||
| 123 | config MPU_SENSORS_MPU6050_ACCEL | ||
| 124 | tristate "MPU6050 built in accelerometer" | ||
| 125 | depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
| 126 | help | ||
| 127 | This enables support for the MPU6050 built in accelerometer. | ||
| 128 | This the built in support for integration with the MPU6050 gyroscope | ||
| 129 | device driver. This is the only accelerometer supported with the | ||
| 130 | MPU6050. Specifying another accelerometer in the board file will | ||
| 131 | result in runtime errors. | ||
| 132 | |||
| 133 | endif | ||
diff --git a/drivers/misc/inv_mpu/accel/Makefile b/drivers/misc/inv_mpu/accel/Makefile new file mode 100644 index 00000000000..1f0f5bec677 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/Makefile | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | # | ||
| 2 | # Accel Slaves to MPUxxxx | ||
| 3 | # | ||
| 4 | obj-$(CONFIG_MPU_SENSORS_ADXL34X) += inv_mpu_adxl34x.o | ||
| 5 | inv_mpu_adxl34x-objs += adxl34x.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_MPU_SENSORS_BMA150) += inv_mpu_bma150.o | ||
| 8 | inv_mpu_bma150-objs += bma150.o | ||
| 9 | |||
| 10 | obj-$(CONFIG_MPU_SENSORS_KXTF9) += inv_mpu_kxtf9.o | ||
| 11 | inv_mpu_kxtf9-objs += kxtf9.o | ||
| 12 | |||
| 13 | obj-$(CONFIG_MPU_SENSORS_BMA222) += inv_mpu_bma222.o | ||
| 14 | inv_mpu_bma222-objs += bma222.o | ||
| 15 | |||
| 16 | obj-$(CONFIG_MPU_SENSORS_BMA250) += inv_mpu_bma250.o | ||
| 17 | inv_mpu_bma250-objs += bma250.o | ||
| 18 | |||
| 19 | obj-$(CONFIG_MPU_SENSORS_KXSD9) += inv_mpu_kxsd9.o | ||
| 20 | inv_mpu_kxsd9-objs += kxsd9.o | ||
| 21 | |||
| 22 | obj-$(CONFIG_MPU_SENSORS_LIS331DLH) += inv_mpu_lis331.o | ||
| 23 | inv_mpu_lis331-objs += lis331.o | ||
| 24 | |||
| 25 | obj-$(CONFIG_MPU_SENSORS_LIS3DH) += inv_mpu_lis3dh.o | ||
| 26 | inv_mpu_lis3dh-objs += lis3dh.o | ||
| 27 | |||
| 28 | obj-$(CONFIG_MPU_SENSORS_LSM303DLX_A) += inv_mpu_lsm303dlx_a.o | ||
| 29 | inv_mpu_lsm303dlx_a-objs += lsm303dlx_a.o | ||
| 30 | |||
| 31 | obj-$(CONFIG_MPU_SENSORS_MMA8450) += inv_mpu_mma8450.o | ||
| 32 | inv_mpu_mma8450-objs += mma8450.o | ||
| 33 | |||
| 34 | obj-$(CONFIG_MPU_SENSORS_MMA845X) += inv_mpu_mma845x.o | ||
| 35 | inv_mpu_mma845x-objs += mma845x.o | ||
| 36 | |||
| 37 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 38 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/accel/adxl34x.c b/drivers/misc/inv_mpu/accel/adxl34x.c new file mode 100644 index 00000000000..f2bff8a7592 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/adxl34x.c | |||
| @@ -0,0 +1,728 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file adxl34x.c | ||
| 26 | * @brief Accelerometer setup and handling methods for AD adxl345 and | ||
| 27 | * adxl346. | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* -------------------------------------------------------------------------- */ | ||
| 31 | |||
| 32 | #include <linux/i2c.h> | ||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/moduleparam.h> | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/errno.h> | ||
| 37 | #include <linux/slab.h> | ||
| 38 | #include <linux/delay.h> | ||
| 39 | #include "mpu-dev.h" | ||
| 40 | |||
| 41 | #include <log.h> | ||
| 42 | |||
| 43 | #include <linux/mpu.h> | ||
| 44 | #include "mlsl.h" | ||
| 45 | #include "mldl_cfg.h" | ||
| 46 | |||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-acc" | ||
| 49 | |||
| 50 | /* -------------------------------------------------------------------------- */ | ||
| 51 | |||
| 52 | /* registers */ | ||
| 53 | #define ADXL34X_ODR_REG (0x2C) | ||
| 54 | #define ADXL34X_PWR_REG (0x2D) | ||
| 55 | #define ADXL34X_DATAFORMAT_REG (0x31) | ||
| 56 | |||
| 57 | /* masks */ | ||
| 58 | #define ADXL34X_ODR_MASK (0x0F) | ||
| 59 | #define ADXL34X_PWR_SLEEP_MASK (0x04) | ||
| 60 | #define ADXL34X_PWR_MEAS_MASK (0x08) | ||
| 61 | #define ADXL34X_DATAFORMAT_JUSTIFY_MASK (0x04) | ||
| 62 | #define ADXL34X_DATAFORMAT_FSR_MASK (0x03) | ||
| 63 | |||
| 64 | /* -------------------------------------------------------------------------- */ | ||
| 65 | |||
| 66 | struct adxl34x_config { | ||
| 67 | unsigned int odr; /** < output data rate in mHz */ | ||
| 68 | unsigned int fsr; /** < full scale range mg */ | ||
| 69 | unsigned int fsr_reg_mask; /** < register setting for fsr */ | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct adxl34x_private_data { | ||
| 73 | struct adxl34x_config suspend; /** < suspend configuration */ | ||
| 74 | struct adxl34x_config resume; /** < resume configuration */ | ||
| 75 | }; | ||
| 76 | |||
| 77 | /** | ||
| 78 | * @brief Set the output data rate for the particular configuration. | ||
| 79 | * | ||
| 80 | * @param mlsl_handle | ||
| 81 | * the handle to the serial channel the device is connected to. | ||
| 82 | * @param pdata | ||
| 83 | * a pointer to the slave platform data. | ||
| 84 | * @param config | ||
| 85 | * Config to modify with new ODR. | ||
| 86 | * @param apply | ||
| 87 | * whether to apply immediately or save the settings to be applied | ||
| 88 | * at the next resume. | ||
| 89 | * @param odr | ||
| 90 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 91 | * | ||
| 92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 93 | */ | ||
| 94 | static int adxl34x_set_odr(void *mlsl_handle, | ||
| 95 | struct ext_slave_platform_data *pdata, | ||
| 96 | struct adxl34x_config *config, | ||
| 97 | int apply, | ||
| 98 | long odr) | ||
| 99 | { | ||
| 100 | int result = INV_SUCCESS; | ||
| 101 | unsigned char new_odr_mask; | ||
| 102 | |||
| 103 | /* ADXL346 (Rev. A) pages 13, 24 */ | ||
| 104 | if (odr >= 3200000) { | ||
| 105 | new_odr_mask = 0x0F; | ||
| 106 | config->odr = 3200000; | ||
| 107 | } else if (odr >= 1600000) { | ||
| 108 | new_odr_mask = 0x0E; | ||
| 109 | config->odr = 1600000; | ||
| 110 | } else if (odr >= 800000) { | ||
| 111 | new_odr_mask = 0x0D; | ||
| 112 | config->odr = 800000; | ||
| 113 | } else if (odr >= 400000) { | ||
| 114 | new_odr_mask = 0x0C; | ||
| 115 | config->odr = 400000; | ||
| 116 | } else if (odr >= 200000) { | ||
| 117 | new_odr_mask = 0x0B; | ||
| 118 | config->odr = 200000; | ||
| 119 | } else if (odr >= 100000) { | ||
| 120 | new_odr_mask = 0x0A; | ||
| 121 | config->odr = 100000; | ||
| 122 | } else if (odr >= 50000) { | ||
| 123 | new_odr_mask = 0x09; | ||
| 124 | config->odr = 50000; | ||
| 125 | } else if (odr >= 25000) { | ||
| 126 | new_odr_mask = 0x08; | ||
| 127 | config->odr = 25000; | ||
| 128 | } else if (odr >= 12500) { | ||
| 129 | new_odr_mask = 0x07; | ||
| 130 | config->odr = 12500; | ||
| 131 | } else if (odr >= 6250) { | ||
| 132 | new_odr_mask = 0x06; | ||
| 133 | config->odr = 6250; | ||
| 134 | } else if (odr >= 3130) { | ||
| 135 | new_odr_mask = 0x05; | ||
| 136 | config->odr = 3130; | ||
| 137 | } else if (odr >= 1560) { | ||
| 138 | new_odr_mask = 0x04; | ||
| 139 | config->odr = 1560; | ||
| 140 | } else if (odr >= 780) { | ||
| 141 | new_odr_mask = 0x03; | ||
| 142 | config->odr = 780; | ||
| 143 | } else if (odr >= 390) { | ||
| 144 | new_odr_mask = 0x02; | ||
| 145 | config->odr = 390; | ||
| 146 | } else if (odr >= 200) { | ||
| 147 | new_odr_mask = 0x01; | ||
| 148 | config->odr = 200; | ||
| 149 | } else { | ||
| 150 | new_odr_mask = 0x00; | ||
| 151 | config->odr = 100; | ||
| 152 | } | ||
| 153 | |||
| 154 | if (apply) { | ||
| 155 | unsigned char reg_odr; | ||
| 156 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 157 | ADXL34X_ODR_REG, 1, ®_odr); | ||
| 158 | if (result) { | ||
| 159 | LOG_RESULT_LOCATION(result); | ||
| 160 | return result; | ||
| 161 | } | ||
| 162 | reg_odr &= ~ADXL34X_ODR_MASK; | ||
| 163 | reg_odr |= new_odr_mask; | ||
| 164 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 165 | ADXL34X_ODR_REG, reg_odr); | ||
| 166 | if (result) { | ||
| 167 | LOG_RESULT_LOCATION(result); | ||
| 168 | return result; | ||
| 169 | } | ||
| 170 | MPL_LOGV("ODR: %d mHz\n", config->odr); | ||
| 171 | } | ||
| 172 | return result; | ||
| 173 | } | ||
| 174 | |||
| 175 | /** | ||
| 176 | * @brief Set the full scale range of the accels | ||
| 177 | * | ||
| 178 | * @param mlsl_handle | ||
| 179 | * the handle to the serial channel the device is connected to. | ||
| 180 | * @param pdata | ||
| 181 | * a pointer to the slave platform data. | ||
| 182 | * @param config | ||
| 183 | * pointer to configuration. | ||
| 184 | * @param apply | ||
| 185 | * whether to apply immediately or save the settings to be applied | ||
| 186 | * at the next resume. | ||
| 187 | * @param fsr | ||
| 188 | * requested full scale range in milli gees (mg). | ||
| 189 | * | ||
| 190 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 191 | */ | ||
| 192 | static int adxl34x_set_fsr(void *mlsl_handle, | ||
| 193 | struct ext_slave_platform_data *pdata, | ||
| 194 | struct adxl34x_config *config, | ||
| 195 | int apply, | ||
| 196 | long fsr) | ||
| 197 | { | ||
| 198 | int result = INV_SUCCESS; | ||
| 199 | |||
| 200 | if (fsr <= 2000) { | ||
| 201 | config->fsr_reg_mask = 0x00; | ||
| 202 | config->fsr = 2000; | ||
| 203 | } else if (fsr <= 4000) { | ||
| 204 | config->fsr_reg_mask = 0x01; | ||
| 205 | config->fsr = 4000; | ||
| 206 | } else if (fsr <= 8000) { | ||
| 207 | config->fsr_reg_mask = 0x02; | ||
| 208 | config->fsr = 8000; | ||
| 209 | } else { /* 8001 -> oo */ | ||
| 210 | config->fsr_reg_mask = 0x03; | ||
| 211 | config->fsr = 16000; | ||
| 212 | } | ||
| 213 | |||
| 214 | if (apply) { | ||
| 215 | unsigned char reg_df; | ||
| 216 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 217 | ADXL34X_DATAFORMAT_REG, 1, ®_df); | ||
| 218 | reg_df &= ~ADXL34X_DATAFORMAT_FSR_MASK; | ||
| 219 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 220 | ADXL34X_DATAFORMAT_REG, | ||
| 221 | reg_df | config->fsr_reg_mask); | ||
| 222 | if (result) { | ||
| 223 | LOG_RESULT_LOCATION(result); | ||
| 224 | return result; | ||
| 225 | } | ||
| 226 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
| 227 | } | ||
| 228 | return result; | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * @brief facility to retrieve the device configuration. | ||
| 233 | * | ||
| 234 | * @param mlsl_handle | ||
| 235 | * the handle to the serial channel the device is connected to. | ||
| 236 | * @param slave | ||
| 237 | * a pointer to the slave descriptor data structure. | ||
| 238 | * @param pdata | ||
| 239 | * a pointer to the slave platform data. | ||
| 240 | * @param data | ||
| 241 | * a pointer to store the returned configuration data structure. | ||
| 242 | * | ||
| 243 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 244 | */ | ||
| 245 | static int adxl34x_get_config(void *mlsl_handle, | ||
| 246 | struct ext_slave_descr *slave, | ||
| 247 | struct ext_slave_platform_data *pdata, | ||
| 248 | struct ext_slave_config *data) | ||
| 249 | { | ||
| 250 | struct adxl34x_private_data *private_data = | ||
| 251 | (struct adxl34x_private_data *)(pdata->private_data); | ||
| 252 | |||
| 253 | if (!data->data) | ||
| 254 | return INV_ERROR_INVALID_PARAMETER; | ||
| 255 | |||
| 256 | switch (data->key) { | ||
| 257 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 258 | (*(unsigned long *)data->data) = | ||
| 259 | (unsigned long) private_data->suspend.odr; | ||
| 260 | break; | ||
| 261 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 262 | (*(unsigned long *)data->data) = | ||
| 263 | (unsigned long) private_data->resume.odr; | ||
| 264 | break; | ||
| 265 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 266 | (*(unsigned long *)data->data) = | ||
| 267 | (unsigned long) private_data->suspend.fsr; | ||
| 268 | break; | ||
| 269 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 270 | (*(unsigned long *)data->data) = | ||
| 271 | (unsigned long) private_data->resume.fsr; | ||
| 272 | break; | ||
| 273 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 274 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 275 | default: | ||
| 276 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 277 | }; | ||
| 278 | |||
| 279 | return INV_SUCCESS; | ||
| 280 | } | ||
| 281 | |||
| 282 | /** | ||
| 283 | * @brief device configuration facility. | ||
| 284 | * | ||
| 285 | * @param mlsl_handle | ||
| 286 | * the handle to the serial channel the device is connected to. | ||
| 287 | * @param slave | ||
| 288 | * a pointer to the slave descriptor data structure. | ||
| 289 | * @param pdata | ||
| 290 | * a pointer to the slave platform data. | ||
| 291 | * @param data | ||
| 292 | * a pointer to the configuration data structure. | ||
| 293 | * | ||
| 294 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 295 | */ | ||
| 296 | static int adxl34x_config(void *mlsl_handle, | ||
| 297 | struct ext_slave_descr *slave, | ||
| 298 | struct ext_slave_platform_data *pdata, | ||
| 299 | struct ext_slave_config *data) | ||
| 300 | { | ||
| 301 | struct adxl34x_private_data *private_data = | ||
| 302 | (struct adxl34x_private_data *)(pdata->private_data); | ||
| 303 | |||
| 304 | if (!data->data) | ||
| 305 | return INV_ERROR_INVALID_PARAMETER; | ||
| 306 | |||
| 307 | switch (data->key) { | ||
| 308 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 309 | return adxl34x_set_odr(mlsl_handle, pdata, | ||
| 310 | &private_data->suspend, | ||
| 311 | data->apply, | ||
| 312 | *((long *)data->data)); | ||
| 313 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 314 | return adxl34x_set_odr(mlsl_handle, pdata, | ||
| 315 | &private_data->resume, | ||
| 316 | data->apply, | ||
| 317 | *((long *)data->data)); | ||
| 318 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 319 | return adxl34x_set_fsr(mlsl_handle, pdata, | ||
| 320 | &private_data->suspend, | ||
| 321 | data->apply, | ||
| 322 | *((long *)data->data)); | ||
| 323 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 324 | return adxl34x_set_fsr(mlsl_handle, pdata, | ||
| 325 | &private_data->resume, | ||
| 326 | data->apply, | ||
| 327 | *((long *)data->data)); | ||
| 328 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 329 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 330 | default: | ||
| 331 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 332 | }; | ||
| 333 | return INV_SUCCESS; | ||
| 334 | } | ||
| 335 | |||
| 336 | /** | ||
| 337 | * @brief suspends the device to put it in its lowest power mode. | ||
| 338 | * | ||
| 339 | * @param mlsl_handle | ||
| 340 | * the handle to the serial channel the device is connected to. | ||
| 341 | * @param slave | ||
| 342 | * a pointer to the slave descriptor data structure. | ||
| 343 | * @param pdata | ||
| 344 | * a pointer to the slave platform data. | ||
| 345 | * | ||
| 346 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 347 | */ | ||
| 348 | static int adxl34x_suspend(void *mlsl_handle, | ||
| 349 | struct ext_slave_descr *slave, | ||
| 350 | struct ext_slave_platform_data *pdata) | ||
| 351 | { | ||
| 352 | int result; | ||
| 353 | |||
| 354 | /* | ||
| 355 | struct adxl34x_config *suspend_config = | ||
| 356 | &((struct adxl34x_private_data *)pdata->private_data)->suspend; | ||
| 357 | |||
| 358 | result = adxl34x_set_odr(mlsl_handle, pdata, suspend_config, | ||
| 359 | true, suspend_config->odr); | ||
| 360 | if (result) { | ||
| 361 | LOG_RESULT_LOCATION(result); | ||
| 362 | return result; | ||
| 363 | } | ||
| 364 | result = adxl34x_set_fsr(mlsl_handle, pdata, suspend_config, | ||
| 365 | true, suspend_config->fsr); | ||
| 366 | if (result) { | ||
| 367 | LOG_RESULT_LOCATION(result); | ||
| 368 | return result; | ||
| 369 | } | ||
| 370 | */ | ||
| 371 | |||
| 372 | /* | ||
| 373 | Page 25 | ||
| 374 | When clearing the sleep bit, it is recommended that the part | ||
| 375 | be placed into standby mode and then set back to measurement mode | ||
| 376 | with a subsequent write. | ||
| 377 | This is done to ensure that the device is properly biased if sleep | ||
| 378 | mode is manually disabled; otherwise, the first few samples of data | ||
| 379 | after the sleep bit is cleared may have additional noise, | ||
| 380 | especially if the device was asleep when the bit was cleared. */ | ||
| 381 | |||
| 382 | /* go in standy-by mode (suspends measurements) */ | ||
| 383 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 384 | ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK); | ||
| 385 | if (result) { | ||
| 386 | LOG_RESULT_LOCATION(result); | ||
| 387 | return result; | ||
| 388 | } | ||
| 389 | /* and then in sleep */ | ||
| 390 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 391 | ADXL34X_PWR_REG, | ||
| 392 | ADXL34X_PWR_MEAS_MASK | ADXL34X_PWR_SLEEP_MASK); | ||
| 393 | if (result) { | ||
| 394 | LOG_RESULT_LOCATION(result); | ||
| 395 | return result; | ||
| 396 | } | ||
| 397 | return result; | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * @brief resume the device in the proper power state given the configuration | ||
| 402 | * chosen. | ||
| 403 | * | ||
| 404 | * @param mlsl_handle | ||
| 405 | * the handle to the serial channel the device is connected to. | ||
| 406 | * @param slave | ||
| 407 | * a pointer to the slave descriptor data structure. | ||
| 408 | * @param pdata | ||
| 409 | * a pointer to the slave platform data. | ||
| 410 | * | ||
| 411 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 412 | */ | ||
| 413 | static int adxl34x_resume(void *mlsl_handle, | ||
| 414 | struct ext_slave_descr *slave, | ||
| 415 | struct ext_slave_platform_data *pdata) | ||
| 416 | { | ||
| 417 | int result = INV_SUCCESS; | ||
| 418 | struct adxl34x_config *resume_config = | ||
| 419 | &((struct adxl34x_private_data *)pdata->private_data)->resume; | ||
| 420 | unsigned char reg; | ||
| 421 | |||
| 422 | /* | ||
| 423 | Page 25 | ||
| 424 | When clearing the sleep bit, it is recommended that the part | ||
| 425 | be placed into standby mode and then set back to measurement mode | ||
| 426 | with a subsequent write. | ||
| 427 | This is done to ensure that the device is properly biased if sleep | ||
| 428 | mode is manually disabled; otherwise, the first few samples of data | ||
| 429 | after the sleep bit is cleared may have additional noise, | ||
| 430 | especially if the device was asleep when the bit was cleared. */ | ||
| 431 | |||
| 432 | /* remove sleep, but leave in stand-by */ | ||
| 433 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 434 | ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK); | ||
| 435 | if (result) { | ||
| 436 | LOG_RESULT_LOCATION(result); | ||
| 437 | return result; | ||
| 438 | } | ||
| 439 | |||
| 440 | result = adxl34x_set_odr(mlsl_handle, pdata, resume_config, | ||
| 441 | true, resume_config->odr); | ||
| 442 | if (result) { | ||
| 443 | LOG_RESULT_LOCATION(result); | ||
| 444 | return result; | ||
| 445 | } | ||
| 446 | |||
| 447 | /* | ||
| 448 | -> FSR | ||
| 449 | -> Justiy bit for Big endianess | ||
| 450 | -> resulution to 10 bits | ||
| 451 | */ | ||
| 452 | reg = ADXL34X_DATAFORMAT_JUSTIFY_MASK; | ||
| 453 | reg |= resume_config->fsr_reg_mask; | ||
| 454 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 455 | ADXL34X_DATAFORMAT_REG, reg); | ||
| 456 | if (result) { | ||
| 457 | LOG_RESULT_LOCATION(result); | ||
| 458 | return result; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* go in measurement mode */ | ||
| 462 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 463 | ADXL34X_PWR_REG, 0x00); | ||
| 464 | if (result) { | ||
| 465 | LOG_RESULT_LOCATION(result); | ||
| 466 | return result; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* DATA_FORMAT: full resolution of +/-2g; data is left justified */ | ||
| 470 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 471 | 0x31, reg); | ||
| 472 | |||
| 473 | return result; | ||
| 474 | } | ||
| 475 | |||
| 476 | /** | ||
| 477 | * @brief one-time device driver initialization function. | ||
| 478 | * If the driver is built as a kernel module, this function will be | ||
| 479 | * called when the module is loaded in the kernel. | ||
| 480 | * If the driver is built-in in the kernel, this function will be | ||
| 481 | * called at boot time. | ||
| 482 | * | ||
| 483 | * @param mlsl_handle | ||
| 484 | * the handle to the serial channel the device is connected to. | ||
| 485 | * @param slave | ||
| 486 | * a pointer to the slave descriptor data structure. | ||
| 487 | * @param pdata | ||
| 488 | * a pointer to the slave platform data. | ||
| 489 | * | ||
| 490 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 491 | */ | ||
| 492 | static int adxl34x_init(void *mlsl_handle, | ||
| 493 | struct ext_slave_descr *slave, | ||
| 494 | struct ext_slave_platform_data *pdata) | ||
| 495 | { | ||
| 496 | int result; | ||
| 497 | long range; | ||
| 498 | |||
| 499 | struct adxl34x_private_data *private_data; | ||
| 500 | private_data = (struct adxl34x_private_data *) | ||
| 501 | kzalloc(sizeof(struct adxl34x_private_data), GFP_KERNEL); | ||
| 502 | |||
| 503 | if (!private_data) | ||
| 504 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 505 | |||
| 506 | pdata->private_data = private_data; | ||
| 507 | |||
| 508 | result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 509 | false, 0); | ||
| 510 | if (result) { | ||
| 511 | LOG_RESULT_LOCATION(result); | ||
| 512 | return result; | ||
| 513 | } | ||
| 514 | result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 515 | false, 200000); | ||
| 516 | if (result) { | ||
| 517 | LOG_RESULT_LOCATION(result); | ||
| 518 | return result; | ||
| 519 | } | ||
| 520 | |||
| 521 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 522 | result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 523 | false, range); | ||
| 524 | result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 525 | false, range); | ||
| 526 | if (result) { | ||
| 527 | LOG_RESULT_LOCATION(result); | ||
| 528 | return result; | ||
| 529 | } | ||
| 530 | |||
| 531 | result = adxl34x_suspend(mlsl_handle, slave, pdata); | ||
| 532 | if (result) { | ||
| 533 | LOG_RESULT_LOCATION(result); | ||
| 534 | return result; | ||
| 535 | } | ||
| 536 | |||
| 537 | return result; | ||
| 538 | } | ||
| 539 | |||
| 540 | /** | ||
| 541 | * @brief one-time device driver exit function. | ||
| 542 | * If the driver is built as a kernel module, this function will be | ||
| 543 | * called when the module is removed from the kernel. | ||
| 544 | * | ||
| 545 | * @param mlsl_handle | ||
| 546 | * the handle to the serial channel the device is connected to. | ||
| 547 | * @param slave | ||
| 548 | * a pointer to the slave descriptor data structure. | ||
| 549 | * @param pdata | ||
| 550 | * a pointer to the slave platform data. | ||
| 551 | * | ||
| 552 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 553 | */ | ||
| 554 | static int adxl34x_exit(void *mlsl_handle, | ||
| 555 | struct ext_slave_descr *slave, | ||
| 556 | struct ext_slave_platform_data *pdata) | ||
| 557 | { | ||
| 558 | kfree(pdata->private_data); | ||
| 559 | return INV_SUCCESS; | ||
| 560 | } | ||
| 561 | |||
| 562 | /** | ||
| 563 | * @brief read the sensor data from the device. | ||
| 564 | * | ||
| 565 | * @param mlsl_handle | ||
| 566 | * the handle to the serial channel the device is connected to. | ||
| 567 | * @param slave | ||
| 568 | * a pointer to the slave descriptor data structure. | ||
| 569 | * @param pdata | ||
| 570 | * a pointer to the slave platform data. | ||
| 571 | * @param data | ||
| 572 | * a buffer to store the data read. | ||
| 573 | * | ||
| 574 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 575 | */ | ||
| 576 | static int adxl34x_read(void *mlsl_handle, | ||
| 577 | struct ext_slave_descr *slave, | ||
| 578 | struct ext_slave_platform_data *pdata, | ||
| 579 | unsigned char *data) | ||
| 580 | { | ||
| 581 | int result; | ||
| 582 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 583 | slave->read_reg, slave->read_len, data); | ||
| 584 | return result; | ||
| 585 | } | ||
| 586 | |||
| 587 | static struct ext_slave_descr adxl34x_descr = { | ||
| 588 | .init = adxl34x_init, | ||
| 589 | .exit = adxl34x_exit, | ||
| 590 | .suspend = adxl34x_suspend, | ||
| 591 | .resume = adxl34x_resume, | ||
| 592 | .read = adxl34x_read, | ||
| 593 | .config = adxl34x_config, | ||
| 594 | .get_config = adxl34x_get_config, | ||
| 595 | .name = "adxl34x", /* 5 or 6 */ | ||
| 596 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 597 | .id = ACCEL_ID_ADXL34X, | ||
| 598 | .read_reg = 0x32, | ||
| 599 | .read_len = 6, | ||
| 600 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 601 | .range = {2, 0}, | ||
| 602 | .trigger = NULL, | ||
| 603 | }; | ||
| 604 | |||
| 605 | static | ||
| 606 | struct ext_slave_descr *adxl34x_get_slave_descr(void) | ||
| 607 | { | ||
| 608 | return &adxl34x_descr; | ||
| 609 | } | ||
| 610 | |||
| 611 | /* -------------------------------------------------------------------------- */ | ||
| 612 | struct adxl34x_mod_private_data { | ||
| 613 | struct i2c_client *client; | ||
| 614 | struct ext_slave_platform_data *pdata; | ||
| 615 | }; | ||
| 616 | |||
| 617 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 618 | |||
| 619 | static int adxl34x_mod_probe(struct i2c_client *client, | ||
| 620 | const struct i2c_device_id *devid) | ||
| 621 | { | ||
| 622 | struct ext_slave_platform_data *pdata; | ||
| 623 | struct adxl34x_mod_private_data *private_data; | ||
| 624 | int result = 0; | ||
| 625 | |||
| 626 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 627 | |||
| 628 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 629 | result = -ENODEV; | ||
| 630 | goto out_no_free; | ||
| 631 | } | ||
| 632 | |||
| 633 | pdata = client->dev.platform_data; | ||
| 634 | if (!pdata) { | ||
| 635 | dev_err(&client->adapter->dev, | ||
| 636 | "Missing platform data for slave %s\n", devid->name); | ||
| 637 | result = -EFAULT; | ||
| 638 | goto out_no_free; | ||
| 639 | } | ||
| 640 | |||
| 641 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 642 | if (!private_data) { | ||
| 643 | result = -ENOMEM; | ||
| 644 | goto out_no_free; | ||
| 645 | } | ||
| 646 | |||
| 647 | i2c_set_clientdata(client, private_data); | ||
| 648 | private_data->client = client; | ||
| 649 | private_data->pdata = pdata; | ||
| 650 | |||
| 651 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 652 | adxl34x_get_slave_descr); | ||
| 653 | if (result) { | ||
| 654 | dev_err(&client->adapter->dev, | ||
| 655 | "Slave registration failed: %s, %d\n", | ||
| 656 | devid->name, result); | ||
| 657 | goto out_free_memory; | ||
| 658 | } | ||
| 659 | |||
| 660 | return result; | ||
| 661 | |||
| 662 | out_free_memory: | ||
| 663 | kfree(private_data); | ||
| 664 | out_no_free: | ||
| 665 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 666 | return result; | ||
| 667 | |||
| 668 | } | ||
| 669 | |||
| 670 | static int adxl34x_mod_remove(struct i2c_client *client) | ||
| 671 | { | ||
| 672 | struct adxl34x_mod_private_data *private_data = | ||
| 673 | i2c_get_clientdata(client); | ||
| 674 | |||
| 675 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 676 | |||
| 677 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 678 | adxl34x_get_slave_descr); | ||
| 679 | |||
| 680 | kfree(private_data); | ||
| 681 | return 0; | ||
| 682 | } | ||
| 683 | |||
| 684 | static const struct i2c_device_id adxl34x_mod_id[] = { | ||
| 685 | { "adxl34x", ACCEL_ID_ADXL34X }, | ||
| 686 | {} | ||
| 687 | }; | ||
| 688 | |||
| 689 | MODULE_DEVICE_TABLE(i2c, adxl34x_mod_id); | ||
| 690 | |||
| 691 | static struct i2c_driver adxl34x_mod_driver = { | ||
| 692 | .class = I2C_CLASS_HWMON, | ||
| 693 | .probe = adxl34x_mod_probe, | ||
| 694 | .remove = adxl34x_mod_remove, | ||
| 695 | .id_table = adxl34x_mod_id, | ||
| 696 | .driver = { | ||
| 697 | .owner = THIS_MODULE, | ||
| 698 | .name = "adxl34x_mod", | ||
| 699 | }, | ||
| 700 | .address_list = normal_i2c, | ||
| 701 | }; | ||
| 702 | |||
| 703 | static int __init adxl34x_mod_init(void) | ||
| 704 | { | ||
| 705 | int res = i2c_add_driver(&adxl34x_mod_driver); | ||
| 706 | pr_info("%s: Probe name %s\n", __func__, "adxl34x_mod"); | ||
| 707 | if (res) | ||
| 708 | pr_err("%s failed\n", __func__); | ||
| 709 | return res; | ||
| 710 | } | ||
| 711 | |||
| 712 | static void __exit adxl34x_mod_exit(void) | ||
| 713 | { | ||
| 714 | pr_info("%s\n", __func__); | ||
| 715 | i2c_del_driver(&adxl34x_mod_driver); | ||
| 716 | } | ||
| 717 | |||
| 718 | module_init(adxl34x_mod_init); | ||
| 719 | module_exit(adxl34x_mod_exit); | ||
| 720 | |||
| 721 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 722 | MODULE_DESCRIPTION("Driver to integrate ADXL34X sensor with the MPU"); | ||
| 723 | MODULE_LICENSE("GPL"); | ||
| 724 | MODULE_ALIAS("adxl34x_mod"); | ||
| 725 | |||
| 726 | /** | ||
| 727 | * @} | ||
| 728 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/bma150.c b/drivers/misc/inv_mpu/accel/bma150.c new file mode 100644 index 00000000000..745d90a6744 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma150.c | |||
| @@ -0,0 +1,777 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file bma150.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Bosch BMA150. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | #include <linux/i2c.h> | ||
| 31 | #include <linux/module.h> | ||
| 32 | #include <linux/moduleparam.h> | ||
| 33 | #include <linux/kernel.h> | ||
| 34 | #include <linux/errno.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/delay.h> | ||
| 37 | #include "mpu-dev.h" | ||
| 38 | |||
| 39 | #include <linux/mpu.h> | ||
| 40 | #include "mlsl.h" | ||
| 41 | #include "mldl_cfg.h" | ||
| 42 | |||
| 43 | /* -------------------------------------------------------------------------- */ | ||
| 44 | /* registers */ | ||
| 45 | #define BMA150_CTRL_REG (0x14) | ||
| 46 | #define BMA150_INT_REG (0x15) | ||
| 47 | #define BMA150_PWR_REG (0x0A) | ||
| 48 | |||
| 49 | /* masks */ | ||
| 50 | #define BMA150_CTRL_MASK (0x18) | ||
| 51 | #define BMA150_CTRL_MASK_ODR (0xF8) | ||
| 52 | #define BMA150_CTRL_MASK_FSR (0xE7) | ||
| 53 | #define BMA150_INT_MASK_WUP (0xF8) | ||
| 54 | #define BMA150_INT_MASK_IRQ (0xDF) | ||
| 55 | #define BMA150_PWR_MASK_SLEEP (0x01) | ||
| 56 | #define BMA150_PWR_MASK_SOFT_RESET (0x02) | ||
| 57 | |||
| 58 | /* -------------------------------------------------------------------------- */ | ||
| 59 | struct bma150_config { | ||
| 60 | unsigned int odr; /** < output data rate mHz */ | ||
| 61 | unsigned int fsr; /** < full scale range mgees */ | ||
| 62 | unsigned int irq_type; /** < type of IRQ, see bma150_set_irq */ | ||
| 63 | unsigned char ctrl_reg; /** < control register value */ | ||
| 64 | unsigned char int_reg; /** < interrupt control register value */ | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct bma150_private_data { | ||
| 68 | struct bma150_config suspend; /** < suspend configuration */ | ||
| 69 | struct bma150_config resume; /** < resume configuration */ | ||
| 70 | }; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * @brief Simply disables the IRQ since it is not usable on BMA150 devices. | ||
| 74 | * | ||
| 75 | * @param mlsl_handle | ||
| 76 | * the handle to the serial channel the device is connected to. | ||
| 77 | * @param pdata | ||
| 78 | * a pointer to the slave platform data. | ||
| 79 | * @param config | ||
| 80 | * configuration to apply to, suspend or resume | ||
| 81 | * @param apply | ||
| 82 | * whether to apply immediately or save the settings to be applied | ||
| 83 | * at the next resume. | ||
| 84 | * @param irq_type | ||
| 85 | * the type of IRQ. Valid values are | ||
| 86 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 87 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 88 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 89 | * The only supported IRQ type is MPU_SLAVE_IRQ_TYPE_NONE which | ||
| 90 | * corresponds to disabling the IRQ completely. | ||
| 91 | * | ||
| 92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 93 | */ | ||
| 94 | static int bma150_set_irq(void *mlsl_handle, | ||
| 95 | struct ext_slave_platform_data *pdata, | ||
| 96 | struct bma150_config *config, | ||
| 97 | int apply, | ||
| 98 | long irq_type) | ||
| 99 | { | ||
| 100 | int result = INV_SUCCESS; | ||
| 101 | |||
| 102 | if (irq_type != MPU_SLAVE_IRQ_TYPE_NONE) | ||
| 103 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 104 | |||
| 105 | config->irq_type = MPU_SLAVE_IRQ_TYPE_NONE; | ||
| 106 | config->int_reg = 0x00; | ||
| 107 | |||
| 108 | if (apply) { | ||
| 109 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 110 | BMA150_CTRL_REG, config->ctrl_reg); | ||
| 111 | if (result) { | ||
| 112 | LOG_RESULT_LOCATION(result); | ||
| 113 | return result; | ||
| 114 | } | ||
| 115 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 116 | BMA150_INT_REG, config->int_reg); | ||
| 117 | if (result) { | ||
| 118 | LOG_RESULT_LOCATION(result); | ||
| 119 | return result; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | return result; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * @brief Set the output data rate for the particular configuration. | ||
| 127 | * | ||
| 128 | * @param mlsl_handle | ||
| 129 | * the handle to the serial channel the device is connected to. | ||
| 130 | * @param pdata | ||
| 131 | * a pointer to the slave platform data. | ||
| 132 | * @param config | ||
| 133 | * Config to modify with new ODR. | ||
| 134 | * @param apply | ||
| 135 | * whether to apply immediately or save the settings to be applied | ||
| 136 | * at the next resume. | ||
| 137 | * @param odr | ||
| 138 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 139 | * | ||
| 140 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 141 | */ | ||
| 142 | static int bma150_set_odr(void *mlsl_handle, | ||
| 143 | struct ext_slave_platform_data *pdata, | ||
| 144 | struct bma150_config *config, | ||
| 145 | int apply, | ||
| 146 | long odr) | ||
| 147 | { | ||
| 148 | unsigned char odr_bits = 0; | ||
| 149 | unsigned char wup_bits = 0; | ||
| 150 | int result = INV_SUCCESS; | ||
| 151 | |||
| 152 | if (odr > 100000) { | ||
| 153 | config->odr = 190000; | ||
| 154 | odr_bits = 0x03; | ||
| 155 | } else if (odr > 50000) { | ||
| 156 | config->odr = 100000; | ||
| 157 | odr_bits = 0x02; | ||
| 158 | } else if (odr > 25000) { | ||
| 159 | config->odr = 50000; | ||
| 160 | odr_bits = 0x01; | ||
| 161 | } else if (odr > 0) { | ||
| 162 | config->odr = 25000; | ||
| 163 | odr_bits = 0x00; | ||
| 164 | } else { | ||
| 165 | config->odr = 0; | ||
| 166 | wup_bits = 0x00; | ||
| 167 | } | ||
| 168 | |||
| 169 | config->int_reg &= BMA150_INT_MASK_WUP; | ||
| 170 | config->ctrl_reg &= BMA150_CTRL_MASK_ODR; | ||
| 171 | config->ctrl_reg |= odr_bits; | ||
| 172 | |||
| 173 | MPL_LOGV("ODR: %d\n", config->odr); | ||
| 174 | if (apply) { | ||
| 175 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 176 | BMA150_CTRL_REG, config->ctrl_reg); | ||
| 177 | if (result) { | ||
| 178 | LOG_RESULT_LOCATION(result); | ||
| 179 | return result; | ||
| 180 | } | ||
| 181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 182 | BMA150_INT_REG, config->int_reg); | ||
| 183 | if (result) { | ||
| 184 | LOG_RESULT_LOCATION(result); | ||
| 185 | return result; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | return result; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * @brief Set the full scale range of the accels | ||
| 194 | * | ||
| 195 | * @param mlsl_handle | ||
| 196 | * the handle to the serial channel the device is connected to. | ||
| 197 | * @param pdata | ||
| 198 | * a pointer to the slave platform data. | ||
| 199 | * @param config | ||
| 200 | * pointer to configuration. | ||
| 201 | * @param apply | ||
| 202 | * whether to apply immediately or save the settings to be applied | ||
| 203 | * at the next resume. | ||
| 204 | * @param fsr | ||
| 205 | * requested full scale range. | ||
| 206 | * | ||
| 207 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 208 | */ | ||
| 209 | static int bma150_set_fsr(void *mlsl_handle, | ||
| 210 | struct ext_slave_platform_data *pdata, | ||
| 211 | struct bma150_config *config, | ||
| 212 | int apply, | ||
| 213 | long fsr) | ||
| 214 | { | ||
| 215 | unsigned char fsr_bits; | ||
| 216 | int result = INV_SUCCESS; | ||
| 217 | |||
| 218 | if (fsr <= 2048) { | ||
| 219 | fsr_bits = 0x00; | ||
| 220 | config->fsr = 2048; | ||
| 221 | } else if (fsr <= 4096) { | ||
| 222 | fsr_bits = 0x08; | ||
| 223 | config->fsr = 4096; | ||
| 224 | } else { | ||
| 225 | fsr_bits = 0x10; | ||
| 226 | config->fsr = 8192; | ||
| 227 | } | ||
| 228 | |||
| 229 | config->ctrl_reg &= BMA150_CTRL_MASK_FSR; | ||
| 230 | config->ctrl_reg |= fsr_bits; | ||
| 231 | |||
| 232 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 233 | if (apply) { | ||
| 234 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 235 | BMA150_CTRL_REG, config->ctrl_reg); | ||
| 236 | if (result) { | ||
| 237 | LOG_RESULT_LOCATION(result); | ||
| 238 | return result; | ||
| 239 | } | ||
| 240 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 241 | BMA150_CTRL_REG, config->ctrl_reg); | ||
| 242 | if (result) { | ||
| 243 | LOG_RESULT_LOCATION(result); | ||
| 244 | return result; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | return result; | ||
| 248 | } | ||
| 249 | |||
| 250 | /** | ||
| 251 | * @brief one-time device driver initialization function. | ||
| 252 | * If the driver is built as a kernel module, this function will be | ||
| 253 | * called when the module is loaded in the kernel. | ||
| 254 | * If the driver is built-in in the kernel, this function will be | ||
| 255 | * called at boot time. | ||
| 256 | * | ||
| 257 | * @param mlsl_handle | ||
| 258 | * the handle to the serial channel the device is connected to. | ||
| 259 | * @param slave | ||
| 260 | * a pointer to the slave descriptor data structure. | ||
| 261 | * @param pdata | ||
| 262 | * a pointer to the slave platform data. | ||
| 263 | * | ||
| 264 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 265 | */ | ||
| 266 | static int bma150_init(void *mlsl_handle, | ||
| 267 | struct ext_slave_descr *slave, | ||
| 268 | struct ext_slave_platform_data *pdata) | ||
| 269 | { | ||
| 270 | int result; | ||
| 271 | unsigned char reg; | ||
| 272 | long range; | ||
| 273 | |||
| 274 | struct bma150_private_data *private_data; | ||
| 275 | private_data = (struct bma150_private_data *) | ||
| 276 | kzalloc(sizeof(struct bma150_private_data), GFP_KERNEL); | ||
| 277 | |||
| 278 | if (!private_data) | ||
| 279 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 280 | |||
| 281 | pdata->private_data = private_data; | ||
| 282 | |||
| 283 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 284 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
| 285 | if (result) { | ||
| 286 | LOG_RESULT_LOCATION(result); | ||
| 287 | return result; | ||
| 288 | } | ||
| 289 | msleep(1); | ||
| 290 | |||
| 291 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 292 | BMA150_CTRL_REG, 1, ®); | ||
| 293 | if (result) { | ||
| 294 | LOG_RESULT_LOCATION(result); | ||
| 295 | return result; | ||
| 296 | } | ||
| 297 | private_data->resume.ctrl_reg = reg; | ||
| 298 | private_data->suspend.ctrl_reg = reg; | ||
| 299 | |||
| 300 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 301 | BMA150_INT_REG, 1, ®); | ||
| 302 | if (result) { | ||
| 303 | LOG_RESULT_LOCATION(result); | ||
| 304 | return result; | ||
| 305 | } | ||
| 306 | private_data->resume.int_reg = reg; | ||
| 307 | private_data->suspend.int_reg = reg; | ||
| 308 | |||
| 309 | result = bma150_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 310 | false, 0); | ||
| 311 | if (result) { | ||
| 312 | LOG_RESULT_LOCATION(result); | ||
| 313 | return result; | ||
| 314 | } | ||
| 315 | result = bma150_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 316 | false, 200000); | ||
| 317 | if (result) { | ||
| 318 | LOG_RESULT_LOCATION(result); | ||
| 319 | return result; | ||
| 320 | } | ||
| 321 | |||
| 322 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 323 | result = bma150_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 324 | false, range); | ||
| 325 | result = bma150_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 326 | false, range); | ||
| 327 | if (result) { | ||
| 328 | LOG_RESULT_LOCATION(result); | ||
| 329 | return result; | ||
| 330 | } | ||
| 331 | |||
| 332 | result = bma150_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 333 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 334 | if (result) { | ||
| 335 | LOG_RESULT_LOCATION(result); | ||
| 336 | return result; | ||
| 337 | } | ||
| 338 | result = bma150_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 339 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 340 | if (result) { | ||
| 341 | LOG_RESULT_LOCATION(result); | ||
| 342 | return result; | ||
| 343 | } | ||
| 344 | |||
| 345 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 346 | BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP); | ||
| 347 | if (result) { | ||
| 348 | LOG_RESULT_LOCATION(result); | ||
| 349 | return result; | ||
| 350 | } | ||
| 351 | |||
| 352 | return result; | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * @brief one-time device driver exit function. | ||
| 357 | * If the driver is built as a kernel module, this function will be | ||
| 358 | * called when the module is removed from the kernel. | ||
| 359 | * | ||
| 360 | * @param mlsl_handle | ||
| 361 | * the handle to the serial channel the device is connected to. | ||
| 362 | * @param slave | ||
| 363 | * a pointer to the slave descriptor data structure. | ||
| 364 | * @param pdata | ||
| 365 | * a pointer to the slave platform data. | ||
| 366 | * | ||
| 367 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 368 | */ | ||
| 369 | static int bma150_exit(void *mlsl_handle, | ||
| 370 | struct ext_slave_descr *slave, | ||
| 371 | struct ext_slave_platform_data *pdata) | ||
| 372 | { | ||
| 373 | kfree(pdata->private_data); | ||
| 374 | return INV_SUCCESS; | ||
| 375 | } | ||
| 376 | |||
| 377 | /** | ||
| 378 | * @brief device configuration facility. | ||
| 379 | * | ||
| 380 | * @param mlsl_handle | ||
| 381 | * the handle to the serial channel the device is connected to. | ||
| 382 | * @param slave | ||
| 383 | * a pointer to the slave descriptor data structure. | ||
| 384 | * @param pdata | ||
| 385 | * a pointer to the slave platform data. | ||
| 386 | * @param data | ||
| 387 | * a pointer to the configuration data structure. | ||
| 388 | * | ||
| 389 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 390 | */ | ||
| 391 | static int bma150_config(void *mlsl_handle, | ||
| 392 | struct ext_slave_descr *slave, | ||
| 393 | struct ext_slave_platform_data *pdata, | ||
| 394 | struct ext_slave_config *data) | ||
| 395 | { | ||
| 396 | struct bma150_private_data *private_data = | ||
| 397 | (struct bma150_private_data *)(pdata->private_data); | ||
| 398 | |||
| 399 | if (!data->data) | ||
| 400 | return INV_ERROR_INVALID_PARAMETER; | ||
| 401 | |||
| 402 | switch (data->key) { | ||
| 403 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 404 | return bma150_set_odr(mlsl_handle, pdata, | ||
| 405 | &private_data->suspend, | ||
| 406 | data->apply, | ||
| 407 | *((long *)data->data)); | ||
| 408 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 409 | return bma150_set_odr(mlsl_handle, pdata, | ||
| 410 | &private_data->resume, | ||
| 411 | data->apply, | ||
| 412 | *((long *)data->data)); | ||
| 413 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 414 | return bma150_set_fsr(mlsl_handle, pdata, | ||
| 415 | &private_data->suspend, | ||
| 416 | data->apply, | ||
| 417 | *((long *)data->data)); | ||
| 418 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 419 | return bma150_set_fsr(mlsl_handle, pdata, | ||
| 420 | &private_data->resume, | ||
| 421 | data->apply, | ||
| 422 | *((long *)data->data)); | ||
| 423 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 424 | return bma150_set_irq(mlsl_handle, pdata, | ||
| 425 | &private_data->suspend, | ||
| 426 | data->apply, | ||
| 427 | *((long *)data->data)); | ||
| 428 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 429 | return bma150_set_irq(mlsl_handle, pdata, | ||
| 430 | &private_data->resume, | ||
| 431 | data->apply, | ||
| 432 | *((long *)data->data)); | ||
| 433 | default: | ||
| 434 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 435 | }; | ||
| 436 | return INV_SUCCESS; | ||
| 437 | } | ||
| 438 | |||
| 439 | /** | ||
| 440 | * @brief facility to retrieve the device configuration. | ||
| 441 | * | ||
| 442 | * @param mlsl_handle | ||
| 443 | * the handle to the serial channel the device is connected to. | ||
| 444 | * @param slave | ||
| 445 | * a pointer to the slave descriptor data structure. | ||
| 446 | * @param pdata | ||
| 447 | * a pointer to the slave platform data. | ||
| 448 | * @param data | ||
| 449 | * a pointer to store the returned configuration data structure. | ||
| 450 | * | ||
| 451 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 452 | */ | ||
| 453 | static int bma150_get_config(void *mlsl_handle, | ||
| 454 | struct ext_slave_descr *slave, | ||
| 455 | struct ext_slave_platform_data *pdata, | ||
| 456 | struct ext_slave_config *data) | ||
| 457 | { | ||
| 458 | struct bma150_private_data *private_data = | ||
| 459 | (struct bma150_private_data *)(pdata->private_data); | ||
| 460 | |||
| 461 | if (!data->data) | ||
| 462 | return INV_ERROR_INVALID_PARAMETER; | ||
| 463 | |||
| 464 | switch (data->key) { | ||
| 465 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 466 | (*(unsigned long *)data->data) = | ||
| 467 | (unsigned long) private_data->suspend.odr; | ||
| 468 | break; | ||
| 469 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 470 | (*(unsigned long *)data->data) = | ||
| 471 | (unsigned long) private_data->resume.odr; | ||
| 472 | break; | ||
| 473 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 474 | (*(unsigned long *)data->data) = | ||
| 475 | (unsigned long) private_data->suspend.fsr; | ||
| 476 | break; | ||
| 477 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 478 | (*(unsigned long *)data->data) = | ||
| 479 | (unsigned long) private_data->resume.fsr; | ||
| 480 | break; | ||
| 481 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 482 | (*(unsigned long *)data->data) = | ||
| 483 | (unsigned long) private_data->suspend.irq_type; | ||
| 484 | break; | ||
| 485 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 486 | (*(unsigned long *)data->data) = | ||
| 487 | (unsigned long) private_data->resume.irq_type; | ||
| 488 | break; | ||
| 489 | default: | ||
| 490 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 491 | }; | ||
| 492 | |||
| 493 | return INV_SUCCESS; | ||
| 494 | } | ||
| 495 | |||
| 496 | /** | ||
| 497 | * @brief suspends the device to put it in its lowest power mode. | ||
| 498 | * | ||
| 499 | * @param mlsl_handle | ||
| 500 | * the handle to the serial channel the device is connected to. | ||
| 501 | * @param slave | ||
| 502 | * a pointer to the slave descriptor data structure. | ||
| 503 | * @param pdata | ||
| 504 | * a pointer to the slave platform data. | ||
| 505 | * | ||
| 506 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 507 | */ | ||
| 508 | static int bma150_suspend(void *mlsl_handle, | ||
| 509 | struct ext_slave_descr *slave, | ||
| 510 | struct ext_slave_platform_data *pdata) | ||
| 511 | { | ||
| 512 | int result; | ||
| 513 | unsigned char ctrl_reg; | ||
| 514 | unsigned char int_reg; | ||
| 515 | |||
| 516 | struct bma150_private_data *private_data = | ||
| 517 | (struct bma150_private_data *)(pdata->private_data); | ||
| 518 | |||
| 519 | ctrl_reg = private_data->suspend.ctrl_reg; | ||
| 520 | int_reg = private_data->suspend.int_reg; | ||
| 521 | |||
| 522 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 523 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
| 524 | if (result) { | ||
| 525 | LOG_RESULT_LOCATION(result); | ||
| 526 | return result; | ||
| 527 | } | ||
| 528 | msleep(1); | ||
| 529 | |||
| 530 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 531 | BMA150_CTRL_REG, ctrl_reg); | ||
| 532 | if (result) { | ||
| 533 | LOG_RESULT_LOCATION(result); | ||
| 534 | return result; | ||
| 535 | } | ||
| 536 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 537 | BMA150_INT_REG, int_reg); | ||
| 538 | if (result) { | ||
| 539 | LOG_RESULT_LOCATION(result); | ||
| 540 | return result; | ||
| 541 | } | ||
| 542 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 543 | BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP); | ||
| 544 | if (result) { | ||
| 545 | LOG_RESULT_LOCATION(result); | ||
| 546 | return result; | ||
| 547 | } | ||
| 548 | |||
| 549 | return result; | ||
| 550 | } | ||
| 551 | |||
| 552 | /** | ||
| 553 | * @brief resume the device in the proper power state given the configuration | ||
| 554 | * chosen. | ||
| 555 | * | ||
| 556 | * @param mlsl_handle | ||
| 557 | * the handle to the serial channel the device is connected to. | ||
| 558 | * @param slave | ||
| 559 | * a pointer to the slave descriptor data structure. | ||
| 560 | * @param pdata | ||
| 561 | * a pointer to the slave platform data. | ||
| 562 | * | ||
| 563 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 564 | */ | ||
| 565 | static int bma150_resume(void *mlsl_handle, | ||
| 566 | struct ext_slave_descr *slave, | ||
| 567 | struct ext_slave_platform_data *pdata) | ||
| 568 | { | ||
| 569 | int result; | ||
| 570 | unsigned char ctrl_reg; | ||
| 571 | unsigned char int_reg; | ||
| 572 | |||
| 573 | struct bma150_private_data *private_data = | ||
| 574 | (struct bma150_private_data *)(pdata->private_data); | ||
| 575 | |||
| 576 | ctrl_reg = private_data->resume.ctrl_reg; | ||
| 577 | int_reg = private_data->resume.int_reg; | ||
| 578 | |||
| 579 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 580 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
| 581 | if (result) { | ||
| 582 | LOG_RESULT_LOCATION(result); | ||
| 583 | return result; | ||
| 584 | } | ||
| 585 | msleep(1); | ||
| 586 | |||
| 587 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 588 | BMA150_CTRL_REG, ctrl_reg); | ||
| 589 | if (result) { | ||
| 590 | LOG_RESULT_LOCATION(result); | ||
| 591 | return result; | ||
| 592 | } | ||
| 593 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 594 | BMA150_INT_REG, int_reg); | ||
| 595 | if (result) { | ||
| 596 | LOG_RESULT_LOCATION(result); | ||
| 597 | return result; | ||
| 598 | } | ||
| 599 | |||
| 600 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 601 | BMA150_PWR_REG, 0x00); | ||
| 602 | if (result) { | ||
| 603 | LOG_RESULT_LOCATION(result); | ||
| 604 | return result; | ||
| 605 | } | ||
| 606 | |||
| 607 | return result; | ||
| 608 | } | ||
| 609 | |||
| 610 | /** | ||
| 611 | * @brief read the sensor data from the device. | ||
| 612 | * | ||
| 613 | * @param mlsl_handle | ||
| 614 | * the handle to the serial channel the device is connected to. | ||
| 615 | * @param slave | ||
| 616 | * a pointer to the slave descriptor data structure. | ||
| 617 | * @param pdata | ||
| 618 | * a pointer to the slave platform data. | ||
| 619 | * @param data | ||
| 620 | * a buffer to store the data read. | ||
| 621 | * | ||
| 622 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 623 | */ | ||
| 624 | static int bma150_read(void *mlsl_handle, | ||
| 625 | struct ext_slave_descr *slave, | ||
| 626 | struct ext_slave_platform_data *pdata, | ||
| 627 | unsigned char *data) | ||
| 628 | { | ||
| 629 | return inv_serial_read(mlsl_handle, pdata->address, | ||
| 630 | slave->read_reg, slave->read_len, data); | ||
| 631 | } | ||
| 632 | |||
| 633 | static struct ext_slave_descr bma150_descr = { | ||
| 634 | .init = bma150_init, | ||
| 635 | .exit = bma150_exit, | ||
| 636 | .suspend = bma150_suspend, | ||
| 637 | .resume = bma150_resume, | ||
| 638 | .read = bma150_read, | ||
| 639 | .config = bma150_config, | ||
| 640 | .get_config = bma150_get_config, | ||
| 641 | .name = "bma150", | ||
| 642 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 643 | .id = ACCEL_ID_BMA150, | ||
| 644 | .read_reg = 0x02, | ||
| 645 | .read_len = 6, | ||
| 646 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 647 | .range = {2, 0}, | ||
| 648 | .trigger = NULL, | ||
| 649 | }; | ||
| 650 | |||
| 651 | static | ||
| 652 | struct ext_slave_descr *bma150_get_slave_descr(void) | ||
| 653 | { | ||
| 654 | return &bma150_descr; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* -------------------------------------------------------------------------- */ | ||
| 658 | |||
| 659 | /* Platform data for the MPU */ | ||
| 660 | struct bma150_mod_private_data { | ||
| 661 | struct i2c_client *client; | ||
| 662 | struct ext_slave_platform_data *pdata; | ||
| 663 | }; | ||
| 664 | |||
| 665 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 666 | |||
| 667 | static int bma150_mod_probe(struct i2c_client *client, | ||
| 668 | const struct i2c_device_id *devid) | ||
| 669 | { | ||
| 670 | struct ext_slave_platform_data *pdata; | ||
| 671 | struct bma150_mod_private_data *private_data; | ||
| 672 | int result = 0; | ||
| 673 | |||
| 674 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 675 | |||
| 676 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 677 | result = -ENODEV; | ||
| 678 | goto out_no_free; | ||
| 679 | } | ||
| 680 | |||
| 681 | pdata = client->dev.platform_data; | ||
| 682 | if (!pdata) { | ||
| 683 | dev_err(&client->adapter->dev, | ||
| 684 | "Missing platform data for slave %s\n", devid->name); | ||
| 685 | result = -EFAULT; | ||
| 686 | goto out_no_free; | ||
| 687 | } | ||
| 688 | |||
| 689 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 690 | if (!private_data) { | ||
| 691 | result = -ENOMEM; | ||
| 692 | goto out_no_free; | ||
| 693 | } | ||
| 694 | |||
| 695 | i2c_set_clientdata(client, private_data); | ||
| 696 | private_data->client = client; | ||
| 697 | private_data->pdata = pdata; | ||
| 698 | |||
| 699 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 700 | bma150_get_slave_descr); | ||
| 701 | if (result) { | ||
| 702 | dev_err(&client->adapter->dev, | ||
| 703 | "Slave registration failed: %s, %d\n", | ||
| 704 | devid->name, result); | ||
| 705 | goto out_free_memory; | ||
| 706 | } | ||
| 707 | |||
| 708 | return result; | ||
| 709 | |||
| 710 | out_free_memory: | ||
| 711 | kfree(private_data); | ||
| 712 | out_no_free: | ||
| 713 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 714 | return result; | ||
| 715 | |||
| 716 | } | ||
| 717 | |||
| 718 | static int bma150_mod_remove(struct i2c_client *client) | ||
| 719 | { | ||
| 720 | struct bma150_mod_private_data *private_data = | ||
| 721 | i2c_get_clientdata(client); | ||
| 722 | |||
| 723 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 724 | |||
| 725 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 726 | bma150_get_slave_descr); | ||
| 727 | |||
| 728 | kfree(private_data); | ||
| 729 | return 0; | ||
| 730 | } | ||
| 731 | |||
| 732 | static const struct i2c_device_id bma150_mod_id[] = { | ||
| 733 | { "bma150", ACCEL_ID_BMA150 }, | ||
| 734 | {} | ||
| 735 | }; | ||
| 736 | |||
| 737 | MODULE_DEVICE_TABLE(i2c, bma150_mod_id); | ||
| 738 | |||
| 739 | static struct i2c_driver bma150_mod_driver = { | ||
| 740 | .class = I2C_CLASS_HWMON, | ||
| 741 | .probe = bma150_mod_probe, | ||
| 742 | .remove = bma150_mod_remove, | ||
| 743 | .id_table = bma150_mod_id, | ||
| 744 | .driver = { | ||
| 745 | .owner = THIS_MODULE, | ||
| 746 | .name = "bma150_mod", | ||
| 747 | }, | ||
| 748 | .address_list = normal_i2c, | ||
| 749 | }; | ||
| 750 | |||
| 751 | static int __init bma150_mod_init(void) | ||
| 752 | { | ||
| 753 | int res = i2c_add_driver(&bma150_mod_driver); | ||
| 754 | pr_info("%s: Probe name %s\n", __func__, "bma150_mod"); | ||
| 755 | if (res) | ||
| 756 | pr_err("%s failed\n", __func__); | ||
| 757 | return res; | ||
| 758 | } | ||
| 759 | |||
| 760 | static void __exit bma150_mod_exit(void) | ||
| 761 | { | ||
| 762 | pr_info("%s\n", __func__); | ||
| 763 | i2c_del_driver(&bma150_mod_driver); | ||
| 764 | } | ||
| 765 | |||
| 766 | module_init(bma150_mod_init); | ||
| 767 | module_exit(bma150_mod_exit); | ||
| 768 | |||
| 769 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 770 | MODULE_DESCRIPTION("Driver to integrate BMA150 sensor with the MPU"); | ||
| 771 | MODULE_LICENSE("GPL"); | ||
| 772 | MODULE_ALIAS("bma150_mod"); | ||
| 773 | |||
| 774 | /** | ||
| 775 | * @} | ||
| 776 | */ | ||
| 777 | |||
diff --git a/drivers/misc/inv_mpu/accel/bma222.c b/drivers/misc/inv_mpu/accel/bma222.c new file mode 100644 index 00000000000..e9fc99b1a62 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma222.c | |||
| @@ -0,0 +1,654 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file bma222.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Bosch BMA222. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* ------------------ */ | ||
| 30 | /* - Include Files. - */ | ||
| 31 | /* ------------------ */ | ||
| 32 | |||
| 33 | #include <linux/i2c.h> | ||
| 34 | #include <linux/module.h> | ||
| 35 | #include <linux/moduleparam.h> | ||
| 36 | #include <linux/kernel.h> | ||
| 37 | #include <linux/errno.h> | ||
| 38 | #include <linux/slab.h> | ||
| 39 | #include <linux/delay.h> | ||
| 40 | #include "mpu-dev.h" | ||
| 41 | |||
| 42 | #include <linux/mpu.h> | ||
| 43 | #include "mlsl.h" | ||
| 44 | #include "mldl_cfg.h" | ||
| 45 | |||
| 46 | /* -------------------------------------------------------------------------- */ | ||
| 47 | |||
| 48 | #define BMA222_STATUS_REG (0x0A) | ||
| 49 | #define BMA222_FSR_REG (0x0F) | ||
| 50 | #define ADXL34X_ODR_REG (0x10) | ||
| 51 | #define BMA222_PWR_REG (0x11) | ||
| 52 | #define BMA222_SOFTRESET_REG (0x14) | ||
| 53 | |||
| 54 | #define BMA222_STATUS_RDY_MASK (0x80) | ||
| 55 | #define BMA222_FSR_MASK (0x0F) | ||
| 56 | #define BMA222_ODR_MASK (0x1F) | ||
| 57 | #define BMA222_PWR_SLEEP_MASK (0x80) | ||
| 58 | #define BMA222_PWR_AWAKE_MASK (0x00) | ||
| 59 | #define BMA222_SOFTRESET_MASK (0xB6) | ||
| 60 | #define BMA222_SOFTRESET_MASK (0xB6) | ||
| 61 | |||
| 62 | /* -------------------------------------------------------------------------- */ | ||
| 63 | |||
| 64 | struct bma222_config { | ||
| 65 | unsigned int odr; /** < output data rate in mHz */ | ||
| 66 | unsigned int fsr; /** < full scale range mg */ | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct bma222_private_data { | ||
| 70 | struct bma222_config suspend; /** < suspend configuration */ | ||
| 71 | struct bma222_config resume; /** < resume configuration */ | ||
| 72 | }; | ||
| 73 | |||
| 74 | |||
| 75 | /* -------------------------------------------------------------------------- */ | ||
| 76 | |||
| 77 | /** | ||
| 78 | * @brief Set the output data rate for the particular configuration. | ||
| 79 | * | ||
| 80 | * @param mlsl_handle | ||
| 81 | * the handle to the serial channel the device is connected to. | ||
| 82 | * @param pdata | ||
| 83 | * a pointer to the slave platform data. | ||
| 84 | * @param config | ||
| 85 | * Config to modify with new ODR. | ||
| 86 | * @param apply | ||
| 87 | * whether to apply immediately or save the settings to be applied | ||
| 88 | * at the next resume. | ||
| 89 | * @param odr | ||
| 90 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 91 | * | ||
| 92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 93 | */ | ||
| 94 | static int bma222_set_odr(void *mlsl_handle, | ||
| 95 | struct ext_slave_platform_data *pdata, | ||
| 96 | struct bma222_config *config, | ||
| 97 | int apply, | ||
| 98 | long odr) | ||
| 99 | { | ||
| 100 | int result = INV_SUCCESS; | ||
| 101 | unsigned char reg_odr; | ||
| 102 | |||
| 103 | if (odr >= 1000000) { | ||
| 104 | reg_odr = 0x0F; | ||
| 105 | config->odr = 1000000; | ||
| 106 | } else if (odr >= 500000) { | ||
| 107 | reg_odr = 0x0E; | ||
| 108 | config->odr = 500000; | ||
| 109 | } else if (odr >= 250000) { | ||
| 110 | reg_odr = 0x0D; | ||
| 111 | config->odr = 250000; | ||
| 112 | } else if (odr >= 125000) { | ||
| 113 | reg_odr = 0x0C; | ||
| 114 | config->odr = 125000; | ||
| 115 | } else if (odr >= 62500) { | ||
| 116 | reg_odr = 0x0B; | ||
| 117 | config->odr = 62500; | ||
| 118 | } else if (odr >= 32000) { | ||
| 119 | reg_odr = 0x0A; | ||
| 120 | config->odr = 32000; | ||
| 121 | } else if (odr >= 16000) { | ||
| 122 | reg_odr = 0x09; | ||
| 123 | config->odr = 16000; | ||
| 124 | } else { | ||
| 125 | reg_odr = 0x08; | ||
| 126 | config->odr = 8000; | ||
| 127 | } | ||
| 128 | |||
| 129 | if (apply) { | ||
| 130 | MPL_LOGV("ODR: %d\n", config->odr); | ||
| 131 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 132 | ADXL34X_ODR_REG, reg_odr); | ||
| 133 | if (result) { | ||
| 134 | LOG_RESULT_LOCATION(result); | ||
| 135 | return result; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | return result; | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * @brief Set the full scale range of the accels | ||
| 143 | * | ||
| 144 | * @param mlsl_handle | ||
| 145 | * the handle to the serial channel the device is connected to. | ||
| 146 | * @param pdata | ||
| 147 | * a pointer to the slave platform data. | ||
| 148 | * @param config | ||
| 149 | * pointer to configuration. | ||
| 150 | * @param apply | ||
| 151 | * whether to apply immediately or save the settings to be applied | ||
| 152 | * at the next resume. | ||
| 153 | * @param fsr | ||
| 154 | * requested full scale range. | ||
| 155 | * | ||
| 156 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 157 | */ | ||
| 158 | static int bma222_set_fsr(void *mlsl_handle, | ||
| 159 | struct ext_slave_platform_data *pdata, | ||
| 160 | struct bma222_config *config, | ||
| 161 | int apply, | ||
| 162 | long fsr) | ||
| 163 | { | ||
| 164 | int result = INV_SUCCESS; | ||
| 165 | unsigned char reg_fsr_mask; | ||
| 166 | |||
| 167 | if (fsr <= 2000) { | ||
| 168 | reg_fsr_mask = 0x03; | ||
| 169 | config->fsr = 2000; | ||
| 170 | } else if (fsr <= 4000) { | ||
| 171 | reg_fsr_mask = 0x05; | ||
| 172 | config->fsr = 4000; | ||
| 173 | } else if (fsr <= 8000) { | ||
| 174 | reg_fsr_mask = 0x08; | ||
| 175 | config->fsr = 8000; | ||
| 176 | } else { /* 8001 -> oo */ | ||
| 177 | reg_fsr_mask = 0x0C; | ||
| 178 | config->fsr = 16000; | ||
| 179 | } | ||
| 180 | |||
| 181 | if (apply) { | ||
| 182 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 183 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 184 | BMA222_FSR_REG, reg_fsr_mask); | ||
| 185 | if (result) { | ||
| 186 | LOG_RESULT_LOCATION(result); | ||
| 187 | return result; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | return result; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * @brief one-time device driver initialization function. | ||
| 195 | * If the driver is built as a kernel module, this function will be | ||
| 196 | * called when the module is loaded in the kernel. | ||
| 197 | * If the driver is built-in in the kernel, this function will be | ||
| 198 | * called at boot time. | ||
| 199 | * | ||
| 200 | * @param mlsl_handle | ||
| 201 | * the handle to the serial channel the device is connected to. | ||
| 202 | * @param slave | ||
| 203 | * a pointer to the slave descriptor data structure. | ||
| 204 | * @param pdata | ||
| 205 | * a pointer to the slave platform data. | ||
| 206 | * | ||
| 207 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 208 | */ | ||
| 209 | static int bma222_init(void *mlsl_handle, | ||
| 210 | struct ext_slave_descr *slave, | ||
| 211 | struct ext_slave_platform_data *pdata) | ||
| 212 | { | ||
| 213 | int result; | ||
| 214 | |||
| 215 | struct bma222_private_data *private_data; | ||
| 216 | private_data = (struct bma222_private_data *) | ||
| 217 | kzalloc(sizeof(struct bma222_private_data), GFP_KERNEL); | ||
| 218 | |||
| 219 | if (!private_data) | ||
| 220 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 221 | |||
| 222 | pdata->private_data = private_data; | ||
| 223 | |||
| 224 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 225 | BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK); | ||
| 226 | if (result) { | ||
| 227 | LOG_RESULT_LOCATION(result); | ||
| 228 | return result; | ||
| 229 | } | ||
| 230 | msleep(1); | ||
| 231 | |||
| 232 | result = bma222_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 233 | false, 0); | ||
| 234 | if (result) { | ||
| 235 | LOG_RESULT_LOCATION(result); | ||
| 236 | return result; | ||
| 237 | } | ||
| 238 | result = bma222_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 239 | false, 200000); | ||
| 240 | if (result) { | ||
| 241 | LOG_RESULT_LOCATION(result); | ||
| 242 | return result; | ||
| 243 | } | ||
| 244 | |||
| 245 | result = bma222_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 246 | false, 2000); | ||
| 247 | result = bma222_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 248 | false, 2000); | ||
| 249 | if (result) { | ||
| 250 | LOG_RESULT_LOCATION(result); | ||
| 251 | return result; | ||
| 252 | } | ||
| 253 | |||
| 254 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 255 | BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK); | ||
| 256 | if (result) { | ||
| 257 | LOG_RESULT_LOCATION(result); | ||
| 258 | return result; | ||
| 259 | } | ||
| 260 | |||
| 261 | return result; | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * @brief one-time device driver exit function. | ||
| 266 | * If the driver is built as a kernel module, this function will be | ||
| 267 | * called when the module is removed from the kernel. | ||
| 268 | * | ||
| 269 | * @param mlsl_handle | ||
| 270 | * the handle to the serial channel the device is connected to. | ||
| 271 | * @param slave | ||
| 272 | * a pointer to the slave descriptor data structure. | ||
| 273 | * @param pdata | ||
| 274 | * a pointer to the slave platform data. | ||
| 275 | * | ||
| 276 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 277 | */ | ||
| 278 | static int bma222_exit(void *mlsl_handle, | ||
| 279 | struct ext_slave_descr *slave, | ||
| 280 | struct ext_slave_platform_data *pdata) | ||
| 281 | { | ||
| 282 | kfree(pdata->private_data); | ||
| 283 | return INV_SUCCESS; | ||
| 284 | } | ||
| 285 | |||
| 286 | |||
| 287 | /** | ||
| 288 | * @brief facility to retrieve the device configuration. | ||
| 289 | * | ||
| 290 | * @param mlsl_handle | ||
| 291 | * the handle to the serial channel the device is connected to. | ||
| 292 | * @param slave | ||
| 293 | * a pointer to the slave descriptor data structure. | ||
| 294 | * @param pdata | ||
| 295 | * a pointer to the slave platform data. | ||
| 296 | * @param data | ||
| 297 | * a pointer to store the returned configuration data structure. | ||
| 298 | * | ||
| 299 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 300 | */ | ||
| 301 | static int bma222_get_config(void *mlsl_handle, | ||
| 302 | struct ext_slave_descr *slave, | ||
| 303 | struct ext_slave_platform_data *pdata, | ||
| 304 | struct ext_slave_config *data) | ||
| 305 | { | ||
| 306 | struct bma222_private_data *private_data = | ||
| 307 | (struct bma222_private_data *)(pdata->private_data); | ||
| 308 | |||
| 309 | if (!data->data) | ||
| 310 | return INV_ERROR_INVALID_PARAMETER; | ||
| 311 | |||
| 312 | switch (data->key) { | ||
| 313 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 314 | (*(unsigned long *)data->data) = | ||
| 315 | (unsigned long) private_data->suspend.odr; | ||
| 316 | break; | ||
| 317 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 318 | (*(unsigned long *)data->data) = | ||
| 319 | (unsigned long) private_data->resume.odr; | ||
| 320 | break; | ||
| 321 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 322 | (*(unsigned long *)data->data) = | ||
| 323 | (unsigned long) private_data->suspend.fsr; | ||
| 324 | break; | ||
| 325 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 326 | (*(unsigned long *)data->data) = | ||
| 327 | (unsigned long) private_data->resume.fsr; | ||
| 328 | break; | ||
| 329 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 330 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 331 | default: | ||
| 332 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 333 | }; | ||
| 334 | |||
| 335 | return INV_SUCCESS; | ||
| 336 | } | ||
| 337 | |||
| 338 | /** | ||
| 339 | * @brief device configuration facility. | ||
| 340 | * | ||
| 341 | * @param mlsl_handle | ||
| 342 | * the handle to the serial channel the device is connected to. | ||
| 343 | * @param slave | ||
| 344 | * a pointer to the slave descriptor data structure. | ||
| 345 | * @param pdata | ||
| 346 | * a pointer to the slave platform data. | ||
| 347 | * @param data | ||
| 348 | * a pointer to the configuration data structure. | ||
| 349 | * | ||
| 350 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 351 | */ | ||
| 352 | static int bma222_config(void *mlsl_handle, | ||
| 353 | struct ext_slave_descr *slave, | ||
| 354 | struct ext_slave_platform_data *pdata, | ||
| 355 | struct ext_slave_config *data) | ||
| 356 | { | ||
| 357 | struct bma222_private_data *private_data = | ||
| 358 | (struct bma222_private_data *)(pdata->private_data); | ||
| 359 | |||
| 360 | if (!data->data) | ||
| 361 | return INV_ERROR_INVALID_PARAMETER; | ||
| 362 | |||
| 363 | switch (data->key) { | ||
| 364 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 365 | return bma222_set_odr(mlsl_handle, pdata, | ||
| 366 | &private_data->suspend, | ||
| 367 | data->apply, | ||
| 368 | *((long *)data->data)); | ||
| 369 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 370 | return bma222_set_odr(mlsl_handle, pdata, | ||
| 371 | &private_data->resume, | ||
| 372 | data->apply, | ||
| 373 | *((long *)data->data)); | ||
| 374 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 375 | return bma222_set_fsr(mlsl_handle, pdata, | ||
| 376 | &private_data->suspend, | ||
| 377 | data->apply, | ||
| 378 | *((long *)data->data)); | ||
| 379 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 380 | return bma222_set_fsr(mlsl_handle, pdata, | ||
| 381 | &private_data->resume, | ||
| 382 | data->apply, | ||
| 383 | *((long *)data->data)); | ||
| 384 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 385 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 386 | default: | ||
| 387 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 388 | }; | ||
| 389 | return INV_SUCCESS; | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * @brief suspends the device to put it in its lowest power mode. | ||
| 394 | * | ||
| 395 | * @param mlsl_handle | ||
| 396 | * the handle to the serial channel the device is connected to. | ||
| 397 | * @param slave | ||
| 398 | * a pointer to the slave descriptor data structure. | ||
| 399 | * @param pdata | ||
| 400 | * a pointer to the slave platform data. | ||
| 401 | * | ||
| 402 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 403 | */ | ||
| 404 | static int bma222_suspend(void *mlsl_handle, | ||
| 405 | struct ext_slave_descr *slave, | ||
| 406 | struct ext_slave_platform_data *pdata) | ||
| 407 | { | ||
| 408 | int result; | ||
| 409 | struct bma222_config *suspend_config = | ||
| 410 | &((struct bma222_private_data *)pdata->private_data)->suspend; | ||
| 411 | |||
| 412 | result = bma222_set_odr(mlsl_handle, pdata, suspend_config, | ||
| 413 | true, suspend_config->odr); | ||
| 414 | if (result) { | ||
| 415 | LOG_RESULT_LOCATION(result); | ||
| 416 | return result; | ||
| 417 | } | ||
| 418 | result = bma222_set_fsr(mlsl_handle, pdata, suspend_config, | ||
| 419 | true, suspend_config->fsr); | ||
| 420 | if (result) { | ||
| 421 | LOG_RESULT_LOCATION(result); | ||
| 422 | return result; | ||
| 423 | } | ||
| 424 | |||
| 425 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 426 | BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK); | ||
| 427 | if (result) { | ||
| 428 | LOG_RESULT_LOCATION(result); | ||
| 429 | return result; | ||
| 430 | } | ||
| 431 | |||
| 432 | msleep(3); /* 3 ms powerup time maximum */ | ||
| 433 | return result; | ||
| 434 | } | ||
| 435 | |||
| 436 | /** | ||
| 437 | * @brief resume the device in the proper power state given the configuration | ||
| 438 | * chosen. | ||
| 439 | * | ||
| 440 | * @param mlsl_handle | ||
| 441 | * the handle to the serial channel the device is connected to. | ||
| 442 | * @param slave | ||
| 443 | * a pointer to the slave descriptor data structure. | ||
| 444 | * @param pdata | ||
| 445 | * a pointer to the slave platform data. | ||
| 446 | * | ||
| 447 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 448 | */ | ||
| 449 | static int bma222_resume(void *mlsl_handle, | ||
| 450 | struct ext_slave_descr *slave, | ||
| 451 | struct ext_slave_platform_data *pdata) | ||
| 452 | { | ||
| 453 | int result; | ||
| 454 | struct bma222_config *resume_config = | ||
| 455 | &((struct bma222_private_data *)pdata->private_data)->resume; | ||
| 456 | |||
| 457 | /* Soft reset */ | ||
| 458 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 459 | BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK); | ||
| 460 | if (result) { | ||
| 461 | LOG_RESULT_LOCATION(result); | ||
| 462 | return result; | ||
| 463 | } | ||
| 464 | msleep(10); | ||
| 465 | |||
| 466 | result = bma222_set_odr(mlsl_handle, pdata, resume_config, | ||
| 467 | true, resume_config->odr); | ||
| 468 | if (result) { | ||
| 469 | LOG_RESULT_LOCATION(result); | ||
| 470 | return result; | ||
| 471 | } | ||
| 472 | result = bma222_set_fsr(mlsl_handle, pdata, resume_config, | ||
| 473 | true, resume_config->fsr); | ||
| 474 | if (result) { | ||
| 475 | LOG_RESULT_LOCATION(result); | ||
| 476 | return result; | ||
| 477 | } | ||
| 478 | |||
| 479 | return result; | ||
| 480 | } | ||
| 481 | |||
| 482 | /** | ||
| 483 | * @brief read the sensor data from the device. | ||
| 484 | * | ||
| 485 | * @param mlsl_handle | ||
| 486 | * the handle to the serial channel the device is connected to. | ||
| 487 | * @param slave | ||
| 488 | * a pointer to the slave descriptor data structure. | ||
| 489 | * @param pdata | ||
| 490 | * a pointer to the slave platform data. | ||
| 491 | * @param data | ||
| 492 | * a buffer to store the data read. | ||
| 493 | * | ||
| 494 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 495 | */ | ||
| 496 | static int bma222_read(void *mlsl_handle, | ||
| 497 | struct ext_slave_descr *slave, | ||
| 498 | struct ext_slave_platform_data *pdata, | ||
| 499 | unsigned char *data) | ||
| 500 | { | ||
| 501 | int result = INV_SUCCESS; | ||
| 502 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 503 | BMA222_STATUS_REG, 1, data); | ||
| 504 | if (data[0] & BMA222_STATUS_RDY_MASK) { | ||
| 505 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 506 | slave->read_reg, slave->read_len, data); | ||
| 507 | return result; | ||
| 508 | } else | ||
| 509 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 510 | } | ||
| 511 | |||
| 512 | static struct ext_slave_descr bma222_descr = { | ||
| 513 | .init = bma222_init, | ||
| 514 | .exit = bma222_exit, | ||
| 515 | .suspend = bma222_suspend, | ||
| 516 | .resume = bma222_resume, | ||
| 517 | .read = bma222_read, | ||
| 518 | .config = bma222_config, | ||
| 519 | .get_config = bma222_get_config, | ||
| 520 | .name = "bma222", | ||
| 521 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 522 | .id = ACCEL_ID_BMA222, | ||
| 523 | .read_reg = 0x02, | ||
| 524 | .read_len = 6, | ||
| 525 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 526 | .range = {2, 0}, | ||
| 527 | .trigger = NULL, | ||
| 528 | }; | ||
| 529 | |||
| 530 | static | ||
| 531 | struct ext_slave_descr *bma222_get_slave_descr(void) | ||
| 532 | { | ||
| 533 | return &bma222_descr; | ||
| 534 | } | ||
| 535 | |||
| 536 | /* -------------------------------------------------------------------------- */ | ||
| 537 | |||
| 538 | struct bma222_mod_private_data { | ||
| 539 | struct i2c_client *client; | ||
| 540 | struct ext_slave_platform_data *pdata; | ||
| 541 | }; | ||
| 542 | |||
| 543 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 544 | |||
| 545 | static int bma222_mod_probe(struct i2c_client *client, | ||
| 546 | const struct i2c_device_id *devid) | ||
| 547 | { | ||
| 548 | struct ext_slave_platform_data *pdata; | ||
| 549 | struct bma222_mod_private_data *private_data; | ||
| 550 | int result = 0; | ||
| 551 | |||
| 552 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 553 | |||
| 554 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 555 | result = -ENODEV; | ||
| 556 | goto out_no_free; | ||
| 557 | } | ||
| 558 | |||
| 559 | pdata = client->dev.platform_data; | ||
| 560 | if (!pdata) { | ||
| 561 | dev_err(&client->adapter->dev, | ||
| 562 | "Missing platform data for slave %s\n", devid->name); | ||
| 563 | result = -EFAULT; | ||
| 564 | goto out_no_free; | ||
| 565 | } | ||
| 566 | |||
| 567 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 568 | if (!private_data) { | ||
| 569 | result = -ENOMEM; | ||
| 570 | goto out_no_free; | ||
| 571 | } | ||
| 572 | |||
| 573 | i2c_set_clientdata(client, private_data); | ||
| 574 | private_data->client = client; | ||
| 575 | private_data->pdata = pdata; | ||
| 576 | |||
| 577 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 578 | bma222_get_slave_descr); | ||
| 579 | if (result) { | ||
| 580 | dev_err(&client->adapter->dev, | ||
| 581 | "Slave registration failed: %s, %d\n", | ||
| 582 | devid->name, result); | ||
| 583 | goto out_free_memory; | ||
| 584 | } | ||
| 585 | |||
| 586 | return result; | ||
| 587 | |||
| 588 | out_free_memory: | ||
| 589 | kfree(private_data); | ||
| 590 | out_no_free: | ||
| 591 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 592 | return result; | ||
| 593 | |||
| 594 | } | ||
| 595 | |||
| 596 | static int bma222_mod_remove(struct i2c_client *client) | ||
| 597 | { | ||
| 598 | struct bma222_mod_private_data *private_data = | ||
| 599 | i2c_get_clientdata(client); | ||
| 600 | |||
| 601 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 602 | |||
| 603 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 604 | bma222_get_slave_descr); | ||
| 605 | |||
| 606 | kfree(private_data); | ||
| 607 | return 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | static const struct i2c_device_id bma222_mod_id[] = { | ||
| 611 | { "bma222", ACCEL_ID_BMA222 }, | ||
| 612 | {} | ||
| 613 | }; | ||
| 614 | |||
| 615 | MODULE_DEVICE_TABLE(i2c, bma222_mod_id); | ||
| 616 | |||
| 617 | static struct i2c_driver bma222_mod_driver = { | ||
| 618 | .class = I2C_CLASS_HWMON, | ||
| 619 | .probe = bma222_mod_probe, | ||
| 620 | .remove = bma222_mod_remove, | ||
| 621 | .id_table = bma222_mod_id, | ||
| 622 | .driver = { | ||
| 623 | .owner = THIS_MODULE, | ||
| 624 | .name = "bma222_mod", | ||
| 625 | }, | ||
| 626 | .address_list = normal_i2c, | ||
| 627 | }; | ||
| 628 | |||
| 629 | static int __init bma222_mod_init(void) | ||
| 630 | { | ||
| 631 | int res = i2c_add_driver(&bma222_mod_driver); | ||
| 632 | pr_info("%s: Probe name %s\n", __func__, "bma222_mod"); | ||
| 633 | if (res) | ||
| 634 | pr_err("%s failed\n", __func__); | ||
| 635 | return res; | ||
| 636 | } | ||
| 637 | |||
| 638 | static void __exit bma222_mod_exit(void) | ||
| 639 | { | ||
| 640 | pr_info("%s\n", __func__); | ||
| 641 | i2c_del_driver(&bma222_mod_driver); | ||
| 642 | } | ||
| 643 | |||
| 644 | module_init(bma222_mod_init); | ||
| 645 | module_exit(bma222_mod_exit); | ||
| 646 | |||
| 647 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 648 | MODULE_DESCRIPTION("Driver to integrate BMA222 sensor with the MPU"); | ||
| 649 | MODULE_LICENSE("GPL"); | ||
| 650 | MODULE_ALIAS("bma222_mod"); | ||
| 651 | |||
| 652 | /** | ||
| 653 | * @} | ||
| 654 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/bma250.c b/drivers/misc/inv_mpu/accel/bma250.c new file mode 100644 index 00000000000..6a245f4566a --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma250.c | |||
| @@ -0,0 +1,787 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file bma250.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Bosch BMA250. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <linux/mpu.h> | ||
| 41 | #include "mlsl.h" | ||
| 42 | #include "mldl_cfg.h" | ||
| 43 | |||
| 44 | /* -------------------------------------------------------------------------- */ | ||
| 45 | |||
| 46 | /* registers */ | ||
| 47 | #define BMA250_STATUS_REG (0x0A) | ||
| 48 | #define BMA250_FSR_REG (0x0F) | ||
| 49 | #define BMA250_ODR_REG (0x10) | ||
| 50 | #define BMA250_PWR_REG (0x11) | ||
| 51 | #define BMA250_SOFTRESET_REG (0x14) | ||
| 52 | #define BMA250_INT_TYPE_REG (0x17) | ||
| 53 | #define BMA250_INT_DST_REG (0x1A) | ||
| 54 | #define BMA250_INT_SRC_REG (0x1E) | ||
| 55 | |||
| 56 | /* masks */ | ||
| 57 | #define BMA250_STATUS_RDY_MASK (0x80) | ||
| 58 | #define BMA250_FSR_MASK (0x0F) | ||
| 59 | #define BMA250_ODR_MASK (0x1F) | ||
| 60 | #define BMA250_PWR_SLEEP_MASK (0x80) | ||
| 61 | #define BMA250_PWR_AWAKE_MASK (0x00) | ||
| 62 | #define BMA250_SOFTRESET_MASK (0xB6) | ||
| 63 | #define BMA250_INT_TYPE_MASK (0x10) | ||
| 64 | #define BMA250_INT_DST_1_MASK (0x01) | ||
| 65 | #define BMA250_INT_DST_2_MASK (0x80) | ||
| 66 | #define BMA250_INT_SRC_MASK (0x00) | ||
| 67 | |||
| 68 | /* -------------------------------------------------------------------------- */ | ||
| 69 | |||
| 70 | struct bma250_config { | ||
| 71 | unsigned int odr; /** < output data rate in mHz */ | ||
| 72 | unsigned int fsr; /** < full scale range mg */ | ||
| 73 | unsigned char irq_type; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct bma250_private_data { | ||
| 77 | struct bma250_config suspend; /** < suspend configuration */ | ||
| 78 | struct bma250_config resume; /** < resume configuration */ | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* -------------------------------------------------------------------------- */ | ||
| 82 | /** | ||
| 83 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
| 84 | * Threshold and duration will not be used unless the type is MOT or | ||
| 85 | * NMOT. | ||
| 86 | * | ||
| 87 | * @param mlsl_handle | ||
| 88 | * the handle to the serial channel the device is connected to. | ||
| 89 | * @param pdata | ||
| 90 | * a pointer to the slave platform data. | ||
| 91 | * @param config | ||
| 92 | * configuration to apply to, suspend or resume | ||
| 93 | * @param apply | ||
| 94 | * whether to apply immediately or save the settings to be applied | ||
| 95 | * at the next resume. | ||
| 96 | * @param irq_type | ||
| 97 | * the type of IRQ. Valid values are | ||
| 98 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 99 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 100 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 101 | * | ||
| 102 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 103 | */ | ||
| 104 | static int bma250_set_irq(void *mlsl_handle, | ||
| 105 | struct ext_slave_platform_data *pdata, | ||
| 106 | struct bma250_config *config, | ||
| 107 | int apply, long irq_type) | ||
| 108 | { | ||
| 109 | int result = INV_SUCCESS; | ||
| 110 | unsigned char irqtype_reg; | ||
| 111 | unsigned char irqdst_reg; | ||
| 112 | unsigned char irqsrc_reg; | ||
| 113 | |||
| 114 | switch (irq_type) { | ||
| 115 | case MPU_SLAVE_IRQ_TYPE_DATA_READY: | ||
| 116 | /* data ready int. */ | ||
| 117 | irqtype_reg = BMA250_INT_TYPE_MASK; | ||
| 118 | /* routed to interrupt pin 1 */ | ||
| 119 | irqdst_reg = BMA250_INT_DST_1_MASK; | ||
| 120 | /* from filtered data */ | ||
| 121 | irqsrc_reg = BMA250_INT_SRC_MASK; | ||
| 122 | break; | ||
| 123 | /* unfinished | ||
| 124 | case MPU_SLAVE_IRQ_TYPE_MOTION: | ||
| 125 | reg1 = 0x00; | ||
| 126 | reg2 = config->mot_int1_cfg; | ||
| 127 | reg3 = ; | ||
| 128 | break; | ||
| 129 | */ | ||
| 130 | case MPU_SLAVE_IRQ_TYPE_NONE: | ||
| 131 | irqtype_reg = 0x00; | ||
| 132 | irqdst_reg = 0x00; | ||
| 133 | irqsrc_reg = 0x00; | ||
| 134 | break; | ||
| 135 | default: | ||
| 136 | return INV_ERROR_INVALID_PARAMETER; | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | |||
| 140 | config->irq_type = (unsigned char)irq_type; | ||
| 141 | |||
| 142 | if (apply) { | ||
| 143 | /* select the type of interrupt to use */ | ||
| 144 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 145 | BMA250_INT_TYPE_REG, irqtype_reg); | ||
| 146 | if (result) { | ||
| 147 | LOG_RESULT_LOCATION(result); | ||
| 148 | return result; | ||
| 149 | } | ||
| 150 | /* select to which interrupt pin to route it to */ | ||
| 151 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 152 | BMA250_INT_DST_REG, irqdst_reg); | ||
| 153 | if (result) { | ||
| 154 | LOG_RESULT_LOCATION(result); | ||
| 155 | return result; | ||
| 156 | } | ||
| 157 | /* select whether the interrupt works off filtered or | ||
| 158 | unfiltered data */ | ||
| 159 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 160 | BMA250_INT_SRC_REG, irqsrc_reg); | ||
| 161 | if (result) { | ||
| 162 | LOG_RESULT_LOCATION(result); | ||
| 163 | return result; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return result; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * @brief Set the output data rate for the particular configuration. | ||
| 171 | * | ||
| 172 | * @param mlsl_handle | ||
| 173 | * the handle to the serial channel the device is connected to. | ||
| 174 | * @param pdata | ||
| 175 | * a pointer to the slave platform data. | ||
| 176 | * @param config | ||
| 177 | * Config to modify with new ODR. | ||
| 178 | * @param apply | ||
| 179 | * whether to apply immediately or save the settings to be applied | ||
| 180 | * at the next resume. | ||
| 181 | * @param odr | ||
| 182 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 183 | * | ||
| 184 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 185 | */ | ||
| 186 | static int bma250_set_odr(void *mlsl_handle, | ||
| 187 | struct ext_slave_platform_data *pdata, | ||
| 188 | struct bma250_config *config, | ||
| 189 | int apply, | ||
| 190 | long odr) | ||
| 191 | { | ||
| 192 | int result = INV_SUCCESS; | ||
| 193 | unsigned char reg_odr; | ||
| 194 | |||
| 195 | /* Table uses bandwidth which is half the sample rate */ | ||
| 196 | odr = odr >> 1; | ||
| 197 | if (odr >= 1000000) { | ||
| 198 | reg_odr = 0x0F; | ||
| 199 | config->odr = 2000000; | ||
| 200 | } else if (odr >= 500000) { | ||
| 201 | reg_odr = 0x0E; | ||
| 202 | config->odr = 1000000; | ||
| 203 | } else if (odr >= 250000) { | ||
| 204 | reg_odr = 0x0D; | ||
| 205 | config->odr = 500000; | ||
| 206 | } else if (odr >= 125000) { | ||
| 207 | reg_odr = 0x0C; | ||
| 208 | config->odr = 250000; | ||
| 209 | } else if (odr >= 62500) { | ||
| 210 | reg_odr = 0x0B; | ||
| 211 | config->odr = 125000; | ||
| 212 | } else if (odr >= 31250) { | ||
| 213 | reg_odr = 0x0A; | ||
| 214 | config->odr = 62500; | ||
| 215 | } else if (odr >= 15630) { | ||
| 216 | reg_odr = 0x09; | ||
| 217 | config->odr = 31250; | ||
| 218 | } else { | ||
| 219 | reg_odr = 0x08; | ||
| 220 | config->odr = 15630; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (apply) { | ||
| 224 | MPL_LOGV("ODR: %d\n", config->odr); | ||
| 225 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 226 | BMA250_ODR_REG, reg_odr); | ||
| 227 | if (result) { | ||
| 228 | LOG_RESULT_LOCATION(result); | ||
| 229 | return result; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | return result; | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * @brief Set the full scale range of the accels | ||
| 238 | * | ||
| 239 | * @param mlsl_handle | ||
| 240 | * the handle to the serial channel the device is connected to. | ||
| 241 | * @param pdata | ||
| 242 | * a pointer to the slave platform data. | ||
| 243 | * @param config | ||
| 244 | * pointer to configuration. | ||
| 245 | * @param apply | ||
| 246 | * whether to apply immediately or save the settings to be applied | ||
| 247 | * at the next resume. | ||
| 248 | * @param fsr | ||
| 249 | * requested full scale range. | ||
| 250 | * | ||
| 251 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 252 | */ | ||
| 253 | static int bma250_set_fsr(void *mlsl_handle, | ||
| 254 | struct ext_slave_platform_data *pdata, | ||
| 255 | struct bma250_config *config, | ||
| 256 | int apply, | ||
| 257 | long fsr) | ||
| 258 | { | ||
| 259 | int result = INV_SUCCESS; | ||
| 260 | unsigned char reg_fsr_mask; | ||
| 261 | |||
| 262 | if (fsr <= 2000) { | ||
| 263 | reg_fsr_mask = 0x03; | ||
| 264 | config->fsr = 2000; | ||
| 265 | } else if (fsr <= 4000) { | ||
| 266 | reg_fsr_mask = 0x05; | ||
| 267 | config->fsr = 4000; | ||
| 268 | } else if (fsr <= 8000) { | ||
| 269 | reg_fsr_mask = 0x08; | ||
| 270 | config->fsr = 8000; | ||
| 271 | } else { /* 8001 -> oo */ | ||
| 272 | reg_fsr_mask = 0x0C; | ||
| 273 | config->fsr = 16000; | ||
| 274 | } | ||
| 275 | |||
| 276 | if (apply) { | ||
| 277 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 278 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 279 | BMA250_FSR_REG, reg_fsr_mask); | ||
| 280 | if (result) { | ||
| 281 | LOG_RESULT_LOCATION(result); | ||
| 282 | return result; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | return result; | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * @brief one-time device driver initialization function. | ||
| 290 | * If the driver is built as a kernel module, this function will be | ||
| 291 | * called when the module is loaded in the kernel. | ||
| 292 | * If the driver is built-in in the kernel, this function will be | ||
| 293 | * called at boot time. | ||
| 294 | * | ||
| 295 | * @param mlsl_handle | ||
| 296 | * the handle to the serial channel the device is connected to. | ||
| 297 | * @param slave | ||
| 298 | * a pointer to the slave descriptor data structure. | ||
| 299 | * @param pdata | ||
| 300 | * a pointer to the slave platform data. | ||
| 301 | * | ||
| 302 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 303 | */ | ||
| 304 | static int bma250_init(void *mlsl_handle, | ||
| 305 | struct ext_slave_descr *slave, | ||
| 306 | struct ext_slave_platform_data *pdata) | ||
| 307 | { | ||
| 308 | int result; | ||
| 309 | long range; | ||
| 310 | |||
| 311 | struct bma250_private_data *private_data; | ||
| 312 | private_data = kzalloc(sizeof(struct bma250_private_data), GFP_KERNEL); | ||
| 313 | |||
| 314 | if (!private_data) | ||
| 315 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 316 | |||
| 317 | pdata->private_data = private_data; | ||
| 318 | |||
| 319 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 320 | BMA250_SOFTRESET_REG, BMA250_SOFTRESET_MASK); | ||
| 321 | if (result) { | ||
| 322 | LOG_RESULT_LOCATION(result); | ||
| 323 | return result; | ||
| 324 | } | ||
| 325 | msleep(1); | ||
| 326 | |||
| 327 | result = bma250_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 328 | false, 0); | ||
| 329 | if (result) { | ||
| 330 | LOG_RESULT_LOCATION(result); | ||
| 331 | return result; | ||
| 332 | } | ||
| 333 | result = bma250_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 334 | false, 200000); | ||
| 335 | if (result) { | ||
| 336 | LOG_RESULT_LOCATION(result); | ||
| 337 | return result; | ||
| 338 | } | ||
| 339 | |||
| 340 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 341 | result = bma250_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 342 | false, range); | ||
| 343 | result = bma250_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 344 | false, range); | ||
| 345 | if (result) { | ||
| 346 | LOG_RESULT_LOCATION(result); | ||
| 347 | return result; | ||
| 348 | } | ||
| 349 | |||
| 350 | result = bma250_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 351 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 352 | if (result) { | ||
| 353 | LOG_RESULT_LOCATION(result); | ||
| 354 | return result; | ||
| 355 | } | ||
| 356 | result = bma250_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 357 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 358 | if (result) { | ||
| 359 | LOG_RESULT_LOCATION(result); | ||
| 360 | return result; | ||
| 361 | } | ||
| 362 | |||
| 363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 364 | BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK); | ||
| 365 | if (result) { | ||
| 366 | LOG_RESULT_LOCATION(result); | ||
| 367 | return result; | ||
| 368 | } | ||
| 369 | |||
| 370 | return result; | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * @brief one-time device driver exit function. | ||
| 375 | * If the driver is built as a kernel module, this function will be | ||
| 376 | * called when the module is removed from the kernel. | ||
| 377 | * | ||
| 378 | * @param mlsl_handle | ||
| 379 | * the handle to the serial channel the device is connected to. | ||
| 380 | * @param slave | ||
| 381 | * a pointer to the slave descriptor data structure. | ||
| 382 | * @param pdata | ||
| 383 | * a pointer to the slave platform data. | ||
| 384 | * | ||
| 385 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 386 | */ | ||
| 387 | static int bma250_exit(void *mlsl_handle, | ||
| 388 | struct ext_slave_descr *slave, | ||
| 389 | struct ext_slave_platform_data *pdata) | ||
| 390 | { | ||
| 391 | kfree(pdata->private_data); | ||
| 392 | return INV_SUCCESS; | ||
| 393 | } | ||
| 394 | |||
| 395 | /** | ||
| 396 | * @brief device configuration facility. | ||
| 397 | * | ||
| 398 | * @param mlsl_handle | ||
| 399 | * the handle to the serial channel the device is connected to. | ||
| 400 | * @param slave | ||
| 401 | * a pointer to the slave descriptor data structure. | ||
| 402 | * @param pdata | ||
| 403 | * a pointer to the slave platform data. | ||
| 404 | * @param data | ||
| 405 | * a pointer to the configuration data structure. | ||
| 406 | * | ||
| 407 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 408 | */ | ||
| 409 | static int bma250_config(void *mlsl_handle, | ||
| 410 | struct ext_slave_descr *slave, | ||
| 411 | struct ext_slave_platform_data *pdata, | ||
| 412 | struct ext_slave_config *data) | ||
| 413 | { | ||
| 414 | struct bma250_private_data *private_data = | ||
| 415 | (struct bma250_private_data *)(pdata->private_data); | ||
| 416 | |||
| 417 | if (!data->data) | ||
| 418 | return INV_ERROR_INVALID_PARAMETER; | ||
| 419 | |||
| 420 | switch (data->key) { | ||
| 421 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 422 | return bma250_set_odr(mlsl_handle, pdata, | ||
| 423 | &private_data->suspend, | ||
| 424 | data->apply, | ||
| 425 | *((long *)data->data)); | ||
| 426 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 427 | return bma250_set_odr(mlsl_handle, pdata, | ||
| 428 | &private_data->resume, | ||
| 429 | data->apply, | ||
| 430 | *((long *)data->data)); | ||
| 431 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 432 | return bma250_set_fsr(mlsl_handle, pdata, | ||
| 433 | &private_data->suspend, | ||
| 434 | data->apply, | ||
| 435 | *((long *)data->data)); | ||
| 436 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 437 | return bma250_set_fsr(mlsl_handle, pdata, | ||
| 438 | &private_data->resume, | ||
| 439 | data->apply, | ||
| 440 | *((long *)data->data)); | ||
| 441 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 442 | return bma250_set_irq(mlsl_handle, pdata, | ||
| 443 | &private_data->suspend, | ||
| 444 | data->apply, | ||
| 445 | *((long *)data->data)); | ||
| 446 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 447 | return bma250_set_irq(mlsl_handle, pdata, | ||
| 448 | &private_data->resume, | ||
| 449 | data->apply, | ||
| 450 | *((long *)data->data)); | ||
| 451 | default: | ||
| 452 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 453 | }; | ||
| 454 | return INV_SUCCESS; | ||
| 455 | } | ||
| 456 | |||
| 457 | /** | ||
| 458 | * @brief facility to retrieve the device configuration. | ||
| 459 | * | ||
| 460 | * @param mlsl_handle | ||
| 461 | * the handle to the serial channel the device is connected to. | ||
| 462 | * @param slave | ||
| 463 | * a pointer to the slave descriptor data structure. | ||
| 464 | * @param pdata | ||
| 465 | * a pointer to the slave platform data. | ||
| 466 | * @param data | ||
| 467 | * a pointer to store the returned configuration data structure. | ||
| 468 | * | ||
| 469 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 470 | */ | ||
| 471 | static int bma250_get_config(void *mlsl_handle, | ||
| 472 | struct ext_slave_descr *slave, | ||
| 473 | struct ext_slave_platform_data *pdata, | ||
| 474 | struct ext_slave_config *data) | ||
| 475 | { | ||
| 476 | struct bma250_private_data *private_data = | ||
| 477 | (struct bma250_private_data *)(pdata->private_data); | ||
| 478 | |||
| 479 | if (!data->data) | ||
| 480 | return INV_ERROR_INVALID_PARAMETER; | ||
| 481 | |||
| 482 | switch (data->key) { | ||
| 483 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 484 | (*(unsigned long *)data->data) = | ||
| 485 | (unsigned long) private_data->suspend.odr; | ||
| 486 | break; | ||
| 487 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 488 | (*(unsigned long *)data->data) = | ||
| 489 | (unsigned long) private_data->resume.odr; | ||
| 490 | break; | ||
| 491 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 492 | (*(unsigned long *)data->data) = | ||
| 493 | (unsigned long) private_data->suspend.fsr; | ||
| 494 | break; | ||
| 495 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 496 | (*(unsigned long *)data->data) = | ||
| 497 | (unsigned long) private_data->resume.fsr; | ||
| 498 | break; | ||
| 499 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 500 | (*(unsigned long *)data->data) = | ||
| 501 | (unsigned long) private_data->suspend.irq_type; | ||
| 502 | break; | ||
| 503 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 504 | (*(unsigned long *)data->data) = | ||
| 505 | (unsigned long) private_data->resume.irq_type; | ||
| 506 | break; | ||
| 507 | default: | ||
| 508 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 509 | }; | ||
| 510 | |||
| 511 | return INV_SUCCESS; | ||
| 512 | } | ||
| 513 | |||
| 514 | /** | ||
| 515 | * @brief suspends the device to put it in its lowest power mode. | ||
| 516 | * | ||
| 517 | * @param mlsl_handle | ||
| 518 | * the handle to the serial channel the device is connected to. | ||
| 519 | * @param slave | ||
| 520 | * a pointer to the slave descriptor data structure. | ||
| 521 | * @param pdata | ||
| 522 | * a pointer to the slave platform data. | ||
| 523 | * | ||
| 524 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 525 | */ | ||
| 526 | static int bma250_suspend(void *mlsl_handle, | ||
| 527 | struct ext_slave_descr *slave, | ||
| 528 | struct ext_slave_platform_data *pdata) | ||
| 529 | { | ||
| 530 | int result; | ||
| 531 | struct bma250_config *suspend_config = | ||
| 532 | &((struct bma250_private_data *)pdata->private_data)->suspend; | ||
| 533 | |||
| 534 | result = bma250_set_odr(mlsl_handle, pdata, suspend_config, | ||
| 535 | true, suspend_config->odr); | ||
| 536 | if (result) { | ||
| 537 | LOG_RESULT_LOCATION(result); | ||
| 538 | return result; | ||
| 539 | } | ||
| 540 | result = bma250_set_fsr(mlsl_handle, pdata, suspend_config, | ||
| 541 | true, suspend_config->fsr); | ||
| 542 | if (result) { | ||
| 543 | LOG_RESULT_LOCATION(result); | ||
| 544 | return result; | ||
| 545 | } | ||
| 546 | result = bma250_set_irq(mlsl_handle, pdata, suspend_config, | ||
| 547 | true, suspend_config->irq_type); | ||
| 548 | if (result) { | ||
| 549 | LOG_RESULT_LOCATION(result); | ||
| 550 | return result; | ||
| 551 | } | ||
| 552 | |||
| 553 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 554 | BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK); | ||
| 555 | if (result) { | ||
| 556 | LOG_RESULT_LOCATION(result); | ||
| 557 | return result; | ||
| 558 | } | ||
| 559 | |||
| 560 | msleep(3); /* 3 ms powerup time maximum */ | ||
| 561 | return result; | ||
| 562 | } | ||
| 563 | |||
| 564 | /** | ||
| 565 | * @brief resume the device in the proper power state given the configuration | ||
| 566 | * chosen. | ||
| 567 | * | ||
| 568 | * @param mlsl_handle | ||
| 569 | * the handle to the serial channel the device is connected to. | ||
| 570 | * @param slave | ||
| 571 | * a pointer to the slave descriptor data structure. | ||
| 572 | * @param pdata | ||
| 573 | * a pointer to the slave platform data. | ||
| 574 | * | ||
| 575 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 576 | */ | ||
| 577 | static int bma250_resume(void *mlsl_handle, | ||
| 578 | struct ext_slave_descr *slave, | ||
| 579 | struct ext_slave_platform_data *pdata) | ||
| 580 | { | ||
| 581 | int result; | ||
| 582 | struct bma250_config *resume_config = | ||
| 583 | &((struct bma250_private_data *)pdata->private_data)->resume; | ||
| 584 | |||
| 585 | result = bma250_set_odr(mlsl_handle, pdata, resume_config, | ||
| 586 | true, resume_config->odr); | ||
| 587 | if (result) { | ||
| 588 | LOG_RESULT_LOCATION(result); | ||
| 589 | return result; | ||
| 590 | } | ||
| 591 | result = bma250_set_fsr(mlsl_handle, pdata, resume_config, | ||
| 592 | true, resume_config->fsr); | ||
| 593 | if (result) { | ||
| 594 | LOG_RESULT_LOCATION(result); | ||
| 595 | return result; | ||
| 596 | } | ||
| 597 | result = bma250_set_irq(mlsl_handle, pdata, resume_config, | ||
| 598 | true, resume_config->irq_type); | ||
| 599 | if (result) { | ||
| 600 | LOG_RESULT_LOCATION(result); | ||
| 601 | return result; | ||
| 602 | } | ||
| 603 | |||
| 604 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 605 | BMA250_PWR_REG, BMA250_PWR_AWAKE_MASK); | ||
| 606 | if (result) { | ||
| 607 | LOG_RESULT_LOCATION(result); | ||
| 608 | return result; | ||
| 609 | } | ||
| 610 | |||
| 611 | return result; | ||
| 612 | } | ||
| 613 | |||
| 614 | /** | ||
| 615 | * @brief read the sensor data from the device. | ||
| 616 | * | ||
| 617 | * @param mlsl_handle | ||
| 618 | * the handle to the serial channel the device is connected to. | ||
| 619 | * @param slave | ||
| 620 | * a pointer to the slave descriptor data structure. | ||
| 621 | * @param pdata | ||
| 622 | * a pointer to the slave platform data. | ||
| 623 | * @param data | ||
| 624 | * a buffer to store the data read. | ||
| 625 | * | ||
| 626 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 627 | */ | ||
| 628 | static int bma250_read(void *mlsl_handle, | ||
| 629 | struct ext_slave_descr *slave, | ||
| 630 | struct ext_slave_platform_data *pdata, | ||
| 631 | unsigned char *data) | ||
| 632 | { | ||
| 633 | int result = INV_SUCCESS; | ||
| 634 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 635 | BMA250_STATUS_REG, 1, data); | ||
| 636 | if (1) { /* KLP - workaroud for small data ready window */ | ||
| 637 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 638 | slave->read_reg, slave->read_len, data); | ||
| 639 | return result; | ||
| 640 | } else | ||
| 641 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 642 | } | ||
| 643 | |||
| 644 | static struct ext_slave_descr bma250_descr = { | ||
| 645 | .init = bma250_init, | ||
| 646 | .exit = bma250_exit, | ||
| 647 | .suspend = bma250_suspend, | ||
| 648 | .resume = bma250_resume, | ||
| 649 | .read = bma250_read, | ||
| 650 | .config = bma250_config, | ||
| 651 | .get_config = bma250_get_config, | ||
| 652 | .name = "bma250", | ||
| 653 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 654 | .id = ACCEL_ID_BMA250, | ||
| 655 | .read_reg = 0x02, | ||
| 656 | .read_len = 6, | ||
| 657 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 658 | .range = {2, 0}, | ||
| 659 | .trigger = NULL, | ||
| 660 | }; | ||
| 661 | |||
| 662 | static | ||
| 663 | struct ext_slave_descr *bma250_get_slave_descr(void) | ||
| 664 | { | ||
| 665 | return &bma250_descr; | ||
| 666 | } | ||
| 667 | |||
| 668 | /* -------------------------------------------------------------------------- */ | ||
| 669 | |||
| 670 | /* Platform data for the MPU */ | ||
| 671 | struct bma250_mod_private_data { | ||
| 672 | struct i2c_client *client; | ||
| 673 | struct ext_slave_platform_data *pdata; | ||
| 674 | }; | ||
| 675 | |||
| 676 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 677 | |||
| 678 | static int bma250_mod_probe(struct i2c_client *client, | ||
| 679 | const struct i2c_device_id *devid) | ||
| 680 | { | ||
| 681 | struct ext_slave_platform_data *pdata; | ||
| 682 | struct bma250_mod_private_data *private_data; | ||
| 683 | int result = 0; | ||
| 684 | |||
| 685 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 686 | |||
| 687 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 688 | result = -ENODEV; | ||
| 689 | goto out_no_free; | ||
| 690 | } | ||
| 691 | |||
| 692 | pdata = client->dev.platform_data; | ||
| 693 | if (!pdata) { | ||
| 694 | dev_err(&client->adapter->dev, | ||
| 695 | "Missing platform data for slave %s\n", devid->name); | ||
| 696 | result = -EFAULT; | ||
| 697 | goto out_no_free; | ||
| 698 | } | ||
| 699 | |||
| 700 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 701 | if (!private_data) { | ||
| 702 | result = -ENOMEM; | ||
| 703 | goto out_no_free; | ||
| 704 | } | ||
| 705 | |||
| 706 | i2c_set_clientdata(client, private_data); | ||
| 707 | private_data->client = client; | ||
| 708 | private_data->pdata = pdata; | ||
| 709 | |||
| 710 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 711 | bma250_get_slave_descr); | ||
| 712 | if (result) { | ||
| 713 | dev_err(&client->adapter->dev, | ||
| 714 | "Slave registration failed: %s, %d\n", | ||
| 715 | devid->name, result); | ||
| 716 | goto out_free_memory; | ||
| 717 | } | ||
| 718 | |||
| 719 | return result; | ||
| 720 | |||
| 721 | out_free_memory: | ||
| 722 | kfree(private_data); | ||
| 723 | out_no_free: | ||
| 724 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 725 | return result; | ||
| 726 | |||
| 727 | } | ||
| 728 | |||
| 729 | static int bma250_mod_remove(struct i2c_client *client) | ||
| 730 | { | ||
| 731 | struct bma250_mod_private_data *private_data = | ||
| 732 | i2c_get_clientdata(client); | ||
| 733 | |||
| 734 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 735 | |||
| 736 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 737 | bma250_get_slave_descr); | ||
| 738 | |||
| 739 | kfree(private_data); | ||
| 740 | return 0; | ||
| 741 | } | ||
| 742 | |||
| 743 | static const struct i2c_device_id bma250_mod_id[] = { | ||
| 744 | { "bma250", ACCEL_ID_BMA250 }, | ||
| 745 | {} | ||
| 746 | }; | ||
| 747 | |||
| 748 | MODULE_DEVICE_TABLE(i2c, bma250_mod_id); | ||
| 749 | |||
| 750 | static struct i2c_driver bma250_mod_driver = { | ||
| 751 | .class = I2C_CLASS_HWMON, | ||
| 752 | .probe = bma250_mod_probe, | ||
| 753 | .remove = bma250_mod_remove, | ||
| 754 | .id_table = bma250_mod_id, | ||
| 755 | .driver = { | ||
| 756 | .owner = THIS_MODULE, | ||
| 757 | .name = "bma250_mod", | ||
| 758 | }, | ||
| 759 | .address_list = normal_i2c, | ||
| 760 | }; | ||
| 761 | |||
| 762 | static int __init bma250_mod_init(void) | ||
| 763 | { | ||
| 764 | int res = i2c_add_driver(&bma250_mod_driver); | ||
| 765 | pr_info("%s: Probe name %s\n", __func__, "bma250_mod"); | ||
| 766 | if (res) | ||
| 767 | pr_err("%s failed\n", __func__); | ||
| 768 | return res; | ||
| 769 | } | ||
| 770 | |||
| 771 | static void __exit bma250_mod_exit(void) | ||
| 772 | { | ||
| 773 | pr_info("%s\n", __func__); | ||
| 774 | i2c_del_driver(&bma250_mod_driver); | ||
| 775 | } | ||
| 776 | |||
| 777 | module_init(bma250_mod_init); | ||
| 778 | module_exit(bma250_mod_exit); | ||
| 779 | |||
| 780 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 781 | MODULE_DESCRIPTION("Driver to integrate BMA250 sensor with the MPU"); | ||
| 782 | MODULE_LICENSE("GPL"); | ||
| 783 | MODULE_ALIAS("bma250_mod"); | ||
| 784 | |||
| 785 | /** | ||
| 786 | * @} | ||
| 787 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/cma3000.c b/drivers/misc/inv_mpu/accel/cma3000.c new file mode 100644 index 00000000000..496d1f29bdc --- /dev/null +++ b/drivers/misc/inv_mpu/accel/cma3000.c | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Accelerometer setup and handling methods for VTI CMA3000. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file cma3000.c | ||
| 26 | * @brief Accelerometer setup and handling methods for VTI CMA3000 | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-acc" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | |||
| 49 | static int cma3000_suspend(void *mlsl_handle, | ||
| 50 | struct ext_slave_descr *slave, | ||
| 51 | struct ext_slave_platform_data *pdata) | ||
| 52 | { | ||
| 53 | int result; | ||
| 54 | /* RAM reset */ | ||
| 55 | result = | ||
| 56 | inv_serial_single_write(mlsl_handle, pdata->address, 0x1d, 0xcd); | ||
| 57 | return result; | ||
| 58 | } | ||
| 59 | |||
| 60 | static int cma3000_resume(void *mlsl_handle, | ||
| 61 | struct ext_slave_descr *slave, | ||
| 62 | struct ext_slave_platform_data *pdata) | ||
| 63 | { | ||
| 64 | |||
| 65 | return INV_SUCCESS; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int cma3000_read(void *mlsl_handle, | ||
| 69 | struct ext_slave_descr *slave, | ||
| 70 | struct ext_slave_platform_data *pdata, | ||
| 71 | unsigned char *data) | ||
| 72 | { | ||
| 73 | int result; | ||
| 74 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 75 | slave->reg, slave->len, data); | ||
| 76 | return result; | ||
| 77 | } | ||
| 78 | |||
| 79 | static struct ext_slave_descr cma3000_descr = { | ||
| 80 | .init = NULL, | ||
| 81 | .exit = NULL, | ||
| 82 | .suspend = cma3000_suspend, | ||
| 83 | .resume = cma3000_resume, | ||
| 84 | .read = cma3000_read, | ||
| 85 | .config = NULL, | ||
| 86 | .get_config = NULL, | ||
| 87 | .name = "cma3000", | ||
| 88 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 89 | .id = ID_INVALID, | ||
| 90 | .read_reg = 0x06, | ||
| 91 | .read_len = 6, | ||
| 92 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 93 | .range = {2, 0}, | ||
| 94 | .trigger = NULL, | ||
| 95 | |||
| 96 | }; | ||
| 97 | |||
| 98 | static | ||
| 99 | struct ext_slave_descr *cma3000_get_slave_descr(void) | ||
| 100 | { | ||
| 101 | return &cma3000_descr; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* -------------------------------------------------------------------------- */ | ||
| 105 | |||
| 106 | struct cma3000_mod_private_data { | ||
| 107 | struct i2c_client *client; | ||
| 108 | struct ext_slave_platform_data *pdata; | ||
| 109 | }; | ||
| 110 | |||
| 111 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 112 | |||
| 113 | static int cma3000_mod_probe(struct i2c_client *client, | ||
| 114 | const struct i2c_device_id *devid) | ||
| 115 | { | ||
| 116 | struct ext_slave_platform_data *pdata; | ||
| 117 | struct cma3000_mod_private_data *private_data; | ||
| 118 | int result = 0; | ||
| 119 | |||
| 120 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 121 | |||
| 122 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 123 | result = -ENODEV; | ||
| 124 | goto out_no_free; | ||
| 125 | } | ||
| 126 | |||
| 127 | pdata = client->dev.platform_data; | ||
| 128 | if (!pdata) { | ||
| 129 | dev_err(&client->adapter->dev, | ||
| 130 | "Missing platform data for slave %s\n", devid->name); | ||
| 131 | result = -EFAULT; | ||
| 132 | goto out_no_free; | ||
| 133 | } | ||
| 134 | |||
| 135 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 136 | if (!private_data) { | ||
| 137 | result = -ENOMEM; | ||
| 138 | goto out_no_free; | ||
| 139 | } | ||
| 140 | |||
| 141 | i2c_set_clientdata(client, private_data); | ||
| 142 | private_data->client = client; | ||
| 143 | private_data->pdata = pdata; | ||
| 144 | |||
| 145 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 146 | cma3000_get_slave_descr); | ||
| 147 | if (result) { | ||
| 148 | dev_err(&client->adapter->dev, | ||
| 149 | "Slave registration failed: %s, %d\n", | ||
| 150 | devid->name, result); | ||
| 151 | goto out_free_memory; | ||
| 152 | } | ||
| 153 | |||
| 154 | return result; | ||
| 155 | |||
| 156 | out_free_memory: | ||
| 157 | kfree(private_data); | ||
| 158 | out_no_free: | ||
| 159 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 160 | return result; | ||
| 161 | |||
| 162 | } | ||
| 163 | |||
| 164 | static int cma3000_mod_remove(struct i2c_client *client) | ||
| 165 | { | ||
| 166 | struct cma3000_mod_private_data *private_data = | ||
| 167 | i2c_get_clientdata(client); | ||
| 168 | |||
| 169 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 170 | |||
| 171 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 172 | cma3000_get_slave_descr); | ||
| 173 | |||
| 174 | kfree(private_data); | ||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | static const struct i2c_device_id cma3000_mod_id[] = { | ||
| 179 | { "cma3000", ACCEL_ID_CMA3000 }, | ||
| 180 | {} | ||
| 181 | }; | ||
| 182 | |||
| 183 | MODULE_DEVICE_TABLE(i2c, cma3000_mod_id); | ||
| 184 | |||
| 185 | static struct i2c_driver cma3000_mod_driver = { | ||
| 186 | .class = I2C_CLASS_HWMON, | ||
| 187 | .probe = cma3000_mod_probe, | ||
| 188 | .remove = cma3000_mod_remove, | ||
| 189 | .id_table = cma3000_mod_id, | ||
| 190 | .driver = { | ||
| 191 | .owner = THIS_MODULE, | ||
| 192 | .name = "cma3000_mod", | ||
| 193 | }, | ||
| 194 | .address_list = normal_i2c, | ||
| 195 | }; | ||
| 196 | |||
| 197 | static int __init cma3000_mod_init(void) | ||
| 198 | { | ||
| 199 | int res = i2c_add_driver(&cma3000_mod_driver); | ||
| 200 | pr_info("%s: Probe name %s\n", __func__, "cma3000_mod"); | ||
| 201 | if (res) | ||
| 202 | pr_err("%s failed\n", __func__); | ||
| 203 | return res; | ||
| 204 | } | ||
| 205 | |||
| 206 | static void __exit cma3000_mod_exit(void) | ||
| 207 | { | ||
| 208 | pr_info("%s\n", __func__); | ||
| 209 | i2c_del_driver(&cma3000_mod_driver); | ||
| 210 | } | ||
| 211 | |||
| 212 | module_init(cma3000_mod_init); | ||
| 213 | module_exit(cma3000_mod_exit); | ||
| 214 | |||
| 215 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 216 | MODULE_DESCRIPTION("Driver to integrate CMA3000 sensor with the MPU"); | ||
| 217 | MODULE_LICENSE("GPL"); | ||
| 218 | MODULE_ALIAS("cma3000_mod"); | ||
| 219 | |||
| 220 | /** | ||
| 221 | * @} | ||
| 222 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/kxsd9.c b/drivers/misc/inv_mpu/accel/kxsd9.c new file mode 100644 index 00000000000..5cb4eaf6b4a --- /dev/null +++ b/drivers/misc/inv_mpu/accel/kxsd9.c | |||
| @@ -0,0 +1,264 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Accelerometer setup and handling methods for Kionix KXSD9. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file kxsd9.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Kionix KXSD9. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-acc" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | |||
| 49 | static int kxsd9_suspend(void *mlsl_handle, | ||
| 50 | struct ext_slave_descr *slave, | ||
| 51 | struct ext_slave_platform_data *pdata) | ||
| 52 | { | ||
| 53 | int result; | ||
| 54 | /* CTRL_REGB: low-power standby mode */ | ||
| 55 | result = | ||
| 56 | inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x0); | ||
| 57 | if (result) { | ||
| 58 | LOG_RESULT_LOCATION(result); | ||
| 59 | return result; | ||
| 60 | } | ||
| 61 | return result; | ||
| 62 | } | ||
| 63 | |||
| 64 | /* full scale setting - register and mask */ | ||
| 65 | #define ACCEL_KIONIX_CTRL_REG (0x0C) | ||
| 66 | #define ACCEL_KIONIX_CTRL_MASK (0x3) | ||
| 67 | |||
| 68 | static int kxsd9_resume(void *mlsl_handle, | ||
| 69 | struct ext_slave_descr *slave, | ||
| 70 | struct ext_slave_platform_data *pdata) | ||
| 71 | { | ||
| 72 | int result = INV_SUCCESS; | ||
| 73 | unsigned char reg; | ||
| 74 | |||
| 75 | /* Full Scale */ | ||
| 76 | reg = 0x0; | ||
| 77 | reg &= ~ACCEL_KIONIX_CTRL_MASK; | ||
| 78 | reg |= 0x00; | ||
| 79 | if (slave->range.mantissa == 4) { /* 4g scale = 4.9951 */ | ||
| 80 | reg |= 0x2; | ||
| 81 | slave->range.fraction = 9951; | ||
| 82 | } else if (slave->range.mantissa == 7) { /* 6g scale = 7.5018 */ | ||
| 83 | reg |= 0x1; | ||
| 84 | slave->range.fraction = 5018; | ||
| 85 | } else if (slave->range.mantissa == 9) { /* 8g scale = 9.9902 */ | ||
| 86 | reg |= 0x0; | ||
| 87 | slave->range.fraction = 9902; | ||
| 88 | } else { | ||
| 89 | slave->range.mantissa = 2; /* 2g scale = 2.5006 */ | ||
| 90 | slave->range.fraction = 5006; | ||
| 91 | reg |= 0x3; | ||
| 92 | } | ||
| 93 | reg |= 0xC0; /* 100Hz LPF */ | ||
| 94 | result = | ||
| 95 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 96 | ACCEL_KIONIX_CTRL_REG, reg); | ||
| 97 | if (result) { | ||
| 98 | LOG_RESULT_LOCATION(result); | ||
| 99 | return result; | ||
| 100 | } | ||
| 101 | /* normal operation */ | ||
| 102 | result = | ||
| 103 | inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x40); | ||
| 104 | if (result) { | ||
| 105 | LOG_RESULT_LOCATION(result); | ||
| 106 | return result; | ||
| 107 | } | ||
| 108 | |||
| 109 | return INV_SUCCESS; | ||
| 110 | } | ||
| 111 | |||
| 112 | static int kxsd9_read(void *mlsl_handle, | ||
| 113 | struct ext_slave_descr *slave, | ||
| 114 | struct ext_slave_platform_data *pdata, | ||
| 115 | unsigned char *data) | ||
| 116 | { | ||
| 117 | int result; | ||
| 118 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 119 | slave->read_reg, slave->read_len, data); | ||
| 120 | return result; | ||
| 121 | } | ||
| 122 | |||
| 123 | static struct ext_slave_descr kxsd9_descr = { | ||
| 124 | .init = NULL, | ||
| 125 | .exit = NULL, | ||
| 126 | .suspend = kxsd9_suspend, | ||
| 127 | .resume = kxsd9_resume, | ||
| 128 | .read = kxsd9_read, | ||
| 129 | .config = NULL, | ||
| 130 | .get_config = NULL, | ||
| 131 | .name = "kxsd9", | ||
| 132 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 133 | .id = ACCEL_ID_KXSD9, | ||
| 134 | .read_reg = 0x00, | ||
| 135 | .read_len = 6, | ||
| 136 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 137 | .range = {2, 5006}, | ||
| 138 | .trigger = NULL, | ||
| 139 | }; | ||
| 140 | |||
| 141 | static | ||
| 142 | struct ext_slave_descr *kxsd9_get_slave_descr(void) | ||
| 143 | { | ||
| 144 | return &kxsd9_descr; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* -------------------------------------------------------------------------- */ | ||
| 148 | struct kxsd9_mod_private_data { | ||
| 149 | struct i2c_client *client; | ||
| 150 | struct ext_slave_platform_data *pdata; | ||
| 151 | }; | ||
| 152 | |||
| 153 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 154 | |||
| 155 | static int kxsd9_mod_probe(struct i2c_client *client, | ||
| 156 | const struct i2c_device_id *devid) | ||
| 157 | { | ||
| 158 | struct ext_slave_platform_data *pdata; | ||
| 159 | struct kxsd9_mod_private_data *private_data; | ||
| 160 | int result = 0; | ||
| 161 | |||
| 162 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 163 | |||
| 164 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 165 | result = -ENODEV; | ||
| 166 | goto out_no_free; | ||
| 167 | } | ||
| 168 | |||
| 169 | pdata = client->dev.platform_data; | ||
| 170 | if (!pdata) { | ||
| 171 | dev_err(&client->adapter->dev, | ||
| 172 | "Missing platform data for slave %s\n", devid->name); | ||
| 173 | result = -EFAULT; | ||
| 174 | goto out_no_free; | ||
| 175 | } | ||
| 176 | |||
| 177 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 178 | if (!private_data) { | ||
| 179 | result = -ENOMEM; | ||
| 180 | goto out_no_free; | ||
| 181 | } | ||
| 182 | |||
| 183 | i2c_set_clientdata(client, private_data); | ||
| 184 | private_data->client = client; | ||
| 185 | private_data->pdata = pdata; | ||
| 186 | |||
| 187 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 188 | kxsd9_get_slave_descr); | ||
| 189 | if (result) { | ||
| 190 | dev_err(&client->adapter->dev, | ||
| 191 | "Slave registration failed: %s, %d\n", | ||
| 192 | devid->name, result); | ||
| 193 | goto out_free_memory; | ||
| 194 | } | ||
| 195 | |||
| 196 | return result; | ||
| 197 | |||
| 198 | out_free_memory: | ||
| 199 | kfree(private_data); | ||
| 200 | out_no_free: | ||
| 201 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 202 | return result; | ||
| 203 | |||
| 204 | } | ||
| 205 | |||
| 206 | static int kxsd9_mod_remove(struct i2c_client *client) | ||
| 207 | { | ||
| 208 | struct kxsd9_mod_private_data *private_data = | ||
| 209 | i2c_get_clientdata(client); | ||
| 210 | |||
| 211 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 212 | |||
| 213 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 214 | kxsd9_get_slave_descr); | ||
| 215 | |||
| 216 | kfree(private_data); | ||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | static const struct i2c_device_id kxsd9_mod_id[] = { | ||
| 221 | { "kxsd9", ACCEL_ID_KXSD9 }, | ||
| 222 | {} | ||
| 223 | }; | ||
| 224 | |||
| 225 | MODULE_DEVICE_TABLE(i2c, kxsd9_mod_id); | ||
| 226 | |||
| 227 | static struct i2c_driver kxsd9_mod_driver = { | ||
| 228 | .class = I2C_CLASS_HWMON, | ||
| 229 | .probe = kxsd9_mod_probe, | ||
| 230 | .remove = kxsd9_mod_remove, | ||
| 231 | .id_table = kxsd9_mod_id, | ||
| 232 | .driver = { | ||
| 233 | .owner = THIS_MODULE, | ||
| 234 | .name = "kxsd9_mod", | ||
| 235 | }, | ||
| 236 | .address_list = normal_i2c, | ||
| 237 | }; | ||
| 238 | |||
| 239 | static int __init kxsd9_mod_init(void) | ||
| 240 | { | ||
| 241 | int res = i2c_add_driver(&kxsd9_mod_driver); | ||
| 242 | pr_info("%s: Probe name %s\n", __func__, "kxsd9_mod"); | ||
| 243 | if (res) | ||
| 244 | pr_err("%s failed\n", __func__); | ||
| 245 | return res; | ||
| 246 | } | ||
| 247 | |||
| 248 | static void __exit kxsd9_mod_exit(void) | ||
| 249 | { | ||
| 250 | pr_info("%s\n", __func__); | ||
| 251 | i2c_del_driver(&kxsd9_mod_driver); | ||
| 252 | } | ||
| 253 | |||
| 254 | module_init(kxsd9_mod_init); | ||
| 255 | module_exit(kxsd9_mod_exit); | ||
| 256 | |||
| 257 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 258 | MODULE_DESCRIPTION("Driver to integrate KXSD9 sensor with the MPU"); | ||
| 259 | MODULE_LICENSE("GPL"); | ||
| 260 | MODULE_ALIAS("kxsd9_mod"); | ||
| 261 | |||
| 262 | /** | ||
| 263 | * @} | ||
| 264 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/kxtf9.c b/drivers/misc/inv_mpu/accel/kxtf9.c new file mode 100644 index 00000000000..80776f249c6 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/kxtf9.c | |||
| @@ -0,0 +1,841 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Accelerometer setup and handling methods for Kionix KXTF9. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file kxtf9.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Kionix KXTF9. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #undef MPL_LOG_NDEBUG | ||
| 32 | #define MPL_LOG_NDEBUG 1 | ||
| 33 | |||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/moduleparam.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/delay.h> | ||
| 41 | #include "mpu-dev.h" | ||
| 42 | |||
| 43 | #include <log.h> | ||
| 44 | #include <linux/mpu.h> | ||
| 45 | #include "mlsl.h" | ||
| 46 | #include "mldl_cfg.h" | ||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-acc" | ||
| 49 | |||
| 50 | #define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */ | ||
| 51 | #define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */ | ||
| 52 | #define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */ | ||
| 53 | #define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */ | ||
| 54 | #define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */ | ||
| 55 | #define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */ | ||
| 56 | #define KXTF9_XOUT_L (0x06) /* 0000 0110 */ | ||
| 57 | #define KXTF9_XOUT_H (0x07) /* 0000 0111 */ | ||
| 58 | #define KXTF9_YOUT_L (0x08) /* 0000 1000 */ | ||
| 59 | #define KXTF9_YOUT_H (0x09) /* 0000 1001 */ | ||
| 60 | #define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */ | ||
| 61 | #define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */ | ||
| 62 | #define KXTF9_ST_RESP (0x0C) /* 0000 1100 */ | ||
| 63 | #define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */ | ||
| 64 | #define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */ | ||
| 65 | #define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */ | ||
| 66 | #define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */ | ||
| 67 | #define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */ | ||
| 68 | #define KXTF9_STATUS_REG (0x18) /* 0001 1000 */ | ||
| 69 | #define KXTF9_INT_REL (0x1A) /* 0001 1010 */ | ||
| 70 | #define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */ | ||
| 71 | #define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */ | ||
| 72 | #define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */ | ||
| 73 | #define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ | ||
| 74 | #define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ | ||
| 75 | #define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */ | ||
| 76 | #define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */ | ||
| 77 | #define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */ | ||
| 78 | #define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */ | ||
| 79 | #define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */ | ||
| 80 | #define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */ | ||
| 81 | #define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */ | ||
| 82 | #define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ | ||
| 83 | #define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ | ||
| 84 | #define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ | ||
| 85 | #define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ | ||
| 86 | #define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */ | ||
| 87 | #define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */ | ||
| 88 | #define KXTF9_HYST_SET (0x5F) /* 0101 1111 */ | ||
| 89 | |||
| 90 | #define KXTF9_MAX_DUR (0xFF) | ||
| 91 | #define KXTF9_MAX_THS (0xFF) | ||
| 92 | #define KXTF9_THS_COUNTS_P_G (32) | ||
| 93 | |||
| 94 | /* -------------------------------------------------------------------------- */ | ||
| 95 | |||
| 96 | struct kxtf9_config { | ||
| 97 | unsigned long odr; /* Output data rate mHz */ | ||
| 98 | unsigned int fsr; /* full scale range mg */ | ||
| 99 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
| 100 | unsigned int dur; /* Motion no-motion duration ms */ | ||
| 101 | unsigned int irq_type; | ||
| 102 | unsigned char reg_ths; | ||
| 103 | unsigned char reg_dur; | ||
| 104 | unsigned char reg_odr; | ||
| 105 | unsigned char reg_int_cfg1; | ||
| 106 | unsigned char reg_int_cfg2; | ||
| 107 | unsigned char ctrl_reg1; | ||
| 108 | }; | ||
| 109 | |||
| 110 | struct kxtf9_private_data { | ||
| 111 | struct kxtf9_config suspend; | ||
| 112 | struct kxtf9_config resume; | ||
| 113 | }; | ||
| 114 | |||
| 115 | static int kxtf9_set_ths(void *mlsl_handle, | ||
| 116 | struct ext_slave_platform_data *pdata, | ||
| 117 | struct kxtf9_config *config, int apply, long ths) | ||
| 118 | { | ||
| 119 | int result = INV_SUCCESS; | ||
| 120 | if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS) | ||
| 121 | ths = (long)(KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G; | ||
| 122 | |||
| 123 | if (ths < 0) | ||
| 124 | ths = 0; | ||
| 125 | |||
| 126 | config->ths = ths; | ||
| 127 | config->reg_ths = (unsigned char) | ||
| 128 | ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000); | ||
| 129 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
| 130 | if (apply) | ||
| 131 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 132 | KXTF9_WUF_THRESH, | ||
| 133 | config->reg_ths); | ||
| 134 | return result; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int kxtf9_set_dur(void *mlsl_handle, | ||
| 138 | struct ext_slave_platform_data *pdata, | ||
| 139 | struct kxtf9_config *config, int apply, long dur) | ||
| 140 | { | ||
| 141 | int result = INV_SUCCESS; | ||
| 142 | long reg_dur = (dur * config->odr) / 1000000L; | ||
| 143 | config->dur = dur; | ||
| 144 | |||
| 145 | if (reg_dur > KXTF9_MAX_DUR) | ||
| 146 | reg_dur = KXTF9_MAX_DUR; | ||
| 147 | |||
| 148 | config->reg_dur = (unsigned char)reg_dur; | ||
| 149 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
| 150 | if (apply) | ||
| 151 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 152 | KXTF9_WUF_TIMER, | ||
| 153 | (unsigned char)reg_dur); | ||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
| 159 | * duration will not be used uless the type is MOT or NMOT. | ||
| 160 | * | ||
| 161 | * @param config configuration to apply to, suspend or resume | ||
| 162 | * @param irq_type The type of IRQ. Valid values are | ||
| 163 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 164 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 165 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 166 | */ | ||
| 167 | static int kxtf9_set_irq(void *mlsl_handle, | ||
| 168 | struct ext_slave_platform_data *pdata, | ||
| 169 | struct kxtf9_config *config, int apply, long irq_type) | ||
| 170 | { | ||
| 171 | int result = INV_SUCCESS; | ||
| 172 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 173 | |||
| 174 | config->irq_type = (unsigned char)irq_type; | ||
| 175 | config->ctrl_reg1 &= ~0x22; | ||
| 176 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 177 | config->ctrl_reg1 |= 0x20; | ||
| 178 | config->reg_int_cfg1 = 0x38; | ||
| 179 | config->reg_int_cfg2 = 0x00; | ||
| 180 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 181 | config->ctrl_reg1 |= 0x02; | ||
| 182 | if ((unsigned long)config == | ||
| 183 | (unsigned long)&private_data->suspend) | ||
| 184 | config->reg_int_cfg1 = 0x34; | ||
| 185 | else | ||
| 186 | config->reg_int_cfg1 = 0x24; | ||
| 187 | config->reg_int_cfg2 = 0xE0; | ||
| 188 | } else { | ||
| 189 | config->reg_int_cfg1 = 0x00; | ||
| 190 | config->reg_int_cfg2 = 0x00; | ||
| 191 | } | ||
| 192 | |||
| 193 | if (apply) { | ||
| 194 | /* Must clear bit 7 before writing new configuration */ | ||
| 195 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 196 | KXTF9_CTRL_REG1, 0x40); | ||
| 197 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 198 | KXTF9_INT_CTRL_REG1, | ||
| 199 | config->reg_int_cfg1); | ||
| 200 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 201 | KXTF9_INT_CTRL_REG2, | ||
| 202 | config->reg_int_cfg2); | ||
| 203 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 204 | KXTF9_CTRL_REG1, | ||
| 205 | config->ctrl_reg1); | ||
| 206 | } | ||
| 207 | MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n", | ||
| 208 | (unsigned long)config->ctrl_reg1, | ||
| 209 | (unsigned long)config->reg_int_cfg1, | ||
| 210 | (unsigned long)config->reg_int_cfg2); | ||
| 211 | |||
| 212 | return result; | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Set the Output data rate for the particular configuration | ||
| 217 | * | ||
| 218 | * @param config Config to modify with new ODR | ||
| 219 | * @param odr Output data rate in units of 1/1000Hz | ||
| 220 | */ | ||
| 221 | static int kxtf9_set_odr(void *mlsl_handle, | ||
| 222 | struct ext_slave_platform_data *pdata, | ||
| 223 | struct kxtf9_config *config, int apply, long odr) | ||
| 224 | { | ||
| 225 | unsigned char bits; | ||
| 226 | int result = INV_SUCCESS; | ||
| 227 | |||
| 228 | /* Data sheet says there is 12.5 hz, but that seems to produce a single | ||
| 229 | * correct data value, thus we remove it from the table */ | ||
| 230 | if (odr > 400000L) { | ||
| 231 | config->odr = 800000L; | ||
| 232 | bits = 0x06; | ||
| 233 | } else if (odr > 200000L) { | ||
| 234 | config->odr = 400000L; | ||
| 235 | bits = 0x05; | ||
| 236 | } else if (odr > 100000L) { | ||
| 237 | config->odr = 200000L; | ||
| 238 | bits = 0x04; | ||
| 239 | } else if (odr > 50000) { | ||
| 240 | config->odr = 100000L; | ||
| 241 | bits = 0x03; | ||
| 242 | } else if (odr > 25000) { | ||
| 243 | config->odr = 50000; | ||
| 244 | bits = 0x02; | ||
| 245 | } else if (odr != 0) { | ||
| 246 | config->odr = 25000; | ||
| 247 | bits = 0x01; | ||
| 248 | } else { | ||
| 249 | config->odr = 0; | ||
| 250 | bits = 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | if (odr != 0) | ||
| 254 | config->ctrl_reg1 |= 0x80; | ||
| 255 | else | ||
| 256 | config->ctrl_reg1 &= ~0x80; | ||
| 257 | |||
| 258 | config->reg_odr = bits; | ||
| 259 | kxtf9_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
| 260 | MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
| 261 | if (apply) { | ||
| 262 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 263 | KXTF9_DATA_CTRL_REG, | ||
| 264 | config->reg_odr); | ||
| 265 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 266 | KXTF9_CTRL_REG1, 0x40); | ||
| 267 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 268 | KXTF9_CTRL_REG1, | ||
| 269 | config->ctrl_reg1); | ||
| 270 | } | ||
| 271 | return result; | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Set the full scale range of the accels | ||
| 276 | * | ||
| 277 | * @param config pointer to configuration | ||
| 278 | * @param fsr requested full scale range | ||
| 279 | */ | ||
| 280 | static int kxtf9_set_fsr(void *mlsl_handle, | ||
| 281 | struct ext_slave_platform_data *pdata, | ||
| 282 | struct kxtf9_config *config, int apply, long fsr) | ||
| 283 | { | ||
| 284 | int result = INV_SUCCESS; | ||
| 285 | |||
| 286 | config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7); | ||
| 287 | if (fsr <= 2000) { | ||
| 288 | config->fsr = 2000; | ||
| 289 | config->ctrl_reg1 |= 0x00; | ||
| 290 | } else if (fsr <= 4000) { | ||
| 291 | config->fsr = 4000; | ||
| 292 | config->ctrl_reg1 |= 0x08; | ||
| 293 | } else { | ||
| 294 | config->fsr = 8000; | ||
| 295 | config->ctrl_reg1 |= 0x10; | ||
| 296 | } | ||
| 297 | |||
| 298 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 299 | if (apply) { | ||
| 300 | /* Must clear bit 7 before writing new configuration */ | ||
| 301 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 302 | KXTF9_CTRL_REG1, 0x40); | ||
| 303 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 304 | KXTF9_CTRL_REG1, | ||
| 305 | config->ctrl_reg1); | ||
| 306 | } | ||
| 307 | return result; | ||
| 308 | } | ||
| 309 | |||
| 310 | static int kxtf9_suspend(void *mlsl_handle, | ||
| 311 | struct ext_slave_descr *slave, | ||
| 312 | struct ext_slave_platform_data *pdata) | ||
| 313 | { | ||
| 314 | int result; | ||
| 315 | unsigned char data; | ||
| 316 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 317 | |||
| 318 | /* Wake up */ | ||
| 319 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 320 | KXTF9_CTRL_REG1, 0x40); | ||
| 321 | if (result) { | ||
| 322 | LOG_RESULT_LOCATION(result); | ||
| 323 | return result; | ||
| 324 | } | ||
| 325 | /* INT_CTRL_REG1: */ | ||
| 326 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 327 | KXTF9_INT_CTRL_REG1, | ||
| 328 | private_data->suspend.reg_int_cfg1); | ||
| 329 | if (result) { | ||
| 330 | LOG_RESULT_LOCATION(result); | ||
| 331 | return result; | ||
| 332 | } | ||
| 333 | /* WUF_THRESH: */ | ||
| 334 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 335 | KXTF9_WUF_THRESH, | ||
| 336 | private_data->suspend.reg_ths); | ||
| 337 | if (result) { | ||
| 338 | LOG_RESULT_LOCATION(result); | ||
| 339 | return result; | ||
| 340 | } | ||
| 341 | /* DATA_CTRL_REG */ | ||
| 342 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 343 | KXTF9_DATA_CTRL_REG, | ||
| 344 | private_data->suspend.reg_odr); | ||
| 345 | if (result) { | ||
| 346 | LOG_RESULT_LOCATION(result); | ||
| 347 | return result; | ||
| 348 | } | ||
| 349 | /* WUF_TIMER */ | ||
| 350 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 351 | KXTF9_WUF_TIMER, | ||
| 352 | private_data->suspend.reg_dur); | ||
| 353 | if (result) { | ||
| 354 | LOG_RESULT_LOCATION(result); | ||
| 355 | return result; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* Normal operation */ | ||
| 359 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 360 | KXTF9_CTRL_REG1, | ||
| 361 | private_data->suspend.ctrl_reg1); | ||
| 362 | if (result) { | ||
| 363 | LOG_RESULT_LOCATION(result); | ||
| 364 | return result; | ||
| 365 | } | ||
| 366 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 367 | KXTF9_INT_REL, 1, &data); | ||
| 368 | if (result) { | ||
| 369 | LOG_RESULT_LOCATION(result); | ||
| 370 | return result; | ||
| 371 | } | ||
| 372 | |||
| 373 | return result; | ||
| 374 | } | ||
| 375 | |||
| 376 | /* full scale setting - register and mask */ | ||
| 377 | #define ACCEL_KIONIX_CTRL_REG (0x1b) | ||
| 378 | #define ACCEL_KIONIX_CTRL_MASK (0x18) | ||
| 379 | |||
| 380 | static int kxtf9_resume(void *mlsl_handle, | ||
| 381 | struct ext_slave_descr *slave, | ||
| 382 | struct ext_slave_platform_data *pdata) | ||
| 383 | { | ||
| 384 | int result = INV_SUCCESS; | ||
| 385 | unsigned char data; | ||
| 386 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 387 | |||
| 388 | /* Wake up */ | ||
| 389 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 390 | KXTF9_CTRL_REG1, 0x40); | ||
| 391 | if (result) { | ||
| 392 | LOG_RESULT_LOCATION(result); | ||
| 393 | return result; | ||
| 394 | } | ||
| 395 | /* INT_CTRL_REG1: */ | ||
| 396 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 397 | KXTF9_INT_CTRL_REG1, | ||
| 398 | private_data->resume.reg_int_cfg1); | ||
| 399 | if (result) { | ||
| 400 | LOG_RESULT_LOCATION(result); | ||
| 401 | return result; | ||
| 402 | } | ||
| 403 | /* WUF_THRESH: */ | ||
| 404 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 405 | KXTF9_WUF_THRESH, | ||
| 406 | private_data->resume.reg_ths); | ||
| 407 | if (result) { | ||
| 408 | LOG_RESULT_LOCATION(result); | ||
| 409 | return result; | ||
| 410 | } | ||
| 411 | /* DATA_CTRL_REG */ | ||
| 412 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 413 | KXTF9_DATA_CTRL_REG, | ||
| 414 | private_data->resume.reg_odr); | ||
| 415 | if (result) { | ||
| 416 | LOG_RESULT_LOCATION(result); | ||
| 417 | return result; | ||
| 418 | } | ||
| 419 | /* WUF_TIMER */ | ||
| 420 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 421 | KXTF9_WUF_TIMER, | ||
| 422 | private_data->resume.reg_dur); | ||
| 423 | if (result) { | ||
| 424 | LOG_RESULT_LOCATION(result); | ||
| 425 | return result; | ||
| 426 | } | ||
| 427 | |||
| 428 | /* Normal operation */ | ||
| 429 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 430 | KXTF9_CTRL_REG1, | ||
| 431 | private_data->resume.ctrl_reg1); | ||
| 432 | if (result) { | ||
| 433 | LOG_RESULT_LOCATION(result); | ||
| 434 | return result; | ||
| 435 | } | ||
| 436 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 437 | KXTF9_INT_REL, 1, &data); | ||
| 438 | if (result) { | ||
| 439 | LOG_RESULT_LOCATION(result); | ||
| 440 | return result; | ||
| 441 | } | ||
| 442 | |||
| 443 | return INV_SUCCESS; | ||
| 444 | } | ||
| 445 | |||
| 446 | static int kxtf9_init(void *mlsl_handle, | ||
| 447 | struct ext_slave_descr *slave, | ||
| 448 | struct ext_slave_platform_data *pdata) | ||
| 449 | { | ||
| 450 | |||
| 451 | struct kxtf9_private_data *private_data; | ||
| 452 | int result = INV_SUCCESS; | ||
| 453 | |||
| 454 | private_data = (struct kxtf9_private_data *) | ||
| 455 | kzalloc(sizeof(struct kxtf9_private_data), GFP_KERNEL); | ||
| 456 | |||
| 457 | if (!private_data) | ||
| 458 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 459 | |||
| 460 | /* RAM reset */ | ||
| 461 | /* Fastest Reset */ | ||
| 462 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 463 | KXTF9_CTRL_REG1, 0x40); | ||
| 464 | if (result) { | ||
| 465 | LOG_RESULT_LOCATION(result); | ||
| 466 | return result; | ||
| 467 | } | ||
| 468 | /* Fastest Reset */ | ||
| 469 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 470 | KXTF9_DATA_CTRL_REG, 0x36); | ||
| 471 | if (result) { | ||
| 472 | LOG_RESULT_LOCATION(result); | ||
| 473 | return result; | ||
| 474 | } | ||
| 475 | /* Reset */ | ||
| 476 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 477 | KXTF9_CTRL_REG3, 0xcd); | ||
| 478 | if (result) { | ||
| 479 | LOG_RESULT_LOCATION(result); | ||
| 480 | return result; | ||
| 481 | } | ||
| 482 | msleep(2); | ||
| 483 | |||
| 484 | pdata->private_data = private_data; | ||
| 485 | |||
| 486 | private_data->resume.ctrl_reg1 = 0xC0; | ||
| 487 | private_data->suspend.ctrl_reg1 = 0x40; | ||
| 488 | |||
| 489 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 490 | false, 1000); | ||
| 491 | if (result) { | ||
| 492 | LOG_RESULT_LOCATION(result); | ||
| 493 | return result; | ||
| 494 | } | ||
| 495 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 496 | false, 2540); | ||
| 497 | if (result) { | ||
| 498 | LOG_RESULT_LOCATION(result); | ||
| 499 | return result; | ||
| 500 | } | ||
| 501 | |||
| 502 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 503 | false, 50000); | ||
| 504 | if (result) { | ||
| 505 | LOG_RESULT_LOCATION(result); | ||
| 506 | return result; | ||
| 507 | } | ||
| 508 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 509 | false, 200000L); | ||
| 510 | |||
| 511 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 512 | false, 2000); | ||
| 513 | if (result) { | ||
| 514 | LOG_RESULT_LOCATION(result); | ||
| 515 | return result; | ||
| 516 | } | ||
| 517 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 518 | false, 2000); | ||
| 519 | if (result) { | ||
| 520 | LOG_RESULT_LOCATION(result); | ||
| 521 | return result; | ||
| 522 | } | ||
| 523 | |||
| 524 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 525 | false, 80); | ||
| 526 | if (result) { | ||
| 527 | LOG_RESULT_LOCATION(result); | ||
| 528 | return result; | ||
| 529 | } | ||
| 530 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
| 531 | false, 40); | ||
| 532 | if (result) { | ||
| 533 | LOG_RESULT_LOCATION(result); | ||
| 534 | return result; | ||
| 535 | } | ||
| 536 | |||
| 537 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 538 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 539 | if (result) { | ||
| 540 | LOG_RESULT_LOCATION(result); | ||
| 541 | return result; | ||
| 542 | } | ||
| 543 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 544 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 545 | if (result) { | ||
| 546 | LOG_RESULT_LOCATION(result); | ||
| 547 | return result; | ||
| 548 | } | ||
| 549 | return result; | ||
| 550 | } | ||
| 551 | |||
| 552 | static int kxtf9_exit(void *mlsl_handle, | ||
| 553 | struct ext_slave_descr *slave, | ||
| 554 | struct ext_slave_platform_data *pdata) | ||
| 555 | { | ||
| 556 | kfree(pdata->private_data); | ||
| 557 | return INV_SUCCESS; | ||
| 558 | } | ||
| 559 | |||
| 560 | static int kxtf9_config(void *mlsl_handle, | ||
| 561 | struct ext_slave_descr *slave, | ||
| 562 | struct ext_slave_platform_data *pdata, | ||
| 563 | struct ext_slave_config *data) | ||
| 564 | { | ||
| 565 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 566 | if (!data->data) | ||
| 567 | return INV_ERROR_INVALID_PARAMETER; | ||
| 568 | |||
| 569 | switch (data->key) { | ||
| 570 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 571 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
| 572 | &private_data->suspend, | ||
| 573 | data->apply, *((long *)data->data)); | ||
| 574 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 575 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
| 576 | &private_data->resume, | ||
| 577 | data->apply, *((long *)data->data)); | ||
| 578 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 579 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
| 580 | &private_data->suspend, | ||
| 581 | data->apply, *((long *)data->data)); | ||
| 582 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 583 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
| 584 | &private_data->resume, | ||
| 585 | data->apply, *((long *)data->data)); | ||
| 586 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 587 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
| 588 | &private_data->suspend, | ||
| 589 | data->apply, *((long *)data->data)); | ||
| 590 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 591 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
| 592 | &private_data->resume, | ||
| 593 | data->apply, *((long *)data->data)); | ||
| 594 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 595 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
| 596 | &private_data->suspend, | ||
| 597 | data->apply, *((long *)data->data)); | ||
| 598 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 599 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
| 600 | &private_data->resume, | ||
| 601 | data->apply, *((long *)data->data)); | ||
| 602 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 603 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
| 604 | &private_data->suspend, | ||
| 605 | data->apply, *((long *)data->data)); | ||
| 606 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 607 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
| 608 | &private_data->resume, | ||
| 609 | data->apply, *((long *)data->data)); | ||
| 610 | default: | ||
| 611 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 612 | }; | ||
| 613 | |||
| 614 | return INV_SUCCESS; | ||
| 615 | } | ||
| 616 | |||
| 617 | static int kxtf9_get_config(void *mlsl_handle, | ||
| 618 | struct ext_slave_descr *slave, | ||
| 619 | struct ext_slave_platform_data *pdata, | ||
| 620 | struct ext_slave_config *data) | ||
| 621 | { | ||
| 622 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
| 623 | if (!data->data) | ||
| 624 | return INV_ERROR_INVALID_PARAMETER; | ||
| 625 | |||
| 626 | switch (data->key) { | ||
| 627 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 628 | (*(unsigned long *)data->data) = | ||
| 629 | (unsigned long)private_data->suspend.odr; | ||
| 630 | break; | ||
| 631 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 632 | (*(unsigned long *)data->data) = | ||
| 633 | (unsigned long)private_data->resume.odr; | ||
| 634 | break; | ||
| 635 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 636 | (*(unsigned long *)data->data) = | ||
| 637 | (unsigned long)private_data->suspend.fsr; | ||
| 638 | break; | ||
| 639 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 640 | (*(unsigned long *)data->data) = | ||
| 641 | (unsigned long)private_data->resume.fsr; | ||
| 642 | break; | ||
| 643 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 644 | (*(unsigned long *)data->data) = | ||
| 645 | (unsigned long)private_data->suspend.ths; | ||
| 646 | break; | ||
| 647 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 648 | (*(unsigned long *)data->data) = | ||
| 649 | (unsigned long)private_data->resume.ths; | ||
| 650 | break; | ||
| 651 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 652 | (*(unsigned long *)data->data) = | ||
| 653 | (unsigned long)private_data->suspend.dur; | ||
| 654 | break; | ||
| 655 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 656 | (*(unsigned long *)data->data) = | ||
| 657 | (unsigned long)private_data->resume.dur; | ||
| 658 | break; | ||
| 659 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 660 | (*(unsigned long *)data->data) = | ||
| 661 | (unsigned long)private_data->suspend.irq_type; | ||
| 662 | break; | ||
| 663 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 664 | (*(unsigned long *)data->data) = | ||
| 665 | (unsigned long)private_data->resume.irq_type; | ||
| 666 | break; | ||
| 667 | default: | ||
| 668 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 669 | }; | ||
| 670 | |||
| 671 | return INV_SUCCESS; | ||
| 672 | } | ||
| 673 | |||
| 674 | static int kxtf9_read(void *mlsl_handle, | ||
| 675 | struct ext_slave_descr *slave, | ||
| 676 | struct ext_slave_platform_data *pdata, | ||
| 677 | unsigned char *data) | ||
| 678 | { | ||
| 679 | int result; | ||
| 680 | unsigned char reg; | ||
| 681 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 682 | KXTF9_INT_SRC_REG2, 1, ®); | ||
| 683 | if (result) { | ||
| 684 | LOG_RESULT_LOCATION(result); | ||
| 685 | return result; | ||
| 686 | } | ||
| 687 | |||
| 688 | if (!(reg & 0x10)) | ||
| 689 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 690 | |||
| 691 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 692 | slave->read_reg, slave->read_len, data); | ||
| 693 | if (result) { | ||
| 694 | LOG_RESULT_LOCATION(result); | ||
| 695 | return result; | ||
| 696 | } | ||
| 697 | return result; | ||
| 698 | } | ||
| 699 | |||
| 700 | static struct ext_slave_descr kxtf9_descr = { | ||
| 701 | .init = kxtf9_init, | ||
| 702 | .exit = kxtf9_exit, | ||
| 703 | .suspend = kxtf9_suspend, | ||
| 704 | .resume = kxtf9_resume, | ||
| 705 | .read = kxtf9_read, | ||
| 706 | .config = kxtf9_config, | ||
| 707 | .get_config = kxtf9_get_config, | ||
| 708 | .name = "kxtf9", | ||
| 709 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 710 | .id = ACCEL_ID_KXTF9, | ||
| 711 | .read_reg = 0x06, | ||
| 712 | .read_len = 6, | ||
| 713 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 714 | .range = {2, 0}, | ||
| 715 | .trigger = NULL, | ||
| 716 | }; | ||
| 717 | |||
| 718 | static | ||
| 719 | struct ext_slave_descr *kxtf9_get_slave_descr(void) | ||
| 720 | { | ||
| 721 | return &kxtf9_descr; | ||
| 722 | } | ||
| 723 | |||
| 724 | /* -------------------------------------------------------------------------- */ | ||
| 725 | struct kxtf9_mod_private_data { | ||
| 726 | struct i2c_client *client; | ||
| 727 | struct ext_slave_platform_data *pdata; | ||
| 728 | }; | ||
| 729 | |||
| 730 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 731 | |||
| 732 | static int kxtf9_mod_probe(struct i2c_client *client, | ||
| 733 | const struct i2c_device_id *devid) | ||
| 734 | { | ||
| 735 | struct ext_slave_platform_data *pdata; | ||
| 736 | struct kxtf9_mod_private_data *private_data; | ||
| 737 | int result = 0; | ||
| 738 | |||
| 739 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 740 | |||
| 741 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 742 | result = -ENODEV; | ||
| 743 | goto out_no_free; | ||
| 744 | } | ||
| 745 | |||
| 746 | pdata = client->dev.platform_data; | ||
| 747 | if (!pdata) { | ||
| 748 | dev_err(&client->adapter->dev, | ||
| 749 | "Missing platform data for slave %s\n", devid->name); | ||
| 750 | result = -EFAULT; | ||
| 751 | goto out_no_free; | ||
| 752 | } | ||
| 753 | |||
| 754 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 755 | if (!private_data) { | ||
| 756 | result = -ENOMEM; | ||
| 757 | goto out_no_free; | ||
| 758 | } | ||
| 759 | |||
| 760 | i2c_set_clientdata(client, private_data); | ||
| 761 | private_data->client = client; | ||
| 762 | private_data->pdata = pdata; | ||
| 763 | |||
| 764 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 765 | kxtf9_get_slave_descr); | ||
| 766 | if (result) { | ||
| 767 | dev_err(&client->adapter->dev, | ||
| 768 | "Slave registration failed: %s, %d\n", | ||
| 769 | devid->name, result); | ||
| 770 | goto out_free_memory; | ||
| 771 | } | ||
| 772 | |||
| 773 | return result; | ||
| 774 | |||
| 775 | out_free_memory: | ||
| 776 | kfree(private_data); | ||
| 777 | out_no_free: | ||
| 778 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 779 | return result; | ||
| 780 | |||
| 781 | } | ||
| 782 | |||
| 783 | static int kxtf9_mod_remove(struct i2c_client *client) | ||
| 784 | { | ||
| 785 | struct kxtf9_mod_private_data *private_data = | ||
| 786 | i2c_get_clientdata(client); | ||
| 787 | |||
| 788 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 789 | |||
| 790 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 791 | kxtf9_get_slave_descr); | ||
| 792 | |||
| 793 | kfree(private_data); | ||
| 794 | return 0; | ||
| 795 | } | ||
| 796 | |||
| 797 | static const struct i2c_device_id kxtf9_mod_id[] = { | ||
| 798 | { "kxtf9", ACCEL_ID_KXTF9 }, | ||
| 799 | {} | ||
| 800 | }; | ||
| 801 | |||
| 802 | MODULE_DEVICE_TABLE(i2c, kxtf9_mod_id); | ||
| 803 | |||
| 804 | static struct i2c_driver kxtf9_mod_driver = { | ||
| 805 | .class = I2C_CLASS_HWMON, | ||
| 806 | .probe = kxtf9_mod_probe, | ||
| 807 | .remove = kxtf9_mod_remove, | ||
| 808 | .id_table = kxtf9_mod_id, | ||
| 809 | .driver = { | ||
| 810 | .owner = THIS_MODULE, | ||
| 811 | .name = "kxtf9_mod", | ||
| 812 | }, | ||
| 813 | .address_list = normal_i2c, | ||
| 814 | }; | ||
| 815 | |||
| 816 | static int __init kxtf9_mod_init(void) | ||
| 817 | { | ||
| 818 | int res = i2c_add_driver(&kxtf9_mod_driver); | ||
| 819 | pr_info("%s: Probe name %s\n", __func__, "kxtf9_mod"); | ||
| 820 | if (res) | ||
| 821 | pr_err("%s failed\n", __func__); | ||
| 822 | return res; | ||
| 823 | } | ||
| 824 | |||
| 825 | static void __exit kxtf9_mod_exit(void) | ||
| 826 | { | ||
| 827 | pr_info("%s\n", __func__); | ||
| 828 | i2c_del_driver(&kxtf9_mod_driver); | ||
| 829 | } | ||
| 830 | |||
| 831 | module_init(kxtf9_mod_init); | ||
| 832 | module_exit(kxtf9_mod_exit); | ||
| 833 | |||
| 834 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 835 | MODULE_DESCRIPTION("Driver to integrate KXTF9 sensor with the MPU"); | ||
| 836 | MODULE_LICENSE("GPL"); | ||
| 837 | MODULE_ALIAS("kxtf9_mod"); | ||
| 838 | |||
| 839 | /** | ||
| 840 | * @} | ||
| 841 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lis331.c b/drivers/misc/inv_mpu/accel/lis331.c new file mode 100644 index 00000000000..bcbec252af9 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lis331.c | |||
| @@ -0,0 +1,745 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file lis331.c | ||
| 26 | * @brief Accelerometer setup and handling methods for ST LIS331DLH. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #undef MPL_LOG_NDEBUG | ||
| 32 | #define MPL_LOG_NDEBUG 1 | ||
| 33 | |||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/moduleparam.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/delay.h> | ||
| 41 | #include "mpu-dev.h" | ||
| 42 | |||
| 43 | #include <log.h> | ||
| 44 | #include <linux/mpu.h> | ||
| 45 | #include "mlsl.h" | ||
| 46 | #include "mldl_cfg.h" | ||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-acc" | ||
| 49 | |||
| 50 | /* full scale setting - register & mask */ | ||
| 51 | #define LIS331DLH_CTRL_REG1 (0x20) | ||
| 52 | #define LIS331DLH_CTRL_REG2 (0x21) | ||
| 53 | #define LIS331DLH_CTRL_REG3 (0x22) | ||
| 54 | #define LIS331DLH_CTRL_REG4 (0x23) | ||
| 55 | #define LIS331DLH_CTRL_REG5 (0x24) | ||
| 56 | #define LIS331DLH_HP_FILTER_RESET (0x25) | ||
| 57 | #define LIS331DLH_REFERENCE (0x26) | ||
| 58 | #define LIS331DLH_STATUS_REG (0x27) | ||
| 59 | #define LIS331DLH_OUT_X_L (0x28) | ||
| 60 | #define LIS331DLH_OUT_X_H (0x29) | ||
| 61 | #define LIS331DLH_OUT_Y_L (0x2a) | ||
| 62 | #define LIS331DLH_OUT_Y_H (0x2b) | ||
| 63 | #define LIS331DLH_OUT_Z_L (0x2b) | ||
| 64 | #define LIS331DLH_OUT_Z_H (0x2d) | ||
| 65 | |||
| 66 | #define LIS331DLH_INT1_CFG (0x30) | ||
| 67 | #define LIS331DLH_INT1_SRC (0x31) | ||
| 68 | #define LIS331DLH_INT1_THS (0x32) | ||
| 69 | #define LIS331DLH_INT1_DURATION (0x33) | ||
| 70 | |||
| 71 | #define LIS331DLH_INT2_CFG (0x34) | ||
| 72 | #define LIS331DLH_INT2_SRC (0x35) | ||
| 73 | #define LIS331DLH_INT2_THS (0x36) | ||
| 74 | #define LIS331DLH_INT2_DURATION (0x37) | ||
| 75 | |||
| 76 | /* CTRL_REG1 */ | ||
| 77 | #define LIS331DLH_CTRL_MASK (0x30) | ||
| 78 | #define LIS331DLH_SLEEP_MASK (0x20) | ||
| 79 | #define LIS331DLH_PWR_MODE_NORMAL (0x20) | ||
| 80 | |||
| 81 | #define LIS331DLH_MAX_DUR (0x7F) | ||
| 82 | |||
| 83 | |||
| 84 | /* -------------------------------------------------------------------------- */ | ||
| 85 | |||
| 86 | struct lis331dlh_config { | ||
| 87 | unsigned int odr; | ||
| 88 | unsigned int fsr; /* full scale range mg */ | ||
| 89 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
| 90 | unsigned int dur; /* Motion no-motion duration ms */ | ||
| 91 | unsigned char reg_ths; | ||
| 92 | unsigned char reg_dur; | ||
| 93 | unsigned char ctrl_reg1; | ||
| 94 | unsigned char irq_type; | ||
| 95 | unsigned char mot_int1_cfg; | ||
| 96 | }; | ||
| 97 | |||
| 98 | struct lis331dlh_private_data { | ||
| 99 | struct lis331dlh_config suspend; | ||
| 100 | struct lis331dlh_config resume; | ||
| 101 | }; | ||
| 102 | |||
| 103 | /* -------------------------------------------------------------------------- */ | ||
| 104 | static int lis331dlh_set_ths(void *mlsl_handle, | ||
| 105 | struct ext_slave_platform_data *pdata, | ||
| 106 | struct lis331dlh_config *config, | ||
| 107 | int apply, long ths) | ||
| 108 | { | ||
| 109 | int result = INV_SUCCESS; | ||
| 110 | if ((unsigned int)ths >= config->fsr) | ||
| 111 | ths = (long)config->fsr - 1; | ||
| 112 | |||
| 113 | if (ths < 0) | ||
| 114 | ths = 0; | ||
| 115 | |||
| 116 | config->ths = ths; | ||
| 117 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
| 118 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
| 119 | if (apply) | ||
| 120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 121 | LIS331DLH_INT1_THS, | ||
| 122 | config->reg_ths); | ||
| 123 | return result; | ||
| 124 | } | ||
| 125 | |||
| 126 | static int lis331dlh_set_dur(void *mlsl_handle, | ||
| 127 | struct ext_slave_platform_data *pdata, | ||
| 128 | struct lis331dlh_config *config, | ||
| 129 | int apply, long dur) | ||
| 130 | { | ||
| 131 | int result = INV_SUCCESS; | ||
| 132 | long reg_dur = (dur * config->odr) / 1000000L; | ||
| 133 | config->dur = dur; | ||
| 134 | |||
| 135 | if (reg_dur > LIS331DLH_MAX_DUR) | ||
| 136 | reg_dur = LIS331DLH_MAX_DUR; | ||
| 137 | |||
| 138 | config->reg_dur = (unsigned char)reg_dur; | ||
| 139 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
| 140 | if (apply) | ||
| 141 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 142 | LIS331DLH_INT1_DURATION, | ||
| 143 | (unsigned char)reg_dur); | ||
| 144 | return result; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
| 149 | * duration will not be used uless the type is MOT or NMOT. | ||
| 150 | * | ||
| 151 | * @param config configuration to apply to, suspend or resume | ||
| 152 | * @param irq_type The type of IRQ. Valid values are | ||
| 153 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 154 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 155 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 156 | */ | ||
| 157 | static int lis331dlh_set_irq(void *mlsl_handle, | ||
| 158 | struct ext_slave_platform_data *pdata, | ||
| 159 | struct lis331dlh_config *config, | ||
| 160 | int apply, long irq_type) | ||
| 161 | { | ||
| 162 | int result = INV_SUCCESS; | ||
| 163 | unsigned char reg1; | ||
| 164 | unsigned char reg2; | ||
| 165 | |||
| 166 | config->irq_type = (unsigned char)irq_type; | ||
| 167 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 168 | reg1 = 0x02; | ||
| 169 | reg2 = 0x00; | ||
| 170 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 171 | reg1 = 0x00; | ||
| 172 | reg2 = config->mot_int1_cfg; | ||
| 173 | } else { | ||
| 174 | reg1 = 0x00; | ||
| 175 | reg2 = 0x00; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (apply) { | ||
| 179 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 180 | LIS331DLH_CTRL_REG3, reg1); | ||
| 181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 182 | LIS331DLH_INT1_CFG, reg2); | ||
| 183 | } | ||
| 184 | |||
| 185 | return result; | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Set the Output data rate for the particular configuration | ||
| 190 | * | ||
| 191 | * @param config Config to modify with new ODR | ||
| 192 | * @param odr Output data rate in units of 1/1000Hz | ||
| 193 | */ | ||
| 194 | static int lis331dlh_set_odr(void *mlsl_handle, | ||
| 195 | struct ext_slave_platform_data *pdata, | ||
| 196 | struct lis331dlh_config *config, | ||
| 197 | int apply, long odr) | ||
| 198 | { | ||
| 199 | unsigned char bits; | ||
| 200 | int result = INV_SUCCESS; | ||
| 201 | |||
| 202 | /* normal power modes */ | ||
| 203 | if (odr > 400000) { | ||
| 204 | config->odr = 1000000; | ||
| 205 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x18; | ||
| 206 | } else if (odr > 100000) { | ||
| 207 | config->odr = 400000; | ||
| 208 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x10; | ||
| 209 | } else if (odr > 50000) { | ||
| 210 | config->odr = 100000; | ||
| 211 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x08; | ||
| 212 | } else if (odr > 10000) { | ||
| 213 | config->odr = 50000; | ||
| 214 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x00; | ||
| 215 | /* low power modes */ | ||
| 216 | } else if (odr > 5000) { | ||
| 217 | config->odr = 10000; | ||
| 218 | bits = 0xC0; | ||
| 219 | } else if (odr > 2000) { | ||
| 220 | config->odr = 5000; | ||
| 221 | bits = 0xA0; | ||
| 222 | } else if (odr > 1000) { | ||
| 223 | config->odr = 2000; | ||
| 224 | bits = 0x80; | ||
| 225 | } else if (odr > 500) { | ||
| 226 | config->odr = 1000; | ||
| 227 | bits = 0x60; | ||
| 228 | } else if (odr > 0) { | ||
| 229 | config->odr = 500; | ||
| 230 | bits = 0x40; | ||
| 231 | } else { | ||
| 232 | config->odr = 0; | ||
| 233 | bits = 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7); | ||
| 237 | lis331dlh_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
| 238 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
| 239 | if (apply) | ||
| 240 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 241 | LIS331DLH_CTRL_REG1, | ||
| 242 | config->ctrl_reg1); | ||
| 243 | return result; | ||
| 244 | } | ||
| 245 | |||
| 246 | /** | ||
| 247 | * Set the full scale range of the accels | ||
| 248 | * | ||
| 249 | * @param config pointer to configuration | ||
| 250 | * @param fsr requested full scale range | ||
| 251 | */ | ||
| 252 | static int lis331dlh_set_fsr(void *mlsl_handle, | ||
| 253 | struct ext_slave_platform_data *pdata, | ||
| 254 | struct lis331dlh_config *config, | ||
| 255 | int apply, long fsr) | ||
| 256 | { | ||
| 257 | unsigned char reg1 = 0x40; | ||
| 258 | int result = INV_SUCCESS; | ||
| 259 | |||
| 260 | if (fsr <= 2048) { | ||
| 261 | config->fsr = 2048; | ||
| 262 | } else if (fsr <= 4096) { | ||
| 263 | reg1 |= 0x30; | ||
| 264 | config->fsr = 4096; | ||
| 265 | } else { | ||
| 266 | reg1 |= 0x10; | ||
| 267 | config->fsr = 8192; | ||
| 268 | } | ||
| 269 | |||
| 270 | lis331dlh_set_ths(mlsl_handle, pdata, config, apply, config->ths); | ||
| 271 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 272 | if (apply) | ||
| 273 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 274 | LIS331DLH_CTRL_REG4, reg1); | ||
| 275 | |||
| 276 | return result; | ||
| 277 | } | ||
| 278 | |||
| 279 | static int lis331dlh_suspend(void *mlsl_handle, | ||
| 280 | struct ext_slave_descr *slave, | ||
| 281 | struct ext_slave_platform_data *pdata) | ||
| 282 | { | ||
| 283 | int result = INV_SUCCESS; | ||
| 284 | unsigned char reg1; | ||
| 285 | unsigned char reg2; | ||
| 286 | struct lis331dlh_private_data *private_data = | ||
| 287 | (struct lis331dlh_private_data *)(pdata->private_data); | ||
| 288 | |||
| 289 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 290 | LIS331DLH_CTRL_REG1, | ||
| 291 | private_data->suspend.ctrl_reg1); | ||
| 292 | |||
| 293 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 294 | LIS331DLH_CTRL_REG2, 0x0f); | ||
| 295 | reg1 = 0x40; | ||
| 296 | if (private_data->suspend.fsr == 8192) | ||
| 297 | reg1 |= 0x30; | ||
| 298 | else if (private_data->suspend.fsr == 4096) | ||
| 299 | reg1 |= 0x10; | ||
| 300 | /* else bits [4..5] are already zero */ | ||
| 301 | |||
| 302 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 303 | LIS331DLH_CTRL_REG4, reg1); | ||
| 304 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 305 | LIS331DLH_INT1_THS, | ||
| 306 | private_data->suspend.reg_ths); | ||
| 307 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 308 | LIS331DLH_INT1_DURATION, | ||
| 309 | private_data->suspend.reg_dur); | ||
| 310 | |||
| 311 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 312 | reg1 = 0x02; | ||
| 313 | reg2 = 0x00; | ||
| 314 | } else if (private_data->suspend.irq_type == | ||
| 315 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 316 | reg1 = 0x00; | ||
| 317 | reg2 = private_data->suspend.mot_int1_cfg; | ||
| 318 | } else { | ||
| 319 | reg1 = 0x00; | ||
| 320 | reg2 = 0x00; | ||
| 321 | } | ||
| 322 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 323 | LIS331DLH_CTRL_REG3, reg1); | ||
| 324 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 325 | LIS331DLH_INT1_CFG, reg2); | ||
| 326 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 327 | LIS331DLH_HP_FILTER_RESET, 1, ®1); | ||
| 328 | return result; | ||
| 329 | } | ||
| 330 | |||
| 331 | static int lis331dlh_resume(void *mlsl_handle, | ||
| 332 | struct ext_slave_descr *slave, | ||
| 333 | struct ext_slave_platform_data *pdata) | ||
| 334 | { | ||
| 335 | int result = INV_SUCCESS; | ||
| 336 | unsigned char reg1; | ||
| 337 | unsigned char reg2; | ||
| 338 | struct lis331dlh_private_data *private_data = | ||
| 339 | (struct lis331dlh_private_data *)(pdata->private_data); | ||
| 340 | |||
| 341 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 342 | LIS331DLH_CTRL_REG1, | ||
| 343 | private_data->resume.ctrl_reg1); | ||
| 344 | if (result) { | ||
| 345 | LOG_RESULT_LOCATION(result); | ||
| 346 | return result; | ||
| 347 | } | ||
| 348 | msleep(6); | ||
| 349 | |||
| 350 | /* Full Scale */ | ||
| 351 | reg1 = 0x40; | ||
| 352 | if (private_data->resume.fsr == 8192) | ||
| 353 | reg1 |= 0x30; | ||
| 354 | else if (private_data->resume.fsr == 4096) | ||
| 355 | reg1 |= 0x10; | ||
| 356 | |||
| 357 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 358 | LIS331DLH_CTRL_REG4, reg1); | ||
| 359 | if (result) { | ||
| 360 | LOG_RESULT_LOCATION(result); | ||
| 361 | return result; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* Configure high pass filter */ | ||
| 365 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 366 | LIS331DLH_CTRL_REG2, 0x0F); | ||
| 367 | if (result) { | ||
| 368 | LOG_RESULT_LOCATION(result); | ||
| 369 | return result; | ||
| 370 | } | ||
| 371 | |||
| 372 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 373 | reg1 = 0x02; | ||
| 374 | reg2 = 0x00; | ||
| 375 | } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 376 | reg1 = 0x00; | ||
| 377 | reg2 = private_data->resume.mot_int1_cfg; | ||
| 378 | } else { | ||
| 379 | reg1 = 0x00; | ||
| 380 | reg2 = 0x00; | ||
| 381 | } | ||
| 382 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 383 | LIS331DLH_CTRL_REG3, reg1); | ||
| 384 | if (result) { | ||
| 385 | LOG_RESULT_LOCATION(result); | ||
| 386 | return result; | ||
| 387 | } | ||
| 388 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 389 | LIS331DLH_INT1_THS, | ||
| 390 | private_data->resume.reg_ths); | ||
| 391 | if (result) { | ||
| 392 | LOG_RESULT_LOCATION(result); | ||
| 393 | return result; | ||
| 394 | } | ||
| 395 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 396 | LIS331DLH_INT1_DURATION, | ||
| 397 | private_data->resume.reg_dur); | ||
| 398 | if (result) { | ||
| 399 | LOG_RESULT_LOCATION(result); | ||
| 400 | return result; | ||
| 401 | } | ||
| 402 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 403 | LIS331DLH_INT1_CFG, reg2); | ||
| 404 | if (result) { | ||
| 405 | LOG_RESULT_LOCATION(result); | ||
| 406 | return result; | ||
| 407 | } | ||
| 408 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 409 | LIS331DLH_HP_FILTER_RESET, 1, ®1); | ||
| 410 | if (result) { | ||
| 411 | LOG_RESULT_LOCATION(result); | ||
| 412 | return result; | ||
| 413 | } | ||
| 414 | return result; | ||
| 415 | } | ||
| 416 | |||
| 417 | static int lis331dlh_read(void *mlsl_handle, | ||
| 418 | struct ext_slave_descr *slave, | ||
| 419 | struct ext_slave_platform_data *pdata, | ||
| 420 | unsigned char *data) | ||
| 421 | { | ||
| 422 | int result = INV_SUCCESS; | ||
| 423 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 424 | LIS331DLH_STATUS_REG, 1, data); | ||
| 425 | if (data[0] & 0x0F) { | ||
| 426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 427 | slave->read_reg, slave->read_len, | ||
| 428 | data); | ||
| 429 | return result; | ||
| 430 | } else | ||
| 431 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 432 | } | ||
| 433 | |||
| 434 | static int lis331dlh_init(void *mlsl_handle, | ||
| 435 | struct ext_slave_descr *slave, | ||
| 436 | struct ext_slave_platform_data *pdata) | ||
| 437 | { | ||
| 438 | struct lis331dlh_private_data *private_data; | ||
| 439 | long range; | ||
| 440 | private_data = (struct lis331dlh_private_data *) | ||
| 441 | kzalloc(sizeof(struct lis331dlh_private_data), GFP_KERNEL); | ||
| 442 | |||
| 443 | if (!private_data) | ||
| 444 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 445 | |||
| 446 | pdata->private_data = private_data; | ||
| 447 | |||
| 448 | private_data->resume.ctrl_reg1 = 0x37; | ||
| 449 | private_data->suspend.ctrl_reg1 = 0x47; | ||
| 450 | private_data->resume.mot_int1_cfg = 0x95; | ||
| 451 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
| 452 | |||
| 453 | lis331dlh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0); | ||
| 454 | lis331dlh_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 455 | false, 200000); | ||
| 456 | |||
| 457 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 458 | lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 459 | false, range); | ||
| 460 | lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 461 | false, range); | ||
| 462 | |||
| 463 | lis331dlh_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 464 | false, 80); | ||
| 465 | lis331dlh_set_ths(mlsl_handle, pdata, &private_data->resume, false, 40); | ||
| 466 | |||
| 467 | |||
| 468 | lis331dlh_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 469 | false, 1000); | ||
| 470 | lis331dlh_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 471 | false, 2540); | ||
| 472 | |||
| 473 | lis331dlh_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 474 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 475 | lis331dlh_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 476 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 477 | return INV_SUCCESS; | ||
| 478 | } | ||
| 479 | |||
| 480 | static int lis331dlh_exit(void *mlsl_handle, | ||
| 481 | struct ext_slave_descr *slave, | ||
| 482 | struct ext_slave_platform_data *pdata) | ||
| 483 | { | ||
| 484 | kfree(pdata->private_data); | ||
| 485 | return INV_SUCCESS; | ||
| 486 | } | ||
| 487 | |||
| 488 | static int lis331dlh_config(void *mlsl_handle, | ||
| 489 | struct ext_slave_descr *slave, | ||
| 490 | struct ext_slave_platform_data *pdata, | ||
| 491 | struct ext_slave_config *data) | ||
| 492 | { | ||
| 493 | struct lis331dlh_private_data *private_data = pdata->private_data; | ||
| 494 | if (!data->data) | ||
| 495 | return INV_ERROR_INVALID_PARAMETER; | ||
| 496 | |||
| 497 | switch (data->key) { | ||
| 498 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 499 | return lis331dlh_set_odr(mlsl_handle, pdata, | ||
| 500 | &private_data->suspend, | ||
| 501 | data->apply, *((long *)data->data)); | ||
| 502 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 503 | return lis331dlh_set_odr(mlsl_handle, pdata, | ||
| 504 | &private_data->resume, | ||
| 505 | data->apply, *((long *)data->data)); | ||
| 506 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 507 | return lis331dlh_set_fsr(mlsl_handle, pdata, | ||
| 508 | &private_data->suspend, | ||
| 509 | data->apply, *((long *)data->data)); | ||
| 510 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 511 | return lis331dlh_set_fsr(mlsl_handle, pdata, | ||
| 512 | &private_data->resume, | ||
| 513 | data->apply, *((long *)data->data)); | ||
| 514 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 515 | return lis331dlh_set_ths(mlsl_handle, pdata, | ||
| 516 | &private_data->suspend, | ||
| 517 | data->apply, *((long *)data->data)); | ||
| 518 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 519 | return lis331dlh_set_ths(mlsl_handle, pdata, | ||
| 520 | &private_data->resume, | ||
| 521 | data->apply, *((long *)data->data)); | ||
| 522 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 523 | return lis331dlh_set_dur(mlsl_handle, pdata, | ||
| 524 | &private_data->suspend, | ||
| 525 | data->apply, *((long *)data->data)); | ||
| 526 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 527 | return lis331dlh_set_dur(mlsl_handle, pdata, | ||
| 528 | &private_data->resume, | ||
| 529 | data->apply, *((long *)data->data)); | ||
| 530 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 531 | return lis331dlh_set_irq(mlsl_handle, pdata, | ||
| 532 | &private_data->suspend, | ||
| 533 | data->apply, *((long *)data->data)); | ||
| 534 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 535 | return lis331dlh_set_irq(mlsl_handle, pdata, | ||
| 536 | &private_data->resume, | ||
| 537 | data->apply, *((long *)data->data)); | ||
| 538 | default: | ||
| 539 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 540 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 541 | }; | ||
| 542 | |||
| 543 | return INV_SUCCESS; | ||
| 544 | } | ||
| 545 | |||
| 546 | static int lis331dlh_get_config(void *mlsl_handle, | ||
| 547 | struct ext_slave_descr *slave, | ||
| 548 | struct ext_slave_platform_data *pdata, | ||
| 549 | struct ext_slave_config *data) | ||
| 550 | { | ||
| 551 | struct lis331dlh_private_data *private_data = pdata->private_data; | ||
| 552 | if (!data->data) | ||
| 553 | return INV_ERROR_INVALID_PARAMETER; | ||
| 554 | |||
| 555 | switch (data->key) { | ||
| 556 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 557 | (*(unsigned long *)data->data) = | ||
| 558 | (unsigned long)private_data->suspend.odr; | ||
| 559 | break; | ||
| 560 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 561 | (*(unsigned long *)data->data) = | ||
| 562 | (unsigned long)private_data->resume.odr; | ||
| 563 | break; | ||
| 564 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 565 | (*(unsigned long *)data->data) = | ||
| 566 | (unsigned long)private_data->suspend.fsr; | ||
| 567 | break; | ||
| 568 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 569 | (*(unsigned long *)data->data) = | ||
| 570 | (unsigned long)private_data->resume.fsr; | ||
| 571 | break; | ||
| 572 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 573 | (*(unsigned long *)data->data) = | ||
| 574 | (unsigned long)private_data->suspend.ths; | ||
| 575 | break; | ||
| 576 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 577 | (*(unsigned long *)data->data) = | ||
| 578 | (unsigned long)private_data->resume.ths; | ||
| 579 | break; | ||
| 580 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 581 | (*(unsigned long *)data->data) = | ||
| 582 | (unsigned long)private_data->suspend.dur; | ||
| 583 | break; | ||
| 584 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 585 | (*(unsigned long *)data->data) = | ||
| 586 | (unsigned long)private_data->resume.dur; | ||
| 587 | break; | ||
| 588 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 589 | (*(unsigned long *)data->data) = | ||
| 590 | (unsigned long)private_data->suspend.irq_type; | ||
| 591 | break; | ||
| 592 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 593 | (*(unsigned long *)data->data) = | ||
| 594 | (unsigned long)private_data->resume.irq_type; | ||
| 595 | break; | ||
| 596 | default: | ||
| 597 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 598 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 599 | }; | ||
| 600 | |||
| 601 | return INV_SUCCESS; | ||
| 602 | } | ||
| 603 | |||
| 604 | static struct ext_slave_descr lis331dlh_descr = { | ||
| 605 | .init = lis331dlh_init, | ||
| 606 | .exit = lis331dlh_exit, | ||
| 607 | .suspend = lis331dlh_suspend, | ||
| 608 | .resume = lis331dlh_resume, | ||
| 609 | .read = lis331dlh_read, | ||
| 610 | .config = lis331dlh_config, | ||
| 611 | .get_config = lis331dlh_get_config, | ||
| 612 | .name = "lis331dlh", | ||
| 613 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 614 | .id = ACCEL_ID_LIS331, | ||
| 615 | .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */ | ||
| 616 | .read_len = 6, | ||
| 617 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 618 | .range = {2, 480}, | ||
| 619 | .trigger = NULL, | ||
| 620 | }; | ||
| 621 | |||
| 622 | static | ||
| 623 | struct ext_slave_descr *lis331_get_slave_descr(void) | ||
| 624 | { | ||
| 625 | return &lis331dlh_descr; | ||
| 626 | } | ||
| 627 | |||
| 628 | /* -------------------------------------------------------------------------- */ | ||
| 629 | struct lis331_mod_private_data { | ||
| 630 | struct i2c_client *client; | ||
| 631 | struct ext_slave_platform_data *pdata; | ||
| 632 | }; | ||
| 633 | |||
| 634 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 635 | |||
| 636 | static int lis331_mod_probe(struct i2c_client *client, | ||
| 637 | const struct i2c_device_id *devid) | ||
| 638 | { | ||
| 639 | struct ext_slave_platform_data *pdata; | ||
| 640 | struct lis331_mod_private_data *private_data; | ||
| 641 | int result = 0; | ||
| 642 | |||
| 643 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 644 | |||
| 645 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 646 | result = -ENODEV; | ||
| 647 | goto out_no_free; | ||
| 648 | } | ||
| 649 | |||
| 650 | pdata = client->dev.platform_data; | ||
| 651 | if (!pdata) { | ||
| 652 | dev_err(&client->adapter->dev, | ||
| 653 | "Missing platform data for slave %s\n", devid->name); | ||
| 654 | result = -EFAULT; | ||
| 655 | goto out_no_free; | ||
| 656 | } | ||
| 657 | |||
| 658 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 659 | if (!private_data) { | ||
| 660 | result = -ENOMEM; | ||
| 661 | goto out_no_free; | ||
| 662 | } | ||
| 663 | |||
| 664 | i2c_set_clientdata(client, private_data); | ||
| 665 | private_data->client = client; | ||
| 666 | private_data->pdata = pdata; | ||
| 667 | |||
| 668 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 669 | lis331_get_slave_descr); | ||
| 670 | if (result) { | ||
| 671 | dev_err(&client->adapter->dev, | ||
| 672 | "Slave registration failed: %s, %d\n", | ||
| 673 | devid->name, result); | ||
| 674 | goto out_free_memory; | ||
| 675 | } | ||
| 676 | |||
| 677 | return result; | ||
| 678 | |||
| 679 | out_free_memory: | ||
| 680 | kfree(private_data); | ||
| 681 | out_no_free: | ||
| 682 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 683 | return result; | ||
| 684 | |||
| 685 | } | ||
| 686 | |||
| 687 | static int lis331_mod_remove(struct i2c_client *client) | ||
| 688 | { | ||
| 689 | struct lis331_mod_private_data *private_data = | ||
| 690 | i2c_get_clientdata(client); | ||
| 691 | |||
| 692 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 693 | |||
| 694 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 695 | lis331_get_slave_descr); | ||
| 696 | |||
| 697 | kfree(private_data); | ||
| 698 | return 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | static const struct i2c_device_id lis331_mod_id[] = { | ||
| 702 | { "lis331", ACCEL_ID_LIS331 }, | ||
| 703 | {} | ||
| 704 | }; | ||
| 705 | |||
| 706 | MODULE_DEVICE_TABLE(i2c, lis331_mod_id); | ||
| 707 | |||
| 708 | static struct i2c_driver lis331_mod_driver = { | ||
| 709 | .class = I2C_CLASS_HWMON, | ||
| 710 | .probe = lis331_mod_probe, | ||
| 711 | .remove = lis331_mod_remove, | ||
| 712 | .id_table = lis331_mod_id, | ||
| 713 | .driver = { | ||
| 714 | .owner = THIS_MODULE, | ||
| 715 | .name = "lis331_mod", | ||
| 716 | }, | ||
| 717 | .address_list = normal_i2c, | ||
| 718 | }; | ||
| 719 | |||
| 720 | static int __init lis331_mod_init(void) | ||
| 721 | { | ||
| 722 | int res = i2c_add_driver(&lis331_mod_driver); | ||
| 723 | pr_info("%s: Probe name %s\n", __func__, "lis331_mod"); | ||
| 724 | if (res) | ||
| 725 | pr_err("%s failed\n", __func__); | ||
| 726 | return res; | ||
| 727 | } | ||
| 728 | |||
| 729 | static void __exit lis331_mod_exit(void) | ||
| 730 | { | ||
| 731 | pr_info("%s\n", __func__); | ||
| 732 | i2c_del_driver(&lis331_mod_driver); | ||
| 733 | } | ||
| 734 | |||
| 735 | module_init(lis331_mod_init); | ||
| 736 | module_exit(lis331_mod_exit); | ||
| 737 | |||
| 738 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 739 | MODULE_DESCRIPTION("Driver to integrate LIS331 sensor with the MPU"); | ||
| 740 | MODULE_LICENSE("GPL"); | ||
| 741 | MODULE_ALIAS("lis331_mod"); | ||
| 742 | |||
| 743 | /** | ||
| 744 | * @} | ||
| 745 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lis3dh.c b/drivers/misc/inv_mpu/accel/lis3dh.c new file mode 100644 index 00000000000..27206e4b847 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lis3dh.c | |||
| @@ -0,0 +1,728 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file lis3dh.c | ||
| 26 | * @brief Accelerometer setup and handling methods for ST LIS3DH. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #undef MPL_LOG_NDEBUG | ||
| 32 | #define MPL_LOG_NDEBUG 0 | ||
| 33 | |||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/moduleparam.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/delay.h> | ||
| 41 | #include "mpu-dev.h" | ||
| 42 | |||
| 43 | #include <log.h> | ||
| 44 | #include <linux/mpu.h> | ||
| 45 | #include "mlsl.h" | ||
| 46 | #include "mldl_cfg.h" | ||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-acc" | ||
| 49 | |||
| 50 | /* full scale setting - register & mask */ | ||
| 51 | #define LIS3DH_CTRL_REG1 (0x20) | ||
| 52 | #define LIS3DH_CTRL_REG2 (0x21) | ||
| 53 | #define LIS3DH_CTRL_REG3 (0x22) | ||
| 54 | #define LIS3DH_CTRL_REG4 (0x23) | ||
| 55 | #define LIS3DH_CTRL_REG5 (0x24) | ||
| 56 | #define LIS3DH_CTRL_REG6 (0x25) | ||
| 57 | #define LIS3DH_REFERENCE (0x26) | ||
| 58 | #define LIS3DH_STATUS_REG (0x27) | ||
| 59 | #define LIS3DH_OUT_X_L (0x28) | ||
| 60 | #define LIS3DH_OUT_X_H (0x29) | ||
| 61 | #define LIS3DH_OUT_Y_L (0x2a) | ||
| 62 | #define LIS3DH_OUT_Y_H (0x2b) | ||
| 63 | #define LIS3DH_OUT_Z_L (0x2c) | ||
| 64 | #define LIS3DH_OUT_Z_H (0x2d) | ||
| 65 | |||
| 66 | #define LIS3DH_INT1_CFG (0x30) | ||
| 67 | #define LIS3DH_INT1_SRC (0x31) | ||
| 68 | #define LIS3DH_INT1_THS (0x32) | ||
| 69 | #define LIS3DH_INT1_DURATION (0x33) | ||
| 70 | |||
| 71 | #define LIS3DH_MAX_DUR (0x7F) | ||
| 72 | |||
| 73 | /* -------------------------------------------------------------------------- */ | ||
| 74 | |||
| 75 | struct lis3dh_config { | ||
| 76 | unsigned long odr; | ||
| 77 | unsigned int fsr; /* full scale range mg */ | ||
| 78 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
| 79 | unsigned int dur; /* Motion no-motion duration ms */ | ||
| 80 | unsigned char reg_ths; | ||
| 81 | unsigned char reg_dur; | ||
| 82 | unsigned char ctrl_reg1; | ||
| 83 | unsigned char irq_type; | ||
| 84 | unsigned char mot_int1_cfg; | ||
| 85 | }; | ||
| 86 | |||
| 87 | struct lis3dh_private_data { | ||
| 88 | struct lis3dh_config suspend; | ||
| 89 | struct lis3dh_config resume; | ||
| 90 | }; | ||
| 91 | |||
| 92 | /* -------------------------------------------------------------------------- */ | ||
| 93 | |||
| 94 | static int lis3dh_set_ths(void *mlsl_handle, | ||
| 95 | struct ext_slave_platform_data *pdata, | ||
| 96 | struct lis3dh_config *config, int apply, long ths) | ||
| 97 | { | ||
| 98 | int result = INV_SUCCESS; | ||
| 99 | if ((unsigned int)ths > 1000 * config->fsr) | ||
| 100 | ths = (long)1000 * config->fsr; | ||
| 101 | |||
| 102 | if (ths < 0) | ||
| 103 | ths = 0; | ||
| 104 | |||
| 105 | config->ths = ths; | ||
| 106 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
| 107 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
| 108 | if (apply) | ||
| 109 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 110 | LIS3DH_INT1_THS, | ||
| 111 | config->reg_ths); | ||
| 112 | return result; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int lis3dh_set_dur(void *mlsl_handle, | ||
| 116 | struct ext_slave_platform_data *pdata, | ||
| 117 | struct lis3dh_config *config, int apply, long dur) | ||
| 118 | { | ||
| 119 | int result = INV_SUCCESS; | ||
| 120 | long reg_dur = (dur * config->odr) / 1000000L; | ||
| 121 | config->dur = dur; | ||
| 122 | |||
| 123 | if (reg_dur > LIS3DH_MAX_DUR) | ||
| 124 | reg_dur = LIS3DH_MAX_DUR; | ||
| 125 | |||
| 126 | config->reg_dur = (unsigned char)reg_dur; | ||
| 127 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
| 128 | if (apply) | ||
| 129 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 130 | LIS3DH_INT1_DURATION, | ||
| 131 | (unsigned char)reg_dur); | ||
| 132 | return result; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
| 137 | * duration will not be used uless the type is MOT or NMOT. | ||
| 138 | * | ||
| 139 | * @param config configuration to apply to, suspend or resume | ||
| 140 | * @param irq_type The type of IRQ. Valid values are | ||
| 141 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 142 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 143 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 144 | */ | ||
| 145 | static int lis3dh_set_irq(void *mlsl_handle, | ||
| 146 | struct ext_slave_platform_data *pdata, | ||
| 147 | struct lis3dh_config *config, | ||
| 148 | int apply, long irq_type) | ||
| 149 | { | ||
| 150 | int result = INV_SUCCESS; | ||
| 151 | unsigned char reg1; | ||
| 152 | unsigned char reg2; | ||
| 153 | |||
| 154 | config->irq_type = (unsigned char)irq_type; | ||
| 155 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 156 | reg1 = 0x10; | ||
| 157 | reg2 = 0x00; | ||
| 158 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 159 | reg1 = 0x40; | ||
| 160 | reg2 = config->mot_int1_cfg; | ||
| 161 | } else { | ||
| 162 | reg1 = 0x00; | ||
| 163 | reg2 = 0x00; | ||
| 164 | } | ||
| 165 | |||
| 166 | if (apply) { | ||
| 167 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 168 | LIS3DH_CTRL_REG3, reg1); | ||
| 169 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 170 | LIS3DH_INT1_CFG, reg2); | ||
| 171 | } | ||
| 172 | |||
| 173 | return result; | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Set the Output data rate for the particular configuration | ||
| 178 | * | ||
| 179 | * @param config Config to modify with new ODR | ||
| 180 | * @param odr Output data rate in units of 1/1000Hz | ||
| 181 | */ | ||
| 182 | static int lis3dh_set_odr(void *mlsl_handle, | ||
| 183 | struct ext_slave_platform_data *pdata, | ||
| 184 | struct lis3dh_config *config, int apply, long odr) | ||
| 185 | { | ||
| 186 | unsigned char bits; | ||
| 187 | int result = INV_SUCCESS; | ||
| 188 | |||
| 189 | if (odr > 400000L) { | ||
| 190 | config->odr = 1250000L; | ||
| 191 | bits = 0x90; | ||
| 192 | } else if (odr > 200000L) { | ||
| 193 | config->odr = 400000L; | ||
| 194 | bits = 0x70; | ||
| 195 | } else if (odr > 100000L) { | ||
| 196 | config->odr = 200000L; | ||
| 197 | bits = 0x60; | ||
| 198 | } else if (odr > 50000) { | ||
| 199 | config->odr = 100000L; | ||
| 200 | bits = 0x50; | ||
| 201 | } else if (odr > 25000) { | ||
| 202 | config->odr = 50000; | ||
| 203 | bits = 0x40; | ||
| 204 | } else if (odr > 10000) { | ||
| 205 | config->odr = 25000; | ||
| 206 | bits = 0x30; | ||
| 207 | } else if (odr > 1000) { | ||
| 208 | config->odr = 10000; | ||
| 209 | bits = 0x20; | ||
| 210 | } else if (odr > 500) { | ||
| 211 | config->odr = 1000; | ||
| 212 | bits = 0x10; | ||
| 213 | } else { | ||
| 214 | config->odr = 0; | ||
| 215 | bits = 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xf); | ||
| 219 | lis3dh_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
| 220 | MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
| 221 | if (apply) | ||
| 222 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 223 | LIS3DH_CTRL_REG1, | ||
| 224 | config->ctrl_reg1); | ||
| 225 | return result; | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Set the full scale range of the accels | ||
| 230 | * | ||
| 231 | * @param config pointer to configuration | ||
| 232 | * @param fsr requested full scale range | ||
| 233 | */ | ||
| 234 | static int lis3dh_set_fsr(void *mlsl_handle, | ||
| 235 | struct ext_slave_platform_data *pdata, | ||
| 236 | struct lis3dh_config *config, int apply, long fsr) | ||
| 237 | { | ||
| 238 | int result = INV_SUCCESS; | ||
| 239 | unsigned char reg1 = 0x48; | ||
| 240 | |||
| 241 | if (fsr <= 2048) { | ||
| 242 | config->fsr = 2048; | ||
| 243 | } else if (fsr <= 4096) { | ||
| 244 | reg1 |= 0x10; | ||
| 245 | config->fsr = 4096; | ||
| 246 | } else if (fsr <= 8192) { | ||
| 247 | reg1 |= 0x20; | ||
| 248 | config->fsr = 8192; | ||
| 249 | } else { | ||
| 250 | reg1 |= 0x30; | ||
| 251 | config->fsr = 16348; | ||
| 252 | } | ||
| 253 | |||
| 254 | lis3dh_set_ths(mlsl_handle, pdata, config, apply, config->ths); | ||
| 255 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 256 | if (apply) | ||
| 257 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 258 | LIS3DH_CTRL_REG4, reg1); | ||
| 259 | |||
| 260 | return result; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int lis3dh_suspend(void *mlsl_handle, | ||
| 264 | struct ext_slave_descr *slave, | ||
| 265 | struct ext_slave_platform_data *pdata) | ||
| 266 | { | ||
| 267 | int result = INV_SUCCESS; | ||
| 268 | unsigned char reg1; | ||
| 269 | unsigned char reg2; | ||
| 270 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
| 271 | |||
| 272 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 273 | LIS3DH_CTRL_REG1, | ||
| 274 | private_data->suspend.ctrl_reg1); | ||
| 275 | |||
| 276 | reg1 = 0x48; | ||
| 277 | if (private_data->suspend.fsr == 16384) | ||
| 278 | reg1 |= 0x30; | ||
| 279 | else if (private_data->suspend.fsr == 8192) | ||
| 280 | reg1 |= 0x20; | ||
| 281 | else if (private_data->suspend.fsr == 4096) | ||
| 282 | reg1 |= 0x10; | ||
| 283 | else if (private_data->suspend.fsr == 2048) | ||
| 284 | reg1 |= 0x00; | ||
| 285 | |||
| 286 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 287 | LIS3DH_CTRL_REG4, reg1); | ||
| 288 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 289 | LIS3DH_INT1_THS, | ||
| 290 | private_data->suspend.reg_ths); | ||
| 291 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 292 | LIS3DH_INT1_DURATION, | ||
| 293 | private_data->suspend.reg_dur); | ||
| 294 | |||
| 295 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 296 | reg1 = 0x10; | ||
| 297 | reg2 = 0x00; | ||
| 298 | } else if (private_data->suspend.irq_type == | ||
| 299 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 300 | reg1 = 0x40; | ||
| 301 | reg2 = private_data->suspend.mot_int1_cfg; | ||
| 302 | } else { | ||
| 303 | reg1 = 0x00; | ||
| 304 | reg2 = 0x00; | ||
| 305 | } | ||
| 306 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 307 | LIS3DH_CTRL_REG3, reg1); | ||
| 308 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 309 | LIS3DH_INT1_CFG, reg2); | ||
| 310 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 311 | LIS3DH_CTRL_REG6, 1, ®1); | ||
| 312 | |||
| 313 | return result; | ||
| 314 | } | ||
| 315 | |||
| 316 | static int lis3dh_resume(void *mlsl_handle, | ||
| 317 | struct ext_slave_descr *slave, | ||
| 318 | struct ext_slave_platform_data *pdata) | ||
| 319 | { | ||
| 320 | int result; | ||
| 321 | unsigned char reg1; | ||
| 322 | unsigned char reg2; | ||
| 323 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
| 324 | |||
| 325 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 326 | LIS3DH_CTRL_REG1, | ||
| 327 | private_data->resume.ctrl_reg1); | ||
| 328 | if (result) { | ||
| 329 | LOG_RESULT_LOCATION(result); | ||
| 330 | return result; | ||
| 331 | } | ||
| 332 | msleep(6); | ||
| 333 | |||
| 334 | /* Full Scale */ | ||
| 335 | reg1 = 0x48; | ||
| 336 | if (private_data->suspend.fsr == 16384) | ||
| 337 | reg1 |= 0x30; | ||
| 338 | else if (private_data->suspend.fsr == 8192) | ||
| 339 | reg1 |= 0x20; | ||
| 340 | else if (private_data->suspend.fsr == 4096) | ||
| 341 | reg1 |= 0x10; | ||
| 342 | else if (private_data->suspend.fsr == 2048) | ||
| 343 | reg1 |= 0x00; | ||
| 344 | |||
| 345 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 346 | LIS3DH_CTRL_REG4, reg1); | ||
| 347 | if (result) { | ||
| 348 | LOG_RESULT_LOCATION(result); | ||
| 349 | return result; | ||
| 350 | } | ||
| 351 | |||
| 352 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 353 | reg1 = 0x10; | ||
| 354 | reg2 = 0x00; | ||
| 355 | } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 356 | reg1 = 0x40; | ||
| 357 | reg2 = private_data->resume.mot_int1_cfg; | ||
| 358 | } else { | ||
| 359 | reg1 = 0x00; | ||
| 360 | reg2 = 0x00; | ||
| 361 | } | ||
| 362 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 363 | LIS3DH_CTRL_REG3, reg1); | ||
| 364 | if (result) { | ||
| 365 | LOG_RESULT_LOCATION(result); | ||
| 366 | return result; | ||
| 367 | } | ||
| 368 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 369 | LIS3DH_INT1_THS, | ||
| 370 | private_data->resume.reg_ths); | ||
| 371 | if (result) { | ||
| 372 | LOG_RESULT_LOCATION(result); | ||
| 373 | return result; | ||
| 374 | } | ||
| 375 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 376 | LIS3DH_INT1_DURATION, | ||
| 377 | private_data->resume.reg_dur); | ||
| 378 | if (result) { | ||
| 379 | LOG_RESULT_LOCATION(result); | ||
| 380 | return result; | ||
| 381 | } | ||
| 382 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 383 | LIS3DH_INT1_CFG, reg2); | ||
| 384 | if (result) { | ||
| 385 | LOG_RESULT_LOCATION(result); | ||
| 386 | return result; | ||
| 387 | } | ||
| 388 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 389 | LIS3DH_CTRL_REG6, 1, ®1); | ||
| 390 | if (result) { | ||
| 391 | LOG_RESULT_LOCATION(result); | ||
| 392 | return result; | ||
| 393 | } | ||
| 394 | return result; | ||
| 395 | } | ||
| 396 | |||
| 397 | static int lis3dh_read(void *mlsl_handle, | ||
| 398 | struct ext_slave_descr *slave, | ||
| 399 | struct ext_slave_platform_data *pdata, | ||
| 400 | unsigned char *data) | ||
| 401 | { | ||
| 402 | int result = INV_SUCCESS; | ||
| 403 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 404 | LIS3DH_STATUS_REG, 1, data); | ||
| 405 | if (data[0] & 0x0F) { | ||
| 406 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 407 | slave->read_reg, slave->read_len, | ||
| 408 | data); | ||
| 409 | return result; | ||
| 410 | } else | ||
| 411 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 412 | } | ||
| 413 | |||
| 414 | static int lis3dh_init(void *mlsl_handle, | ||
| 415 | struct ext_slave_descr *slave, | ||
| 416 | struct ext_slave_platform_data *pdata) | ||
| 417 | { | ||
| 418 | int result; | ||
| 419 | long range; | ||
| 420 | struct lis3dh_private_data *private_data; | ||
| 421 | private_data = (struct lis3dh_private_data *) | ||
| 422 | kzalloc(sizeof(struct lis3dh_private_data), GFP_KERNEL); | ||
| 423 | |||
| 424 | if (!private_data) | ||
| 425 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 426 | |||
| 427 | pdata->private_data = private_data; | ||
| 428 | |||
| 429 | private_data->resume.ctrl_reg1 = 0x67; | ||
| 430 | private_data->suspend.ctrl_reg1 = 0x18; | ||
| 431 | private_data->resume.mot_int1_cfg = 0x95; | ||
| 432 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
| 433 | |||
| 434 | lis3dh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0); | ||
| 435 | lis3dh_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 436 | false, 200000L); | ||
| 437 | |||
| 438 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 439 | lis3dh_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 440 | false, range); | ||
| 441 | lis3dh_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 442 | false, range); | ||
| 443 | |||
| 444 | lis3dh_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 445 | false, 80); | ||
| 446 | lis3dh_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
| 447 | false, 40); | ||
| 448 | |||
| 449 | lis3dh_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 450 | false, 1000); | ||
| 451 | lis3dh_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 452 | false, 2540); | ||
| 453 | |||
| 454 | lis3dh_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 455 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 456 | lis3dh_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 457 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 458 | |||
| 459 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 460 | LIS3DH_CTRL_REG1, 0x07); | ||
| 461 | msleep(6); | ||
| 462 | |||
| 463 | return INV_SUCCESS; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int lis3dh_exit(void *mlsl_handle, | ||
| 467 | struct ext_slave_descr *slave, | ||
| 468 | struct ext_slave_platform_data *pdata) | ||
| 469 | { | ||
| 470 | kfree(pdata->private_data); | ||
| 471 | return INV_SUCCESS; | ||
| 472 | } | ||
| 473 | |||
| 474 | static int lis3dh_config(void *mlsl_handle, | ||
| 475 | struct ext_slave_descr *slave, | ||
| 476 | struct ext_slave_platform_data *pdata, | ||
| 477 | struct ext_slave_config *data) | ||
| 478 | { | ||
| 479 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
| 480 | if (!data->data) | ||
| 481 | return INV_ERROR_INVALID_PARAMETER; | ||
| 482 | |||
| 483 | switch (data->key) { | ||
| 484 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 485 | return lis3dh_set_odr(mlsl_handle, pdata, | ||
| 486 | &private_data->suspend, | ||
| 487 | data->apply, *((long *)data->data)); | ||
| 488 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 489 | return lis3dh_set_odr(mlsl_handle, pdata, | ||
| 490 | &private_data->resume, | ||
| 491 | data->apply, *((long *)data->data)); | ||
| 492 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 493 | return lis3dh_set_fsr(mlsl_handle, pdata, | ||
| 494 | &private_data->suspend, | ||
| 495 | data->apply, *((long *)data->data)); | ||
| 496 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 497 | return lis3dh_set_fsr(mlsl_handle, pdata, | ||
| 498 | &private_data->resume, | ||
| 499 | data->apply, *((long *)data->data)); | ||
| 500 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 501 | return lis3dh_set_ths(mlsl_handle, pdata, | ||
| 502 | &private_data->suspend, | ||
| 503 | data->apply, *((long *)data->data)); | ||
| 504 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 505 | return lis3dh_set_ths(mlsl_handle, pdata, | ||
| 506 | &private_data->resume, | ||
| 507 | data->apply, *((long *)data->data)); | ||
| 508 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 509 | return lis3dh_set_dur(mlsl_handle, pdata, | ||
| 510 | &private_data->suspend, | ||
| 511 | data->apply, *((long *)data->data)); | ||
| 512 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 513 | return lis3dh_set_dur(mlsl_handle, pdata, | ||
| 514 | &private_data->resume, | ||
| 515 | data->apply, *((long *)data->data)); | ||
| 516 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 517 | return lis3dh_set_irq(mlsl_handle, pdata, | ||
| 518 | &private_data->suspend, | ||
| 519 | data->apply, *((long *)data->data)); | ||
| 520 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 521 | return lis3dh_set_irq(mlsl_handle, pdata, | ||
| 522 | &private_data->resume, | ||
| 523 | data->apply, *((long *)data->data)); | ||
| 524 | default: | ||
| 525 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 526 | }; | ||
| 527 | return INV_SUCCESS; | ||
| 528 | } | ||
| 529 | |||
| 530 | static int lis3dh_get_config(void *mlsl_handle, | ||
| 531 | struct ext_slave_descr *slave, | ||
| 532 | struct ext_slave_platform_data *pdata, | ||
| 533 | struct ext_slave_config *data) | ||
| 534 | { | ||
| 535 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
| 536 | if (!data->data) | ||
| 537 | return INV_ERROR_INVALID_PARAMETER; | ||
| 538 | |||
| 539 | switch (data->key) { | ||
| 540 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 541 | (*(unsigned long *)data->data) = | ||
| 542 | (unsigned long)private_data->suspend.odr; | ||
| 543 | break; | ||
| 544 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 545 | (*(unsigned long *)data->data) = | ||
| 546 | (unsigned long)private_data->resume.odr; | ||
| 547 | break; | ||
| 548 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 549 | (*(unsigned long *)data->data) = | ||
| 550 | (unsigned long)private_data->suspend.fsr; | ||
| 551 | break; | ||
| 552 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 553 | (*(unsigned long *)data->data) = | ||
| 554 | (unsigned long)private_data->resume.fsr; | ||
| 555 | break; | ||
| 556 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 557 | (*(unsigned long *)data->data) = | ||
| 558 | (unsigned long)private_data->suspend.ths; | ||
| 559 | break; | ||
| 560 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 561 | (*(unsigned long *)data->data) = | ||
| 562 | (unsigned long)private_data->resume.ths; | ||
| 563 | break; | ||
| 564 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 565 | (*(unsigned long *)data->data) = | ||
| 566 | (unsigned long)private_data->suspend.dur; | ||
| 567 | break; | ||
| 568 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 569 | (*(unsigned long *)data->data) = | ||
| 570 | (unsigned long)private_data->resume.dur; | ||
| 571 | break; | ||
| 572 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 573 | (*(unsigned long *)data->data) = | ||
| 574 | (unsigned long)private_data->suspend.irq_type; | ||
| 575 | break; | ||
| 576 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 577 | (*(unsigned long *)data->data) = | ||
| 578 | (unsigned long)private_data->resume.irq_type; | ||
| 579 | break; | ||
| 580 | default: | ||
| 581 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 582 | }; | ||
| 583 | |||
| 584 | return INV_SUCCESS; | ||
| 585 | } | ||
| 586 | |||
| 587 | static struct ext_slave_descr lis3dh_descr = { | ||
| 588 | .init = lis3dh_init, | ||
| 589 | .exit = lis3dh_exit, | ||
| 590 | .suspend = lis3dh_suspend, | ||
| 591 | .resume = lis3dh_resume, | ||
| 592 | .read = lis3dh_read, | ||
| 593 | .config = lis3dh_config, | ||
| 594 | .get_config = lis3dh_get_config, | ||
| 595 | .name = "lis3dh", | ||
| 596 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 597 | .id = ACCEL_ID_LIS3DH, | ||
| 598 | .read_reg = 0x28 | 0x80, /* 0x80 for burst reads */ | ||
| 599 | .read_len = 6, | ||
| 600 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 601 | .range = {2, 480}, | ||
| 602 | .trigger = NULL, | ||
| 603 | }; | ||
| 604 | |||
| 605 | static | ||
| 606 | struct ext_slave_descr *lis3dh_get_slave_descr(void) | ||
| 607 | { | ||
| 608 | return &lis3dh_descr; | ||
| 609 | } | ||
| 610 | |||
| 611 | /* -------------------------------------------------------------------------- */ | ||
| 612 | struct lis3dh_mod_private_data { | ||
| 613 | struct i2c_client *client; | ||
| 614 | struct ext_slave_platform_data *pdata; | ||
| 615 | }; | ||
| 616 | |||
| 617 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 618 | |||
| 619 | static int lis3dh_mod_probe(struct i2c_client *client, | ||
| 620 | const struct i2c_device_id *devid) | ||
| 621 | { | ||
| 622 | struct ext_slave_platform_data *pdata; | ||
| 623 | struct lis3dh_mod_private_data *private_data; | ||
| 624 | int result = 0; | ||
| 625 | |||
| 626 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 627 | |||
| 628 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 629 | result = -ENODEV; | ||
| 630 | goto out_no_free; | ||
| 631 | } | ||
| 632 | |||
| 633 | pdata = client->dev.platform_data; | ||
| 634 | if (!pdata) { | ||
| 635 | dev_err(&client->adapter->dev, | ||
| 636 | "Missing platform data for slave %s\n", devid->name); | ||
| 637 | result = -EFAULT; | ||
| 638 | goto out_no_free; | ||
| 639 | } | ||
| 640 | |||
| 641 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 642 | if (!private_data) { | ||
| 643 | result = -ENOMEM; | ||
| 644 | goto out_no_free; | ||
| 645 | } | ||
| 646 | |||
| 647 | i2c_set_clientdata(client, private_data); | ||
| 648 | private_data->client = client; | ||
| 649 | private_data->pdata = pdata; | ||
| 650 | |||
| 651 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 652 | lis3dh_get_slave_descr); | ||
| 653 | if (result) { | ||
| 654 | dev_err(&client->adapter->dev, | ||
| 655 | "Slave registration failed: %s, %d\n", | ||
| 656 | devid->name, result); | ||
| 657 | goto out_free_memory; | ||
| 658 | } | ||
| 659 | |||
| 660 | return result; | ||
| 661 | |||
| 662 | out_free_memory: | ||
| 663 | kfree(private_data); | ||
| 664 | out_no_free: | ||
| 665 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 666 | return result; | ||
| 667 | |||
| 668 | } | ||
| 669 | |||
| 670 | static int lis3dh_mod_remove(struct i2c_client *client) | ||
| 671 | { | ||
| 672 | struct lis3dh_mod_private_data *private_data = | ||
| 673 | i2c_get_clientdata(client); | ||
| 674 | |||
| 675 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 676 | |||
| 677 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 678 | lis3dh_get_slave_descr); | ||
| 679 | |||
| 680 | kfree(private_data); | ||
| 681 | return 0; | ||
| 682 | } | ||
| 683 | |||
| 684 | static const struct i2c_device_id lis3dh_mod_id[] = { | ||
| 685 | { "lis3dh", ACCEL_ID_LIS3DH }, | ||
| 686 | {} | ||
| 687 | }; | ||
| 688 | |||
| 689 | MODULE_DEVICE_TABLE(i2c, lis3dh_mod_id); | ||
| 690 | |||
| 691 | static struct i2c_driver lis3dh_mod_driver = { | ||
| 692 | .class = I2C_CLASS_HWMON, | ||
| 693 | .probe = lis3dh_mod_probe, | ||
| 694 | .remove = lis3dh_mod_remove, | ||
| 695 | .id_table = lis3dh_mod_id, | ||
| 696 | .driver = { | ||
| 697 | .owner = THIS_MODULE, | ||
| 698 | .name = "lis3dh_mod", | ||
| 699 | }, | ||
| 700 | .address_list = normal_i2c, | ||
| 701 | }; | ||
| 702 | |||
| 703 | static int __init lis3dh_mod_init(void) | ||
| 704 | { | ||
| 705 | int res = i2c_add_driver(&lis3dh_mod_driver); | ||
| 706 | pr_info("%s: Probe name %s\n", __func__, "lis3dh_mod"); | ||
| 707 | if (res) | ||
| 708 | pr_err("%s failed\n", __func__); | ||
| 709 | return res; | ||
| 710 | } | ||
| 711 | |||
| 712 | static void __exit lis3dh_mod_exit(void) | ||
| 713 | { | ||
| 714 | pr_info("%s\n", __func__); | ||
| 715 | i2c_del_driver(&lis3dh_mod_driver); | ||
| 716 | } | ||
| 717 | |||
| 718 | module_init(lis3dh_mod_init); | ||
| 719 | module_exit(lis3dh_mod_exit); | ||
| 720 | |||
| 721 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 722 | MODULE_DESCRIPTION("Driver to integrate LIS3DH sensor with the MPU"); | ||
| 723 | MODULE_LICENSE("GPL"); | ||
| 724 | MODULE_ALIAS("lis3dh_mod"); | ||
| 725 | |||
| 726 | /* | ||
| 727 | * @} | ||
| 728 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lsm303dlx_a.c b/drivers/misc/inv_mpu/accel/lsm303dlx_a.c new file mode 100644 index 00000000000..576282a0fb1 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lsm303dlx_a.c | |||
| @@ -0,0 +1,881 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file lsm303dlx_a.c | ||
| 26 | * @brief Accelerometer setup and handling methods for ST LSM303DLH | ||
| 27 | * or LSM303DLM accel. | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* -------------------------------------------------------------------------- */ | ||
| 31 | |||
| 32 | #include <linux/i2c.h> | ||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/moduleparam.h> | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/errno.h> | ||
| 37 | #include <linux/slab.h> | ||
| 38 | #include <linux/delay.h> | ||
| 39 | #include "mpu-dev.h" | ||
| 40 | |||
| 41 | #include <log.h> | ||
| 42 | #include <linux/mpu.h> | ||
| 43 | #include "mlsl.h" | ||
| 44 | #include "mldl_cfg.h" | ||
| 45 | #undef MPL_LOG_TAG | ||
| 46 | #define MPL_LOG_TAG "MPL-acc" | ||
| 47 | |||
| 48 | /* -------------------------------------------------------------------------- */ | ||
| 49 | |||
| 50 | /* full scale setting - register & mask */ | ||
| 51 | #define LSM303DLx_CTRL_REG1 (0x20) | ||
| 52 | #define LSM303DLx_CTRL_REG2 (0x21) | ||
| 53 | #define LSM303DLx_CTRL_REG3 (0x22) | ||
| 54 | #define LSM303DLx_CTRL_REG4 (0x23) | ||
| 55 | #define LSM303DLx_CTRL_REG5 (0x24) | ||
| 56 | #define LSM303DLx_HP_FILTER_RESET (0x25) | ||
| 57 | #define LSM303DLx_REFERENCE (0x26) | ||
| 58 | #define LSM303DLx_STATUS_REG (0x27) | ||
| 59 | #define LSM303DLx_OUT_X_L (0x28) | ||
| 60 | #define LSM303DLx_OUT_X_H (0x29) | ||
| 61 | #define LSM303DLx_OUT_Y_L (0x2a) | ||
| 62 | #define LSM303DLx_OUT_Y_H (0x2b) | ||
| 63 | #define LSM303DLx_OUT_Z_L (0x2b) | ||
| 64 | #define LSM303DLx_OUT_Z_H (0x2d) | ||
| 65 | |||
| 66 | #define LSM303DLx_INT1_CFG (0x30) | ||
| 67 | #define LSM303DLx_INT1_SRC (0x31) | ||
| 68 | #define LSM303DLx_INT1_THS (0x32) | ||
| 69 | #define LSM303DLx_INT1_DURATION (0x33) | ||
| 70 | |||
| 71 | #define LSM303DLx_INT2_CFG (0x34) | ||
| 72 | #define LSM303DLx_INT2_SRC (0x35) | ||
| 73 | #define LSM303DLx_INT2_THS (0x36) | ||
| 74 | #define LSM303DLx_INT2_DURATION (0x37) | ||
| 75 | |||
| 76 | #define LSM303DLx_CTRL_MASK (0x30) | ||
| 77 | #define LSM303DLx_SLEEP_MASK (0x20) | ||
| 78 | #define LSM303DLx_PWR_MODE_NORMAL (0x20) | ||
| 79 | |||
| 80 | #define LSM303DLx_MAX_DUR (0x7F) | ||
| 81 | |||
| 82 | /* -------------------------------------------------------------------------- */ | ||
| 83 | |||
| 84 | struct lsm303dlx_a_config { | ||
| 85 | unsigned int odr; | ||
| 86 | unsigned int fsr; /** < full scale range mg */ | ||
| 87 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
| 88 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
| 89 | unsigned char reg_ths; | ||
| 90 | unsigned char reg_dur; | ||
| 91 | unsigned char ctrl_reg1; | ||
| 92 | unsigned char irq_type; | ||
| 93 | unsigned char mot_int1_cfg; | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct lsm303dlx_a_private_data { | ||
| 97 | struct lsm303dlx_a_config suspend; | ||
| 98 | struct lsm303dlx_a_config resume; | ||
| 99 | }; | ||
| 100 | |||
| 101 | /* -------------------------------------------------------------------------- */ | ||
| 102 | |||
| 103 | static int lsm303dlx_a_set_ths(void *mlsl_handle, | ||
| 104 | struct ext_slave_platform_data *pdata, | ||
| 105 | struct lsm303dlx_a_config *config, | ||
| 106 | int apply, | ||
| 107 | long ths) | ||
| 108 | { | ||
| 109 | int result = INV_SUCCESS; | ||
| 110 | if ((unsigned int) ths >= config->fsr) | ||
| 111 | ths = (long) config->fsr - 1; | ||
| 112 | |||
| 113 | if (ths < 0) | ||
| 114 | ths = 0; | ||
| 115 | |||
| 116 | config->ths = ths; | ||
| 117 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
| 118 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
| 119 | if (apply) | ||
| 120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 121 | LSM303DLx_INT1_THS, | ||
| 122 | config->reg_ths); | ||
| 123 | return result; | ||
| 124 | } | ||
| 125 | |||
| 126 | static int lsm303dlx_a_set_dur(void *mlsl_handle, | ||
| 127 | struct ext_slave_platform_data *pdata, | ||
| 128 | struct lsm303dlx_a_config *config, | ||
| 129 | int apply, | ||
| 130 | long dur) | ||
| 131 | { | ||
| 132 | int result = INV_SUCCESS; | ||
| 133 | long reg_dur = (dur * config->odr) / 1000000L; | ||
| 134 | config->dur = dur; | ||
| 135 | |||
| 136 | if (reg_dur > LSM303DLx_MAX_DUR) | ||
| 137 | reg_dur = LSM303DLx_MAX_DUR; | ||
| 138 | |||
| 139 | config->reg_dur = (unsigned char) reg_dur; | ||
| 140 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
| 141 | if (apply) | ||
| 142 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 143 | LSM303DLx_INT1_DURATION, | ||
| 144 | (unsigned char)reg_dur); | ||
| 145 | return result; | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
| 150 | * duration will not be used uless the type is MOT or NMOT. | ||
| 151 | * | ||
| 152 | * @param config configuration to apply to, suspend or resume | ||
| 153 | * @param irq_type The type of IRQ. Valid values are | ||
| 154 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 155 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 156 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 157 | */ | ||
| 158 | static int lsm303dlx_a_set_irq(void *mlsl_handle, | ||
| 159 | struct ext_slave_platform_data *pdata, | ||
| 160 | struct lsm303dlx_a_config *config, | ||
| 161 | int apply, | ||
| 162 | long irq_type) | ||
| 163 | { | ||
| 164 | int result = INV_SUCCESS; | ||
| 165 | unsigned char reg1; | ||
| 166 | unsigned char reg2; | ||
| 167 | |||
| 168 | config->irq_type = (unsigned char)irq_type; | ||
| 169 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 170 | reg1 = 0x02; | ||
| 171 | reg2 = 0x00; | ||
| 172 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 173 | reg1 = 0x00; | ||
| 174 | reg2 = config->mot_int1_cfg; | ||
| 175 | } else { | ||
| 176 | reg1 = 0x00; | ||
| 177 | reg2 = 0x00; | ||
| 178 | } | ||
| 179 | |||
| 180 | if (apply) { | ||
| 181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 182 | LSM303DLx_CTRL_REG3, reg1); | ||
| 183 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 184 | LSM303DLx_INT1_CFG, reg2); | ||
| 185 | } | ||
| 186 | |||
| 187 | return result; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * @brief Set the output data rate for the particular configuration. | ||
| 192 | * | ||
| 193 | * @param mlsl_handle | ||
| 194 | * the handle to the serial channel the device is connected to. | ||
| 195 | * @param pdata | ||
| 196 | * a pointer to the slave platform data. | ||
| 197 | * @param config | ||
| 198 | * Config to modify with new ODR. | ||
| 199 | * @param apply | ||
| 200 | * whether to apply immediately or save the settings to be applied | ||
| 201 | * at the next resume. | ||
| 202 | * @param odr | ||
| 203 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 204 | * | ||
| 205 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 206 | */ | ||
| 207 | static int lsm303dlx_a_set_odr(void *mlsl_handle, | ||
| 208 | struct ext_slave_platform_data *pdata, | ||
| 209 | struct lsm303dlx_a_config *config, | ||
| 210 | int apply, | ||
| 211 | long odr) | ||
| 212 | { | ||
| 213 | unsigned char bits; | ||
| 214 | int result = INV_SUCCESS; | ||
| 215 | |||
| 216 | /* normal power modes */ | ||
| 217 | if (odr > 400000) { | ||
| 218 | config->odr = 1000000; | ||
| 219 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x18; | ||
| 220 | } else if (odr > 100000) { | ||
| 221 | config->odr = 400000; | ||
| 222 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x10; | ||
| 223 | } else if (odr > 50000) { | ||
| 224 | config->odr = 100000; | ||
| 225 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x08; | ||
| 226 | } else if (odr > 10000) { | ||
| 227 | config->odr = 50000; | ||
| 228 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x00; | ||
| 229 | /* low power modes */ | ||
| 230 | } else if (odr > 5000) { | ||
| 231 | config->odr = 10000; | ||
| 232 | bits = 0xC0; | ||
| 233 | } else if (odr > 2000) { | ||
| 234 | config->odr = 5000; | ||
| 235 | bits = 0xA0; | ||
| 236 | } else if (odr > 1000) { | ||
| 237 | config->odr = 2000; | ||
| 238 | bits = 0x80; | ||
| 239 | } else if (odr > 500) { | ||
| 240 | config->odr = 1000; | ||
| 241 | bits = 0x60; | ||
| 242 | } else if (odr > 0) { | ||
| 243 | config->odr = 500; | ||
| 244 | bits = 0x40; | ||
| 245 | } else { | ||
| 246 | config->odr = 0; | ||
| 247 | bits = 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7); | ||
| 251 | lsm303dlx_a_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
| 252 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
| 253 | if (apply) | ||
| 254 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 255 | LSM303DLx_CTRL_REG1, | ||
| 256 | config->ctrl_reg1); | ||
| 257 | return result; | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * @brief Set the full scale range of the accels | ||
| 262 | * | ||
| 263 | * @param mlsl_handle | ||
| 264 | * the handle to the serial channel the device is connected to. | ||
| 265 | * @param pdata | ||
| 266 | * a pointer to the slave platform data. | ||
| 267 | * @param config | ||
| 268 | * pointer to configuration. | ||
| 269 | * @param apply | ||
| 270 | * whether to apply immediately or save the settings to be applied | ||
| 271 | * at the next resume. | ||
| 272 | * @param fsr | ||
| 273 | * requested full scale range. | ||
| 274 | * | ||
| 275 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 276 | */ | ||
| 277 | static int lsm303dlx_a_set_fsr(void *mlsl_handle, | ||
| 278 | struct ext_slave_platform_data *pdata, | ||
| 279 | struct lsm303dlx_a_config *config, | ||
| 280 | int apply, | ||
| 281 | long fsr) | ||
| 282 | { | ||
| 283 | unsigned char reg1 = 0x40; | ||
| 284 | int result = INV_SUCCESS; | ||
| 285 | |||
| 286 | if (fsr <= 2048) { | ||
| 287 | config->fsr = 2048; | ||
| 288 | } else if (fsr <= 4096) { | ||
| 289 | reg1 |= 0x30; | ||
| 290 | config->fsr = 4096; | ||
| 291 | } else { | ||
| 292 | reg1 |= 0x10; | ||
| 293 | config->fsr = 8192; | ||
| 294 | } | ||
| 295 | |||
| 296 | lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
| 297 | config, apply, config->ths); | ||
| 298 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 299 | if (apply) | ||
| 300 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 301 | LSM303DLx_CTRL_REG4, reg1); | ||
| 302 | |||
| 303 | return result; | ||
| 304 | } | ||
| 305 | |||
| 306 | /** | ||
| 307 | * @brief suspends the device to put it in its lowest power mode. | ||
| 308 | * | ||
| 309 | * @param mlsl_handle | ||
| 310 | * the handle to the serial channel the device is connected to. | ||
| 311 | * @param slave | ||
| 312 | * a pointer to the slave descriptor data structure. | ||
| 313 | * @param pdata | ||
| 314 | * a pointer to the slave platform data. | ||
| 315 | * | ||
| 316 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 317 | */ | ||
| 318 | static int lsm303dlx_a_suspend(void *mlsl_handle, | ||
| 319 | struct ext_slave_descr *slave, | ||
| 320 | struct ext_slave_platform_data *pdata) | ||
| 321 | { | ||
| 322 | int result = INV_SUCCESS; | ||
| 323 | unsigned char reg1; | ||
| 324 | unsigned char reg2; | ||
| 325 | struct lsm303dlx_a_private_data *private_data = | ||
| 326 | (struct lsm303dlx_a_private_data *)(pdata->private_data); | ||
| 327 | |||
| 328 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 329 | LSM303DLx_CTRL_REG1, | ||
| 330 | private_data->suspend.ctrl_reg1); | ||
| 331 | |||
| 332 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 333 | LSM303DLx_CTRL_REG2, 0x0f); | ||
| 334 | reg1 = 0x40; | ||
| 335 | if (private_data->suspend.fsr == 8192) | ||
| 336 | reg1 |= 0x30; | ||
| 337 | else if (private_data->suspend.fsr == 4096) | ||
| 338 | reg1 |= 0x10; | ||
| 339 | /* else bits [4..5] are already zero */ | ||
| 340 | |||
| 341 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 342 | LSM303DLx_CTRL_REG4, reg1); | ||
| 343 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 344 | LSM303DLx_INT1_THS, | ||
| 345 | private_data->suspend.reg_ths); | ||
| 346 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 347 | LSM303DLx_INT1_DURATION, | ||
| 348 | private_data->suspend.reg_dur); | ||
| 349 | |||
| 350 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 351 | reg1 = 0x02; | ||
| 352 | reg2 = 0x00; | ||
| 353 | } else if (private_data->suspend.irq_type == | ||
| 354 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 355 | reg1 = 0x00; | ||
| 356 | reg2 = private_data->suspend.mot_int1_cfg; | ||
| 357 | } else { | ||
| 358 | reg1 = 0x00; | ||
| 359 | reg2 = 0x00; | ||
| 360 | } | ||
| 361 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 362 | LSM303DLx_CTRL_REG3, reg1); | ||
| 363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 364 | LSM303DLx_INT1_CFG, reg2); | ||
| 365 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 366 | LSM303DLx_HP_FILTER_RESET, 1, ®1); | ||
| 367 | return result; | ||
| 368 | } | ||
| 369 | |||
| 370 | /** | ||
| 371 | * @brief resume the device in the proper power state given the configuration | ||
| 372 | * chosen. | ||
| 373 | * | ||
| 374 | * @param mlsl_handle | ||
| 375 | * the handle to the serial channel the device is connected to. | ||
| 376 | * @param slave | ||
| 377 | * a pointer to the slave descriptor data structure. | ||
| 378 | * @param pdata | ||
| 379 | * a pointer to the slave platform data. | ||
| 380 | * | ||
| 381 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 382 | */ | ||
| 383 | static int lsm303dlx_a_resume(void *mlsl_handle, | ||
| 384 | struct ext_slave_descr *slave, | ||
| 385 | struct ext_slave_platform_data *pdata) | ||
| 386 | { | ||
| 387 | int result = INV_SUCCESS; | ||
| 388 | unsigned char reg1; | ||
| 389 | unsigned char reg2; | ||
| 390 | struct lsm303dlx_a_private_data *private_data = | ||
| 391 | (struct lsm303dlx_a_private_data *)(pdata->private_data); | ||
| 392 | |||
| 393 | |||
| 394 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 395 | LSM303DLx_CTRL_REG1, | ||
| 396 | private_data->resume.ctrl_reg1); | ||
| 397 | if (result) { | ||
| 398 | LOG_RESULT_LOCATION(result); | ||
| 399 | return result; | ||
| 400 | } | ||
| 401 | msleep(6); | ||
| 402 | |||
| 403 | /* Full Scale */ | ||
| 404 | reg1 = 0x40; | ||
| 405 | if (private_data->resume.fsr == 8192) | ||
| 406 | reg1 |= 0x30; | ||
| 407 | else if (private_data->resume.fsr == 4096) | ||
| 408 | reg1 |= 0x10; | ||
| 409 | |||
| 410 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 411 | LSM303DLx_CTRL_REG4, reg1); | ||
| 412 | if (result) { | ||
| 413 | LOG_RESULT_LOCATION(result); | ||
| 414 | return result; | ||
| 415 | } | ||
| 416 | |||
| 417 | /* Configure high pass filter */ | ||
| 418 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 419 | LSM303DLx_CTRL_REG2, 0x0F); | ||
| 420 | if (result) { | ||
| 421 | LOG_RESULT_LOCATION(result); | ||
| 422 | return result; | ||
| 423 | } | ||
| 424 | |||
| 425 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 426 | reg1 = 0x02; | ||
| 427 | reg2 = 0x00; | ||
| 428 | } else if (private_data->resume.irq_type == | ||
| 429 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
| 430 | reg1 = 0x00; | ||
| 431 | reg2 = private_data->resume.mot_int1_cfg; | ||
| 432 | } else { | ||
| 433 | reg1 = 0x00; | ||
| 434 | reg2 = 0x00; | ||
| 435 | } | ||
| 436 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 437 | LSM303DLx_CTRL_REG3, reg1); | ||
| 438 | if (result) { | ||
| 439 | LOG_RESULT_LOCATION(result); | ||
| 440 | return result; | ||
| 441 | } | ||
| 442 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 443 | LSM303DLx_INT1_THS, | ||
| 444 | private_data->resume.reg_ths); | ||
| 445 | if (result) { | ||
| 446 | LOG_RESULT_LOCATION(result); | ||
| 447 | return result; | ||
| 448 | } | ||
| 449 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 450 | LSM303DLx_INT1_DURATION, | ||
| 451 | private_data->resume.reg_dur); | ||
| 452 | if (result) { | ||
| 453 | LOG_RESULT_LOCATION(result); | ||
| 454 | return result; | ||
| 455 | } | ||
| 456 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 457 | LSM303DLx_INT1_CFG, reg2); | ||
| 458 | if (result) { | ||
| 459 | LOG_RESULT_LOCATION(result); | ||
| 460 | return result; | ||
| 461 | } | ||
| 462 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 463 | LSM303DLx_HP_FILTER_RESET, 1, ®1); | ||
| 464 | if (result) { | ||
| 465 | LOG_RESULT_LOCATION(result); | ||
| 466 | return result; | ||
| 467 | } | ||
| 468 | return result; | ||
| 469 | } | ||
| 470 | |||
| 471 | /** | ||
| 472 | * @brief read the sensor data from the device. | ||
| 473 | * | ||
| 474 | * @param mlsl_handle | ||
| 475 | * the handle to the serial channel the device is connected to. | ||
| 476 | * @param slave | ||
| 477 | * a pointer to the slave descriptor data structure. | ||
| 478 | * @param pdata | ||
| 479 | * a pointer to the slave platform data. | ||
| 480 | * @param data | ||
| 481 | * a buffer to store the data read. | ||
| 482 | * | ||
| 483 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 484 | */ | ||
| 485 | static int lsm303dlx_a_read(void *mlsl_handle, | ||
| 486 | struct ext_slave_descr *slave, | ||
| 487 | struct ext_slave_platform_data *pdata, | ||
| 488 | unsigned char *data) | ||
| 489 | { | ||
| 490 | int result = INV_SUCCESS; | ||
| 491 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 492 | LSM303DLx_STATUS_REG, 1, data); | ||
| 493 | if (data[0] & 0x0F) { | ||
| 494 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 495 | slave->read_reg, slave->read_len, data); | ||
| 496 | return result; | ||
| 497 | } else | ||
| 498 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
| 499 | } | ||
| 500 | |||
| 501 | /** | ||
| 502 | * @brief one-time device driver initialization function. | ||
| 503 | * If the driver is built as a kernel module, this function will be | ||
| 504 | * called when the module is loaded in the kernel. | ||
| 505 | * If the driver is built-in in the kernel, this function will be | ||
| 506 | * called at boot time. | ||
| 507 | * | ||
| 508 | * @param mlsl_handle | ||
| 509 | * the handle to the serial channel the device is connected to. | ||
| 510 | * @param slave | ||
| 511 | * a pointer to the slave descriptor data structure. | ||
| 512 | * @param pdata | ||
| 513 | * a pointer to the slave platform data. | ||
| 514 | * | ||
| 515 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 516 | */ | ||
| 517 | static int lsm303dlx_a_init(void *mlsl_handle, | ||
| 518 | struct ext_slave_descr *slave, | ||
| 519 | struct ext_slave_platform_data *pdata) | ||
| 520 | { | ||
| 521 | long range; | ||
| 522 | struct lsm303dlx_a_private_data *private_data; | ||
| 523 | private_data = (struct lsm303dlx_a_private_data *) | ||
| 524 | kzalloc(sizeof(struct lsm303dlx_a_private_data), GFP_KERNEL); | ||
| 525 | |||
| 526 | if (!private_data) | ||
| 527 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 528 | |||
| 529 | pdata->private_data = private_data; | ||
| 530 | |||
| 531 | private_data->resume.ctrl_reg1 = 0x37; | ||
| 532 | private_data->suspend.ctrl_reg1 = 0x47; | ||
| 533 | private_data->resume.mot_int1_cfg = 0x95; | ||
| 534 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
| 535 | |||
| 536 | lsm303dlx_a_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 537 | false, 0); | ||
| 538 | lsm303dlx_a_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 539 | false, 200000); | ||
| 540 | |||
| 541 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 542 | lsm303dlx_a_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 543 | false, range); | ||
| 544 | lsm303dlx_a_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 545 | false, range); | ||
| 546 | |||
| 547 | lsm303dlx_a_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 548 | false, 80); | ||
| 549 | lsm303dlx_a_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
| 550 | false, 40); | ||
| 551 | |||
| 552 | lsm303dlx_a_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 553 | false, 1000); | ||
| 554 | lsm303dlx_a_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 555 | false, 2540); | ||
| 556 | |||
| 557 | lsm303dlx_a_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 558 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 559 | lsm303dlx_a_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 560 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 561 | return INV_SUCCESS; | ||
| 562 | } | ||
| 563 | |||
| 564 | /** | ||
| 565 | * @brief one-time device driver exit function. | ||
| 566 | * If the driver is built as a kernel module, this function will be | ||
| 567 | * called when the module is removed from the kernel. | ||
| 568 | * | ||
| 569 | * @param mlsl_handle | ||
| 570 | * the handle to the serial channel the device is connected to. | ||
| 571 | * @param slave | ||
| 572 | * a pointer to the slave descriptor data structure. | ||
| 573 | * @param pdata | ||
| 574 | * a pointer to the slave platform data. | ||
| 575 | * | ||
| 576 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 577 | */ | ||
| 578 | static int lsm303dlx_a_exit(void *mlsl_handle, | ||
| 579 | struct ext_slave_descr *slave, | ||
| 580 | struct ext_slave_platform_data *pdata) | ||
| 581 | { | ||
| 582 | kfree(pdata->private_data); | ||
| 583 | return INV_SUCCESS; | ||
| 584 | } | ||
| 585 | |||
| 586 | /** | ||
| 587 | * @brief device configuration facility. | ||
| 588 | * | ||
| 589 | * @param mlsl_handle | ||
| 590 | * the handle to the serial channel the device is connected to. | ||
| 591 | * @param slave | ||
| 592 | * a pointer to the slave descriptor data structure. | ||
| 593 | * @param pdata | ||
| 594 | * a pointer to the slave platform data. | ||
| 595 | * @param data | ||
| 596 | * a pointer to the configuration data structure. | ||
| 597 | * | ||
| 598 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 599 | */ | ||
| 600 | static int lsm303dlx_a_config(void *mlsl_handle, | ||
| 601 | struct ext_slave_descr *slave, | ||
| 602 | struct ext_slave_platform_data *pdata, | ||
| 603 | struct ext_slave_config *data) | ||
| 604 | { | ||
| 605 | struct lsm303dlx_a_private_data *private_data = pdata->private_data; | ||
| 606 | if (!data->data) | ||
| 607 | return INV_ERROR_INVALID_PARAMETER; | ||
| 608 | |||
| 609 | switch (data->key) { | ||
| 610 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 611 | return lsm303dlx_a_set_odr(mlsl_handle, pdata, | ||
| 612 | &private_data->suspend, | ||
| 613 | data->apply, | ||
| 614 | *((long *)data->data)); | ||
| 615 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 616 | return lsm303dlx_a_set_odr(mlsl_handle, pdata, | ||
| 617 | &private_data->resume, | ||
| 618 | data->apply, | ||
| 619 | *((long *)data->data)); | ||
| 620 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 621 | return lsm303dlx_a_set_fsr(mlsl_handle, pdata, | ||
| 622 | &private_data->suspend, | ||
| 623 | data->apply, | ||
| 624 | *((long *)data->data)); | ||
| 625 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 626 | return lsm303dlx_a_set_fsr(mlsl_handle, pdata, | ||
| 627 | &private_data->resume, | ||
| 628 | data->apply, | ||
| 629 | *((long *)data->data)); | ||
| 630 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 631 | return lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
| 632 | &private_data->suspend, | ||
| 633 | data->apply, | ||
| 634 | *((long *)data->data)); | ||
| 635 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 636 | return lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
| 637 | &private_data->resume, | ||
| 638 | data->apply, | ||
| 639 | *((long *)data->data)); | ||
| 640 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 641 | return lsm303dlx_a_set_dur(mlsl_handle, pdata, | ||
| 642 | &private_data->suspend, | ||
| 643 | data->apply, | ||
| 644 | *((long *)data->data)); | ||
| 645 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 646 | return lsm303dlx_a_set_dur(mlsl_handle, pdata, | ||
| 647 | &private_data->resume, | ||
| 648 | data->apply, | ||
| 649 | *((long *)data->data)); | ||
| 650 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 651 | return lsm303dlx_a_set_irq(mlsl_handle, pdata, | ||
| 652 | &private_data->suspend, | ||
| 653 | data->apply, | ||
| 654 | *((long *)data->data)); | ||
| 655 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 656 | return lsm303dlx_a_set_irq(mlsl_handle, pdata, | ||
| 657 | &private_data->resume, | ||
| 658 | data->apply, | ||
| 659 | *((long *)data->data)); | ||
| 660 | default: | ||
| 661 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 662 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 663 | }; | ||
| 664 | |||
| 665 | return INV_SUCCESS; | ||
| 666 | } | ||
| 667 | |||
| 668 | /** | ||
| 669 | * @brief facility to retrieve the device configuration. | ||
| 670 | * | ||
| 671 | * @param mlsl_handle | ||
| 672 | * the handle to the serial channel the device is connected to. | ||
| 673 | * @param slave | ||
| 674 | * a pointer to the slave descriptor data structure. | ||
| 675 | * @param pdata | ||
| 676 | * a pointer to the slave platform data. | ||
| 677 | * @param data | ||
| 678 | * a pointer to store the returned configuration data structure. | ||
| 679 | * | ||
| 680 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 681 | */ | ||
| 682 | static int lsm303dlx_a_get_config(void *mlsl_handle, | ||
| 683 | struct ext_slave_descr *slave, | ||
| 684 | struct ext_slave_platform_data *pdata, | ||
| 685 | struct ext_slave_config *data) | ||
| 686 | { | ||
| 687 | struct lsm303dlx_a_private_data *private_data = pdata->private_data; | ||
| 688 | if (!data->data) | ||
| 689 | return INV_ERROR_INVALID_PARAMETER; | ||
| 690 | |||
| 691 | switch (data->key) { | ||
| 692 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 693 | (*(unsigned long *)data->data) = | ||
| 694 | (unsigned long) private_data->suspend.odr; | ||
| 695 | break; | ||
| 696 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 697 | (*(unsigned long *)data->data) = | ||
| 698 | (unsigned long) private_data->resume.odr; | ||
| 699 | break; | ||
| 700 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 701 | (*(unsigned long *)data->data) = | ||
| 702 | (unsigned long) private_data->suspend.fsr; | ||
| 703 | break; | ||
| 704 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 705 | (*(unsigned long *)data->data) = | ||
| 706 | (unsigned long) private_data->resume.fsr; | ||
| 707 | break; | ||
| 708 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 709 | (*(unsigned long *)data->data) = | ||
| 710 | (unsigned long) private_data->suspend.ths; | ||
| 711 | break; | ||
| 712 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 713 | (*(unsigned long *)data->data) = | ||
| 714 | (unsigned long) private_data->resume.ths; | ||
| 715 | break; | ||
| 716 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 717 | (*(unsigned long *)data->data) = | ||
| 718 | (unsigned long) private_data->suspend.dur; | ||
| 719 | break; | ||
| 720 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 721 | (*(unsigned long *)data->data) = | ||
| 722 | (unsigned long) private_data->resume.dur; | ||
| 723 | break; | ||
| 724 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 725 | (*(unsigned long *)data->data) = | ||
| 726 | (unsigned long) private_data->suspend.irq_type; | ||
| 727 | break; | ||
| 728 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 729 | (*(unsigned long *)data->data) = | ||
| 730 | (unsigned long) private_data->resume.irq_type; | ||
| 731 | break; | ||
| 732 | default: | ||
| 733 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 734 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 735 | }; | ||
| 736 | |||
| 737 | return INV_SUCCESS; | ||
| 738 | } | ||
| 739 | |||
| 740 | static struct ext_slave_descr lsm303dlx_a_descr = { | ||
| 741 | .init = lsm303dlx_a_init, | ||
| 742 | .exit = lsm303dlx_a_exit, | ||
| 743 | .suspend = lsm303dlx_a_suspend, | ||
| 744 | .resume = lsm303dlx_a_resume, | ||
| 745 | .read = lsm303dlx_a_read, | ||
| 746 | .config = lsm303dlx_a_config, | ||
| 747 | .get_config = lsm303dlx_a_get_config, | ||
| 748 | .name = "lsm303dlx_a", | ||
| 749 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 750 | .id = ACCEL_ID_LSM303DLX, | ||
| 751 | .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */ | ||
| 752 | .read_len = 6, | ||
| 753 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 754 | .range = {2, 480}, | ||
| 755 | .trigger = NULL, | ||
| 756 | }; | ||
| 757 | |||
| 758 | static | ||
| 759 | struct ext_slave_descr *lsm303dlx_a_get_slave_descr(void) | ||
| 760 | { | ||
| 761 | return &lsm303dlx_a_descr; | ||
| 762 | } | ||
| 763 | |||
| 764 | /* -------------------------------------------------------------------------- */ | ||
| 765 | struct lsm303dlx_a_mod_private_data { | ||
| 766 | struct i2c_client *client; | ||
| 767 | struct ext_slave_platform_data *pdata; | ||
| 768 | }; | ||
| 769 | |||
| 770 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 771 | |||
| 772 | static int lsm303dlx_a_mod_probe(struct i2c_client *client, | ||
| 773 | const struct i2c_device_id *devid) | ||
| 774 | { | ||
| 775 | struct ext_slave_platform_data *pdata; | ||
| 776 | struct lsm303dlx_a_mod_private_data *private_data; | ||
| 777 | int result = 0; | ||
| 778 | |||
| 779 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 780 | |||
| 781 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 782 | result = -ENODEV; | ||
| 783 | goto out_no_free; | ||
| 784 | } | ||
| 785 | |||
| 786 | pdata = client->dev.platform_data; | ||
| 787 | if (!pdata) { | ||
| 788 | dev_err(&client->adapter->dev, | ||
| 789 | "Missing platform data for slave %s\n", devid->name); | ||
| 790 | result = -EFAULT; | ||
| 791 | goto out_no_free; | ||
| 792 | } | ||
| 793 | |||
| 794 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 795 | if (!private_data) { | ||
| 796 | result = -ENOMEM; | ||
| 797 | goto out_no_free; | ||
| 798 | } | ||
| 799 | |||
| 800 | i2c_set_clientdata(client, private_data); | ||
| 801 | private_data->client = client; | ||
| 802 | private_data->pdata = pdata; | ||
| 803 | |||
| 804 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 805 | lsm303dlx_a_get_slave_descr); | ||
| 806 | if (result) { | ||
| 807 | dev_err(&client->adapter->dev, | ||
| 808 | "Slave registration failed: %s, %d\n", | ||
| 809 | devid->name, result); | ||
| 810 | goto out_free_memory; | ||
| 811 | } | ||
| 812 | |||
| 813 | return result; | ||
| 814 | |||
| 815 | out_free_memory: | ||
| 816 | kfree(private_data); | ||
| 817 | out_no_free: | ||
| 818 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 819 | return result; | ||
| 820 | |||
| 821 | } | ||
| 822 | |||
| 823 | static int lsm303dlx_a_mod_remove(struct i2c_client *client) | ||
| 824 | { | ||
| 825 | struct lsm303dlx_a_mod_private_data *private_data = | ||
| 826 | i2c_get_clientdata(client); | ||
| 827 | |||
| 828 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 829 | |||
| 830 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 831 | lsm303dlx_a_get_slave_descr); | ||
| 832 | |||
| 833 | kfree(private_data); | ||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 837 | static const struct i2c_device_id lsm303dlx_a_mod_id[] = { | ||
| 838 | { "lsm303dlx", ACCEL_ID_LSM303DLX }, | ||
| 839 | {} | ||
| 840 | }; | ||
| 841 | |||
| 842 | MODULE_DEVICE_TABLE(i2c, lsm303dlx_a_mod_id); | ||
| 843 | |||
| 844 | static struct i2c_driver lsm303dlx_a_mod_driver = { | ||
| 845 | .class = I2C_CLASS_HWMON, | ||
| 846 | .probe = lsm303dlx_a_mod_probe, | ||
| 847 | .remove = lsm303dlx_a_mod_remove, | ||
| 848 | .id_table = lsm303dlx_a_mod_id, | ||
| 849 | .driver = { | ||
| 850 | .owner = THIS_MODULE, | ||
| 851 | .name = "lsm303dlx_a_mod", | ||
| 852 | }, | ||
| 853 | .address_list = normal_i2c, | ||
| 854 | }; | ||
| 855 | |||
| 856 | static int __init lsm303dlx_a_mod_init(void) | ||
| 857 | { | ||
| 858 | int res = i2c_add_driver(&lsm303dlx_a_mod_driver); | ||
| 859 | pr_info("%s: Probe name %s\n", __func__, "lsm303dlx_a_mod"); | ||
| 860 | if (res) | ||
| 861 | pr_err("%s failed\n", __func__); | ||
| 862 | return res; | ||
| 863 | } | ||
| 864 | |||
| 865 | static void __exit lsm303dlx_a_mod_exit(void) | ||
| 866 | { | ||
| 867 | pr_info("%s\n", __func__); | ||
| 868 | i2c_del_driver(&lsm303dlx_a_mod_driver); | ||
| 869 | } | ||
| 870 | |||
| 871 | module_init(lsm303dlx_a_mod_init); | ||
| 872 | module_exit(lsm303dlx_a_mod_exit); | ||
| 873 | |||
| 874 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 875 | MODULE_DESCRIPTION("Driver to integrate LSM303DLX_A sensor with the MPU"); | ||
| 876 | MODULE_LICENSE("GPL"); | ||
| 877 | MODULE_ALIAS("lsm303dlx_a_mod"); | ||
| 878 | |||
| 879 | /** | ||
| 880 | * @} | ||
| 881 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mma8450.c b/drivers/misc/inv_mpu/accel/mma8450.c new file mode 100644 index 00000000000..f698ee98bf5 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mma8450.c | |||
| @@ -0,0 +1,804 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mma8450.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Freescale MMA8450. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-acc" | ||
| 46 | |||
| 47 | /* full scale setting - register & mask */ | ||
| 48 | #define ACCEL_MMA8450_XYZ_DATA_CFG (0x16) | ||
| 49 | |||
| 50 | #define ACCEL_MMA8450_CTRL_REG1 (0x38) | ||
| 51 | #define ACCEL_MMA8450_CTRL_REG2 (0x39) | ||
| 52 | #define ACCEL_MMA8450_CTRL_REG4 (0x3B) | ||
| 53 | #define ACCEL_MMA8450_CTRL_REG5 (0x3C) | ||
| 54 | |||
| 55 | #define ACCEL_MMA8450_CTRL_REG (0x38) | ||
| 56 | #define ACCEL_MMA8450_CTRL_MASK (0x03) | ||
| 57 | |||
| 58 | #define ACCEL_MMA8450_SLEEP_MASK (0x03) | ||
| 59 | |||
| 60 | /* -------------------------------------------------------------------------- */ | ||
| 61 | |||
| 62 | struct mma8450_config { | ||
| 63 | unsigned int odr; | ||
| 64 | unsigned int fsr; /** < full scale range mg */ | ||
| 65 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
| 66 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
| 67 | unsigned char reg_ths; | ||
| 68 | unsigned char reg_dur; | ||
| 69 | unsigned char ctrl_reg1; | ||
| 70 | unsigned char irq_type; | ||
| 71 | unsigned char mot_int1_cfg; | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct mma8450_private_data { | ||
| 75 | struct mma8450_config suspend; | ||
| 76 | struct mma8450_config resume; | ||
| 77 | }; | ||
| 78 | |||
| 79 | |||
| 80 | /* -------------------------------------------------------------------------- */ | ||
| 81 | |||
| 82 | static int mma8450_set_ths(void *mlsl_handle, | ||
| 83 | struct ext_slave_platform_data *pdata, | ||
| 84 | struct mma8450_config *config, | ||
| 85 | int apply, | ||
| 86 | long ths) | ||
| 87 | { | ||
| 88 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int mma8450_set_dur(void *mlsl_handle, | ||
| 92 | struct ext_slave_platform_data *pdata, | ||
| 93 | struct mma8450_config *config, | ||
| 94 | int apply, | ||
| 95 | long dur) | ||
| 96 | { | ||
| 97 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
| 102 | * Threshold and duration will not be used unless the type is MOT or | ||
| 103 | * NMOT. | ||
| 104 | * | ||
| 105 | * @param mlsl_handle | ||
| 106 | * the handle to the serial channel the device is connected to. | ||
| 107 | * @param pdata | ||
| 108 | * a pointer to the slave platform data. | ||
| 109 | * @param config | ||
| 110 | * configuration to apply to, suspend or resume | ||
| 111 | * @param apply | ||
| 112 | * whether to apply immediately or save the settings to be applied | ||
| 113 | * at the next resume. | ||
| 114 | * @param irq_type | ||
| 115 | * the type of IRQ. Valid values are | ||
| 116 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 117 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 118 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 119 | * | ||
| 120 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 121 | */ | ||
| 122 | static int mma8450_set_irq(void *mlsl_handle, | ||
| 123 | struct ext_slave_platform_data *pdata, | ||
| 124 | struct mma8450_config *config, | ||
| 125 | int apply, | ||
| 126 | long irq_type) | ||
| 127 | { | ||
| 128 | int result = INV_SUCCESS; | ||
| 129 | unsigned char reg1; | ||
| 130 | unsigned char reg2; | ||
| 131 | unsigned char reg3; | ||
| 132 | |||
| 133 | config->irq_type = (unsigned char)irq_type; | ||
| 134 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 135 | reg1 = 0x01; | ||
| 136 | reg2 = 0x01; | ||
| 137 | reg3 = 0x07; | ||
| 138 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) { | ||
| 139 | reg1 = 0x00; | ||
| 140 | reg2 = 0x00; | ||
| 141 | reg3 = 0x00; | ||
| 142 | } else { | ||
| 143 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 144 | } | ||
| 145 | |||
| 146 | if (apply) { | ||
| 147 | /* XYZ_DATA_CFG: event flag enabled on Z axis */ | ||
| 148 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 149 | ACCEL_MMA8450_XYZ_DATA_CFG, reg3); | ||
| 150 | if (result) { | ||
| 151 | LOG_RESULT_LOCATION(result); | ||
| 152 | return result; | ||
| 153 | } | ||
| 154 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 155 | ACCEL_MMA8450_CTRL_REG4, reg1); | ||
| 156 | if (result) { | ||
| 157 | LOG_RESULT_LOCATION(result); | ||
| 158 | return result; | ||
| 159 | } | ||
| 160 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 161 | ACCEL_MMA8450_CTRL_REG5, reg2); | ||
| 162 | if (result) { | ||
| 163 | LOG_RESULT_LOCATION(result); | ||
| 164 | return result; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | return result; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * @brief Set the output data rate for the particular configuration. | ||
| 173 | * | ||
| 174 | * @param mlsl_handle | ||
| 175 | * the handle to the serial channel the device is connected to. | ||
| 176 | * @param pdata | ||
| 177 | * a pointer to the slave platform data. | ||
| 178 | * @param config | ||
| 179 | * Config to modify with new ODR. | ||
| 180 | * @param apply | ||
| 181 | * whether to apply immediately or save the settings to be applied | ||
| 182 | * at the next resume. | ||
| 183 | * @param odr | ||
| 184 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 185 | * | ||
| 186 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 187 | */ | ||
| 188 | static int mma8450_set_odr(void *mlsl_handle, | ||
| 189 | struct ext_slave_platform_data *pdata, | ||
| 190 | struct mma8450_config *config, | ||
| 191 | int apply, | ||
| 192 | long odr) | ||
| 193 | { | ||
| 194 | unsigned char bits; | ||
| 195 | int result = INV_SUCCESS; | ||
| 196 | |||
| 197 | if (odr > 200000) { | ||
| 198 | config->odr = 400000; | ||
| 199 | bits = 0x00; | ||
| 200 | } else if (odr > 100000) { | ||
| 201 | config->odr = 200000; | ||
| 202 | bits = 0x04; | ||
| 203 | } else if (odr > 50000) { | ||
| 204 | config->odr = 100000; | ||
| 205 | bits = 0x08; | ||
| 206 | } else if (odr > 25000) { | ||
| 207 | config->odr = 50000; | ||
| 208 | bits = 0x0C; | ||
| 209 | } else if (odr > 12500) { | ||
| 210 | config->odr = 25000; | ||
| 211 | bits = 0x40; /* Sleep -> Auto wake mode */ | ||
| 212 | } else if (odr > 1563) { | ||
| 213 | config->odr = 12500; | ||
| 214 | bits = 0x10; | ||
| 215 | } else if (odr > 0) { | ||
| 216 | config->odr = 1563; | ||
| 217 | bits = 0x14; | ||
| 218 | } else { | ||
| 219 | config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */ | ||
| 220 | config->odr = 0; | ||
| 221 | bits = 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x3); | ||
| 225 | if (apply) { | ||
| 226 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 227 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
| 228 | if (result) { | ||
| 229 | LOG_RESULT_LOCATION(result); | ||
| 230 | return result; | ||
| 231 | } | ||
| 232 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 233 | ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1); | ||
| 234 | if (result) { | ||
| 235 | LOG_RESULT_LOCATION(result); | ||
| 236 | return result; | ||
| 237 | } | ||
| 238 | MPL_LOGV("ODR: %d mHz, 0x%02x\n", | ||
| 239 | config->odr, (int)config->ctrl_reg1); | ||
| 240 | } | ||
| 241 | return result; | ||
| 242 | } | ||
| 243 | |||
| 244 | /** | ||
| 245 | * @brief Set the full scale range of the accels | ||
| 246 | * | ||
| 247 | * @param mlsl_handle | ||
| 248 | * the handle to the serial channel the device is connected to. | ||
| 249 | * @param pdata | ||
| 250 | * a pointer to the slave platform data. | ||
| 251 | * @param config | ||
| 252 | * pointer to configuration. | ||
| 253 | * @param apply | ||
| 254 | * whether to apply immediately or save the settings to be applied | ||
| 255 | * at the next resume. | ||
| 256 | * @param fsr | ||
| 257 | * requested full scale range. | ||
| 258 | * | ||
| 259 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 260 | */ | ||
| 261 | static int mma8450_set_fsr(void *mlsl_handle, | ||
| 262 | struct ext_slave_platform_data *pdata, | ||
| 263 | struct mma8450_config *config, | ||
| 264 | int apply, | ||
| 265 | long fsr) | ||
| 266 | { | ||
| 267 | unsigned char bits; | ||
| 268 | int result = INV_SUCCESS; | ||
| 269 | |||
| 270 | if (fsr <= 2000) { | ||
| 271 | bits = 0x01; | ||
| 272 | config->fsr = 2000; | ||
| 273 | } else if (fsr <= 4000) { | ||
| 274 | bits = 0x02; | ||
| 275 | config->fsr = 4000; | ||
| 276 | } else { | ||
| 277 | bits = 0x03; | ||
| 278 | config->fsr = 8000; | ||
| 279 | } | ||
| 280 | |||
| 281 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xFC); | ||
| 282 | if (apply) { | ||
| 283 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 284 | ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1); | ||
| 285 | if (result) { | ||
| 286 | LOG_RESULT_LOCATION(result); | ||
| 287 | return result; | ||
| 288 | } | ||
| 289 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
| 290 | } | ||
| 291 | return result; | ||
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * @brief suspends the device to put it in its lowest power mode. | ||
| 296 | * | ||
| 297 | * @param mlsl_handle | ||
| 298 | * the handle to the serial channel the device is connected to. | ||
| 299 | * @param slave | ||
| 300 | * a pointer to the slave descriptor data structure. | ||
| 301 | * @param pdata | ||
| 302 | * a pointer to the slave platform data. | ||
| 303 | * | ||
| 304 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 305 | */ | ||
| 306 | static int mma8450_suspend(void *mlsl_handle, | ||
| 307 | struct ext_slave_descr *slave, | ||
| 308 | struct ext_slave_platform_data *pdata) | ||
| 309 | { | ||
| 310 | int result; | ||
| 311 | struct mma8450_private_data *private_data = pdata->private_data; | ||
| 312 | |||
| 313 | if (private_data->suspend.fsr == 4000) | ||
| 314 | slave->range.mantissa = 4; | ||
| 315 | else if (private_data->suspend.fsr == 8000) | ||
| 316 | slave->range.mantissa = 8; | ||
| 317 | else | ||
| 318 | slave->range.mantissa = 2; | ||
| 319 | slave->range.fraction = 0; | ||
| 320 | |||
| 321 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 322 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
| 323 | if (result) { | ||
| 324 | LOG_RESULT_LOCATION(result); | ||
| 325 | return result; | ||
| 326 | } | ||
| 327 | if (private_data->suspend.ctrl_reg1) { | ||
| 328 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 329 | ACCEL_MMA8450_CTRL_REG1, | ||
| 330 | private_data->suspend.ctrl_reg1); | ||
| 331 | if (result) { | ||
| 332 | LOG_RESULT_LOCATION(result); | ||
| 333 | return result; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | result = mma8450_set_irq(mlsl_handle, pdata, | ||
| 338 | &private_data->suspend, | ||
| 339 | true, private_data->suspend.irq_type); | ||
| 340 | if (result) { | ||
| 341 | LOG_RESULT_LOCATION(result); | ||
| 342 | return result; | ||
| 343 | } | ||
| 344 | return result; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * @brief resume the device in the proper power state given the configuration | ||
| 349 | * chosen. | ||
| 350 | * | ||
| 351 | * @param mlsl_handle | ||
| 352 | * the handle to the serial channel the device is connected to. | ||
| 353 | * @param slave | ||
| 354 | * a pointer to the slave descriptor data structure. | ||
| 355 | * @param pdata | ||
| 356 | * a pointer to the slave platform data. | ||
| 357 | * | ||
| 358 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 359 | */ | ||
| 360 | static int mma8450_resume(void *mlsl_handle, | ||
| 361 | struct ext_slave_descr *slave, | ||
| 362 | struct ext_slave_platform_data *pdata) | ||
| 363 | { | ||
| 364 | int result = INV_SUCCESS; | ||
| 365 | struct mma8450_private_data *private_data = pdata->private_data; | ||
| 366 | |||
| 367 | /* Full Scale */ | ||
| 368 | if (private_data->resume.fsr == 4000) | ||
| 369 | slave->range.mantissa = 4; | ||
| 370 | else if (private_data->resume.fsr == 8000) | ||
| 371 | slave->range.mantissa = 8; | ||
| 372 | else | ||
| 373 | slave->range.mantissa = 2; | ||
| 374 | slave->range.fraction = 0; | ||
| 375 | |||
| 376 | if (result) { | ||
| 377 | LOG_RESULT_LOCATION(result); | ||
| 378 | return result; | ||
| 379 | } | ||
| 380 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 381 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
| 382 | if (result) { | ||
| 383 | LOG_RESULT_LOCATION(result); | ||
| 384 | return result; | ||
| 385 | } | ||
| 386 | if (private_data->resume.ctrl_reg1) { | ||
| 387 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 388 | ACCEL_MMA8450_CTRL_REG1, | ||
| 389 | private_data->resume.ctrl_reg1); | ||
| 390 | if (result) { | ||
| 391 | LOG_RESULT_LOCATION(result); | ||
| 392 | return result; | ||
| 393 | } | ||
| 394 | } | ||
| 395 | result = mma8450_set_irq(mlsl_handle, pdata, | ||
| 396 | &private_data->resume, | ||
| 397 | true, private_data->resume.irq_type); | ||
| 398 | if (result) { | ||
| 399 | LOG_RESULT_LOCATION(result); | ||
| 400 | return result; | ||
| 401 | } | ||
| 402 | |||
| 403 | return result; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * @brief read the sensor data from the device. | ||
| 408 | * | ||
| 409 | * @param mlsl_handle | ||
| 410 | * the handle to the serial channel the device is connected to. | ||
| 411 | * @param slave | ||
| 412 | * a pointer to the slave descriptor data structure. | ||
| 413 | * @param pdata | ||
| 414 | * a pointer to the slave platform data. | ||
| 415 | * @param data | ||
| 416 | * a buffer to store the data read. | ||
| 417 | * | ||
| 418 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 419 | */ | ||
| 420 | static int mma8450_read(void *mlsl_handle, | ||
| 421 | struct ext_slave_descr *slave, | ||
| 422 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 423 | { | ||
| 424 | int result; | ||
| 425 | unsigned char local_data[4]; /* Status register + 3 bytes data */ | ||
| 426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 427 | 0x00, sizeof(local_data), local_data); | ||
| 428 | if (result) { | ||
| 429 | LOG_RESULT_LOCATION(result); | ||
| 430 | return result; | ||
| 431 | } | ||
| 432 | memcpy(data, &local_data[1], (slave->read_len) - 1); | ||
| 433 | |||
| 434 | MPL_LOGV("Data Not Ready: %02x %02x %02x %02x\n", | ||
| 435 | local_data[0], local_data[1], | ||
| 436 | local_data[2], local_data[3]); | ||
| 437 | |||
| 438 | return result; | ||
| 439 | } | ||
| 440 | |||
| 441 | /** | ||
| 442 | * @brief one-time device driver initialization function. | ||
| 443 | * If the driver is built as a kernel module, this function will be | ||
| 444 | * called when the module is loaded in the kernel. | ||
| 445 | * If the driver is built-in in the kernel, this function will be | ||
| 446 | * called at boot time. | ||
| 447 | * | ||
| 448 | * @param mlsl_handle | ||
| 449 | * the handle to the serial channel the device is connected to. | ||
| 450 | * @param slave | ||
| 451 | * a pointer to the slave descriptor data structure. | ||
| 452 | * @param pdata | ||
| 453 | * a pointer to the slave platform data. | ||
| 454 | * | ||
| 455 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 456 | */ | ||
| 457 | static int mma8450_init(void *mlsl_handle, | ||
| 458 | struct ext_slave_descr *slave, | ||
| 459 | struct ext_slave_platform_data *pdata) | ||
| 460 | { | ||
| 461 | struct mma8450_private_data *private_data; | ||
| 462 | private_data = (struct mma8450_private_data *) | ||
| 463 | kzalloc(sizeof(struct mma8450_private_data), GFP_KERNEL); | ||
| 464 | |||
| 465 | if (!private_data) | ||
| 466 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 467 | |||
| 468 | pdata->private_data = private_data; | ||
| 469 | |||
| 470 | mma8450_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 471 | false, 0); | ||
| 472 | mma8450_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 473 | false, 200000); | ||
| 474 | mma8450_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 475 | false, 2000); | ||
| 476 | mma8450_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 477 | false, 2000); | ||
| 478 | mma8450_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 479 | false, | ||
| 480 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 481 | mma8450_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 482 | false, | ||
| 483 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 484 | return INV_SUCCESS; | ||
| 485 | } | ||
| 486 | |||
| 487 | /** | ||
| 488 | * @brief one-time device driver exit function. | ||
| 489 | * If the driver is built as a kernel module, this function will be | ||
| 490 | * called when the module is removed from the kernel. | ||
| 491 | * | ||
| 492 | * @param mlsl_handle | ||
| 493 | * the handle to the serial channel the device is connected to. | ||
| 494 | * @param slave | ||
| 495 | * a pointer to the slave descriptor data structure. | ||
| 496 | * @param pdata | ||
| 497 | * a pointer to the slave platform data. | ||
| 498 | * | ||
| 499 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 500 | */ | ||
| 501 | static int mma8450_exit(void *mlsl_handle, | ||
| 502 | struct ext_slave_descr *slave, | ||
| 503 | struct ext_slave_platform_data *pdata) | ||
| 504 | { | ||
| 505 | kfree(pdata->private_data); | ||
| 506 | return INV_SUCCESS; | ||
| 507 | } | ||
| 508 | |||
| 509 | /** | ||
| 510 | * @brief device configuration facility. | ||
| 511 | * | ||
| 512 | * @param mlsl_handle | ||
| 513 | * the handle to the serial channel the device is connected to. | ||
| 514 | * @param slave | ||
| 515 | * a pointer to the slave descriptor data structure. | ||
| 516 | * @param pdata | ||
| 517 | * a pointer to the slave platform data. | ||
| 518 | * @param data | ||
| 519 | * a pointer to the configuration data structure. | ||
| 520 | * | ||
| 521 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 522 | */ | ||
| 523 | static int mma8450_config(void *mlsl_handle, | ||
| 524 | struct ext_slave_descr *slave, | ||
| 525 | struct ext_slave_platform_data *pdata, | ||
| 526 | struct ext_slave_config *data) | ||
| 527 | { | ||
| 528 | struct mma8450_private_data *private_data = pdata->private_data; | ||
| 529 | if (!data->data) | ||
| 530 | return INV_ERROR_INVALID_PARAMETER; | ||
| 531 | |||
| 532 | switch (data->key) { | ||
| 533 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 534 | return mma8450_set_odr(mlsl_handle, pdata, | ||
| 535 | &private_data->suspend, | ||
| 536 | data->apply, | ||
| 537 | *((long *)data->data)); | ||
| 538 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 539 | return mma8450_set_odr(mlsl_handle, pdata, | ||
| 540 | &private_data->resume, | ||
| 541 | data->apply, | ||
| 542 | *((long *)data->data)); | ||
| 543 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 544 | return mma8450_set_fsr(mlsl_handle, pdata, | ||
| 545 | &private_data->suspend, | ||
| 546 | data->apply, | ||
| 547 | *((long *)data->data)); | ||
| 548 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 549 | return mma8450_set_fsr(mlsl_handle, pdata, | ||
| 550 | &private_data->resume, | ||
| 551 | data->apply, | ||
| 552 | *((long *)data->data)); | ||
| 553 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 554 | return mma8450_set_ths(mlsl_handle, pdata, | ||
| 555 | &private_data->suspend, | ||
| 556 | data->apply, | ||
| 557 | *((long *)data->data)); | ||
| 558 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 559 | return mma8450_set_ths(mlsl_handle, pdata, | ||
| 560 | &private_data->resume, | ||
| 561 | data->apply, | ||
| 562 | *((long *)data->data)); | ||
| 563 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 564 | return mma8450_set_dur(mlsl_handle, pdata, | ||
| 565 | &private_data->suspend, | ||
| 566 | data->apply, | ||
| 567 | *((long *)data->data)); | ||
| 568 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 569 | return mma8450_set_dur(mlsl_handle, pdata, | ||
| 570 | &private_data->resume, | ||
| 571 | data->apply, | ||
| 572 | *((long *)data->data)); | ||
| 573 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 574 | return mma8450_set_irq(mlsl_handle, pdata, | ||
| 575 | &private_data->suspend, | ||
| 576 | data->apply, | ||
| 577 | *((long *)data->data)); | ||
| 578 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 579 | return mma8450_set_irq(mlsl_handle, pdata, | ||
| 580 | &private_data->resume, | ||
| 581 | data->apply, | ||
| 582 | *((long *)data->data)); | ||
| 583 | default: | ||
| 584 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 585 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 586 | }; | ||
| 587 | |||
| 588 | return INV_SUCCESS; | ||
| 589 | } | ||
| 590 | |||
| 591 | /** | ||
| 592 | * @brief facility to retrieve the device configuration. | ||
| 593 | * | ||
| 594 | * @param mlsl_handle | ||
| 595 | * the handle to the serial channel the device is connected to. | ||
| 596 | * @param slave | ||
| 597 | * a pointer to the slave descriptor data structure. | ||
| 598 | * @param pdata | ||
| 599 | * a pointer to the slave platform data. | ||
| 600 | * @param data | ||
| 601 | * a pointer to store the returned configuration data structure. | ||
| 602 | * | ||
| 603 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 604 | */ | ||
| 605 | static int mma8450_get_config(void *mlsl_handle, | ||
| 606 | struct ext_slave_descr *slave, | ||
| 607 | struct ext_slave_platform_data *pdata, | ||
| 608 | struct ext_slave_config *data) | ||
| 609 | { | ||
| 610 | struct mma8450_private_data *private_data = pdata->private_data; | ||
| 611 | if (!data->data) | ||
| 612 | return INV_ERROR_INVALID_PARAMETER; | ||
| 613 | |||
| 614 | switch (data->key) { | ||
| 615 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 616 | (*(unsigned long *)data->data) = | ||
| 617 | (unsigned long) private_data->suspend.odr; | ||
| 618 | break; | ||
| 619 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 620 | (*(unsigned long *)data->data) = | ||
| 621 | (unsigned long) private_data->resume.odr; | ||
| 622 | break; | ||
| 623 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 624 | (*(unsigned long *)data->data) = | ||
| 625 | (unsigned long) private_data->suspend.fsr; | ||
| 626 | break; | ||
| 627 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 628 | (*(unsigned long *)data->data) = | ||
| 629 | (unsigned long) private_data->resume.fsr; | ||
| 630 | break; | ||
| 631 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 632 | (*(unsigned long *)data->data) = | ||
| 633 | (unsigned long) private_data->suspend.ths; | ||
| 634 | break; | ||
| 635 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 636 | (*(unsigned long *)data->data) = | ||
| 637 | (unsigned long) private_data->resume.ths; | ||
| 638 | break; | ||
| 639 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 640 | (*(unsigned long *)data->data) = | ||
| 641 | (unsigned long) private_data->suspend.dur; | ||
| 642 | break; | ||
| 643 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 644 | (*(unsigned long *)data->data) = | ||
| 645 | (unsigned long) private_data->resume.dur; | ||
| 646 | break; | ||
| 647 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 648 | (*(unsigned long *)data->data) = | ||
| 649 | (unsigned long) private_data->suspend.irq_type; | ||
| 650 | break; | ||
| 651 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 652 | (*(unsigned long *)data->data) = | ||
| 653 | (unsigned long) private_data->resume.irq_type; | ||
| 654 | break; | ||
| 655 | default: | ||
| 656 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 657 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 658 | }; | ||
| 659 | |||
| 660 | return INV_SUCCESS; | ||
| 661 | } | ||
| 662 | |||
| 663 | static struct ext_slave_descr mma8450_descr = { | ||
| 664 | .init = mma8450_init, | ||
| 665 | .exit = mma8450_exit, | ||
| 666 | .suspend = mma8450_suspend, | ||
| 667 | .resume = mma8450_resume, | ||
| 668 | .read = mma8450_read, | ||
| 669 | .config = mma8450_config, | ||
| 670 | .get_config = mma8450_get_config, | ||
| 671 | .name = "mma8450", | ||
| 672 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 673 | .id = ACCEL_ID_MMA8450, | ||
| 674 | .read_reg = 0x00, | ||
| 675 | .read_len = 4, | ||
| 676 | .endian = EXT_SLAVE_FS8_BIG_ENDIAN, | ||
| 677 | .range = {2, 0}, | ||
| 678 | .trigger = NULL, | ||
| 679 | }; | ||
| 680 | |||
| 681 | static | ||
| 682 | struct ext_slave_descr *mma8450_get_slave_descr(void) | ||
| 683 | { | ||
| 684 | return &mma8450_descr; | ||
| 685 | } | ||
| 686 | |||
| 687 | /* -------------------------------------------------------------------------- */ | ||
| 688 | struct mma8450_mod_private_data { | ||
| 689 | struct i2c_client *client; | ||
| 690 | struct ext_slave_platform_data *pdata; | ||
| 691 | }; | ||
| 692 | |||
| 693 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 694 | |||
| 695 | static int mma8450_mod_probe(struct i2c_client *client, | ||
| 696 | const struct i2c_device_id *devid) | ||
| 697 | { | ||
| 698 | struct ext_slave_platform_data *pdata; | ||
| 699 | struct mma8450_mod_private_data *private_data; | ||
| 700 | int result = 0; | ||
| 701 | |||
| 702 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 703 | |||
| 704 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 705 | result = -ENODEV; | ||
| 706 | goto out_no_free; | ||
| 707 | } | ||
| 708 | |||
| 709 | pdata = client->dev.platform_data; | ||
| 710 | if (!pdata) { | ||
| 711 | dev_err(&client->adapter->dev, | ||
| 712 | "Missing platform data for slave %s\n", devid->name); | ||
| 713 | result = -EFAULT; | ||
| 714 | goto out_no_free; | ||
| 715 | } | ||
| 716 | |||
| 717 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 718 | if (!private_data) { | ||
| 719 | result = -ENOMEM; | ||
| 720 | goto out_no_free; | ||
| 721 | } | ||
| 722 | |||
| 723 | i2c_set_clientdata(client, private_data); | ||
| 724 | private_data->client = client; | ||
| 725 | private_data->pdata = pdata; | ||
| 726 | |||
| 727 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 728 | mma8450_get_slave_descr); | ||
| 729 | if (result) { | ||
| 730 | dev_err(&client->adapter->dev, | ||
| 731 | "Slave registration failed: %s, %d\n", | ||
| 732 | devid->name, result); | ||
| 733 | goto out_free_memory; | ||
| 734 | } | ||
| 735 | |||
| 736 | return result; | ||
| 737 | |||
| 738 | out_free_memory: | ||
| 739 | kfree(private_data); | ||
| 740 | out_no_free: | ||
| 741 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 742 | return result; | ||
| 743 | |||
| 744 | } | ||
| 745 | |||
| 746 | static int mma8450_mod_remove(struct i2c_client *client) | ||
| 747 | { | ||
| 748 | struct mma8450_mod_private_data *private_data = | ||
| 749 | i2c_get_clientdata(client); | ||
| 750 | |||
| 751 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 752 | |||
| 753 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 754 | mma8450_get_slave_descr); | ||
| 755 | |||
| 756 | kfree(private_data); | ||
| 757 | return 0; | ||
| 758 | } | ||
| 759 | |||
| 760 | static const struct i2c_device_id mma8450_mod_id[] = { | ||
| 761 | { "mma8450", ACCEL_ID_MMA8450 }, | ||
| 762 | {} | ||
| 763 | }; | ||
| 764 | |||
| 765 | MODULE_DEVICE_TABLE(i2c, mma8450_mod_id); | ||
| 766 | |||
| 767 | static struct i2c_driver mma8450_mod_driver = { | ||
| 768 | .class = I2C_CLASS_HWMON, | ||
| 769 | .probe = mma8450_mod_probe, | ||
| 770 | .remove = mma8450_mod_remove, | ||
| 771 | .id_table = mma8450_mod_id, | ||
| 772 | .driver = { | ||
| 773 | .owner = THIS_MODULE, | ||
| 774 | .name = "mma8450_mod", | ||
| 775 | }, | ||
| 776 | .address_list = normal_i2c, | ||
| 777 | }; | ||
| 778 | |||
| 779 | static int __init mma8450_mod_init(void) | ||
| 780 | { | ||
| 781 | int res = i2c_add_driver(&mma8450_mod_driver); | ||
| 782 | pr_info("%s: Probe name %s\n", __func__, "mma8450_mod"); | ||
| 783 | if (res) | ||
| 784 | pr_err("%s failed\n", __func__); | ||
| 785 | return res; | ||
| 786 | } | ||
| 787 | |||
| 788 | static void __exit mma8450_mod_exit(void) | ||
| 789 | { | ||
| 790 | pr_info("%s\n", __func__); | ||
| 791 | i2c_del_driver(&mma8450_mod_driver); | ||
| 792 | } | ||
| 793 | |||
| 794 | module_init(mma8450_mod_init); | ||
| 795 | module_exit(mma8450_mod_exit); | ||
| 796 | |||
| 797 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 798 | MODULE_DESCRIPTION("Driver to integrate MMA8450 sensor with the MPU"); | ||
| 799 | MODULE_LICENSE("GPL"); | ||
| 800 | MODULE_ALIAS("mma8450_mod"); | ||
| 801 | |||
| 802 | /** | ||
| 803 | * @} | ||
| 804 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mma845x.c b/drivers/misc/inv_mpu/accel/mma845x.c new file mode 100644 index 00000000000..5f62b22388b --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mma845x.c | |||
| @@ -0,0 +1,713 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mma845x.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Freescale MMA845X | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-acc" | ||
| 46 | |||
| 47 | #define ACCEL_MMA845X_XYZ_DATA_CFG (0x0E) | ||
| 48 | #define ACCEL_MMA845X_CTRL_REG1 (0x2A) | ||
| 49 | #define ACCEL_MMA845X_CTRL_REG4 (0x2D) | ||
| 50 | #define ACCEL_MMA845X_CTRL_REG5 (0x2E) | ||
| 51 | |||
| 52 | #define ACCEL_MMA845X_SLEEP_MASK (0x01) | ||
| 53 | |||
| 54 | /* full scale setting - register & mask */ | ||
| 55 | #define ACCEL_MMA845X_CFG_REG (0x0E) | ||
| 56 | #define ACCEL_MMA845X_CTRL_MASK (0x03) | ||
| 57 | |||
| 58 | /* -------------------------------------------------------------------------- */ | ||
| 59 | |||
| 60 | struct mma845x_config { | ||
| 61 | unsigned int odr; | ||
| 62 | unsigned int fsr; /** < full scale range mg */ | ||
| 63 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
| 64 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
| 65 | unsigned char reg_ths; | ||
| 66 | unsigned char reg_dur; | ||
| 67 | unsigned char ctrl_reg1; | ||
| 68 | unsigned char irq_type; | ||
| 69 | unsigned char mot_int1_cfg; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct mma845x_private_data { | ||
| 73 | struct mma845x_config suspend; | ||
| 74 | struct mma845x_config resume; | ||
| 75 | }; | ||
| 76 | |||
| 77 | /* -------------------------------------------------------------------------- */ | ||
| 78 | |||
| 79 | static int mma845x_set_ths(void *mlsl_handle, | ||
| 80 | struct ext_slave_platform_data *pdata, | ||
| 81 | struct mma845x_config *config, | ||
| 82 | int apply, | ||
| 83 | long ths) | ||
| 84 | { | ||
| 85 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 86 | } | ||
| 87 | |||
| 88 | static int mma845x_set_dur(void *mlsl_handle, | ||
| 89 | struct ext_slave_platform_data *pdata, | ||
| 90 | struct mma845x_config *config, | ||
| 91 | int apply, | ||
| 92 | long dur) | ||
| 93 | { | ||
| 94 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
| 99 | * Threshold and duration will not be used unless the type is MOT or | ||
| 100 | * NMOT. | ||
| 101 | * | ||
| 102 | * @param mlsl_handle | ||
| 103 | * the handle to the serial channel the device is connected to. | ||
| 104 | * @param pdata | ||
| 105 | * a pointer to the slave platform data. | ||
| 106 | * @param config | ||
| 107 | * configuration to apply to, suspend or resume | ||
| 108 | * @param apply | ||
| 109 | * whether to apply immediately or save the settings to be applied | ||
| 110 | * at the next resume. | ||
| 111 | * @param irq_type | ||
| 112 | * the type of IRQ. Valid values are | ||
| 113 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
| 114 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
| 115 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
| 116 | * | ||
| 117 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 118 | */ | ||
| 119 | static int mma845x_set_irq(void *mlsl_handle, | ||
| 120 | struct ext_slave_platform_data *pdata, | ||
| 121 | struct mma845x_config *config, | ||
| 122 | int apply, | ||
| 123 | long irq_type) | ||
| 124 | { | ||
| 125 | int result = INV_SUCCESS; | ||
| 126 | unsigned char reg1; | ||
| 127 | unsigned char reg2; | ||
| 128 | |||
| 129 | config->irq_type = (unsigned char)irq_type; | ||
| 130 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
| 131 | reg1 = 0x01; | ||
| 132 | reg2 = 0x01; | ||
| 133 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) { | ||
| 134 | reg1 = 0x00; | ||
| 135 | reg2 = 0x00; | ||
| 136 | } else { | ||
| 137 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 138 | } | ||
| 139 | |||
| 140 | if (apply) { | ||
| 141 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 142 | ACCEL_MMA845X_CTRL_REG4, reg1); | ||
| 143 | if (result) { | ||
| 144 | LOG_RESULT_LOCATION(result); | ||
| 145 | return result; | ||
| 146 | } | ||
| 147 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 148 | ACCEL_MMA845X_CTRL_REG5, reg2); | ||
| 149 | if (result) { | ||
| 150 | LOG_RESULT_LOCATION(result); | ||
| 151 | return result; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | return result; | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * @brief Set the output data rate for the particular configuration. | ||
| 160 | * | ||
| 161 | * @param mlsl_handle | ||
| 162 | * the handle to the serial channel the device is connected to. | ||
| 163 | * @param pdata | ||
| 164 | * a pointer to the slave platform data. | ||
| 165 | * @param config | ||
| 166 | * Config to modify with new ODR. | ||
| 167 | * @param apply | ||
| 168 | * whether to apply immediately or save the settings to be applied | ||
| 169 | * at the next resume. | ||
| 170 | * @param odr | ||
| 171 | * Output data rate in units of 1/1000Hz (mHz). | ||
| 172 | * | ||
| 173 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 174 | */ | ||
| 175 | static int mma845x_set_odr(void *mlsl_handle, | ||
| 176 | struct ext_slave_platform_data *pdata, | ||
| 177 | struct mma845x_config *config, | ||
| 178 | int apply, | ||
| 179 | long odr) | ||
| 180 | { | ||
| 181 | unsigned char bits; | ||
| 182 | int result = INV_SUCCESS; | ||
| 183 | |||
| 184 | if (odr > 400000) { | ||
| 185 | config->odr = 800000; | ||
| 186 | bits = 0x01; | ||
| 187 | } else if (odr > 200000) { | ||
| 188 | config->odr = 400000; | ||
| 189 | bits = 0x09; | ||
| 190 | } else if (odr > 100000) { | ||
| 191 | config->odr = 200000; | ||
| 192 | bits = 0x11; | ||
| 193 | } else if (odr > 50000) { | ||
| 194 | config->odr = 100000; | ||
| 195 | bits = 0x19; | ||
| 196 | } else if (odr > 12500) { | ||
| 197 | config->odr = 50000; | ||
| 198 | bits = 0x21; | ||
| 199 | } else if (odr > 6250) { | ||
| 200 | config->odr = 12500; | ||
| 201 | bits = 0x29; | ||
| 202 | } else if (odr > 1560) { | ||
| 203 | config->odr = 6250; | ||
| 204 | bits = 0x31; | ||
| 205 | } else if (odr > 0) { | ||
| 206 | config->odr = 1560; | ||
| 207 | bits = 0x39; | ||
| 208 | } else { | ||
| 209 | config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */ | ||
| 210 | config->odr = 0; | ||
| 211 | bits = 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | config->ctrl_reg1 = bits; | ||
| 215 | if (apply) { | ||
| 216 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 217 | ACCEL_MMA845X_CTRL_REG1, | ||
| 218 | config->ctrl_reg1); | ||
| 219 | if (result) { | ||
| 220 | LOG_RESULT_LOCATION(result); | ||
| 221 | return result; | ||
| 222 | } | ||
| 223 | MPL_LOGV("ODR: %d mHz, 0x%02x\n", config->odr, | ||
| 224 | (int)config->ctrl_reg1); | ||
| 225 | } | ||
| 226 | return result; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * @brief Set the full scale range of the accels | ||
| 231 | * | ||
| 232 | * @param mlsl_handle | ||
| 233 | * the handle to the serial channel the device is connected to. | ||
| 234 | * @param pdata | ||
| 235 | * a pointer to the slave platform data. | ||
| 236 | * @param config | ||
| 237 | * pointer to configuration. | ||
| 238 | * @param apply | ||
| 239 | * whether to apply immediately or save the settings to be applied | ||
| 240 | * at the next resume. | ||
| 241 | * @param fsr | ||
| 242 | * requested full scale range. | ||
| 243 | * | ||
| 244 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 245 | */ | ||
| 246 | static int mma845x_set_fsr(void *mlsl_handle, | ||
| 247 | struct ext_slave_platform_data *pdata, | ||
| 248 | struct mma845x_config *config, | ||
| 249 | int apply, | ||
| 250 | long fsr) | ||
| 251 | { | ||
| 252 | unsigned char bits; | ||
| 253 | int result = INV_SUCCESS; | ||
| 254 | |||
| 255 | if (fsr <= 2000) { | ||
| 256 | bits = 0x00; | ||
| 257 | config->fsr = 2000; | ||
| 258 | } else if (fsr <= 4000) { | ||
| 259 | bits = 0x01; | ||
| 260 | config->fsr = 4000; | ||
| 261 | } else { | ||
| 262 | bits = 0x02; | ||
| 263 | config->fsr = 8000; | ||
| 264 | } | ||
| 265 | |||
| 266 | if (apply) { | ||
| 267 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 268 | ACCEL_MMA845X_XYZ_DATA_CFG, | ||
| 269 | bits); | ||
| 270 | if (result) { | ||
| 271 | LOG_RESULT_LOCATION(result); | ||
| 272 | return result; | ||
| 273 | } | ||
| 274 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
| 275 | } | ||
| 276 | return result; | ||
| 277 | } | ||
| 278 | |||
| 279 | /** | ||
| 280 | * @brief suspends the device to put it in its lowest power mode. | ||
| 281 | * | ||
| 282 | * @param mlsl_handle | ||
| 283 | * the handle to the serial channel the device is connected to. | ||
| 284 | * @param slave | ||
| 285 | * a pointer to the slave descriptor data structure. | ||
| 286 | * @param pdata | ||
| 287 | * a pointer to the slave platform data. | ||
| 288 | * | ||
| 289 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 290 | */ | ||
| 291 | static int mma845x_suspend(void *mlsl_handle, | ||
| 292 | struct ext_slave_descr *slave, | ||
| 293 | struct ext_slave_platform_data *pdata) | ||
| 294 | { | ||
| 295 | int result; | ||
| 296 | struct mma845x_private_data *private_data = pdata->private_data; | ||
| 297 | |||
| 298 | /* Full Scale */ | ||
| 299 | if (private_data->suspend.fsr == 4000) | ||
| 300 | slave->range.mantissa = 4; | ||
| 301 | else if (private_data->suspend.fsr == 8000) | ||
| 302 | slave->range.mantissa = 8; | ||
| 303 | else | ||
| 304 | slave->range.mantissa = 2; | ||
| 305 | |||
| 306 | slave->range.fraction = 0; | ||
| 307 | |||
| 308 | result = mma845x_set_fsr(mlsl_handle, pdata, | ||
| 309 | &private_data->suspend, | ||
| 310 | true, private_data->suspend.fsr); | ||
| 311 | if (result) { | ||
| 312 | LOG_RESULT_LOCATION(result); | ||
| 313 | return result; | ||
| 314 | } | ||
| 315 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 316 | ACCEL_MMA845X_CTRL_REG1, | ||
| 317 | private_data->suspend.ctrl_reg1); | ||
| 318 | if (result) { | ||
| 319 | LOG_RESULT_LOCATION(result); | ||
| 320 | return result; | ||
| 321 | } | ||
| 322 | |||
| 323 | return result; | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * @brief resume the device in the proper power state given the configuration | ||
| 328 | * chosen. | ||
| 329 | * | ||
| 330 | * @param mlsl_handle | ||
| 331 | * the handle to the serial channel the device is connected to. | ||
| 332 | * @param slave | ||
| 333 | * a pointer to the slave descriptor data structure. | ||
| 334 | * @param pdata | ||
| 335 | * a pointer to the slave platform data. | ||
| 336 | * | ||
| 337 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 338 | */ | ||
| 339 | static int mma845x_resume(void *mlsl_handle, | ||
| 340 | struct ext_slave_descr *slave, | ||
| 341 | struct ext_slave_platform_data *pdata) | ||
| 342 | { | ||
| 343 | int result = INV_SUCCESS; | ||
| 344 | struct mma845x_private_data *private_data = pdata->private_data; | ||
| 345 | |||
| 346 | /* Full Scale */ | ||
| 347 | if (private_data->resume.fsr == 4000) | ||
| 348 | slave->range.mantissa = 4; | ||
| 349 | else if (private_data->resume.fsr == 8000) | ||
| 350 | slave->range.mantissa = 8; | ||
| 351 | else | ||
| 352 | slave->range.mantissa = 2; | ||
| 353 | |||
| 354 | slave->range.fraction = 0; | ||
| 355 | |||
| 356 | result = mma845x_set_fsr(mlsl_handle, pdata, | ||
| 357 | &private_data->resume, | ||
| 358 | true, private_data->resume.fsr); | ||
| 359 | if (result) { | ||
| 360 | LOG_RESULT_LOCATION(result); | ||
| 361 | return result; | ||
| 362 | } | ||
| 363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 364 | ACCEL_MMA845X_CTRL_REG1, | ||
| 365 | private_data->resume.ctrl_reg1); | ||
| 366 | if (result) { | ||
| 367 | LOG_RESULT_LOCATION(result); | ||
| 368 | return result; | ||
| 369 | } | ||
| 370 | |||
| 371 | return result; | ||
| 372 | } | ||
| 373 | |||
| 374 | /** | ||
| 375 | * @brief read the sensor data from the device. | ||
| 376 | * | ||
| 377 | * @param mlsl_handle | ||
| 378 | * the handle to the serial channel the device is connected to. | ||
| 379 | * @param slave | ||
| 380 | * a pointer to the slave descriptor data structure. | ||
| 381 | * @param pdata | ||
| 382 | * a pointer to the slave platform data. | ||
| 383 | * @param data | ||
| 384 | * a buffer to store the data read. | ||
| 385 | * | ||
| 386 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
| 387 | */ | ||
| 388 | static int mma845x_read(void *mlsl_handle, | ||
| 389 | struct ext_slave_descr *slave, | ||
| 390 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 391 | { | ||
| 392 | int result; | ||
| 393 | unsigned char local_data[7]; /* Status register + 6 bytes data */ | ||
| 394 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 395 | slave->read_reg, sizeof(local_data), | ||
| 396 | local_data); | ||
| 397 | if (result) { | ||
| 398 | LOG_RESULT_LOCATION(result); | ||
| 399 | return result; | ||
| 400 | } | ||
| 401 | memcpy(data, &local_data[1], slave->read_len); | ||
| 402 | return result; | ||
| 403 | } | ||
| 404 | |||
| 405 | static int mma845x_init(void *mlsl_handle, | ||
| 406 | struct ext_slave_descr *slave, | ||
| 407 | struct ext_slave_platform_data *pdata) | ||
| 408 | { | ||
| 409 | long range; | ||
| 410 | struct mma845x_private_data *private_data; | ||
| 411 | private_data = (struct mma845x_private_data *) | ||
| 412 | kzalloc(sizeof(struct mma845x_private_data), GFP_KERNEL); | ||
| 413 | |||
| 414 | if (!private_data) | ||
| 415 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 416 | |||
| 417 | pdata->private_data = private_data; | ||
| 418 | |||
| 419 | mma845x_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 420 | false, 0); | ||
| 421 | mma845x_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 422 | false, 200000); | ||
| 423 | |||
| 424 | range = range_fixedpoint_to_long_mg(slave->range); | ||
| 425 | mma845x_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 426 | false, range); | ||
| 427 | mma845x_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 428 | false, range); | ||
| 429 | |||
| 430 | mma845x_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 431 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 432 | mma845x_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 433 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 434 | return INV_SUCCESS; | ||
| 435 | } | ||
| 436 | |||
| 437 | static int mma845x_exit(void *mlsl_handle, | ||
| 438 | struct ext_slave_descr *slave, | ||
| 439 | struct ext_slave_platform_data *pdata) | ||
| 440 | { | ||
| 441 | kfree(pdata->private_data); | ||
| 442 | return INV_SUCCESS; | ||
| 443 | } | ||
| 444 | |||
| 445 | static int mma845x_config(void *mlsl_handle, | ||
| 446 | struct ext_slave_descr *slave, | ||
| 447 | struct ext_slave_platform_data *pdata, | ||
| 448 | struct ext_slave_config *data) | ||
| 449 | { | ||
| 450 | struct mma845x_private_data *private_data = pdata->private_data; | ||
| 451 | if (!data->data) | ||
| 452 | return INV_ERROR_INVALID_PARAMETER; | ||
| 453 | |||
| 454 | switch (data->key) { | ||
| 455 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 456 | return mma845x_set_odr(mlsl_handle, pdata, | ||
| 457 | &private_data->suspend, | ||
| 458 | data->apply, | ||
| 459 | *((long *)data->data)); | ||
| 460 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 461 | return mma845x_set_odr(mlsl_handle, pdata, | ||
| 462 | &private_data->resume, | ||
| 463 | data->apply, | ||
| 464 | *((long *)data->data)); | ||
| 465 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 466 | return mma845x_set_fsr(mlsl_handle, pdata, | ||
| 467 | &private_data->suspend, | ||
| 468 | data->apply, | ||
| 469 | *((long *)data->data)); | ||
| 470 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 471 | return mma845x_set_fsr(mlsl_handle, pdata, | ||
| 472 | &private_data->resume, | ||
| 473 | data->apply, | ||
| 474 | *((long *)data->data)); | ||
| 475 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 476 | return mma845x_set_ths(mlsl_handle, pdata, | ||
| 477 | &private_data->suspend, | ||
| 478 | data->apply, | ||
| 479 | *((long *)data->data)); | ||
| 480 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 481 | return mma845x_set_ths(mlsl_handle, pdata, | ||
| 482 | &private_data->resume, | ||
| 483 | data->apply, | ||
| 484 | *((long *)data->data)); | ||
| 485 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 486 | return mma845x_set_dur(mlsl_handle, pdata, | ||
| 487 | &private_data->suspend, | ||
| 488 | data->apply, | ||
| 489 | *((long *)data->data)); | ||
| 490 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 491 | return mma845x_set_dur(mlsl_handle, pdata, | ||
| 492 | &private_data->resume, | ||
| 493 | data->apply, | ||
| 494 | *((long *)data->data)); | ||
| 495 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 496 | return mma845x_set_irq(mlsl_handle, pdata, | ||
| 497 | &private_data->suspend, | ||
| 498 | data->apply, | ||
| 499 | *((long *)data->data)); | ||
| 500 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 501 | return mma845x_set_irq(mlsl_handle, pdata, | ||
| 502 | &private_data->resume, | ||
| 503 | data->apply, | ||
| 504 | *((long *)data->data)); | ||
| 505 | default: | ||
| 506 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 507 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 508 | }; | ||
| 509 | |||
| 510 | return INV_SUCCESS; | ||
| 511 | } | ||
| 512 | |||
| 513 | static int mma845x_get_config(void *mlsl_handle, | ||
| 514 | struct ext_slave_descr *slave, | ||
| 515 | struct ext_slave_platform_data *pdata, | ||
| 516 | struct ext_slave_config *data) | ||
| 517 | { | ||
| 518 | struct mma845x_private_data *private_data = pdata->private_data; | ||
| 519 | if (!data->data) | ||
| 520 | return INV_ERROR_INVALID_PARAMETER; | ||
| 521 | |||
| 522 | switch (data->key) { | ||
| 523 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 524 | (*(unsigned long *)data->data) = | ||
| 525 | (unsigned long) private_data->suspend.odr; | ||
| 526 | break; | ||
| 527 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 528 | (*(unsigned long *)data->data) = | ||
| 529 | (unsigned long) private_data->resume.odr; | ||
| 530 | break; | ||
| 531 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 532 | (*(unsigned long *)data->data) = | ||
| 533 | (unsigned long) private_data->suspend.fsr; | ||
| 534 | break; | ||
| 535 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 536 | (*(unsigned long *)data->data) = | ||
| 537 | (unsigned long) private_data->resume.fsr; | ||
| 538 | break; | ||
| 539 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 540 | (*(unsigned long *)data->data) = | ||
| 541 | (unsigned long) private_data->suspend.ths; | ||
| 542 | break; | ||
| 543 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 544 | (*(unsigned long *)data->data) = | ||
| 545 | (unsigned long) private_data->resume.ths; | ||
| 546 | break; | ||
| 547 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 548 | (*(unsigned long *)data->data) = | ||
| 549 | (unsigned long) private_data->suspend.dur; | ||
| 550 | break; | ||
| 551 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 552 | (*(unsigned long *)data->data) = | ||
| 553 | (unsigned long) private_data->resume.dur; | ||
| 554 | break; | ||
| 555 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 556 | (*(unsigned long *)data->data) = | ||
| 557 | (unsigned long) private_data->suspend.irq_type; | ||
| 558 | break; | ||
| 559 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 560 | (*(unsigned long *)data->data) = | ||
| 561 | (unsigned long) private_data->resume.irq_type; | ||
| 562 | break; | ||
| 563 | default: | ||
| 564 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 565 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 566 | }; | ||
| 567 | |||
| 568 | return INV_SUCCESS; | ||
| 569 | } | ||
| 570 | |||
| 571 | static struct ext_slave_descr mma845x_descr = { | ||
| 572 | .init = mma845x_init, | ||
| 573 | .exit = mma845x_exit, | ||
| 574 | .suspend = mma845x_suspend, | ||
| 575 | .resume = mma845x_resume, | ||
| 576 | .read = mma845x_read, | ||
| 577 | .config = mma845x_config, | ||
| 578 | .get_config = mma845x_get_config, | ||
| 579 | .name = "mma845x", | ||
| 580 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 581 | .id = ACCEL_ID_MMA845X, | ||
| 582 | .read_reg = 0x00, | ||
| 583 | .read_len = 6, | ||
| 584 | .endian = EXT_SLAVE_FS16_BIG_ENDIAN, | ||
| 585 | .range = {2, 0}, | ||
| 586 | .trigger = NULL, | ||
| 587 | }; | ||
| 588 | |||
| 589 | static | ||
| 590 | struct ext_slave_descr *mma845x_get_slave_descr(void) | ||
| 591 | { | ||
| 592 | return &mma845x_descr; | ||
| 593 | } | ||
| 594 | |||
| 595 | /* -------------------------------------------------------------------------- */ | ||
| 596 | struct mma845x_mod_private_data { | ||
| 597 | struct i2c_client *client; | ||
| 598 | struct ext_slave_platform_data *pdata; | ||
| 599 | }; | ||
| 600 | |||
| 601 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 602 | |||
| 603 | static int mma845x_mod_probe(struct i2c_client *client, | ||
| 604 | const struct i2c_device_id *devid) | ||
| 605 | { | ||
| 606 | struct ext_slave_platform_data *pdata; | ||
| 607 | struct mma845x_mod_private_data *private_data; | ||
| 608 | int result = 0; | ||
| 609 | |||
| 610 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 611 | |||
| 612 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 613 | result = -ENODEV; | ||
| 614 | goto out_no_free; | ||
| 615 | } | ||
| 616 | |||
| 617 | pdata = client->dev.platform_data; | ||
| 618 | if (!pdata) { | ||
| 619 | dev_err(&client->adapter->dev, | ||
| 620 | "Missing platform data for slave %s\n", devid->name); | ||
| 621 | result = -EFAULT; | ||
| 622 | goto out_no_free; | ||
| 623 | } | ||
| 624 | |||
| 625 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 626 | if (!private_data) { | ||
| 627 | result = -ENOMEM; | ||
| 628 | goto out_no_free; | ||
| 629 | } | ||
| 630 | |||
| 631 | i2c_set_clientdata(client, private_data); | ||
| 632 | private_data->client = client; | ||
| 633 | private_data->pdata = pdata; | ||
| 634 | |||
| 635 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 636 | mma845x_get_slave_descr); | ||
| 637 | if (result) { | ||
| 638 | dev_err(&client->adapter->dev, | ||
| 639 | "Slave registration failed: %s, %d\n", | ||
| 640 | devid->name, result); | ||
| 641 | goto out_free_memory; | ||
| 642 | } | ||
| 643 | |||
| 644 | return result; | ||
| 645 | |||
| 646 | out_free_memory: | ||
| 647 | kfree(private_data); | ||
| 648 | out_no_free: | ||
| 649 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 650 | return result; | ||
| 651 | |||
| 652 | } | ||
| 653 | |||
| 654 | static int mma845x_mod_remove(struct i2c_client *client) | ||
| 655 | { | ||
| 656 | struct mma845x_mod_private_data *private_data = | ||
| 657 | i2c_get_clientdata(client); | ||
| 658 | |||
| 659 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 660 | |||
| 661 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 662 | mma845x_get_slave_descr); | ||
| 663 | |||
| 664 | kfree(private_data); | ||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
| 668 | static const struct i2c_device_id mma845x_mod_id[] = { | ||
| 669 | { "mma845x", ACCEL_ID_MMA845X }, | ||
| 670 | {} | ||
| 671 | }; | ||
| 672 | |||
| 673 | MODULE_DEVICE_TABLE(i2c, mma845x_mod_id); | ||
| 674 | |||
| 675 | static struct i2c_driver mma845x_mod_driver = { | ||
| 676 | .class = I2C_CLASS_HWMON, | ||
| 677 | .probe = mma845x_mod_probe, | ||
| 678 | .remove = mma845x_mod_remove, | ||
| 679 | .id_table = mma845x_mod_id, | ||
| 680 | .driver = { | ||
| 681 | .owner = THIS_MODULE, | ||
| 682 | .name = "mma845x_mod", | ||
| 683 | }, | ||
| 684 | .address_list = normal_i2c, | ||
| 685 | }; | ||
| 686 | |||
| 687 | static int __init mma845x_mod_init(void) | ||
| 688 | { | ||
| 689 | int res = i2c_add_driver(&mma845x_mod_driver); | ||
| 690 | pr_info("%s: Probe name %s\n", __func__, "mma845x_mod"); | ||
| 691 | if (res) | ||
| 692 | pr_err("%s failed\n", __func__); | ||
| 693 | return res; | ||
| 694 | } | ||
| 695 | |||
| 696 | static void __exit mma845x_mod_exit(void) | ||
| 697 | { | ||
| 698 | pr_info("%s\n", __func__); | ||
| 699 | i2c_del_driver(&mma845x_mod_driver); | ||
| 700 | } | ||
| 701 | |||
| 702 | module_init(mma845x_mod_init); | ||
| 703 | module_exit(mma845x_mod_exit); | ||
| 704 | |||
| 705 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 706 | MODULE_DESCRIPTION("Driver to integrate MMA845X sensor with the MPU"); | ||
| 707 | MODULE_LICENSE("GPL"); | ||
| 708 | MODULE_ALIAS("mma845x_mod"); | ||
| 709 | |||
| 710 | |||
| 711 | /** | ||
| 712 | * @} | ||
| 713 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.c b/drivers/misc/inv_mpu/accel/mpu6050.c new file mode 100644 index 00000000000..c5bb6784a41 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mpu6050.c | |||
| @@ -0,0 +1,695 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup ACCELDL | ||
| 22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mpu6050.c | ||
| 26 | * @brief Accelerometer setup and handling methods for Invensense MPU6050 | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/delay.h> | ||
| 37 | #include <linux/slab.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mpu6050b1.h" | ||
| 43 | #include "mlsl.h" | ||
| 44 | #include "mldl_cfg.h" | ||
| 45 | #undef MPL_LOG_TAG | ||
| 46 | #define MPL_LOG_TAG "MPL-acc" | ||
| 47 | |||
| 48 | /* -------------------------------------------------------------------------- */ | ||
| 49 | |||
| 50 | struct mpu6050_config { | ||
| 51 | unsigned int odr; /**< output data rate 1/1000 Hz */ | ||
| 52 | unsigned int fsr; /**< full scale range mg */ | ||
| 53 | unsigned int ths; /**< mot/no-mot thseshold mg */ | ||
| 54 | unsigned int dur; /**< mot/no-mot duration ms */ | ||
| 55 | unsigned int irq_type; /**< irq type */ | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct mpu6050_private_data { | ||
| 59 | struct mpu6050_config suspend; | ||
| 60 | struct mpu6050_config resume; | ||
| 61 | struct mldl_cfg *mldl_cfg_ref; | ||
| 62 | }; | ||
| 63 | |||
| 64 | /* -------------------------------------------------------------------------- */ | ||
| 65 | |||
| 66 | static int mpu6050_set_mldl_cfg_ref(void *mlsl_handle, | ||
| 67 | struct ext_slave_platform_data *pdata, | ||
| 68 | struct mldl_cfg *mldl_cfg_ref) | ||
| 69 | { | ||
| 70 | struct mpu6050_private_data *private_data = | ||
| 71 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 72 | private_data->mldl_cfg_ref = mldl_cfg_ref; | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | static int mpu6050_set_lp_mode(void *mlsl_handle, | ||
| 76 | struct ext_slave_platform_data *pdata, | ||
| 77 | unsigned char lpa_freq) | ||
| 78 | { | ||
| 79 | unsigned char b = 0; | ||
| 80 | /* Reducing the duration setting for lp mode */ | ||
| 81 | b = 1; | ||
| 82 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 83 | MPUREG_ACCEL_MOT_DUR, b); | ||
| 84 | /* Setting the cycle bit and LPA wake up freq */ | ||
| 85 | inv_serial_read(mlsl_handle, pdata->address, MPUREG_PWR_MGMT_1, 1, | ||
| 86 | &b); | ||
| 87 | b |= BIT_CYCLE | BIT_PD_PTAT; | ||
| 88 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 89 | MPUREG_PWR_MGMT_1, | ||
| 90 | b); | ||
| 91 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 92 | MPUREG_PWR_MGMT_2, 1, &b); | ||
| 93 | b |= lpa_freq & BITS_LPA_WAKE_CTRL; | ||
| 94 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 95 | MPUREG_PWR_MGMT_2, b); | ||
| 96 | |||
| 97 | return INV_SUCCESS; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int mpu6050_set_fp_mode(void *mlsl_handle, | ||
| 101 | struct ext_slave_platform_data *pdata) | ||
| 102 | { | ||
| 103 | unsigned char b; | ||
| 104 | struct mpu6050_private_data *private_data = | ||
| 105 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 106 | /* Resetting the cycle bit and LPA wake up freq */ | ||
| 107 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 108 | MPUREG_PWR_MGMT_1, 1, &b); | ||
| 109 | b &= ~BIT_CYCLE & ~BIT_PD_PTAT; | ||
| 110 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 111 | MPUREG_PWR_MGMT_1, b); | ||
| 112 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 113 | MPUREG_PWR_MGMT_2, 1, &b); | ||
| 114 | b &= ~BITS_LPA_WAKE_CTRL; | ||
| 115 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 116 | MPUREG_PWR_MGMT_2, b); | ||
| 117 | /* Resetting the duration setting for fp mode */ | ||
| 118 | b = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB; | ||
| 119 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 120 | MPUREG_ACCEL_MOT_DUR, b); | ||
| 121 | |||
| 122 | return INV_SUCCESS; | ||
| 123 | } | ||
| 124 | /** | ||
| 125 | * Record the odr for use in computing duration values. | ||
| 126 | * | ||
| 127 | * @param config Config to set, suspend or resume structure | ||
| 128 | * @param odr output data rate in 1/1000 hz | ||
| 129 | */ | ||
| 130 | static int mpu6050_set_odr(void *mlsl_handle, | ||
| 131 | struct ext_slave_platform_data *pdata, | ||
| 132 | struct mpu6050_config *config, long apply, long odr) | ||
| 133 | { | ||
| 134 | int result; | ||
| 135 | unsigned char b; | ||
| 136 | unsigned char lpa_freq = 1; /* Default value */ | ||
| 137 | long base; | ||
| 138 | int total_divider; | ||
| 139 | struct mpu6050_private_data *private_data = | ||
| 140 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 141 | struct mldl_cfg *mldl_cfg_ref = | ||
| 142 | (struct mldl_cfg *)private_data->mldl_cfg_ref; | ||
| 143 | |||
| 144 | if (mldl_cfg_ref) { | ||
| 145 | base = 1000 * | ||
| 146 | inv_mpu_get_sampling_rate_hz(mldl_cfg_ref->mpu_gyro_cfg) | ||
| 147 | * (mldl_cfg_ref->mpu_gyro_cfg->divider + 1); | ||
| 148 | } else { | ||
| 149 | /* have no reference to mldl_cfg => assume base rate is 1000 */ | ||
| 150 | base = 1000000L; | ||
| 151 | } | ||
| 152 | |||
| 153 | if (odr != 0) { | ||
| 154 | total_divider = (base / odr) - 1; | ||
| 155 | /* final odr MAY be different from requested odr due to | ||
| 156 | integer truncation */ | ||
| 157 | config->odr = base / (total_divider + 1); | ||
| 158 | } else { | ||
| 159 | config->odr = 0; | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* if the DMP and/or gyros are on, don't set the ODR => | ||
| 164 | the DMP/gyro mldl_cfg->divider setting will handle it */ | ||
| 165 | if (apply | ||
| 166 | && (mldl_cfg_ref && | ||
| 167 | !(mldl_cfg_ref->inv_mpu_cfg->requested_sensors & | ||
| 168 | INV_DMP_PROCESSOR))) { | ||
| 169 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 170 | MPUREG_SMPLRT_DIV, | ||
| 171 | (unsigned char)total_divider); | ||
| 172 | if (result) { | ||
| 173 | LOG_RESULT_LOCATION(result); | ||
| 174 | return result; | ||
| 175 | } | ||
| 176 | MPL_LOGI("ODR : %d mHz\n", config->odr); | ||
| 177 | } | ||
| 178 | /* Decide whether to put accel in LP mode or pull out of LP mode | ||
| 179 | based on the odr. */ | ||
| 180 | switch (odr) { | ||
| 181 | case 1000: | ||
| 182 | lpa_freq = BITS_LPA_WAKE_1HZ; | ||
| 183 | break; | ||
| 184 | case 2000: | ||
| 185 | lpa_freq = BITS_LPA_WAKE_2HZ; | ||
| 186 | break; | ||
| 187 | case 10000: | ||
| 188 | lpa_freq = BITS_LPA_WAKE_10HZ; | ||
| 189 | break; | ||
| 190 | case 40000: | ||
| 191 | lpa_freq = BITS_LPA_WAKE_40HZ; | ||
| 192 | break; | ||
| 193 | default: | ||
| 194 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 195 | MPUREG_PWR_MGMT_1, 1, &b); | ||
| 196 | b &= BIT_CYCLE; | ||
| 197 | if (b == BIT_CYCLE) { | ||
| 198 | MPL_LOGI(" Accel LP - > FP mode. \n "); | ||
| 199 | mpu6050_set_fp_mode(mlsl_handle, pdata); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | /* If lpa_freq default value was changed, set into LP mode */ | ||
| 203 | if (lpa_freq != 1) { | ||
| 204 | MPL_LOGI(" Accel FP - > LP mode. \n "); | ||
| 205 | mpu6050_set_lp_mode(mlsl_handle, pdata, lpa_freq); | ||
| 206 | } | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int mpu6050_set_fsr(void *mlsl_handle, | ||
| 211 | struct ext_slave_platform_data *pdata, | ||
| 212 | struct mpu6050_config *config, long apply, long fsr) | ||
| 213 | { | ||
| 214 | unsigned char fsr_mask; | ||
| 215 | int result; | ||
| 216 | |||
| 217 | if (fsr <= 2000) { | ||
| 218 | config->fsr = 2000; | ||
| 219 | fsr_mask = 0x00; | ||
| 220 | } else if (fsr <= 4000) { | ||
| 221 | config->fsr = 4000; | ||
| 222 | fsr_mask = 0x08; | ||
| 223 | } else if (fsr <= 8000) { | ||
| 224 | config->fsr = 8000; | ||
| 225 | fsr_mask = 0x10; | ||
| 226 | } else { /* fsr = [8001, oo) */ | ||
| 227 | config->fsr = 16000; | ||
| 228 | fsr_mask = 0x18; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (apply) { | ||
| 232 | unsigned char reg; | ||
| 233 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 234 | MPUREG_ACCEL_CONFIG, 1, ®); | ||
| 235 | if (result) { | ||
| 236 | LOG_RESULT_LOCATION(result); | ||
| 237 | return result; | ||
| 238 | } | ||
| 239 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 240 | MPUREG_ACCEL_CONFIG, | ||
| 241 | reg | fsr_mask); | ||
| 242 | if (result) { | ||
| 243 | LOG_RESULT_LOCATION(result); | ||
| 244 | return result; | ||
| 245 | } | ||
| 246 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
| 247 | } | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static int mpu6050_set_irq(void *mlsl_handle, | ||
| 252 | struct ext_slave_platform_data *pdata, | ||
| 253 | struct mpu6050_config *config, long apply, | ||
| 254 | long irq_type) | ||
| 255 | { | ||
| 256 | int result; | ||
| 257 | unsigned char reg_int_cfg; | ||
| 258 | |||
| 259 | switch (irq_type) { | ||
| 260 | case MPU_SLAVE_IRQ_TYPE_DATA_READY: | ||
| 261 | config->irq_type = irq_type; | ||
| 262 | reg_int_cfg = BIT_RAW_RDY_EN; | ||
| 263 | break; | ||
| 264 | /* todo: add MOTION, NO_MOTION, and FREEFALL */ | ||
| 265 | case MPU_SLAVE_IRQ_TYPE_NONE: | ||
| 266 | /* Do nothing, not even set the interrupt because it is | ||
| 267 | shared with the gyro */ | ||
| 268 | config->irq_type = irq_type; | ||
| 269 | return 0; | ||
| 270 | default: | ||
| 271 | return INV_ERROR_INVALID_PARAMETER; | ||
| 272 | } | ||
| 273 | |||
| 274 | if (apply) { | ||
| 275 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 276 | MPUREG_INT_ENABLE, | ||
| 277 | reg_int_cfg); | ||
| 278 | if (result) { | ||
| 279 | LOG_RESULT_LOCATION(result); | ||
| 280 | return result; | ||
| 281 | } | ||
| 282 | MPL_LOGV("irq_type: %d\n", config->irq_type); | ||
| 283 | } | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int mpu6050_set_ths(void *mlsl_handle, | ||
| 289 | struct ext_slave_platform_data *slave, | ||
| 290 | struct mpu6050_config *config, long apply, long ths) | ||
| 291 | { | ||
| 292 | if (ths < 0) | ||
| 293 | ths = 0; | ||
| 294 | |||
| 295 | config->ths = ths; | ||
| 296 | MPL_LOGV("THS: %d\n", config->ths); | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int mpu6050_set_dur(void *mlsl_handle, | ||
| 301 | struct ext_slave_platform_data *slave, | ||
| 302 | struct mpu6050_config *config, long apply, long dur) | ||
| 303 | { | ||
| 304 | if (dur < 0) | ||
| 305 | dur = 0; | ||
| 306 | |||
| 307 | config->dur = dur; | ||
| 308 | MPL_LOGV("DUR: %d\n", config->dur); | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | |||
| 313 | static int mpu6050_init(void *mlsl_handle, | ||
| 314 | struct ext_slave_descr *slave, | ||
| 315 | struct ext_slave_platform_data *pdata) | ||
| 316 | { | ||
| 317 | int result; | ||
| 318 | struct mpu6050_private_data *private_data; | ||
| 319 | |||
| 320 | |||
| 321 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 322 | |||
| 323 | if (!private_data) | ||
| 324 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 325 | |||
| 326 | pdata->private_data = private_data; | ||
| 327 | |||
| 328 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 329 | false, 0); | ||
| 330 | if (result) { | ||
| 331 | LOG_RESULT_LOCATION(result); | ||
| 332 | return result; | ||
| 333 | } | ||
| 334 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 335 | false, 200000); | ||
| 336 | if (result) { | ||
| 337 | LOG_RESULT_LOCATION(result); | ||
| 338 | return result; | ||
| 339 | } | ||
| 340 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
| 341 | false, 2000); | ||
| 342 | if (result) { | ||
| 343 | LOG_RESULT_LOCATION(result); | ||
| 344 | return result; | ||
| 345 | } | ||
| 346 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 347 | false, 2000); | ||
| 348 | if (result) { | ||
| 349 | LOG_RESULT_LOCATION(result); | ||
| 350 | return result; | ||
| 351 | } | ||
| 352 | |||
| 353 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 354 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 355 | if (result) { | ||
| 356 | LOG_RESULT_LOCATION(result); | ||
| 357 | return result; | ||
| 358 | } | ||
| 359 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 360 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
| 361 | if (result) { | ||
| 362 | LOG_RESULT_LOCATION(result); | ||
| 363 | return result; | ||
| 364 | } | ||
| 365 | |||
| 366 | result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
| 367 | false, 80); | ||
| 368 | if (result) { | ||
| 369 | LOG_RESULT_LOCATION(result); | ||
| 370 | return result; | ||
| 371 | } | ||
| 372 | result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
| 373 | false, 40); | ||
| 374 | if (result) { | ||
| 375 | LOG_RESULT_LOCATION(result); | ||
| 376 | return result; | ||
| 377 | } | ||
| 378 | result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
| 379 | false, 1000); | ||
| 380 | if (result) { | ||
| 381 | LOG_RESULT_LOCATION(result); | ||
| 382 | return result; | ||
| 383 | } | ||
| 384 | result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
| 385 | false, 2540); | ||
| 386 | if (result) { | ||
| 387 | LOG_RESULT_LOCATION(result); | ||
| 388 | return result; | ||
| 389 | } | ||
| 390 | |||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | static int mpu6050_exit(void *mlsl_handle, | ||
| 395 | struct ext_slave_descr *slave, | ||
| 396 | struct ext_slave_platform_data *pdata) | ||
| 397 | { | ||
| 398 | kfree(pdata->private_data); | ||
| 399 | pdata->private_data = NULL; | ||
| 400 | return 0; | ||
| 401 | } | ||
| 402 | |||
| 403 | static int mpu6050_suspend(void *mlsl_handle, | ||
| 404 | struct ext_slave_descr *slave, | ||
| 405 | struct ext_slave_platform_data *pdata) | ||
| 406 | { | ||
| 407 | unsigned char reg; | ||
| 408 | int result; | ||
| 409 | struct mpu6050_private_data *private_data = | ||
| 410 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 411 | |||
| 412 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
| 413 | true, private_data->suspend.odr); | ||
| 414 | if (result) { | ||
| 415 | LOG_RESULT_LOCATION(result); | ||
| 416 | return result; | ||
| 417 | } | ||
| 418 | |||
| 419 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
| 420 | true, private_data->suspend.irq_type); | ||
| 421 | if (result) { | ||
| 422 | LOG_RESULT_LOCATION(result); | ||
| 423 | return result; | ||
| 424 | } | ||
| 425 | |||
| 426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 427 | MPUREG_PWR_MGMT_2, 1, ®); | ||
| 428 | if (result) { | ||
| 429 | LOG_RESULT_LOCATION(result); | ||
| 430 | return result; | ||
| 431 | } | ||
| 432 | reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
| 433 | |||
| 434 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 435 | MPUREG_PWR_MGMT_2, reg); | ||
| 436 | if (result) { | ||
| 437 | LOG_RESULT_LOCATION(result); | ||
| 438 | return result; | ||
| 439 | } | ||
| 440 | |||
| 441 | return 0; | ||
| 442 | } | ||
| 443 | |||
| 444 | static int mpu6050_resume(void *mlsl_handle, | ||
| 445 | struct ext_slave_descr *slave, | ||
| 446 | struct ext_slave_platform_data *pdata) | ||
| 447 | { | ||
| 448 | int result; | ||
| 449 | unsigned char reg; | ||
| 450 | struct mpu6050_private_data *private_data = | ||
| 451 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 452 | |||
| 453 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 454 | MPUREG_PWR_MGMT_1, 1, ®); | ||
| 455 | if (result) { | ||
| 456 | LOG_RESULT_LOCATION(result); | ||
| 457 | return result; | ||
| 458 | } | ||
| 459 | |||
| 460 | if (reg & BIT_SLEEP) { | ||
| 461 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 462 | MPUREG_PWR_MGMT_1, reg & ~BIT_SLEEP); | ||
| 463 | if (result) { | ||
| 464 | LOG_RESULT_LOCATION(result); | ||
| 465 | return result; | ||
| 466 | } | ||
| 467 | } | ||
| 468 | msleep(2); | ||
| 469 | |||
| 470 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 471 | MPUREG_PWR_MGMT_2, 1, ®); | ||
| 472 | if (result) { | ||
| 473 | LOG_RESULT_LOCATION(result); | ||
| 474 | return result; | ||
| 475 | } | ||
| 476 | reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
| 477 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 478 | MPUREG_PWR_MGMT_2, reg); | ||
| 479 | if (result) { | ||
| 480 | LOG_RESULT_LOCATION(result); | ||
| 481 | return result; | ||
| 482 | } | ||
| 483 | |||
| 484 | /* settings */ | ||
| 485 | |||
| 486 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
| 487 | true, private_data->resume.fsr); | ||
| 488 | if (result) { | ||
| 489 | LOG_RESULT_LOCATION(result); | ||
| 490 | return result; | ||
| 491 | } | ||
| 492 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
| 493 | true, private_data->resume.odr); | ||
| 494 | if (result) { | ||
| 495 | LOG_RESULT_LOCATION(result); | ||
| 496 | return result; | ||
| 497 | } | ||
| 498 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
| 499 | true, private_data->resume.irq_type); | ||
| 500 | |||
| 501 | /* motion, no_motion */ | ||
| 502 | /* TODO : port these in their respective _set_thrs and _set_dur | ||
| 503 | functions and use the APPLY paremeter to apply just like | ||
| 504 | _set_odr, _set_irq, and _set_fsr. */ | ||
| 505 | reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB; | ||
| 506 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 507 | MPUREG_ACCEL_MOT_THR, reg); | ||
| 508 | if (result) { | ||
| 509 | LOG_RESULT_LOCATION(result); | ||
| 510 | return result; | ||
| 511 | } | ||
| 512 | reg = (unsigned char) | ||
| 513 | ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths); | ||
| 514 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 515 | MPUREG_ACCEL_ZRMOT_THR, reg); | ||
| 516 | if (result) { | ||
| 517 | LOG_RESULT_LOCATION(result); | ||
| 518 | return result; | ||
| 519 | } | ||
| 520 | reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB; | ||
| 521 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 522 | MPUREG_ACCEL_MOT_DUR, reg); | ||
| 523 | if (result) { | ||
| 524 | LOG_RESULT_LOCATION(result); | ||
| 525 | return result; | ||
| 526 | } | ||
| 527 | reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB; | ||
| 528 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 529 | MPUREG_ACCEL_ZRMOT_DUR, reg); | ||
| 530 | if (result) { | ||
| 531 | LOG_RESULT_LOCATION(result); | ||
| 532 | return result; | ||
| 533 | } | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | static int mpu6050_read(void *mlsl_handle, | ||
| 538 | struct ext_slave_descr *slave, | ||
| 539 | struct ext_slave_platform_data *pdata, | ||
| 540 | unsigned char *data) | ||
| 541 | { | ||
| 542 | int result; | ||
| 543 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 544 | slave->read_reg, slave->read_len, data); | ||
| 545 | return result; | ||
| 546 | } | ||
| 547 | |||
| 548 | static int mpu6050_config(void *mlsl_handle, | ||
| 549 | struct ext_slave_descr *slave, | ||
| 550 | struct ext_slave_platform_data *pdata, | ||
| 551 | struct ext_slave_config *data) | ||
| 552 | { | ||
| 553 | struct mpu6050_private_data *private_data = | ||
| 554 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 555 | if (!data->data) | ||
| 556 | return INV_ERROR_INVALID_PARAMETER; | ||
| 557 | |||
| 558 | switch (data->key) { | ||
| 559 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 560 | return mpu6050_set_odr(mlsl_handle, pdata, | ||
| 561 | &private_data->suspend, | ||
| 562 | data->apply, *((long *)data->data)); | ||
| 563 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 564 | return mpu6050_set_odr(mlsl_handle, pdata, | ||
| 565 | &private_data->resume, | ||
| 566 | data->apply, *((long *)data->data)); | ||
| 567 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 568 | return mpu6050_set_fsr(mlsl_handle, pdata, | ||
| 569 | &private_data->suspend, | ||
| 570 | data->apply, *((long *)data->data)); | ||
| 571 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 572 | return mpu6050_set_fsr(mlsl_handle, pdata, | ||
| 573 | &private_data->resume, | ||
| 574 | data->apply, *((long *)data->data)); | ||
| 575 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 576 | return mpu6050_set_ths(mlsl_handle, pdata, | ||
| 577 | &private_data->suspend, | ||
| 578 | data->apply, *((long *)data->data)); | ||
| 579 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 580 | return mpu6050_set_ths(mlsl_handle, pdata, | ||
| 581 | &private_data->resume, | ||
| 582 | data->apply, *((long *)data->data)); | ||
| 583 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 584 | return mpu6050_set_dur(mlsl_handle, pdata, | ||
| 585 | &private_data->suspend, | ||
| 586 | data->apply, *((long *)data->data)); | ||
| 587 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 588 | return mpu6050_set_dur(mlsl_handle, pdata, | ||
| 589 | &private_data->resume, | ||
| 590 | data->apply, *((long *)data->data)); | ||
| 591 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 592 | return mpu6050_set_irq(mlsl_handle, pdata, | ||
| 593 | &private_data->suspend, | ||
| 594 | data->apply, *((long *)data->data)); | ||
| 595 | break; | ||
| 596 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 597 | return mpu6050_set_irq(mlsl_handle, pdata, | ||
| 598 | &private_data->resume, | ||
| 599 | data->apply, *((long *)data->data)); | ||
| 600 | case MPU_SLAVE_CONFIG_INTERNAL_REFERENCE: | ||
| 601 | return mpu6050_set_mldl_cfg_ref(mlsl_handle, pdata, | ||
| 602 | (struct mldl_cfg *)data->data); | ||
| 603 | break; | ||
| 604 | |||
| 605 | default: | ||
| 606 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 607 | }; | ||
| 608 | |||
| 609 | return 0; | ||
| 610 | } | ||
| 611 | |||
| 612 | static int mpu6050_get_config(void *mlsl_handle, | ||
| 613 | struct ext_slave_descr *slave, | ||
| 614 | struct ext_slave_platform_data *pdata, | ||
| 615 | struct ext_slave_config *data) | ||
| 616 | { | ||
| 617 | struct mpu6050_private_data *private_data = | ||
| 618 | (struct mpu6050_private_data *)pdata->private_data; | ||
| 619 | if (!data->data) | ||
| 620 | return INV_ERROR_INVALID_PARAMETER; | ||
| 621 | |||
| 622 | switch (data->key) { | ||
| 623 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 624 | (*(unsigned long *)data->data) = | ||
| 625 | (unsigned long)private_data->suspend.odr; | ||
| 626 | break; | ||
| 627 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 628 | (*(unsigned long *)data->data) = | ||
| 629 | (unsigned long)private_data->resume.odr; | ||
| 630 | break; | ||
| 631 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 632 | (*(unsigned long *)data->data) = | ||
| 633 | (unsigned long)private_data->suspend.fsr; | ||
| 634 | break; | ||
| 635 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 636 | (*(unsigned long *)data->data) = | ||
| 637 | (unsigned long)private_data->resume.fsr; | ||
| 638 | break; | ||
| 639 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 640 | (*(unsigned long *)data->data) = | ||
| 641 | (unsigned long)private_data->suspend.ths; | ||
| 642 | break; | ||
| 643 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 644 | (*(unsigned long *)data->data) = | ||
| 645 | (unsigned long)private_data->resume.ths; | ||
| 646 | break; | ||
| 647 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 648 | (*(unsigned long *)data->data) = | ||
| 649 | (unsigned long)private_data->suspend.dur; | ||
| 650 | break; | ||
| 651 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 652 | (*(unsigned long *)data->data) = | ||
| 653 | (unsigned long)private_data->resume.dur; | ||
| 654 | break; | ||
| 655 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 656 | (*(unsigned long *)data->data) = | ||
| 657 | (unsigned long)private_data->suspend.irq_type; | ||
| 658 | break; | ||
| 659 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 660 | (*(unsigned long *)data->data) = | ||
| 661 | (unsigned long)private_data->resume.irq_type; | ||
| 662 | break; | ||
| 663 | default: | ||
| 664 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 665 | }; | ||
| 666 | |||
| 667 | return 0; | ||
| 668 | } | ||
| 669 | |||
| 670 | static struct ext_slave_descr mpu6050_descr = { | ||
| 671 | .init = mpu6050_init, | ||
| 672 | .exit = mpu6050_exit, | ||
| 673 | .suspend = mpu6050_suspend, | ||
| 674 | .resume = mpu6050_resume, | ||
| 675 | .read = mpu6050_read, | ||
| 676 | .config = mpu6050_config, | ||
| 677 | .get_config = mpu6050_get_config, | ||
| 678 | .name = "mpu6050", | ||
| 679 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
| 680 | .id = ACCEL_ID_MPU6050, | ||
| 681 | .read_reg = 0x3B, | ||
| 682 | .read_len = 6, | ||
| 683 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 684 | .range = {2, 0}, | ||
| 685 | .trigger = NULL, | ||
| 686 | }; | ||
| 687 | |||
| 688 | struct ext_slave_descr *mpu6050_get_slave_descr(void) | ||
| 689 | { | ||
| 690 | return &mpu6050_descr; | ||
| 691 | } | ||
| 692 | |||
| 693 | /** | ||
| 694 | * @} | ||
| 695 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.h b/drivers/misc/inv_mpu/accel/mpu6050.h new file mode 100644 index 00000000000..c347bcb4d77 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mpu6050.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | |||
| 21 | #ifndef __MPU6050_H__ | ||
| 22 | #define __MPU6050_H__ | ||
| 23 | |||
| 24 | #include <linux/mpu.h> | ||
| 25 | |||
| 26 | struct ext_slave_descr *mpu6050_get_slave_descr(void); | ||
| 27 | |||
| 28 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/Kconfig b/drivers/misc/inv_mpu/compass/Kconfig new file mode 100644 index 00000000000..7e6bac87d31 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/Kconfig | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | menuconfig INV_SENSORS_COMPASS | ||
| 2 | bool "Compass Slave Sensors" | ||
| 3 | default y | ||
| 4 | ---help--- | ||
| 5 | Say Y here to get to see options for device drivers for various | ||
| 6 | compasses. This option alone does not add any kernel code. | ||
| 7 | |||
| 8 | If you say N, all options in this submenu will be skipped and disabled. | ||
| 9 | |||
| 10 | if INV_SENSORS_COMPASS | ||
| 11 | |||
| 12 | config MPU_SENSORS_AK8963 | ||
| 13 | tristate "AKM ak8963" | ||
| 14 | help | ||
| 15 | This enables support for the AKM ak8963 compass | ||
| 16 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 17 | device driver. Only one compass can be registered at a time. | ||
| 18 | Specifying more that one compass in the board file will result | ||
| 19 | in runtime errors. | ||
| 20 | |||
| 21 | config MPU_SENSORS_AK8975 | ||
| 22 | tristate "AKM ak8975" | ||
| 23 | help | ||
| 24 | This enables support for the AKM ak8975 compass | ||
| 25 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 26 | device driver. Only one compass can be registered at a time. | ||
| 27 | Specifying more that one compass in the board file will result | ||
| 28 | in runtime errors. | ||
| 29 | |||
| 30 | config MPU_SENSORS_AK8972 | ||
| 31 | tristate "AKM ak8972" | ||
| 32 | help | ||
| 33 | This enables support for the AKM ak8972 compass | ||
| 34 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 35 | device driver. Only one compass can be registered at a time. | ||
| 36 | Specifying more that one compass in the board file will result | ||
| 37 | in runtime errors. | ||
| 38 | |||
| 39 | config MPU_SENSORS_MMC314X | ||
| 40 | tristate "MEMSIC mmc314x" | ||
| 41 | help | ||
| 42 | This enables support for the MEMSIC mmc314x compass | ||
| 43 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 44 | device driver. Only one compass can be registered at a time. | ||
| 45 | Specifying more that one compass in the board file will result | ||
| 46 | in runtime errors. | ||
| 47 | |||
| 48 | config MPU_SENSORS_AMI30X | ||
| 49 | tristate "Aichi Steel ami30X" | ||
| 50 | help | ||
| 51 | This enables support for the Aichi Steel ami304/ami305 compass | ||
| 52 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 53 | device driver. Only one compass can be registered at a time. | ||
| 54 | Specifying more that one compass in the board file will result | ||
| 55 | in runtime errors. | ||
| 56 | |||
| 57 | config MPU_SENSORS_AMI306 | ||
| 58 | tristate "Aichi Steel ami306" | ||
| 59 | help | ||
| 60 | This enables support for the Aichi Steel ami306 compass | ||
| 61 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 62 | device driver. Only one compass can be registered at a time. | ||
| 63 | Specifying more that one compass in the board file will result | ||
| 64 | in runtime errors. | ||
| 65 | |||
| 66 | config MPU_SENSORS_HMC5883 | ||
| 67 | tristate "Honeywell hmc5883" | ||
| 68 | help | ||
| 69 | This enables support for the Honeywell hmc5883 compass | ||
| 70 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 71 | device driver. Only one compass can be registered at a time. | ||
| 72 | Specifying more that one compass in the board file will result | ||
| 73 | in runtime errors. | ||
| 74 | |||
| 75 | config MPU_SENSORS_LSM303DLX_M | ||
| 76 | tristate "ST lsm303dlx" | ||
| 77 | help | ||
| 78 | This enables support for the ST lsm303dlh and lsm303dlm compasses | ||
| 79 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 80 | device driver. Only one compass can be registered at a time. | ||
| 81 | Specifying more that one compass in the board file will result | ||
| 82 | in runtime errors. | ||
| 83 | |||
| 84 | config MPU_SENSORS_MMC314XMS | ||
| 85 | tristate "MEMSIC mmc314xMS" | ||
| 86 | help | ||
| 87 | This enables support for the MEMSIC mmc314xMS compass | ||
| 88 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 89 | device driver. Only one compass can be registered at a time. | ||
| 90 | Specifying more that one compass in the board file will result | ||
| 91 | in runtime errors. | ||
| 92 | |||
| 93 | config MPU_SENSORS_YAS529 | ||
| 94 | tristate "Yamaha yas529" | ||
| 95 | depends on INPUT_YAS_MAGNETOMETER | ||
| 96 | help | ||
| 97 | This enables support for the Yamaha yas529 compass | ||
| 98 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 99 | device driver. Only one compass can be registered at a time. | ||
| 100 | Specifying more that one compass in the board file will result | ||
| 101 | in runtime errors. | ||
| 102 | |||
| 103 | config MPU_SENSORS_YAS530 | ||
| 104 | tristate "Yamaha yas530" | ||
| 105 | help | ||
| 106 | This enables support for the Yamaha yas530 compass | ||
| 107 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 108 | device driver. Only one compass can be registered at a time. | ||
| 109 | Specifying more that one compass in the board file will result | ||
| 110 | in runtime errors. | ||
| 111 | |||
| 112 | config MPU_SENSORS_HSCDTD002B | ||
| 113 | tristate "Alps hscdtd002b" | ||
| 114 | help | ||
| 115 | This enables support for the Alps hscdtd002b compass | ||
| 116 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 117 | device driver. Only one compass can be registered at a time. | ||
| 118 | Specifying more that one compass in the board file will result | ||
| 119 | in runtime errors. | ||
| 120 | |||
| 121 | config MPU_SENSORS_HSCDTD004A | ||
| 122 | tristate "Alps hscdtd004a" | ||
| 123 | help | ||
| 124 | This enables support for the Alps hscdtd004a compass | ||
| 125 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 126 | device driver. Only one compass can be registered at a time. | ||
| 127 | Specifying more that one compass in the board file will result | ||
| 128 | in runtime errors. | ||
| 129 | |||
| 130 | endif | ||
diff --git a/drivers/misc/inv_mpu/compass/Makefile b/drivers/misc/inv_mpu/compass/Makefile new file mode 100644 index 00000000000..d76ce1b06f3 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/Makefile | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | # | ||
| 2 | # Compass Slaves MPUxxxx | ||
| 3 | # | ||
| 4 | obj-$(CONFIG_MPU_SENSORS_AMI30X) += inv_mpu_ami30x.o | ||
| 5 | inv_mpu_ami30x-objs += ami30x.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_MPU_SENSORS_AMI306) += inv_mpu_ami306.o | ||
| 8 | inv_mpu_ami306-objs += ami306.o | ||
| 9 | |||
| 10 | obj-$(CONFIG_MPU_SENSORS_HMC5883) += inv_mpu_hmc5883.o | ||
| 11 | inv_mpu_hmc5883-objs += hmc5883.o | ||
| 12 | |||
| 13 | obj-$(CONFIG_MPU_SENSORS_LSM303DLX_M) += inv_mpu_lsm303dlx_m.o | ||
| 14 | inv_mpu_lsm303dlx_m-objs += lsm303dlx_m.o | ||
| 15 | |||
| 16 | obj-$(CONFIG_MPU_SENSORS_MMC314X) += inv_mpu_mmc314x.o | ||
| 17 | inv_mpu_mmc314x-objs += mmc314x.o | ||
| 18 | |||
| 19 | obj-$(CONFIG_MPU_SENSORS_YAS529) += inv_mpu_yas529.o | ||
| 20 | inv_mpu_yas529-objs += yas529-kernel.o | ||
| 21 | |||
| 22 | obj-$(CONFIG_MPU_SENSORS_YAS530) += inv_mpu_yas530.o | ||
| 23 | inv_mpu_yas530-objs += yas530.o | ||
| 24 | |||
| 25 | obj-$(CONFIG_MPU_SENSORS_HSCDTD002B) += inv_mpu_hscdtd002b.o | ||
| 26 | inv_mpu_hscdtd002b-objs += hscdtd002b.o | ||
| 27 | |||
| 28 | obj-$(CONFIG_MPU_SENSORS_HSCDTD004A) += inv_mpu_hscdtd004a.o | ||
| 29 | inv_mpu_hscdtd004a-objs += hscdtd004a.o | ||
| 30 | |||
| 31 | obj-$(CONFIG_MPU_SENSORS_AK8963) += inv_mpu_ak89xx.o | ||
| 32 | inv_mpu_ak89xx-objs += ak89xx.o | ||
| 33 | |||
| 34 | obj-$(CONFIG_MPU_SENSORS_AK8975) += inv_mpu_ak8975.o | ||
| 35 | inv_mpu_ak8975-objs += ak8975.o | ||
| 36 | |||
| 37 | obj-$(CONFIG_MPU_SENSORS_AK8972) += inv_mpu_ak8972.o | ||
| 38 | inv_mpu_ak8972-objs += ak8972.o | ||
| 39 | |||
| 40 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 41 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/compass/ak8972.c b/drivers/misc/inv_mpu/compass/ak8972.c new file mode 100644 index 00000000000..7eb15b44039 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak8972.c | |||
| @@ -0,0 +1,499 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file ak8972.c | ||
| 25 | * @brief Magnetometer setup and handling methods for the AKM AK8972 compass device. | ||
| 26 | */ | ||
| 27 | |||
| 28 | /* -------------------------------------------------------------------------- */ | ||
| 29 | |||
| 30 | #include <linux/i2c.h> | ||
| 31 | #include <linux/module.h> | ||
| 32 | #include <linux/moduleparam.h> | ||
| 33 | #include <linux/kernel.h> | ||
| 34 | #include <linux/errno.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/delay.h> | ||
| 37 | #include "mpu-dev.h" | ||
| 38 | |||
| 39 | #include <log.h> | ||
| 40 | #include <linux/mpu.h> | ||
| 41 | #include "mlsl.h" | ||
| 42 | #include "mldl_cfg.h" | ||
| 43 | #undef MPL_LOG_TAG | ||
| 44 | #define MPL_LOG_TAG "MPL-compass" | ||
| 45 | |||
| 46 | /* -------------------------------------------------------------------------- */ | ||
| 47 | #define AK8972_REG_ST1 (0x02) | ||
| 48 | #define AK8972_REG_HXL (0x03) | ||
| 49 | #define AK8972_REG_ST2 (0x09) | ||
| 50 | |||
| 51 | #define AK8972_REG_CNTL (0x0A) | ||
| 52 | #define AK8972_REG_ASAX (0x10) | ||
| 53 | #define AK8972_REG_ASAY (0x11) | ||
| 54 | #define AK8972_REG_ASAZ (0x12) | ||
| 55 | |||
| 56 | #define AK8972_CNTL_MODE_POWER_DOWN (0x00) | ||
| 57 | #define AK8972_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
| 58 | #define AK8972_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
| 59 | |||
| 60 | /* -------------------------------------------------------------------------- */ | ||
| 61 | struct ak8972_config { | ||
| 62 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct ak8972_private_data { | ||
| 66 | struct ak8972_config init; | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* -------------------------------------------------------------------------- */ | ||
| 70 | static int ak8972_init(void *mlsl_handle, | ||
| 71 | struct ext_slave_descr *slave, | ||
| 72 | struct ext_slave_platform_data *pdata) | ||
| 73 | { | ||
| 74 | int result; | ||
| 75 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
| 76 | |||
| 77 | struct ak8972_private_data *private_data; | ||
| 78 | private_data = (struct ak8972_private_data *) | ||
| 79 | kzalloc(sizeof(struct ak8972_private_data), GFP_KERNEL); | ||
| 80 | |||
| 81 | if (!private_data) | ||
| 82 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 83 | |||
| 84 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 85 | AK8972_REG_CNTL, | ||
| 86 | AK8972_CNTL_MODE_POWER_DOWN); | ||
| 87 | if (result) { | ||
| 88 | LOG_RESULT_LOCATION(result); | ||
| 89 | return result; | ||
| 90 | } | ||
| 91 | /* Wait at least 100us */ | ||
| 92 | udelay(100); | ||
| 93 | |||
| 94 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 95 | AK8972_REG_CNTL, | ||
| 96 | AK8972_CNTL_MODE_FUSE_ROM_ACCESS); | ||
| 97 | if (result) { | ||
| 98 | LOG_RESULT_LOCATION(result); | ||
| 99 | return result; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Wait at least 200us */ | ||
| 103 | udelay(200); | ||
| 104 | |||
| 105 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 106 | AK8972_REG_ASAX, | ||
| 107 | COMPASS_NUM_AXES, serial_data); | ||
| 108 | if (result) { | ||
| 109 | LOG_RESULT_LOCATION(result); | ||
| 110 | return result; | ||
| 111 | } | ||
| 112 | |||
| 113 | pdata->private_data = private_data; | ||
| 114 | |||
| 115 | private_data->init.asa[0] = serial_data[0]; | ||
| 116 | private_data->init.asa[1] = serial_data[1]; | ||
| 117 | private_data->init.asa[2] = serial_data[2]; | ||
| 118 | |||
| 119 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 120 | AK8972_REG_CNTL, | ||
| 121 | AK8972_CNTL_MODE_POWER_DOWN); | ||
| 122 | if (result) { | ||
| 123 | LOG_RESULT_LOCATION(result); | ||
| 124 | return result; | ||
| 125 | } | ||
| 126 | |||
| 127 | udelay(100); | ||
| 128 | return INV_SUCCESS; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int ak8972_exit(void *mlsl_handle, | ||
| 132 | struct ext_slave_descr *slave, | ||
| 133 | struct ext_slave_platform_data *pdata) | ||
| 134 | { | ||
| 135 | kfree(pdata->private_data); | ||
| 136 | return INV_SUCCESS; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int ak8972_suspend(void *mlsl_handle, | ||
| 140 | struct ext_slave_descr *slave, | ||
| 141 | struct ext_slave_platform_data *pdata) | ||
| 142 | { | ||
| 143 | int result = INV_SUCCESS; | ||
| 144 | result = | ||
| 145 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 146 | AK8972_REG_CNTL, | ||
| 147 | AK8972_CNTL_MODE_POWER_DOWN); | ||
| 148 | msleep(1); /* wait at least 100us */ | ||
| 149 | if (result) { | ||
| 150 | LOG_RESULT_LOCATION(result); | ||
| 151 | return result; | ||
| 152 | } | ||
| 153 | return result; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int ak8972_resume(void *mlsl_handle, | ||
| 157 | struct ext_slave_descr *slave, | ||
| 158 | struct ext_slave_platform_data *pdata) | ||
| 159 | { | ||
| 160 | int result = INV_SUCCESS; | ||
| 161 | result = | ||
| 162 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 163 | AK8972_REG_CNTL, | ||
| 164 | AK8972_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 165 | if (result) { | ||
| 166 | LOG_RESULT_LOCATION(result); | ||
| 167 | return result; | ||
| 168 | } | ||
| 169 | return result; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int ak8972_read(void *mlsl_handle, | ||
| 173 | struct ext_slave_descr *slave, | ||
| 174 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 175 | { | ||
| 176 | unsigned char regs[8]; | ||
| 177 | unsigned char *stat = ®s[0]; | ||
| 178 | unsigned char *stat2 = ®s[7]; | ||
| 179 | int result = INV_SUCCESS; | ||
| 180 | int status = INV_SUCCESS; | ||
| 181 | |||
| 182 | result = | ||
| 183 | inv_serial_read(mlsl_handle, pdata->address, AK8972_REG_ST1, | ||
| 184 | 8, regs); | ||
| 185 | if (result) { | ||
| 186 | LOG_RESULT_LOCATION(result); | ||
| 187 | return result; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Always return the data and the status registers */ | ||
| 191 | memcpy(data, ®s[1], 6); | ||
| 192 | data[6] = regs[0]; | ||
| 193 | data[7] = regs[7]; | ||
| 194 | |||
| 195 | /* | ||
| 196 | * ST : data ready - | ||
| 197 | * Measurement has been completed and data is ready to be read. | ||
| 198 | */ | ||
| 199 | if (*stat & 0x01) | ||
| 200 | status = INV_SUCCESS; | ||
| 201 | |||
| 202 | /* | ||
| 203 | * ST2 : data error - | ||
| 204 | * occurs when data read is started outside of a readable period; | ||
| 205 | * data read would not be correct. | ||
| 206 | * Valid in continuous measurement mode only. | ||
| 207 | * In single measurement mode this error should not occour but we | ||
| 208 | * stil account for it and return an error, since the data would be | ||
| 209 | * corrupted. | ||
| 210 | * DERR bit is self-clearing when ST2 register is read. | ||
| 211 | */ | ||
| 212 | if (*stat2 & 0x04) | ||
| 213 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
| 214 | /* | ||
| 215 | * ST2 : overflow - | ||
| 216 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
| 217 | * This is likely to happen in presence of an external magnetic | ||
| 218 | * disturbance; it indicates, the sensor data is incorrect and should | ||
| 219 | * be ignored. | ||
| 220 | * An error is returned. | ||
| 221 | * HOFL bit clears when a new measurement starts. | ||
| 222 | */ | ||
| 223 | if (*stat2 & 0x08) | ||
| 224 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 225 | /* | ||
| 226 | * ST : overrun - | ||
| 227 | * the previous sample was not fetched and lost. | ||
| 228 | * Valid in continuous measurement mode only. | ||
| 229 | * In single measurement mode this error should not occour and we | ||
| 230 | * don't consider this condition an error. | ||
| 231 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
| 232 | * read. | ||
| 233 | */ | ||
| 234 | if (*stat & 0x02) { | ||
| 235 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
| 236 | status = INV_SUCCESS; | ||
| 237 | } | ||
| 238 | |||
| 239 | /* | ||
| 240 | * trigger next measurement if: | ||
| 241 | * - stat is non zero; | ||
| 242 | * - if stat is zero and stat2 is non zero. | ||
| 243 | * Won't trigger if data is not ready and there was no error. | ||
| 244 | */ | ||
| 245 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
| 246 | result = inv_serial_single_write( | ||
| 247 | mlsl_handle, pdata->address, | ||
| 248 | AK8972_REG_CNTL, AK8972_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 249 | if (result) { | ||
| 250 | LOG_RESULT_LOCATION(result); | ||
| 251 | return result; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return status; | ||
| 256 | } | ||
| 257 | |||
| 258 | static int ak8972_config(void *mlsl_handle, | ||
| 259 | struct ext_slave_descr *slave, | ||
| 260 | struct ext_slave_platform_data *pdata, | ||
| 261 | struct ext_slave_config *data) | ||
| 262 | { | ||
| 263 | int result; | ||
| 264 | if (!data->data) | ||
| 265 | return INV_ERROR_INVALID_PARAMETER; | ||
| 266 | |||
| 267 | switch (data->key) { | ||
| 268 | case MPU_SLAVE_WRITE_REGISTERS: | ||
| 269 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 270 | data->len, | ||
| 271 | (unsigned char *)data->data); | ||
| 272 | if (result) { | ||
| 273 | LOG_RESULT_LOCATION(result); | ||
| 274 | return result; | ||
| 275 | } | ||
| 276 | break; | ||
| 277 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 278 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 279 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 280 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 281 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 282 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 283 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 284 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 285 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 286 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 287 | default: | ||
| 288 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 289 | }; | ||
| 290 | |||
| 291 | return INV_SUCCESS; | ||
| 292 | } | ||
| 293 | |||
| 294 | static int ak8972_get_config(void *mlsl_handle, | ||
| 295 | struct ext_slave_descr *slave, | ||
| 296 | struct ext_slave_platform_data *pdata, | ||
| 297 | struct ext_slave_config *data) | ||
| 298 | { | ||
| 299 | struct ak8972_private_data *private_data = pdata->private_data; | ||
| 300 | int result; | ||
| 301 | if (!data->data) | ||
| 302 | return INV_ERROR_INVALID_PARAMETER; | ||
| 303 | |||
| 304 | switch (data->key) { | ||
| 305 | case MPU_SLAVE_READ_REGISTERS: | ||
| 306 | { | ||
| 307 | unsigned char *serial_data = | ||
| 308 | (unsigned char *)data->data; | ||
| 309 | result = | ||
| 310 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 311 | serial_data[0], data->len - 1, | ||
| 312 | &serial_data[1]); | ||
| 313 | if (result) { | ||
| 314 | LOG_RESULT_LOCATION(result); | ||
| 315 | return result; | ||
| 316 | } | ||
| 317 | break; | ||
| 318 | } | ||
| 319 | case MPU_SLAVE_READ_SCALE: | ||
| 320 | { | ||
| 321 | unsigned char *serial_data = | ||
| 322 | (unsigned char *)data->data; | ||
| 323 | serial_data[0] = private_data->init.asa[0]; | ||
| 324 | serial_data[1] = private_data->init.asa[1]; | ||
| 325 | serial_data[2] = private_data->init.asa[2]; | ||
| 326 | result = INV_SUCCESS; | ||
| 327 | if (result) { | ||
| 328 | LOG_RESULT_LOCATION(result); | ||
| 329 | return result; | ||
| 330 | } | ||
| 331 | break; | ||
| 332 | } | ||
| 333 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 334 | (*(unsigned long *)data->data) = 0; | ||
| 335 | break; | ||
| 336 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 337 | (*(unsigned long *)data->data) = 8000; | ||
| 338 | break; | ||
| 339 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 340 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 341 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 342 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 343 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 344 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 345 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 346 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 347 | default: | ||
| 348 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 349 | }; | ||
| 350 | |||
| 351 | return INV_SUCCESS; | ||
| 352 | } | ||
| 353 | |||
| 354 | static struct ext_slave_read_trigger ak8972_read_trigger = { | ||
| 355 | /*.reg = */ 0x0A, | ||
| 356 | /*.value = */ 0x01 | ||
| 357 | }; | ||
| 358 | |||
| 359 | static struct ext_slave_descr ak8972_descr = { | ||
| 360 | .init = ak8972_init, | ||
| 361 | .exit = ak8972_exit, | ||
| 362 | .suspend = ak8972_suspend, | ||
| 363 | .resume = ak8972_resume, | ||
| 364 | .read = ak8972_read, | ||
| 365 | .config = ak8972_config, | ||
| 366 | .get_config = ak8972_get_config, | ||
| 367 | .name = "ak8972", | ||
| 368 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 369 | .id = COMPASS_ID_AK8972, | ||
| 370 | .read_reg = 0x01, | ||
| 371 | .read_len = 9, | ||
| 372 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 373 | .range = {39321, 6000}, | ||
| 374 | .trigger = &ak8972_read_trigger, | ||
| 375 | }; | ||
| 376 | |||
| 377 | static | ||
| 378 | struct ext_slave_descr *ak8972_get_slave_descr(void) | ||
| 379 | { | ||
| 380 | return &ak8972_descr; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* -------------------------------------------------------------------------- */ | ||
| 384 | struct ak8972_mod_private_data { | ||
| 385 | struct i2c_client *client; | ||
| 386 | struct ext_slave_platform_data *pdata; | ||
| 387 | }; | ||
| 388 | |||
| 389 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 390 | |||
| 391 | static int ak8972_mod_probe(struct i2c_client *client, | ||
| 392 | const struct i2c_device_id *devid) | ||
| 393 | { | ||
| 394 | struct ext_slave_platform_data *pdata; | ||
| 395 | struct ak8972_mod_private_data *private_data; | ||
| 396 | int result = 0; | ||
| 397 | |||
| 398 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 399 | |||
| 400 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 401 | result = -ENODEV; | ||
| 402 | goto out_no_free; | ||
| 403 | } | ||
| 404 | |||
| 405 | pdata = client->dev.platform_data; | ||
| 406 | if (!pdata) { | ||
| 407 | dev_err(&client->adapter->dev, | ||
| 408 | "Missing platform data for slave %s\n", devid->name); | ||
| 409 | result = -EFAULT; | ||
| 410 | goto out_no_free; | ||
| 411 | } | ||
| 412 | |||
| 413 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 414 | if (!private_data) { | ||
| 415 | result = -ENOMEM; | ||
| 416 | goto out_no_free; | ||
| 417 | } | ||
| 418 | |||
| 419 | i2c_set_clientdata(client, private_data); | ||
| 420 | private_data->client = client; | ||
| 421 | private_data->pdata = pdata; | ||
| 422 | |||
| 423 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 424 | ak8972_get_slave_descr); | ||
| 425 | if (result) { | ||
| 426 | dev_err(&client->adapter->dev, | ||
| 427 | "Slave registration failed: %s, %d\n", | ||
| 428 | devid->name, result); | ||
| 429 | goto out_free_memory; | ||
| 430 | } | ||
| 431 | |||
| 432 | return result; | ||
| 433 | |||
| 434 | out_free_memory: | ||
| 435 | kfree(private_data); | ||
| 436 | out_no_free: | ||
| 437 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 438 | return result; | ||
| 439 | |||
| 440 | } | ||
| 441 | |||
| 442 | static int ak8972_mod_remove(struct i2c_client *client) | ||
| 443 | { | ||
| 444 | struct ak8972_mod_private_data *private_data = | ||
| 445 | i2c_get_clientdata(client); | ||
| 446 | |||
| 447 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 448 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 449 | ak8972_get_slave_descr); | ||
| 450 | |||
| 451 | kfree(private_data); | ||
| 452 | return 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | static const struct i2c_device_id ak8972_mod_id[] = { | ||
| 456 | { "ak8972", COMPASS_ID_AK8972 }, | ||
| 457 | {} | ||
| 458 | }; | ||
| 459 | |||
| 460 | MODULE_DEVICE_TABLE(i2c, ak8972_mod_id); | ||
| 461 | |||
| 462 | static struct i2c_driver ak8972_mod_driver = { | ||
| 463 | .class = I2C_CLASS_HWMON, | ||
| 464 | .probe = ak8972_mod_probe, | ||
| 465 | .remove = ak8972_mod_remove, | ||
| 466 | .id_table = ak8972_mod_id, | ||
| 467 | .driver = { | ||
| 468 | .owner = THIS_MODULE, | ||
| 469 | .name = "ak8972_mod", | ||
| 470 | }, | ||
| 471 | .address_list = normal_i2c, | ||
| 472 | }; | ||
| 473 | |||
| 474 | static int __init ak8972_mod_init(void) | ||
| 475 | { | ||
| 476 | int res = i2c_add_driver(&ak8972_mod_driver); | ||
| 477 | pr_info("%s: Probe name %s\n", __func__, "ak8972_mod"); | ||
| 478 | if (res) | ||
| 479 | pr_err("%s failed\n", __func__); | ||
| 480 | return res; | ||
| 481 | } | ||
| 482 | |||
| 483 | static void __exit ak8972_mod_exit(void) | ||
| 484 | { | ||
| 485 | pr_info("%s\n", __func__); | ||
| 486 | i2c_del_driver(&ak8972_mod_driver); | ||
| 487 | } | ||
| 488 | |||
| 489 | module_init(ak8972_mod_init); | ||
| 490 | module_exit(ak8972_mod_exit); | ||
| 491 | |||
| 492 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 493 | MODULE_DESCRIPTION("Driver to integrate AK8972 sensor with the MPU"); | ||
| 494 | MODULE_LICENSE("GPL"); | ||
| 495 | MODULE_ALIAS("ak8972_mod"); | ||
| 496 | |||
| 497 | /** | ||
| 498 | * @} | ||
| 499 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ak8975.c b/drivers/misc/inv_mpu/compass/ak8975.c new file mode 100644 index 00000000000..3642e29e89a --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak8975.c | |||
| @@ -0,0 +1,500 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file ak8975.c | ||
| 25 | * @brief Magnetometer setup and handling methods for the AKM AK8975, | ||
| 26 | * AKM AK8975B, and AKM AK8975C compass devices. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | #define AK8975_REG_ST1 (0x02) | ||
| 49 | #define AK8975_REG_HXL (0x03) | ||
| 50 | #define AK8975_REG_ST2 (0x09) | ||
| 51 | |||
| 52 | #define AK8975_REG_CNTL (0x0A) | ||
| 53 | #define AK8975_REG_ASAX (0x10) | ||
| 54 | #define AK8975_REG_ASAY (0x11) | ||
| 55 | #define AK8975_REG_ASAZ (0x12) | ||
| 56 | |||
| 57 | #define AK8975_CNTL_MODE_POWER_DOWN (0x00) | ||
| 58 | #define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
| 59 | #define AK8975_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
| 60 | |||
| 61 | /* -------------------------------------------------------------------------- */ | ||
| 62 | struct ak8975_config { | ||
| 63 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct ak8975_private_data { | ||
| 67 | struct ak8975_config init; | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* -------------------------------------------------------------------------- */ | ||
| 71 | static int ak8975_init(void *mlsl_handle, | ||
| 72 | struct ext_slave_descr *slave, | ||
| 73 | struct ext_slave_platform_data *pdata) | ||
| 74 | { | ||
| 75 | int result; | ||
| 76 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
| 77 | |||
| 78 | struct ak8975_private_data *private_data; | ||
| 79 | private_data = (struct ak8975_private_data *) | ||
| 80 | kzalloc(sizeof(struct ak8975_private_data), GFP_KERNEL); | ||
| 81 | |||
| 82 | if (!private_data) | ||
| 83 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 84 | |||
| 85 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 86 | AK8975_REG_CNTL, | ||
| 87 | AK8975_CNTL_MODE_POWER_DOWN); | ||
| 88 | if (result) { | ||
| 89 | LOG_RESULT_LOCATION(result); | ||
| 90 | return result; | ||
| 91 | } | ||
| 92 | /* Wait at least 100us */ | ||
| 93 | udelay(100); | ||
| 94 | |||
| 95 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 96 | AK8975_REG_CNTL, | ||
| 97 | AK8975_CNTL_MODE_FUSE_ROM_ACCESS); | ||
| 98 | if (result) { | ||
| 99 | LOG_RESULT_LOCATION(result); | ||
| 100 | return result; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Wait at least 200us */ | ||
| 104 | udelay(200); | ||
| 105 | |||
| 106 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 107 | AK8975_REG_ASAX, | ||
| 108 | COMPASS_NUM_AXES, serial_data); | ||
| 109 | if (result) { | ||
| 110 | LOG_RESULT_LOCATION(result); | ||
| 111 | return result; | ||
| 112 | } | ||
| 113 | |||
| 114 | pdata->private_data = private_data; | ||
| 115 | |||
| 116 | private_data->init.asa[0] = serial_data[0]; | ||
| 117 | private_data->init.asa[1] = serial_data[1]; | ||
| 118 | private_data->init.asa[2] = serial_data[2]; | ||
| 119 | |||
| 120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 121 | AK8975_REG_CNTL, | ||
| 122 | AK8975_CNTL_MODE_POWER_DOWN); | ||
| 123 | if (result) { | ||
| 124 | LOG_RESULT_LOCATION(result); | ||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | udelay(100); | ||
| 129 | return INV_SUCCESS; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int ak8975_exit(void *mlsl_handle, | ||
| 133 | struct ext_slave_descr *slave, | ||
| 134 | struct ext_slave_platform_data *pdata) | ||
| 135 | { | ||
| 136 | kfree(pdata->private_data); | ||
| 137 | return INV_SUCCESS; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int ak8975_suspend(void *mlsl_handle, | ||
| 141 | struct ext_slave_descr *slave, | ||
| 142 | struct ext_slave_platform_data *pdata) | ||
| 143 | { | ||
| 144 | int result = INV_SUCCESS; | ||
| 145 | result = | ||
| 146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 147 | AK8975_REG_CNTL, | ||
| 148 | AK8975_CNTL_MODE_POWER_DOWN); | ||
| 149 | msleep(1); /* wait at least 100us */ | ||
| 150 | if (result) { | ||
| 151 | LOG_RESULT_LOCATION(result); | ||
| 152 | return result; | ||
| 153 | } | ||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | static int ak8975_resume(void *mlsl_handle, | ||
| 158 | struct ext_slave_descr *slave, | ||
| 159 | struct ext_slave_platform_data *pdata) | ||
| 160 | { | ||
| 161 | int result = INV_SUCCESS; | ||
| 162 | result = | ||
| 163 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 164 | AK8975_REG_CNTL, | ||
| 165 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 166 | if (result) { | ||
| 167 | LOG_RESULT_LOCATION(result); | ||
| 168 | return result; | ||
| 169 | } | ||
| 170 | return result; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int ak8975_read(void *mlsl_handle, | ||
| 174 | struct ext_slave_descr *slave, | ||
| 175 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 176 | { | ||
| 177 | unsigned char regs[8]; | ||
| 178 | unsigned char *stat = ®s[0]; | ||
| 179 | unsigned char *stat2 = ®s[7]; | ||
| 180 | int result = INV_SUCCESS; | ||
| 181 | int status = INV_SUCCESS; | ||
| 182 | |||
| 183 | result = | ||
| 184 | inv_serial_read(mlsl_handle, pdata->address, AK8975_REG_ST1, | ||
| 185 | 8, regs); | ||
| 186 | if (result) { | ||
| 187 | LOG_RESULT_LOCATION(result); | ||
| 188 | return result; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Always return the data and the status registers */ | ||
| 192 | memcpy(data, ®s[1], 6); | ||
| 193 | data[6] = regs[0]; | ||
| 194 | data[7] = regs[7]; | ||
| 195 | |||
| 196 | /* | ||
| 197 | * ST : data ready - | ||
| 198 | * Measurement has been completed and data is ready to be read. | ||
| 199 | */ | ||
| 200 | if (*stat & 0x01) | ||
| 201 | status = INV_SUCCESS; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * ST2 : data error - | ||
| 205 | * occurs when data read is started outside of a readable period; | ||
| 206 | * data read would not be correct. | ||
| 207 | * Valid in continuous measurement mode only. | ||
| 208 | * In single measurement mode this error should not occour but we | ||
| 209 | * stil account for it and return an error, since the data would be | ||
| 210 | * corrupted. | ||
| 211 | * DERR bit is self-clearing when ST2 register is read. | ||
| 212 | */ | ||
| 213 | if (*stat2 & 0x04) | ||
| 214 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
| 215 | /* | ||
| 216 | * ST2 : overflow - | ||
| 217 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
| 218 | * This is likely to happen in presence of an external magnetic | ||
| 219 | * disturbance; it indicates, the sensor data is incorrect and should | ||
| 220 | * be ignored. | ||
| 221 | * An error is returned. | ||
| 222 | * HOFL bit clears when a new measurement starts. | ||
| 223 | */ | ||
| 224 | if (*stat2 & 0x08) | ||
| 225 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 226 | /* | ||
| 227 | * ST : overrun - | ||
| 228 | * the previous sample was not fetched and lost. | ||
| 229 | * Valid in continuous measurement mode only. | ||
| 230 | * In single measurement mode this error should not occour and we | ||
| 231 | * don't consider this condition an error. | ||
| 232 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
| 233 | * read. | ||
| 234 | */ | ||
| 235 | if (*stat & 0x02) { | ||
| 236 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
| 237 | status = INV_SUCCESS; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * trigger next measurement if: | ||
| 242 | * - stat is non zero; | ||
| 243 | * - if stat is zero and stat2 is non zero. | ||
| 244 | * Won't trigger if data is not ready and there was no error. | ||
| 245 | */ | ||
| 246 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
| 247 | result = inv_serial_single_write( | ||
| 248 | mlsl_handle, pdata->address, | ||
| 249 | AK8975_REG_CNTL, AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 250 | if (result) { | ||
| 251 | LOG_RESULT_LOCATION(result); | ||
| 252 | return result; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | return status; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int ak8975_config(void *mlsl_handle, | ||
| 260 | struct ext_slave_descr *slave, | ||
| 261 | struct ext_slave_platform_data *pdata, | ||
| 262 | struct ext_slave_config *data) | ||
| 263 | { | ||
| 264 | int result; | ||
| 265 | if (!data->data) | ||
| 266 | return INV_ERROR_INVALID_PARAMETER; | ||
| 267 | |||
| 268 | switch (data->key) { | ||
| 269 | case MPU_SLAVE_WRITE_REGISTERS: | ||
| 270 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 271 | data->len, | ||
| 272 | (unsigned char *)data->data); | ||
| 273 | if (result) { | ||
| 274 | LOG_RESULT_LOCATION(result); | ||
| 275 | return result; | ||
| 276 | } | ||
| 277 | break; | ||
| 278 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 279 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 280 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 281 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 282 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 283 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 284 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 285 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 286 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 287 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 288 | default: | ||
| 289 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 290 | }; | ||
| 291 | |||
| 292 | return INV_SUCCESS; | ||
| 293 | } | ||
| 294 | |||
| 295 | static int ak8975_get_config(void *mlsl_handle, | ||
| 296 | struct ext_slave_descr *slave, | ||
| 297 | struct ext_slave_platform_data *pdata, | ||
| 298 | struct ext_slave_config *data) | ||
| 299 | { | ||
| 300 | struct ak8975_private_data *private_data = pdata->private_data; | ||
| 301 | int result; | ||
| 302 | if (!data->data) | ||
| 303 | return INV_ERROR_INVALID_PARAMETER; | ||
| 304 | |||
| 305 | switch (data->key) { | ||
| 306 | case MPU_SLAVE_READ_REGISTERS: | ||
| 307 | { | ||
| 308 | unsigned char *serial_data = | ||
| 309 | (unsigned char *)data->data; | ||
| 310 | result = | ||
| 311 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 312 | serial_data[0], data->len - 1, | ||
| 313 | &serial_data[1]); | ||
| 314 | if (result) { | ||
| 315 | LOG_RESULT_LOCATION(result); | ||
| 316 | return result; | ||
| 317 | } | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | case MPU_SLAVE_READ_SCALE: | ||
| 321 | { | ||
| 322 | unsigned char *serial_data = | ||
| 323 | (unsigned char *)data->data; | ||
| 324 | serial_data[0] = private_data->init.asa[0]; | ||
| 325 | serial_data[1] = private_data->init.asa[1]; | ||
| 326 | serial_data[2] = private_data->init.asa[2]; | ||
| 327 | result = INV_SUCCESS; | ||
| 328 | if (result) { | ||
| 329 | LOG_RESULT_LOCATION(result); | ||
| 330 | return result; | ||
| 331 | } | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 335 | (*(unsigned long *)data->data) = 0; | ||
| 336 | break; | ||
| 337 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 338 | (*(unsigned long *)data->data) = 8000; | ||
| 339 | break; | ||
| 340 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 341 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 342 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 343 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 344 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 345 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 346 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 347 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 348 | default: | ||
| 349 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 350 | }; | ||
| 351 | |||
| 352 | return INV_SUCCESS; | ||
| 353 | } | ||
| 354 | |||
| 355 | static struct ext_slave_read_trigger ak8975_read_trigger = { | ||
| 356 | /*.reg = */ 0x0A, | ||
| 357 | /*.value = */ 0x01 | ||
| 358 | }; | ||
| 359 | |||
| 360 | static struct ext_slave_descr ak8975_descr = { | ||
| 361 | .init = ak8975_init, | ||
| 362 | .exit = ak8975_exit, | ||
| 363 | .suspend = ak8975_suspend, | ||
| 364 | .resume = ak8975_resume, | ||
| 365 | .read = ak8975_read, | ||
| 366 | .config = ak8975_config, | ||
| 367 | .get_config = ak8975_get_config, | ||
| 368 | .name = "ak8975", | ||
| 369 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 370 | .id = COMPASS_ID_AK8975, | ||
| 371 | .read_reg = 0x01, | ||
| 372 | .read_len = 10, | ||
| 373 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 374 | .range = {9830, 4000}, | ||
| 375 | .trigger = &ak8975_read_trigger, | ||
| 376 | }; | ||
| 377 | |||
| 378 | static | ||
| 379 | struct ext_slave_descr *ak8975_get_slave_descr(void) | ||
| 380 | { | ||
| 381 | return &ak8975_descr; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* -------------------------------------------------------------------------- */ | ||
| 385 | struct ak8975_mod_private_data { | ||
| 386 | struct i2c_client *client; | ||
| 387 | struct ext_slave_platform_data *pdata; | ||
| 388 | }; | ||
| 389 | |||
| 390 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 391 | |||
| 392 | static int ak8975_mod_probe(struct i2c_client *client, | ||
| 393 | const struct i2c_device_id *devid) | ||
| 394 | { | ||
| 395 | struct ext_slave_platform_data *pdata; | ||
| 396 | struct ak8975_mod_private_data *private_data; | ||
| 397 | int result = 0; | ||
| 398 | |||
| 399 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 400 | |||
| 401 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 402 | result = -ENODEV; | ||
| 403 | goto out_no_free; | ||
| 404 | } | ||
| 405 | |||
| 406 | pdata = client->dev.platform_data; | ||
| 407 | if (!pdata) { | ||
| 408 | dev_err(&client->adapter->dev, | ||
| 409 | "Missing platform data for slave %s\n", devid->name); | ||
| 410 | result = -EFAULT; | ||
| 411 | goto out_no_free; | ||
| 412 | } | ||
| 413 | |||
| 414 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 415 | if (!private_data) { | ||
| 416 | result = -ENOMEM; | ||
| 417 | goto out_no_free; | ||
| 418 | } | ||
| 419 | |||
| 420 | i2c_set_clientdata(client, private_data); | ||
| 421 | private_data->client = client; | ||
| 422 | private_data->pdata = pdata; | ||
| 423 | |||
| 424 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 425 | ak8975_get_slave_descr); | ||
| 426 | if (result) { | ||
| 427 | dev_err(&client->adapter->dev, | ||
| 428 | "Slave registration failed: %s, %d\n", | ||
| 429 | devid->name, result); | ||
| 430 | goto out_free_memory; | ||
| 431 | } | ||
| 432 | |||
| 433 | return result; | ||
| 434 | |||
| 435 | out_free_memory: | ||
| 436 | kfree(private_data); | ||
| 437 | out_no_free: | ||
| 438 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 439 | return result; | ||
| 440 | |||
| 441 | } | ||
| 442 | |||
| 443 | static int ak8975_mod_remove(struct i2c_client *client) | ||
| 444 | { | ||
| 445 | struct ak8975_mod_private_data *private_data = | ||
| 446 | i2c_get_clientdata(client); | ||
| 447 | |||
| 448 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 449 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 450 | ak8975_get_slave_descr); | ||
| 451 | |||
| 452 | kfree(private_data); | ||
| 453 | return 0; | ||
| 454 | } | ||
| 455 | |||
| 456 | static const struct i2c_device_id ak8975_mod_id[] = { | ||
| 457 | { "ak8975", COMPASS_ID_AK8975 }, | ||
| 458 | {} | ||
| 459 | }; | ||
| 460 | |||
| 461 | MODULE_DEVICE_TABLE(i2c, ak8975_mod_id); | ||
| 462 | |||
| 463 | static struct i2c_driver ak8975_mod_driver = { | ||
| 464 | .class = I2C_CLASS_HWMON, | ||
| 465 | .probe = ak8975_mod_probe, | ||
| 466 | .remove = ak8975_mod_remove, | ||
| 467 | .id_table = ak8975_mod_id, | ||
| 468 | .driver = { | ||
| 469 | .owner = THIS_MODULE, | ||
| 470 | .name = "ak8975_mod", | ||
| 471 | }, | ||
| 472 | .address_list = normal_i2c, | ||
| 473 | }; | ||
| 474 | |||
| 475 | static int __init ak8975_mod_init(void) | ||
| 476 | { | ||
| 477 | int res = i2c_add_driver(&ak8975_mod_driver); | ||
| 478 | pr_info("%s: Probe name %s\n", __func__, "ak8975_mod"); | ||
| 479 | if (res) | ||
| 480 | pr_err("%s failed\n", __func__); | ||
| 481 | return res; | ||
| 482 | } | ||
| 483 | |||
| 484 | static void __exit ak8975_mod_exit(void) | ||
| 485 | { | ||
| 486 | pr_info("%s\n", __func__); | ||
| 487 | i2c_del_driver(&ak8975_mod_driver); | ||
| 488 | } | ||
| 489 | |||
| 490 | module_init(ak8975_mod_init); | ||
| 491 | module_exit(ak8975_mod_exit); | ||
| 492 | |||
| 493 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 494 | MODULE_DESCRIPTION("Driver to integrate AK8975 sensor with the MPU"); | ||
| 495 | MODULE_LICENSE("GPL"); | ||
| 496 | MODULE_ALIAS("ak8975_mod"); | ||
| 497 | |||
| 498 | /** | ||
| 499 | * @} | ||
| 500 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ak89xx.c b/drivers/misc/inv_mpu/compass/ak89xx.c new file mode 100644 index 00000000000..d15b0b8dbe6 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak89xx.c | |||
| @@ -0,0 +1,522 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file ak89xx.c | ||
| 25 | * @brief Magnetometer setup and handling methods for the AKM | ||
| 26 | * compass devices. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | #define AK89XX_REG_ST1 (0x02) | ||
| 49 | #define AK89XX_REG_HXL (0x03) | ||
| 50 | #define AK89XX_REG_ST2 (0x09) | ||
| 51 | |||
| 52 | #define AK89XX_REG_CNTL (0x0A) | ||
| 53 | #define AK89XX_REG_ASAX (0x10) | ||
| 54 | #define AK89XX_REG_ASAY (0x11) | ||
| 55 | #define AK89XX_REG_ASAZ (0x12) | ||
| 56 | |||
| 57 | #define AK89XX_CNTL_MODE_POWER_DOWN (0x00) | ||
| 58 | #define AK89XX_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
| 59 | #define AK89XX_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
| 60 | |||
| 61 | /* -------------------------------------------------------------------------- */ | ||
| 62 | struct ak89xx_config { | ||
| 63 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct ak89xx_private_data { | ||
| 67 | struct ak89xx_config init; | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* -------------------------------------------------------------------------- */ | ||
| 71 | static int ak89xx_init(void *mlsl_handle, | ||
| 72 | struct ext_slave_descr *slave, | ||
| 73 | struct ext_slave_platform_data *pdata) | ||
| 74 | { | ||
| 75 | int result; | ||
| 76 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
| 77 | |||
| 78 | struct ak89xx_private_data *private_data; | ||
| 79 | private_data = (struct ak89xx_private_data *) | ||
| 80 | kzalloc(sizeof(struct ak89xx_private_data), GFP_KERNEL); | ||
| 81 | |||
| 82 | if (!private_data) | ||
| 83 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 84 | |||
| 85 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 86 | AK89XX_REG_CNTL, | ||
| 87 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
| 88 | if (result) { | ||
| 89 | LOG_RESULT_LOCATION(result); | ||
| 90 | return result; | ||
| 91 | } | ||
| 92 | /* Wait at least 100us */ | ||
| 93 | udelay(100); | ||
| 94 | |||
| 95 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 96 | AK89XX_REG_CNTL, | ||
| 97 | AK89XX_CNTL_MODE_FUSE_ROM_ACCESS); | ||
| 98 | if (result) { | ||
| 99 | LOG_RESULT_LOCATION(result); | ||
| 100 | return result; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Wait at least 200us */ | ||
| 104 | udelay(200); | ||
| 105 | |||
| 106 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 107 | AK89XX_REG_ASAX, | ||
| 108 | COMPASS_NUM_AXES, serial_data); | ||
| 109 | if (result) { | ||
| 110 | LOG_RESULT_LOCATION(result); | ||
| 111 | return result; | ||
| 112 | } | ||
| 113 | |||
| 114 | pdata->private_data = private_data; | ||
| 115 | |||
| 116 | private_data->init.asa[0] = serial_data[0]; | ||
| 117 | private_data->init.asa[1] = serial_data[1]; | ||
| 118 | private_data->init.asa[2] = serial_data[2]; | ||
| 119 | |||
| 120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 121 | AK89XX_REG_CNTL, | ||
| 122 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
| 123 | if (result) { | ||
| 124 | LOG_RESULT_LOCATION(result); | ||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | udelay(100); | ||
| 129 | return INV_SUCCESS; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int ak89xx_exit(void *mlsl_handle, | ||
| 133 | struct ext_slave_descr *slave, | ||
| 134 | struct ext_slave_platform_data *pdata) | ||
| 135 | { | ||
| 136 | kfree(pdata->private_data); | ||
| 137 | return INV_SUCCESS; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int ak89xx_suspend(void *mlsl_handle, | ||
| 141 | struct ext_slave_descr *slave, | ||
| 142 | struct ext_slave_platform_data *pdata) | ||
| 143 | { | ||
| 144 | int result = INV_SUCCESS; | ||
| 145 | result = | ||
| 146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 147 | AK89XX_REG_CNTL, | ||
| 148 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
| 149 | msleep(1); /* wait at least 100us */ | ||
| 150 | if (result) { | ||
| 151 | LOG_RESULT_LOCATION(result); | ||
| 152 | return result; | ||
| 153 | } | ||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | static int ak89xx_resume(void *mlsl_handle, | ||
| 158 | struct ext_slave_descr *slave, | ||
| 159 | struct ext_slave_platform_data *pdata) | ||
| 160 | { | ||
| 161 | int result = INV_SUCCESS; | ||
| 162 | result = | ||
| 163 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 164 | AK89XX_REG_CNTL, | ||
| 165 | AK89XX_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 166 | if (result) { | ||
| 167 | LOG_RESULT_LOCATION(result); | ||
| 168 | return result; | ||
| 169 | } | ||
| 170 | return result; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int ak89xx_read(void *mlsl_handle, | ||
| 174 | struct ext_slave_descr *slave, | ||
| 175 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
| 176 | { | ||
| 177 | unsigned char regs[8]; | ||
| 178 | unsigned char *stat = ®s[0]; | ||
| 179 | unsigned char *stat2 = ®s[7]; | ||
| 180 | int result = INV_SUCCESS; | ||
| 181 | int status = INV_SUCCESS; | ||
| 182 | |||
| 183 | result = | ||
| 184 | inv_serial_read(mlsl_handle, pdata->address, AK89XX_REG_ST1, | ||
| 185 | 8, regs); | ||
| 186 | if (result) { | ||
| 187 | LOG_RESULT_LOCATION(result); | ||
| 188 | return result; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Always return the data and the status registers */ | ||
| 192 | memcpy(data, ®s[1], 6); | ||
| 193 | data[6] = regs[0]; | ||
| 194 | data[7] = regs[7]; | ||
| 195 | |||
| 196 | /* | ||
| 197 | * ST : data ready - | ||
| 198 | * Measurement has been completed and data is ready to be read. | ||
| 199 | */ | ||
| 200 | if (*stat & 0x01) | ||
| 201 | status = INV_SUCCESS; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * ST2 : data error - | ||
| 205 | * occurs when data read is started outside of a readable period; | ||
| 206 | * data read would not be correct. | ||
| 207 | * Valid in continuous measurement mode only. | ||
| 208 | * In single measurement mode this error should not occour but we | ||
| 209 | * stil account for it and return an error, since the data would be | ||
| 210 | * corrupted. | ||
| 211 | * DERR bit is self-clearing when ST2 register is read. | ||
| 212 | * | ||
| 213 | * This bit is always zero on the AK8963. | ||
| 214 | */ | ||
| 215 | if (*stat2 & 0x04) | ||
| 216 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
| 217 | /* | ||
| 218 | * ST2 : overflow - | ||
| 219 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
| 220 | * This is likely to happen in presence of an external magnetic | ||
| 221 | * disturbance; it indicates, the sensor data is incorrect and should | ||
| 222 | * be ignored. | ||
| 223 | * An error is returned. | ||
| 224 | * HOFL bit clears when a new measurement starts. | ||
| 225 | */ | ||
| 226 | if (*stat2 & 0x08) | ||
| 227 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 228 | /* | ||
| 229 | * ST : overrun - | ||
| 230 | * the previous sample was not fetched and lost. | ||
| 231 | * Valid in continuous measurement mode only. | ||
| 232 | * In single measurement mode this error should not occour and we | ||
| 233 | * don't consider this condition an error. | ||
| 234 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
| 235 | * read. | ||
| 236 | */ | ||
| 237 | if (*stat & 0x02) { | ||
| 238 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
| 239 | status = INV_SUCCESS; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* | ||
| 243 | * trigger next measurement if: | ||
| 244 | * - stat is non zero; | ||
| 245 | * - if stat is zero and stat2 is non zero. | ||
| 246 | * Won't trigger if data is not ready and there was no error. | ||
| 247 | */ | ||
| 248 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
| 249 | result = inv_serial_single_write( | ||
| 250 | mlsl_handle, pdata->address, | ||
| 251 | AK89XX_REG_CNTL, AK89XX_CNTL_MODE_SINGLE_MEASUREMENT); | ||
| 252 | if (result) { | ||
| 253 | LOG_RESULT_LOCATION(result); | ||
| 254 | return result; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | return status; | ||
| 259 | } | ||
| 260 | |||
| 261 | static int ak89xx_config(void *mlsl_handle, | ||
| 262 | struct ext_slave_descr *slave, | ||
| 263 | struct ext_slave_platform_data *pdata, | ||
| 264 | struct ext_slave_config *data) | ||
| 265 | { | ||
| 266 | int result; | ||
| 267 | if (!data->data) | ||
| 268 | return INV_ERROR_INVALID_PARAMETER; | ||
| 269 | |||
| 270 | switch (data->key) { | ||
| 271 | case MPU_SLAVE_WRITE_REGISTERS: | ||
| 272 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 273 | data->len, | ||
| 274 | (unsigned char *)data->data); | ||
| 275 | if (result) { | ||
| 276 | LOG_RESULT_LOCATION(result); | ||
| 277 | return result; | ||
| 278 | } | ||
| 279 | break; | ||
| 280 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 281 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 282 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 283 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 284 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 285 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 286 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 287 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 288 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 289 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 290 | default: | ||
| 291 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 292 | }; | ||
| 293 | |||
| 294 | return INV_SUCCESS; | ||
| 295 | } | ||
| 296 | |||
| 297 | static int ak89xx_get_config(void *mlsl_handle, | ||
| 298 | struct ext_slave_descr *slave, | ||
| 299 | struct ext_slave_platform_data *pdata, | ||
| 300 | struct ext_slave_config *data) | ||
| 301 | { | ||
| 302 | struct ak89xx_private_data *private_data = pdata->private_data; | ||
| 303 | int result; | ||
| 304 | if (!data->data) | ||
| 305 | return INV_ERROR_INVALID_PARAMETER; | ||
| 306 | |||
| 307 | switch (data->key) { | ||
| 308 | case MPU_SLAVE_READ_REGISTERS: | ||
| 309 | { | ||
| 310 | unsigned char *serial_data = | ||
| 311 | (unsigned char *)data->data; | ||
| 312 | result = | ||
| 313 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 314 | serial_data[0], data->len - 1, | ||
| 315 | &serial_data[1]); | ||
| 316 | if (result) { | ||
| 317 | LOG_RESULT_LOCATION(result); | ||
| 318 | return result; | ||
| 319 | } | ||
| 320 | break; | ||
| 321 | } | ||
| 322 | case MPU_SLAVE_READ_SCALE: | ||
| 323 | { | ||
| 324 | unsigned char *serial_data = | ||
| 325 | (unsigned char *)data->data; | ||
| 326 | serial_data[0] = private_data->init.asa[0]; | ||
| 327 | serial_data[1] = private_data->init.asa[1]; | ||
| 328 | serial_data[2] = private_data->init.asa[2]; | ||
| 329 | result = INV_SUCCESS; | ||
| 330 | if (result) { | ||
| 331 | LOG_RESULT_LOCATION(result); | ||
| 332 | return result; | ||
| 333 | } | ||
| 334 | break; | ||
| 335 | } | ||
| 336 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 337 | (*(unsigned long *)data->data) = 0; | ||
| 338 | break; | ||
| 339 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 340 | (*(unsigned long *)data->data) = 8000; | ||
| 341 | break; | ||
| 342 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 343 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 344 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 345 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 346 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 347 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 348 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 349 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 350 | default: | ||
| 351 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 352 | }; | ||
| 353 | |||
| 354 | return INV_SUCCESS; | ||
| 355 | } | ||
| 356 | |||
| 357 | static struct ext_slave_read_trigger ak89xx_read_trigger = { | ||
| 358 | /*.reg = */ 0x0A, | ||
| 359 | /*.value = */ 0x01 | ||
| 360 | }; | ||
| 361 | |||
| 362 | static struct ext_slave_descr ak89xx_descr = { | ||
| 363 | .init = ak89xx_init, | ||
| 364 | .exit = ak89xx_exit, | ||
| 365 | .suspend = ak89xx_suspend, | ||
| 366 | .resume = ak89xx_resume, | ||
| 367 | .read = ak89xx_read, | ||
| 368 | .config = ak89xx_config, | ||
| 369 | .get_config = ak89xx_get_config, | ||
| 370 | .name = "ak89xx", | ||
| 371 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 372 | .id = COMPASS_ID_AK8963, | ||
| 373 | .read_reg = 0x01, | ||
| 374 | .read_len = 10, | ||
| 375 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 376 | .range = {9830, 4000}, | ||
| 377 | .trigger = &ak89xx_read_trigger, | ||
| 378 | }; | ||
| 379 | |||
| 380 | static | ||
| 381 | struct ext_slave_descr *ak89xx_get_slave_descr(void) | ||
| 382 | { | ||
| 383 | return &ak89xx_descr; | ||
| 384 | } | ||
| 385 | |||
| 386 | /* -------------------------------------------------------------------------- */ | ||
| 387 | struct ak89xx_mod_private_data { | ||
| 388 | struct i2c_client *client; | ||
| 389 | struct ext_slave_platform_data *pdata; | ||
| 390 | }; | ||
| 391 | |||
| 392 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 393 | |||
| 394 | static int ak89xx_mod_probe(struct i2c_client *client, | ||
| 395 | const struct i2c_device_id *devid) | ||
| 396 | { | ||
| 397 | struct ext_slave_platform_data *pdata; | ||
| 398 | struct ak89xx_mod_private_data *private_data; | ||
| 399 | int result = 0; | ||
| 400 | |||
| 401 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 402 | |||
| 403 | /* Override default values based on model. */ | ||
| 404 | ak89xx_descr.id = devid->driver_data; | ||
| 405 | /* ak89xx_descr.name = devid->name; */ | ||
| 406 | switch (ak89xx_descr.id) { | ||
| 407 | case COMPASS_ID_AK8963: | ||
| 408 | break; | ||
| 409 | case COMPASS_ID_AK8972: | ||
| 410 | ak89xx_descr.read_len = 9; | ||
| 411 | ak89xx_descr.range.mantissa = 39321; | ||
| 412 | ak89xx_descr.range.fraction = 6000; | ||
| 413 | break; | ||
| 414 | case COMPASS_ID_AK8975: | ||
| 415 | break; | ||
| 416 | default: | ||
| 417 | result = -EFAULT; | ||
| 418 | goto out_no_free; | ||
| 419 | } | ||
| 420 | |||
| 421 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 422 | result = -ENODEV; | ||
| 423 | goto out_no_free; | ||
| 424 | } | ||
| 425 | |||
| 426 | pdata = client->dev.platform_data; | ||
| 427 | if (!pdata) { | ||
| 428 | dev_err(&client->adapter->dev, | ||
| 429 | "Missing platform data for slave %s\n", devid->name); | ||
| 430 | result = -EFAULT; | ||
| 431 | goto out_no_free; | ||
| 432 | } | ||
| 433 | |||
| 434 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 435 | if (!private_data) { | ||
| 436 | result = -ENOMEM; | ||
| 437 | goto out_no_free; | ||
| 438 | } | ||
| 439 | |||
| 440 | i2c_set_clientdata(client, private_data); | ||
| 441 | private_data->client = client; | ||
| 442 | private_data->pdata = pdata; | ||
| 443 | |||
| 444 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 445 | ak89xx_get_slave_descr); | ||
| 446 | if (result) { | ||
| 447 | dev_err(&client->adapter->dev, | ||
| 448 | "Slave registration failed: %s, %d\n", | ||
| 449 | devid->name, result); | ||
| 450 | goto out_free_memory; | ||
| 451 | } | ||
| 452 | |||
| 453 | return result; | ||
| 454 | |||
| 455 | out_free_memory: | ||
| 456 | kfree(private_data); | ||
| 457 | out_no_free: | ||
| 458 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 459 | return result; | ||
| 460 | |||
| 461 | } | ||
| 462 | |||
| 463 | static int ak89xx_mod_remove(struct i2c_client *client) | ||
| 464 | { | ||
| 465 | struct ak89xx_mod_private_data *private_data = | ||
| 466 | i2c_get_clientdata(client); | ||
| 467 | |||
| 468 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 469 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 470 | ak89xx_get_slave_descr); | ||
| 471 | |||
| 472 | kfree(private_data); | ||
| 473 | return 0; | ||
| 474 | } | ||
| 475 | |||
| 476 | static const struct i2c_device_id ak89xx_mod_id[] = { | ||
| 477 | { "ak8963", COMPASS_ID_AK8963 }, | ||
| 478 | { "ak8972", COMPASS_ID_AK8972 }, | ||
| 479 | { "ak8975", COMPASS_ID_AK8975 }, | ||
| 480 | {} | ||
| 481 | }; | ||
| 482 | |||
| 483 | MODULE_DEVICE_TABLE(i2c, ak89xx_mod_id); | ||
| 484 | |||
| 485 | static struct i2c_driver ak89xx_mod_driver = { | ||
| 486 | .class = I2C_CLASS_HWMON, | ||
| 487 | .probe = ak89xx_mod_probe, | ||
| 488 | .remove = ak89xx_mod_remove, | ||
| 489 | .id_table = ak89xx_mod_id, | ||
| 490 | .driver = { | ||
| 491 | .owner = THIS_MODULE, | ||
| 492 | .name = "ak89xx_mod", | ||
| 493 | }, | ||
| 494 | .address_list = normal_i2c, | ||
| 495 | }; | ||
| 496 | |||
| 497 | static int __init ak89xx_mod_init(void) | ||
| 498 | { | ||
| 499 | int res = i2c_add_driver(&ak89xx_mod_driver); | ||
| 500 | pr_info("%s: Probe name %s\n", __func__, "ak89xx_mod"); | ||
| 501 | if (res) | ||
| 502 | pr_err("%s failed\n", __func__); | ||
| 503 | return res; | ||
| 504 | } | ||
| 505 | |||
| 506 | static void __exit ak89xx_mod_exit(void) | ||
| 507 | { | ||
| 508 | pr_info("%s\n", __func__); | ||
| 509 | i2c_del_driver(&ak89xx_mod_driver); | ||
| 510 | } | ||
| 511 | |||
| 512 | module_init(ak89xx_mod_init); | ||
| 513 | module_exit(ak89xx_mod_exit); | ||
| 514 | |||
| 515 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 516 | MODULE_DESCRIPTION("Driver to integrate AKM AK89XX sensor with the MPU"); | ||
| 517 | MODULE_LICENSE("GPL"); | ||
| 518 | MODULE_ALIAS("ak89xx_mod"); | ||
| 519 | |||
| 520 | /** | ||
| 521 | * @} | ||
| 522 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami306.c b/drivers/misc/inv_mpu/compass/ami306.c new file mode 100644 index 00000000000..f645457d161 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami306.c | |||
| @@ -0,0 +1,1020 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file ami306.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Aichi AMI306 | ||
| 26 | * compass. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include "ami_hw.h" | ||
| 41 | #include "ami_sensor_def.h" | ||
| 42 | |||
| 43 | #include <log.h> | ||
| 44 | #include <linux/mpu.h> | ||
| 45 | #include "mlsl.h" | ||
| 46 | #include "mldl_cfg.h" | ||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-compass" | ||
| 49 | |||
| 50 | /* -------------------------------------------------------------------------- */ | ||
| 51 | #define AMI306_REG_DATAX (0x10) | ||
| 52 | #define AMI306_REG_STAT1 (0x18) | ||
| 53 | #define AMI306_REG_CNTL1 (0x1B) | ||
| 54 | #define AMI306_REG_CNTL2 (0x1C) | ||
| 55 | #define AMI306_REG_CNTL3 (0x1D) | ||
| 56 | #define AMI306_REG_CNTL4_1 (0x5C) | ||
| 57 | #define AMI306_REG_CNTL4_2 (0x5D) | ||
| 58 | |||
| 59 | #define AMI306_BIT_CNTL1_PC1 (0x80) | ||
| 60 | #define AMI306_BIT_CNTL1_ODR1 (0x10) | ||
| 61 | #define AMI306_BIT_CNTL1_FS1 (0x02) | ||
| 62 | |||
| 63 | #define AMI306_BIT_CNTL2_IEN (0x10) | ||
| 64 | #define AMI306_BIT_CNTL2_DREN (0x08) | ||
| 65 | #define AMI306_BIT_CNTL2_DRP (0x04) | ||
| 66 | #define AMI306_BIT_CNTL3_F0RCE (0x40) | ||
| 67 | |||
| 68 | #define AMI_FINE_MAX (96) | ||
| 69 | #define AMI_STANDARD_OFFSET (0x800) | ||
| 70 | #define AMI_GAIN_COR_DEFAULT (1000) | ||
| 71 | |||
| 72 | /* -------------------------------------------------------------------------- */ | ||
| 73 | struct ami306_private_data { | ||
| 74 | int isstandby; | ||
| 75 | unsigned char fine[3]; | ||
| 76 | struct ami_sensor_parametor param; | ||
| 77 | struct ami_win_parameter win; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* -------------------------------------------------------------------------- */ | ||
| 81 | static inline unsigned short little_u8_to_u16(unsigned char *p_u8) | ||
| 82 | { | ||
| 83 | return p_u8[0] | (p_u8[1] << 8); | ||
| 84 | } | ||
| 85 | |||
| 86 | static int ami306_set_bits8(void *mlsl_handle, | ||
| 87 | struct ext_slave_platform_data *pdata, | ||
| 88 | unsigned char reg, unsigned char bits) | ||
| 89 | { | ||
| 90 | int result; | ||
| 91 | unsigned char buf; | ||
| 92 | |||
| 93 | result = inv_serial_read(mlsl_handle, pdata->address, reg, 1, &buf); | ||
| 94 | if (result) { | ||
| 95 | LOG_RESULT_LOCATION(result); | ||
| 96 | return result; | ||
| 97 | } | ||
| 98 | |||
| 99 | buf |= bits; | ||
| 100 | result = inv_serial_single_write(mlsl_handle, pdata->address, reg, buf); | ||
| 101 | if (result) { | ||
| 102 | LOG_RESULT_LOCATION(result); | ||
| 103 | return result; | ||
| 104 | } | ||
| 105 | return result; | ||
| 106 | } | ||
| 107 | |||
| 108 | static int ami306_wait_data_ready(void *mlsl_handle, | ||
| 109 | struct ext_slave_platform_data *pdata, | ||
| 110 | unsigned long usecs, unsigned long times) | ||
| 111 | { | ||
| 112 | int result = 0; | ||
| 113 | unsigned char buf; | ||
| 114 | |||
| 115 | for (; 0 < times; --times) { | ||
| 116 | udelay(usecs); | ||
| 117 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 118 | AMI_REG_STA1, 1, &buf); | ||
| 119 | if (buf & AMI_STA1_DRDY_BIT) | ||
| 120 | return 0; | ||
| 121 | else if (buf & AMI_STA1_DOR_BIT) | ||
| 122 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 123 | } | ||
| 124 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 125 | } | ||
| 126 | |||
| 127 | static int ami306_read_raw_data(void *mlsl_handle, | ||
| 128 | struct ext_slave_platform_data *pdata, | ||
| 129 | short dat[3]) | ||
| 130 | { | ||
| 131 | int result; | ||
| 132 | unsigned char buf[6]; | ||
| 133 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 134 | AMI_REG_DATAX, sizeof(buf), buf); | ||
| 135 | if (result) { | ||
| 136 | LOG_RESULT_LOCATION(result); | ||
| 137 | return result; | ||
| 138 | } | ||
| 139 | dat[0] = little_u8_to_u16(&buf[0]); | ||
| 140 | dat[1] = little_u8_to_u16(&buf[2]); | ||
| 141 | dat[2] = little_u8_to_u16(&buf[4]); | ||
| 142 | return result; | ||
| 143 | } | ||
| 144 | |||
| 145 | #define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */ | ||
| 146 | #define AMI_DRDYWAIT 800 /* u(micro) sec */ | ||
| 147 | static int ami306_force_mesurement(void *mlsl_handle, | ||
| 148 | struct ext_slave_platform_data *pdata, | ||
| 149 | short ver[3]) | ||
| 150 | { | ||
| 151 | int result; | ||
| 152 | int status; | ||
| 153 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 154 | AMI_REG_CTRL3, AMI_CTRL3_FORCE_BIT); | ||
| 155 | if (result) { | ||
| 156 | LOG_RESULT_LOCATION(result); | ||
| 157 | return result; | ||
| 158 | } | ||
| 159 | |||
| 160 | result = ami306_wait_data_ready(mlsl_handle, pdata, | ||
| 161 | AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY); | ||
| 162 | if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) { | ||
| 163 | LOG_RESULT_LOCATION(result); | ||
| 164 | return result; | ||
| 165 | } | ||
| 166 | /* READ DATA X,Y,Z */ | ||
| 167 | status = ami306_read_raw_data(mlsl_handle, pdata, ver); | ||
| 168 | if (status) { | ||
| 169 | LOG_RESULT_LOCATION(status); | ||
| 170 | return status; | ||
| 171 | } | ||
| 172 | |||
| 173 | return result; | ||
| 174 | } | ||
| 175 | |||
| 176 | static int ami306_mea(void *mlsl_handle, | ||
| 177 | struct ext_slave_platform_data *pdata, short val[3]) | ||
| 178 | { | ||
| 179 | int result = ami306_force_mesurement(mlsl_handle, pdata, val); | ||
| 180 | if (result) { | ||
| 181 | LOG_RESULT_LOCATION(result); | ||
| 182 | return result; | ||
| 183 | } | ||
| 184 | val[0] += AMI_STANDARD_OFFSET; | ||
| 185 | val[1] += AMI_STANDARD_OFFSET; | ||
| 186 | val[2] += AMI_STANDARD_OFFSET; | ||
| 187 | return result; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int ami306_write_offset(void *mlsl_handle, | ||
| 191 | struct ext_slave_platform_data *pdata, | ||
| 192 | unsigned char *fine) | ||
| 193 | { | ||
| 194 | int result = 0; | ||
| 195 | unsigned char dat[3]; | ||
| 196 | dat[0] = AMI_REG_OFFX; | ||
| 197 | dat[1] = 0x7f & fine[0]; | ||
| 198 | dat[2] = 0; | ||
| 199 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 200 | sizeof(dat), dat); | ||
| 201 | dat[0] = AMI_REG_OFFY; | ||
| 202 | dat[1] = 0x7f & fine[1]; | ||
| 203 | dat[2] = 0; | ||
| 204 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 205 | sizeof(dat), dat); | ||
| 206 | dat[0] = AMI_REG_OFFZ; | ||
| 207 | dat[1] = 0x7f & fine[2]; | ||
| 208 | dat[2] = 0; | ||
| 209 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 210 | sizeof(dat), dat); | ||
| 211 | return result; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int ami306_start_sensor(void *mlsl_handle, | ||
| 215 | struct ext_slave_platform_data *pdata) | ||
| 216 | { | ||
| 217 | int result = 0; | ||
| 218 | unsigned char buf[3]; | ||
| 219 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 220 | |||
| 221 | /* Step 1 */ | ||
| 222 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 223 | AMI_REG_CTRL1, | ||
| 224 | AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); | ||
| 225 | if (result) { | ||
| 226 | LOG_RESULT_LOCATION(result); | ||
| 227 | return result; | ||
| 228 | } | ||
| 229 | /* Step 2 */ | ||
| 230 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 231 | AMI_REG_CTRL2, AMI_CTRL2_DREN); | ||
| 232 | if (result) { | ||
| 233 | LOG_RESULT_LOCATION(result); | ||
| 234 | return result; | ||
| 235 | } | ||
| 236 | /* Step 3 */ | ||
| 237 | buf[0] = AMI_REG_CTRL4; | ||
| 238 | buf[1] = AMI_CTRL4_HS & 0xFF; | ||
| 239 | buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF; | ||
| 240 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 241 | sizeof(buf), buf); | ||
| 242 | if (result) { | ||
| 243 | LOG_RESULT_LOCATION(result); | ||
| 244 | return result; | ||
| 245 | } | ||
| 246 | /* Step 4 */ | ||
| 247 | result = ami306_write_offset(mlsl_handle, pdata, private_data->fine); | ||
| 248 | if (result) { | ||
| 249 | LOG_RESULT_LOCATION(result); | ||
| 250 | return result; | ||
| 251 | } | ||
| 252 | return result; | ||
| 253 | } | ||
| 254 | |||
| 255 | /** | ||
| 256 | * This function does this. | ||
| 257 | * | ||
| 258 | * @param mlsl_handle this param is this. | ||
| 259 | * @param slave | ||
| 260 | * @param pdata | ||
| 261 | * | ||
| 262 | * @return INV_SUCCESS or non-zero error code. | ||
| 263 | */ | ||
| 264 | static int ami306_read_param(void *mlsl_handle, | ||
| 265 | struct ext_slave_descr *slave, | ||
| 266 | struct ext_slave_platform_data *pdata) | ||
| 267 | { | ||
| 268 | int result = 0; | ||
| 269 | unsigned char regs[12]; | ||
| 270 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 271 | struct ami_sensor_parametor *param = &private_data->param; | ||
| 272 | |||
| 273 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 274 | AMI_REG_SENX, sizeof(regs), regs); | ||
| 275 | if (result) { | ||
| 276 | LOG_RESULT_LOCATION(result); | ||
| 277 | return result; | ||
| 278 | } | ||
| 279 | |||
| 280 | /* Little endian 16 bit registers */ | ||
| 281 | param->m_gain.x = little_u8_to_u16(®s[0]); | ||
| 282 | param->m_gain.y = little_u8_to_u16(®s[2]); | ||
| 283 | param->m_gain.z = little_u8_to_u16(®s[4]); | ||
| 284 | |||
| 285 | param->m_interference.xy = regs[7]; | ||
| 286 | param->m_interference.xz = regs[6]; | ||
| 287 | param->m_interference.yx = regs[9]; | ||
| 288 | param->m_interference.yz = regs[8]; | ||
| 289 | param->m_interference.zx = regs[11]; | ||
| 290 | param->m_interference.zy = regs[10]; | ||
| 291 | |||
| 292 | param->m_offset.x = AMI_STANDARD_OFFSET; | ||
| 293 | param->m_offset.y = AMI_STANDARD_OFFSET; | ||
| 294 | param->m_offset.z = AMI_STANDARD_OFFSET; | ||
| 295 | |||
| 296 | param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT; | ||
| 297 | param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT; | ||
| 298 | param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT; | ||
| 299 | |||
| 300 | return result; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int ami306_initial_b0_adjust(void *mlsl_handle, | ||
| 304 | struct ext_slave_descr *slave, | ||
| 305 | struct ext_slave_platform_data *pdata) | ||
| 306 | { | ||
| 307 | int result; | ||
| 308 | unsigned char fine[3] = { 0 }; | ||
| 309 | short data[3]; | ||
| 310 | int diff[3] = { 0x7fff, 0x7fff, 0x7fff }; | ||
| 311 | int fn = 0; | ||
| 312 | int ax = 0; | ||
| 313 | unsigned char buf[3]; | ||
| 314 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 315 | |||
| 316 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 317 | AMI_REG_CTRL2, AMI_CTRL2_DREN); | ||
| 318 | if (result) { | ||
| 319 | LOG_RESULT_LOCATION(result); | ||
| 320 | return result; | ||
| 321 | } | ||
| 322 | |||
| 323 | buf[0] = AMI_REG_CTRL4; | ||
| 324 | buf[1] = AMI_CTRL4_HS & 0xFF; | ||
| 325 | buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF; | ||
| 326 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 327 | sizeof(buf), buf); | ||
| 328 | if (result) { | ||
| 329 | LOG_RESULT_LOCATION(result); | ||
| 330 | return result; | ||
| 331 | } | ||
| 332 | |||
| 333 | for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */ | ||
| 334 | fine[0] = fine[1] = fine[2] = fn; | ||
| 335 | result = ami306_write_offset(mlsl_handle, pdata, fine); | ||
| 336 | if (result) { | ||
| 337 | LOG_RESULT_LOCATION(result); | ||
| 338 | return result; | ||
| 339 | } | ||
| 340 | |||
| 341 | result = ami306_force_mesurement(mlsl_handle, pdata, data); | ||
| 342 | if (result) { | ||
| 343 | LOG_RESULT_LOCATION(result); | ||
| 344 | return result; | ||
| 345 | } | ||
| 346 | MPL_LOGV("[%d] x:%-5d y:%-5d z:%-5d\n", | ||
| 347 | fn, data[0], data[1], data[2]); | ||
| 348 | |||
| 349 | for (ax = 0; ax < 3; ax++) { | ||
| 350 | /* search point most close to zero. */ | ||
| 351 | if (diff[ax] > abs(data[ax])) { | ||
| 352 | private_data->fine[ax] = fn; | ||
| 353 | diff[ax] = abs(data[ax]); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | MPL_LOGV("fine x:%-5d y:%-5d z:%-5d\n", | ||
| 358 | private_data->fine[0], private_data->fine[1], | ||
| 359 | private_data->fine[2]); | ||
| 360 | |||
| 361 | result = ami306_write_offset(mlsl_handle, pdata, private_data->fine); | ||
| 362 | if (result) { | ||
| 363 | LOG_RESULT_LOCATION(result); | ||
| 364 | return result; | ||
| 365 | } | ||
| 366 | |||
| 367 | /* Software Reset */ | ||
| 368 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 369 | AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT); | ||
| 370 | if (result) { | ||
| 371 | LOG_RESULT_LOCATION(result); | ||
| 372 | return result; | ||
| 373 | } | ||
| 374 | return result; | ||
| 375 | } | ||
| 376 | |||
| 377 | #define SEH_RANGE_MIN 100 | ||
| 378 | #define SEH_RANGE_MAX 3950 | ||
| 379 | static int ami306_search_offset(void *mlsl_handle, | ||
| 380 | struct ext_slave_descr *slave, | ||
| 381 | struct ext_slave_platform_data *pdata) | ||
| 382 | { | ||
| 383 | int result; | ||
| 384 | int axis; | ||
| 385 | unsigned char regs[6]; | ||
| 386 | unsigned char run_flg[3] = { 1, 1, 1 }; | ||
| 387 | unsigned char fine[3]; | ||
| 388 | unsigned char win_range_fine[3]; | ||
| 389 | unsigned short fine_output[3]; | ||
| 390 | short val[3]; | ||
| 391 | unsigned short cnt[3] = { 0 }; | ||
| 392 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 393 | |||
| 394 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 395 | AMI_FINEOUTPUT_X, sizeof(regs), regs); | ||
| 396 | if (result) { | ||
| 397 | LOG_RESULT_LOCATION(result); | ||
| 398 | return result; | ||
| 399 | } | ||
| 400 | fine_output[0] = little_u8_to_u16(®s[0]); | ||
| 401 | fine_output[1] = little_u8_to_u16(®s[2]); | ||
| 402 | fine_output[2] = little_u8_to_u16(®s[4]); | ||
| 403 | |||
| 404 | for (axis = 0; axis < 3; ++axis) { | ||
| 405 | if (fine_output[axis] == 0) { | ||
| 406 | MPL_LOGV("error fine_output %d axis:%d\n", | ||
| 407 | __LINE__, axis); | ||
| 408 | return -1; | ||
| 409 | } | ||
| 410 | /* fines per a window */ | ||
| 411 | win_range_fine[axis] = (SEH_RANGE_MAX - SEH_RANGE_MIN) | ||
| 412 | / fine_output[axis]; | ||
| 413 | } | ||
| 414 | |||
| 415 | /* get current fine */ | ||
| 416 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 417 | AMI_REG_OFFX, 2, ®s[0]); | ||
| 418 | if (result) { | ||
| 419 | LOG_RESULT_LOCATION(result); | ||
| 420 | return result; | ||
| 421 | } | ||
| 422 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 423 | AMI_REG_OFFY, 2, ®s[2]); | ||
| 424 | if (result) { | ||
| 425 | LOG_RESULT_LOCATION(result); | ||
| 426 | return result; | ||
| 427 | } | ||
| 428 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 429 | AMI_REG_OFFZ, 2, ®s[4]); | ||
| 430 | if (result) { | ||
| 431 | LOG_RESULT_LOCATION(result); | ||
| 432 | return result; | ||
| 433 | } | ||
| 434 | |||
| 435 | fine[0] = (unsigned char)(regs[0] & 0x7f); | ||
| 436 | fine[1] = (unsigned char)(regs[2] & 0x7f); | ||
| 437 | fine[2] = (unsigned char)(regs[4] & 0x7f); | ||
| 438 | |||
| 439 | while (run_flg[0] == 1 || run_flg[1] == 1 || run_flg[2] == 1) { | ||
| 440 | |||
| 441 | result = ami306_mea(mlsl_handle, pdata, val); | ||
| 442 | if (result) { | ||
| 443 | LOG_RESULT_LOCATION(result); | ||
| 444 | return result; | ||
| 445 | } | ||
| 446 | MPL_LOGV("val x:%-5d y:%-5d z:%-5d\n", val[0], val[1], val[2]); | ||
| 447 | MPL_LOGV("now fine x:%-5d y:%-5d z:%-5d\n", | ||
| 448 | fine[0], fine[1], fine[2]); | ||
| 449 | |||
| 450 | for (axis = 0; axis < 3; ++axis) { | ||
| 451 | if (axis == 0) { /* X-axis is reversed */ | ||
| 452 | val[axis] = 0x0FFF & ~val[axis]; | ||
| 453 | } | ||
| 454 | if (val[axis] < SEH_RANGE_MIN) { | ||
| 455 | /* At the case of less low limmit. */ | ||
| 456 | fine[axis] -= win_range_fine[axis]; | ||
| 457 | MPL_LOGV("min : fine=%d diff=%d\n", | ||
| 458 | fine[axis], win_range_fine[axis]); | ||
| 459 | } | ||
| 460 | if (val[axis] > SEH_RANGE_MAX) { | ||
| 461 | /* At the case of over high limmit. */ | ||
| 462 | fine[axis] += win_range_fine[axis]; | ||
| 463 | MPL_LOGV("max : fine=%d diff=%d\n", | ||
| 464 | fine[axis], win_range_fine[axis]); | ||
| 465 | } | ||
| 466 | if (SEH_RANGE_MIN <= val[axis] && | ||
| 467 | val[axis] <= SEH_RANGE_MAX) { | ||
| 468 | /* In the current window. */ | ||
| 469 | int diff_fine = | ||
| 470 | (val[axis] - AMI_STANDARD_OFFSET) / | ||
| 471 | fine_output[axis]; | ||
| 472 | fine[axis] += diff_fine; | ||
| 473 | run_flg[axis] = 0; | ||
| 474 | MPL_LOGV("mid : fine=%d diff=%d\n", | ||
| 475 | fine[axis], diff_fine); | ||
| 476 | } | ||
| 477 | |||
| 478 | if (!(0 <= fine[axis] && fine[axis] < AMI_FINE_MAX)) { | ||
| 479 | MPL_LOGE("fine err :%d\n", cnt[axis]); | ||
| 480 | goto out; | ||
| 481 | } | ||
| 482 | if (cnt[axis] > 3) { | ||
| 483 | MPL_LOGE("cnt err :%d\n", cnt[axis]); | ||
| 484 | goto out; | ||
| 485 | } | ||
| 486 | cnt[axis]++; | ||
| 487 | } | ||
| 488 | MPL_LOGV("new fine x:%-5d y:%-5d z:%-5d\n", | ||
| 489 | fine[0], fine[1], fine[2]); | ||
| 490 | |||
| 491 | /* set current fine */ | ||
| 492 | result = ami306_write_offset(mlsl_handle, pdata, fine); | ||
| 493 | if (result) { | ||
| 494 | LOG_RESULT_LOCATION(result); | ||
| 495 | return result; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | memcpy(private_data->fine, fine, sizeof(fine)); | ||
| 499 | out: | ||
| 500 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 501 | AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT); | ||
| 502 | if (result) { | ||
| 503 | LOG_RESULT_LOCATION(result); | ||
| 504 | return result; | ||
| 505 | } | ||
| 506 | udelay(250 + 50); | ||
| 507 | return 0; | ||
| 508 | } | ||
| 509 | |||
| 510 | static int ami306_read_win(void *mlsl_handle, | ||
| 511 | struct ext_slave_descr *slave, | ||
| 512 | struct ext_slave_platform_data *pdata) | ||
| 513 | { | ||
| 514 | int result = 0; | ||
| 515 | unsigned char regs[6]; | ||
| 516 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 517 | struct ami_win_parameter *win = &private_data->win; | ||
| 518 | |||
| 519 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 520 | AMI_REG_OFFOTPX, sizeof(regs), regs); | ||
| 521 | if (result) { | ||
| 522 | LOG_RESULT_LOCATION(result); | ||
| 523 | return result; | ||
| 524 | } | ||
| 525 | |||
| 526 | win->m_0Gauss_fine.x = (unsigned char)(regs[0] & 0x7f); | ||
| 527 | win->m_0Gauss_fine.y = (unsigned char)(regs[2] & 0x7f); | ||
| 528 | win->m_0Gauss_fine.z = (unsigned char)(regs[4] & 0x7f); | ||
| 529 | |||
| 530 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 531 | AMI_REG_OFFX, 2, ®s[0]); | ||
| 532 | if (result) { | ||
| 533 | LOG_RESULT_LOCATION(result); | ||
| 534 | return result; | ||
| 535 | } | ||
| 536 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 537 | AMI_REG_OFFY, 2, ®s[2]); | ||
| 538 | if (result) { | ||
| 539 | LOG_RESULT_LOCATION(result); | ||
| 540 | return result; | ||
| 541 | } | ||
| 542 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 543 | AMI_REG_OFFZ, 2, ®s[4]); | ||
| 544 | if (result) { | ||
| 545 | LOG_RESULT_LOCATION(result); | ||
| 546 | return result; | ||
| 547 | } | ||
| 548 | |||
| 549 | win->m_fine.x = (unsigned char)(regs[0] & 0x7f); | ||
| 550 | win->m_fine.y = (unsigned char)(regs[2] & 0x7f); | ||
| 551 | win->m_fine.z = (unsigned char)(regs[4] & 0x7f); | ||
| 552 | |||
| 553 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 554 | AMI_FINEOUTPUT_X, sizeof(regs), regs); | ||
| 555 | if (result) { | ||
| 556 | LOG_RESULT_LOCATION(result); | ||
| 557 | return result; | ||
| 558 | } | ||
| 559 | win->m_fine_output.x = little_u8_to_u16(®s[0]); | ||
| 560 | win->m_fine_output.y = little_u8_to_u16(®s[2]); | ||
| 561 | win->m_fine_output.z = little_u8_to_u16(®s[4]); | ||
| 562 | |||
| 563 | return result; | ||
| 564 | } | ||
| 565 | |||
| 566 | static int ami306_suspend(void *mlsl_handle, | ||
| 567 | struct ext_slave_descr *slave, | ||
| 568 | struct ext_slave_platform_data *pdata) | ||
| 569 | { | ||
| 570 | int result; | ||
| 571 | unsigned char reg; | ||
| 572 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 573 | AMI306_REG_CNTL1, 1, ®); | ||
| 574 | if (result) { | ||
| 575 | LOG_RESULT_LOCATION(result); | ||
| 576 | return result; | ||
| 577 | } | ||
| 578 | |||
| 579 | reg &= ~(AMI306_BIT_CNTL1_PC1 | AMI306_BIT_CNTL1_FS1); | ||
| 580 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 581 | AMI306_REG_CNTL1, reg); | ||
| 582 | if (result) { | ||
| 583 | LOG_RESULT_LOCATION(result); | ||
| 584 | return result; | ||
| 585 | } | ||
| 586 | |||
| 587 | return result; | ||
| 588 | } | ||
| 589 | |||
| 590 | static int ami306_resume(void *mlsl_handle, | ||
| 591 | struct ext_slave_descr *slave, | ||
| 592 | struct ext_slave_platform_data *pdata) | ||
| 593 | { | ||
| 594 | int result = INV_SUCCESS; | ||
| 595 | unsigned char regs[] = { | ||
| 596 | AMI306_REG_CNTL4_1, | ||
| 597 | 0x7E, | ||
| 598 | 0xA0 | ||
| 599 | }; | ||
| 600 | /* Step1. Set CNTL1 reg to power model active (Write CNTL1:PC1=1) */ | ||
| 601 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 602 | AMI306_REG_CNTL1, | ||
| 603 | AMI306_BIT_CNTL1_PC1 | | ||
| 604 | AMI306_BIT_CNTL1_FS1); | ||
| 605 | if (result) { | ||
| 606 | LOG_RESULT_LOCATION(result); | ||
| 607 | return result; | ||
| 608 | } | ||
| 609 | |||
| 610 | /* Step2. Set CNTL2 reg to DRDY active high and enabled | ||
| 611 | (Write CNTL2:DREN=1) */ | ||
| 612 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 613 | AMI306_REG_CNTL2, | ||
| 614 | AMI306_BIT_CNTL2_DREN | | ||
| 615 | AMI306_BIT_CNTL2_DRP); | ||
| 616 | if (result) { | ||
| 617 | LOG_RESULT_LOCATION(result); | ||
| 618 | return result; | ||
| 619 | } | ||
| 620 | |||
| 621 | /* Step3. Set CNTL4 reg to for measurement speed: Write CNTL4, 0xA07E */ | ||
| 622 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
| 623 | ARRAY_SIZE(regs), regs); | ||
| 624 | if (result) { | ||
| 625 | LOG_RESULT_LOCATION(result); | ||
| 626 | return result; | ||
| 627 | } | ||
| 628 | |||
| 629 | /* Step4. skipped */ | ||
| 630 | |||
| 631 | /* Step5. Set CNTL3 reg to forced measurement period | ||
| 632 | (Write CNTL3:FORCE=1) */ | ||
| 633 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 634 | AMI306_REG_CNTL3, | ||
| 635 | AMI306_BIT_CNTL3_F0RCE); | ||
| 636 | |||
| 637 | return result; | ||
| 638 | } | ||
| 639 | |||
| 640 | static int ami306_read(void *mlsl_handle, | ||
| 641 | struct ext_slave_descr *slave, | ||
| 642 | struct ext_slave_platform_data *pdata, | ||
| 643 | unsigned char *data) | ||
| 644 | { | ||
| 645 | int result = INV_SUCCESS; | ||
| 646 | int ii; | ||
| 647 | short val[COMPASS_NUM_AXES]; | ||
| 648 | |||
| 649 | result = ami306_mea(mlsl_handle, pdata, val); | ||
| 650 | if (result) { | ||
| 651 | LOG_RESULT_LOCATION(result); | ||
| 652 | return result; | ||
| 653 | } | ||
| 654 | for (ii = 0; ii < COMPASS_NUM_AXES; ii++) { | ||
| 655 | val[ii] -= AMI_STANDARD_OFFSET; | ||
| 656 | data[2 * ii] = val[ii] & 0xFF; | ||
| 657 | data[(2 * ii) + 1] = (val[ii] >> 8) & 0xFF; | ||
| 658 | } | ||
| 659 | return result; | ||
| 660 | } | ||
| 661 | |||
| 662 | static int ami306_init(void *mlsl_handle, | ||
| 663 | struct ext_slave_descr *slave, | ||
| 664 | struct ext_slave_platform_data *pdata) | ||
| 665 | { | ||
| 666 | int result; | ||
| 667 | struct ami306_private_data *private_data; | ||
| 668 | private_data = (struct ami306_private_data *) | ||
| 669 | kzalloc(sizeof(struct ami306_private_data), GFP_KERNEL); | ||
| 670 | |||
| 671 | if (!private_data) | ||
| 672 | return INV_ERROR_MEMORY_EXAUSTED; | ||
| 673 | |||
| 674 | pdata->private_data = private_data; | ||
| 675 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
| 676 | AMI_REG_CTRL1, | ||
| 677 | AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); | ||
| 678 | if (result) { | ||
| 679 | LOG_RESULT_LOCATION(result); | ||
| 680 | return result; | ||
| 681 | } | ||
| 682 | /* Read Parameters */ | ||
| 683 | result = ami306_read_param(mlsl_handle, slave, pdata); | ||
| 684 | if (result) { | ||
| 685 | LOG_RESULT_LOCATION(result); | ||
| 686 | return result; | ||
| 687 | } | ||
| 688 | /* Read Window */ | ||
| 689 | result = ami306_initial_b0_adjust(mlsl_handle, slave, pdata); | ||
| 690 | if (result) { | ||
| 691 | LOG_RESULT_LOCATION(result); | ||
| 692 | return result; | ||
| 693 | } | ||
| 694 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
| 695 | if (result) { | ||
| 696 | LOG_RESULT_LOCATION(result); | ||
| 697 | return result; | ||
| 698 | } | ||
| 699 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
| 700 | if (result) { | ||
| 701 | LOG_RESULT_LOCATION(result); | ||
| 702 | return result; | ||
| 703 | } | ||
| 704 | |||
| 705 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 706 | AMI306_REG_CNTL1, 0); | ||
| 707 | if (result) { | ||
| 708 | LOG_RESULT_LOCATION(result); | ||
| 709 | return result; | ||
| 710 | } | ||
| 711 | |||
| 712 | return INV_SUCCESS; | ||
| 713 | } | ||
| 714 | |||
| 715 | static int ami306_exit(void *mlsl_handle, | ||
| 716 | struct ext_slave_descr *slave, | ||
| 717 | struct ext_slave_platform_data *pdata) | ||
| 718 | { | ||
| 719 | kfree(pdata->private_data); | ||
| 720 | return INV_SUCCESS; | ||
| 721 | } | ||
| 722 | |||
| 723 | static int ami306_config(void *mlsl_handle, | ||
| 724 | struct ext_slave_descr *slave, | ||
| 725 | struct ext_slave_platform_data *pdata, | ||
| 726 | struct ext_slave_config *data) | ||
| 727 | { | ||
| 728 | if (!data->data) { | ||
| 729 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 730 | return INV_ERROR_INVALID_PARAMETER; | ||
| 731 | } | ||
| 732 | |||
| 733 | switch (data->key) { | ||
| 734 | case MPU_SLAVE_PARAM: | ||
| 735 | case MPU_SLAVE_WINDOW: | ||
| 736 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 737 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 738 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 739 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 740 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 741 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 742 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 743 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 744 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 745 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 746 | default: | ||
| 747 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 748 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 749 | }; | ||
| 750 | |||
| 751 | return INV_SUCCESS; | ||
| 752 | } | ||
| 753 | |||
| 754 | static int ami306_get_config(void *mlsl_handle, | ||
| 755 | struct ext_slave_descr *slave, | ||
| 756 | struct ext_slave_platform_data *pdata, | ||
| 757 | struct ext_slave_config *data) | ||
| 758 | { | ||
| 759 | int result; | ||
| 760 | struct ami306_private_data *private_data = pdata->private_data; | ||
| 761 | if (!data->data) { | ||
| 762 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 763 | return INV_ERROR_INVALID_PARAMETER; | ||
| 764 | } | ||
| 765 | |||
| 766 | switch (data->key) { | ||
| 767 | case MPU_SLAVE_PARAM: | ||
| 768 | if (sizeof(struct ami_sensor_parametor) > data->len) { | ||
| 769 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 770 | return INV_ERROR_INVALID_PARAMETER; | ||
| 771 | } | ||
| 772 | if (data->apply) { | ||
| 773 | result = ami306_read_param(mlsl_handle, slave, pdata); | ||
| 774 | if (result) { | ||
| 775 | LOG_RESULT_LOCATION(result); | ||
| 776 | return result; | ||
| 777 | } | ||
| 778 | } | ||
| 779 | memcpy(data->data, &private_data->param, | ||
| 780 | sizeof(struct ami_sensor_parametor)); | ||
| 781 | break; | ||
| 782 | case MPU_SLAVE_WINDOW: | ||
| 783 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
| 784 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 785 | return INV_ERROR_INVALID_PARAMETER; | ||
| 786 | } | ||
| 787 | if (data->apply) { | ||
| 788 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
| 789 | if (result) { | ||
| 790 | LOG_RESULT_LOCATION(result); | ||
| 791 | return result; | ||
| 792 | } | ||
| 793 | } | ||
| 794 | memcpy(data->data, &private_data->win, | ||
| 795 | sizeof(struct ami_win_parameter)); | ||
| 796 | break; | ||
| 797 | case MPU_SLAVE_SEARCHOFFSET: | ||
| 798 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
| 799 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 800 | return INV_ERROR_INVALID_PARAMETER; | ||
| 801 | } | ||
| 802 | if (data->apply) { | ||
| 803 | result = ami306_search_offset(mlsl_handle, | ||
| 804 | slave, pdata); | ||
| 805 | if (result) { | ||
| 806 | LOG_RESULT_LOCATION(result); | ||
| 807 | return result; | ||
| 808 | } | ||
| 809 | /* Start sensor */ | ||
| 810 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
| 811 | if (result) { | ||
| 812 | LOG_RESULT_LOCATION(result); | ||
| 813 | return result; | ||
| 814 | } | ||
| 815 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
| 816 | if (result) { | ||
| 817 | LOG_RESULT_LOCATION(result); | ||
| 818 | return result; | ||
| 819 | } | ||
| 820 | } | ||
| 821 | memcpy(data->data, &private_data->win, | ||
| 822 | sizeof(struct ami_win_parameter)); | ||
| 823 | break; | ||
| 824 | case MPU_SLAVE_READWINPARAMS: | ||
| 825 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
| 826 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 827 | return INV_ERROR_INVALID_PARAMETER; | ||
| 828 | } | ||
| 829 | if (data->apply) { | ||
| 830 | result = ami306_initial_b0_adjust(mlsl_handle, | ||
| 831 | slave, pdata); | ||
| 832 | if (result) { | ||
| 833 | LOG_RESULT_LOCATION(result); | ||
| 834 | return result; | ||
| 835 | } | ||
| 836 | /* Start sensor */ | ||
| 837 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
| 838 | if (result) { | ||
| 839 | LOG_RESULT_LOCATION(result); | ||
| 840 | return result; | ||
| 841 | } | ||
| 842 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
| 843 | if (result) { | ||
| 844 | LOG_RESULT_LOCATION(result); | ||
| 845 | return result; | ||
| 846 | } | ||
| 847 | } | ||
| 848 | memcpy(data->data, &private_data->win, | ||
| 849 | sizeof(struct ami_win_parameter)); | ||
| 850 | break; | ||
| 851 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
| 852 | (*(unsigned long *)data->data) = 0; | ||
| 853 | break; | ||
| 854 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
| 855 | (*(unsigned long *)data->data) = 50000; | ||
| 856 | break; | ||
| 857 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
| 858 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
| 859 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
| 860 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
| 861 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
| 862 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
| 863 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
| 864 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
| 865 | case MPU_SLAVE_READ_SCALE: | ||
| 866 | default: | ||
| 867 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 868 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 869 | }; | ||
| 870 | |||
| 871 | return INV_SUCCESS; | ||
| 872 | } | ||
| 873 | |||
| 874 | static struct ext_slave_read_trigger ami306_read_trigger = { | ||
| 875 | /*.reg = */ AMI_REG_CTRL3, | ||
| 876 | /*.value = */ AMI_CTRL3_FORCE_BIT | ||
| 877 | }; | ||
| 878 | |||
| 879 | static struct ext_slave_descr ami306_descr = { | ||
| 880 | .init = ami306_init, | ||
| 881 | .exit = ami306_exit, | ||
| 882 | .suspend = ami306_suspend, | ||
| 883 | .resume = ami306_resume, | ||
| 884 | .read = ami306_read, | ||
| 885 | .config = ami306_config, | ||
| 886 | .get_config = ami306_get_config, | ||
| 887 | .name = "ami306", | ||
| 888 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 889 | .id = COMPASS_ID_AMI306, | ||
| 890 | .read_reg = 0x0E, | ||
| 891 | .read_len = 13, | ||
| 892 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 893 | .range = {5461, 3333}, | ||
| 894 | .trigger = &ami306_read_trigger, | ||
| 895 | }; | ||
| 896 | |||
| 897 | static | ||
| 898 | struct ext_slave_descr *ami306_get_slave_descr(void) | ||
| 899 | { | ||
| 900 | return &ami306_descr; | ||
| 901 | } | ||
| 902 | |||
| 903 | /* -------------------------------------------------------------------------- */ | ||
| 904 | struct ami306_mod_private_data { | ||
| 905 | struct i2c_client *client; | ||
| 906 | struct ext_slave_platform_data *pdata; | ||
| 907 | }; | ||
| 908 | |||
| 909 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 910 | |||
| 911 | static int ami306_mod_probe(struct i2c_client *client, | ||
| 912 | const struct i2c_device_id *devid) | ||
| 913 | { | ||
| 914 | struct ext_slave_platform_data *pdata; | ||
| 915 | struct ami306_mod_private_data *private_data; | ||
| 916 | int result = 0; | ||
| 917 | |||
| 918 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 919 | |||
| 920 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 921 | result = -ENODEV; | ||
| 922 | goto out_no_free; | ||
| 923 | } | ||
| 924 | |||
| 925 | pdata = client->dev.platform_data; | ||
| 926 | if (!pdata) { | ||
| 927 | dev_err(&client->adapter->dev, | ||
| 928 | "Missing platform data for slave %s\n", devid->name); | ||
| 929 | result = -EFAULT; | ||
| 930 | goto out_no_free; | ||
| 931 | } | ||
| 932 | |||
| 933 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 934 | if (!private_data) { | ||
| 935 | result = -ENOMEM; | ||
| 936 | goto out_no_free; | ||
| 937 | } | ||
| 938 | |||
| 939 | i2c_set_clientdata(client, private_data); | ||
| 940 | private_data->client = client; | ||
| 941 | private_data->pdata = pdata; | ||
| 942 | |||
| 943 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 944 | ami306_get_slave_descr); | ||
| 945 | if (result) { | ||
| 946 | dev_err(&client->adapter->dev, | ||
| 947 | "Slave registration failed: %s, %d\n", | ||
| 948 | devid->name, result); | ||
| 949 | goto out_free_memory; | ||
| 950 | } | ||
| 951 | |||
| 952 | return result; | ||
| 953 | |||
| 954 | out_free_memory: | ||
| 955 | kfree(private_data); | ||
| 956 | out_no_free: | ||
| 957 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 958 | return result; | ||
| 959 | |||
| 960 | } | ||
| 961 | |||
| 962 | static int ami306_mod_remove(struct i2c_client *client) | ||
| 963 | { | ||
| 964 | struct ami306_mod_private_data *private_data = | ||
| 965 | i2c_get_clientdata(client); | ||
| 966 | |||
| 967 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 968 | |||
| 969 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 970 | ami306_get_slave_descr); | ||
| 971 | |||
| 972 | kfree(private_data); | ||
| 973 | return 0; | ||
| 974 | } | ||
| 975 | |||
| 976 | static const struct i2c_device_id ami306_mod_id[] = { | ||
| 977 | { "ami306", COMPASS_ID_AMI306 }, | ||
| 978 | {} | ||
| 979 | }; | ||
| 980 | |||
| 981 | MODULE_DEVICE_TABLE(i2c, ami306_mod_id); | ||
| 982 | |||
| 983 | static struct i2c_driver ami306_mod_driver = { | ||
| 984 | .class = I2C_CLASS_HWMON, | ||
| 985 | .probe = ami306_mod_probe, | ||
| 986 | .remove = ami306_mod_remove, | ||
| 987 | .id_table = ami306_mod_id, | ||
| 988 | .driver = { | ||
| 989 | .owner = THIS_MODULE, | ||
| 990 | .name = "ami306_mod", | ||
| 991 | }, | ||
| 992 | .address_list = normal_i2c, | ||
| 993 | }; | ||
| 994 | |||
| 995 | static int __init ami306_mod_init(void) | ||
| 996 | { | ||
| 997 | int res = i2c_add_driver(&ami306_mod_driver); | ||
| 998 | pr_info("%s: Probe name %s\n", __func__, "ami306_mod"); | ||
| 999 | if (res) | ||
| 1000 | pr_err("%s failed\n", __func__); | ||
| 1001 | return res; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | static void __exit ami306_mod_exit(void) | ||
| 1005 | { | ||
| 1006 | pr_info("%s\n", __func__); | ||
| 1007 | i2c_del_driver(&ami306_mod_driver); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | module_init(ami306_mod_init); | ||
| 1011 | module_exit(ami306_mod_exit); | ||
| 1012 | |||
| 1013 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 1014 | MODULE_DESCRIPTION("Driver to integrate AMI306 sensor with the MPU"); | ||
| 1015 | MODULE_LICENSE("GPL"); | ||
| 1016 | MODULE_ALIAS("ami306_mod"); | ||
| 1017 | |||
| 1018 | /** | ||
| 1019 | * @} | ||
| 1020 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami30x.c b/drivers/misc/inv_mpu/compass/ami30x.c new file mode 100644 index 00000000000..0c4937c4426 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami30x.c | |||
| @@ -0,0 +1,308 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file ami30x.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Aichi AMI304 | ||
| 26 | * and AMI305 compass devices. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | #define AMI30X_REG_DATAX (0x10) | ||
| 49 | #define AMI30X_REG_STAT1 (0x18) | ||
| 50 | #define AMI30X_REG_CNTL1 (0x1B) | ||
| 51 | #define AMI30X_REG_CNTL2 (0x1C) | ||
| 52 | #define AMI30X_REG_CNTL3 (0x1D) | ||
| 53 | |||
| 54 | #define AMI30X_BIT_CNTL1_PC1 (0x80) | ||
| 55 | #define AMI30X_BIT_CNTL1_ODR1 (0x10) | ||
| 56 | #define AMI30X_BIT_CNTL1_FS1 (0x02) | ||
| 57 | |||
| 58 | #define AMI30X_BIT_CNTL2_IEN (0x10) | ||
| 59 | #define AMI30X_BIT_CNTL2_DREN (0x08) | ||
| 60 | #define AMI30X_BIT_CNTL2_DRP (0x04) | ||
| 61 | #define AMI30X_BIT_CNTL3_F0RCE (0x40) | ||
| 62 | |||
| 63 | /* -------------------------------------------------------------------------- */ | ||
| 64 | static int ami30x_suspend(void *mlsl_handle, | ||
| 65 | struct ext_slave_descr *slave, | ||
| 66 | struct ext_slave_platform_data *pdata) | ||
| 67 | { | ||
| 68 | int result; | ||
| 69 | unsigned char reg; | ||
| 70 | result = | ||
| 71 | inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_CNTL1, | ||
| 72 | 1, ®); | ||
| 73 | if (result) { | ||
| 74 | LOG_RESULT_LOCATION(result); | ||
| 75 | return result; | ||
| 76 | } | ||
| 77 | |||
| 78 | reg &= ~(AMI30X_BIT_CNTL1_PC1 | AMI30X_BIT_CNTL1_FS1); | ||
| 79 | result = | ||
| 80 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 81 | AMI30X_REG_CNTL1, reg); | ||
| 82 | if (result) { | ||
| 83 | LOG_RESULT_LOCATION(result); | ||
| 84 | return result; | ||
| 85 | } | ||
| 86 | |||
| 87 | return result; | ||
| 88 | } | ||
| 89 | |||
| 90 | static int ami30x_resume(void *mlsl_handle, | ||
| 91 | struct ext_slave_descr *slave, | ||
| 92 | struct ext_slave_platform_data *pdata) | ||
| 93 | { | ||
| 94 | int result = INV_SUCCESS; | ||
| 95 | |||
| 96 | /* Set CNTL1 reg to power model active */ | ||
| 97 | result = | ||
| 98 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 99 | AMI30X_REG_CNTL1, | ||
| 100 | AMI30X_BIT_CNTL1_PC1 | | ||
| 101 | AMI30X_BIT_CNTL1_FS1); | ||
| 102 | if (result) { | ||
| 103 | LOG_RESULT_LOCATION(result); | ||
| 104 | return result; | ||
| 105 | } | ||
| 106 | /* Set CNTL2 reg to DRDY active high and enabled */ | ||
| 107 | result = | ||
| 108 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 109 | AMI30X_REG_CNTL2, | ||
| 110 | AMI30X_BIT_CNTL2_DREN | | ||
| 111 | AMI30X_BIT_CNTL2_DRP); | ||
| 112 | if (result) { | ||
| 113 | LOG_RESULT_LOCATION(result); | ||
| 114 | return result; | ||
| 115 | } | ||
| 116 | /* Set CNTL3 reg to forced measurement period */ | ||
| 117 | result = | ||
| 118 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 119 | AMI30X_REG_CNTL3, AMI30X_BIT_CNTL3_F0RCE); | ||
| 120 | |||
| 121 | return result; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int ami30x_read(void *mlsl_handle, | ||
| 125 | struct ext_slave_descr *slave, | ||
| 126 | struct ext_slave_platform_data *pdata, | ||
| 127 | unsigned char *data) | ||
| 128 | { | ||
| 129 | unsigned char stat; | ||
| 130 | int result = INV_SUCCESS; | ||
| 131 | |||
| 132 | /* Read status reg and check if data ready (DRDY) */ | ||
| 133 | result = | ||
| 134 | inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_STAT1, | ||
| 135 | 1, &stat); | ||
| 136 | if (result) { | ||
| 137 | LOG_RESULT_LOCATION(result); | ||
| 138 | return result; | ||
| 139 | } | ||
| 140 | |||
| 141 | if (stat & 0x40) { | ||
| 142 | result = | ||
| 143 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 144 | AMI30X_REG_DATAX, 6, (unsigned char *)data); | ||
| 145 | if (result) { | ||
| 146 | LOG_RESULT_LOCATION(result); | ||
| 147 | return result; | ||
| 148 | } | ||
| 149 | /* start another measurement */ | ||
| 150 | result = | ||
| 151 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 152 | AMI30X_REG_CNTL3, | ||
| 153 | AMI30X_BIT_CNTL3_F0RCE); | ||
| 154 | if (result) { | ||
| 155 | LOG_RESULT_LOCATION(result); | ||
| 156 | return result; | ||
| 157 | } | ||
| 158 | |||
| 159 | return INV_SUCCESS; | ||
| 160 | } | ||
| 161 | |||
| 162 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | /* For AMI305,the range field needs to be modified to {9830.4f} */ | ||
| 167 | static struct ext_slave_descr ami30x_descr = { | ||
| 168 | .init = NULL, | ||
| 169 | .exit = NULL, | ||
| 170 | .suspend = ami30x_suspend, | ||
| 171 | .resume = ami30x_resume, | ||
| 172 | .read = ami30x_read, | ||
| 173 | .config = NULL, | ||
| 174 | .get_config = NULL, | ||
| 175 | .name = "ami30x", | ||
| 176 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 177 | .id = COMPASS_ID_AMI30X, | ||
| 178 | .read_reg = 0x06, | ||
| 179 | .read_len = 6, | ||
| 180 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 181 | .range = {5461, 3333}, | ||
| 182 | .trigger = NULL, | ||
| 183 | }; | ||
| 184 | |||
| 185 | static | ||
| 186 | struct ext_slave_descr *ami30x_get_slave_descr(void) | ||
| 187 | { | ||
| 188 | return &ami30x_descr; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* -------------------------------------------------------------------------- */ | ||
| 192 | struct ami30x_mod_private_data { | ||
| 193 | struct i2c_client *client; | ||
| 194 | struct ext_slave_platform_data *pdata; | ||
| 195 | }; | ||
| 196 | |||
| 197 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 198 | |||
| 199 | static int ami30x_mod_probe(struct i2c_client *client, | ||
| 200 | const struct i2c_device_id *devid) | ||
| 201 | { | ||
| 202 | struct ext_slave_platform_data *pdata; | ||
| 203 | struct ami30x_mod_private_data *private_data; | ||
| 204 | int result = 0; | ||
| 205 | |||
| 206 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 207 | |||
| 208 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 209 | result = -ENODEV; | ||
| 210 | goto out_no_free; | ||
| 211 | } | ||
| 212 | |||
| 213 | pdata = client->dev.platform_data; | ||
| 214 | if (!pdata) { | ||
| 215 | dev_err(&client->adapter->dev, | ||
| 216 | "Missing platform data for slave %s\n", devid->name); | ||
| 217 | result = -EFAULT; | ||
| 218 | goto out_no_free; | ||
| 219 | } | ||
| 220 | |||
| 221 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 222 | if (!private_data) { | ||
| 223 | result = -ENOMEM; | ||
| 224 | goto out_no_free; | ||
| 225 | } | ||
| 226 | |||
| 227 | i2c_set_clientdata(client, private_data); | ||
| 228 | private_data->client = client; | ||
| 229 | private_data->pdata = pdata; | ||
| 230 | |||
| 231 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 232 | ami30x_get_slave_descr); | ||
| 233 | if (result) { | ||
| 234 | dev_err(&client->adapter->dev, | ||
| 235 | "Slave registration failed: %s, %d\n", | ||
| 236 | devid->name, result); | ||
| 237 | goto out_free_memory; | ||
| 238 | } | ||
| 239 | |||
| 240 | return result; | ||
| 241 | |||
| 242 | out_free_memory: | ||
| 243 | kfree(private_data); | ||
| 244 | out_no_free: | ||
| 245 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 246 | return result; | ||
| 247 | |||
| 248 | } | ||
| 249 | |||
| 250 | static int ami30x_mod_remove(struct i2c_client *client) | ||
| 251 | { | ||
| 252 | struct ami30x_mod_private_data *private_data = | ||
| 253 | i2c_get_clientdata(client); | ||
| 254 | |||
| 255 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 256 | |||
| 257 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 258 | ami30x_get_slave_descr); | ||
| 259 | |||
| 260 | kfree(private_data); | ||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | |||
| 264 | static const struct i2c_device_id ami30x_mod_id[] = { | ||
| 265 | { "ami30x", COMPASS_ID_AMI30X }, | ||
| 266 | {} | ||
| 267 | }; | ||
| 268 | |||
| 269 | MODULE_DEVICE_TABLE(i2c, ami30x_mod_id); | ||
| 270 | |||
| 271 | static struct i2c_driver ami30x_mod_driver = { | ||
| 272 | .class = I2C_CLASS_HWMON, | ||
| 273 | .probe = ami30x_mod_probe, | ||
| 274 | .remove = ami30x_mod_remove, | ||
| 275 | .id_table = ami30x_mod_id, | ||
| 276 | .driver = { | ||
| 277 | .owner = THIS_MODULE, | ||
| 278 | .name = "ami30x_mod", | ||
| 279 | }, | ||
| 280 | .address_list = normal_i2c, | ||
| 281 | }; | ||
| 282 | |||
| 283 | static int __init ami30x_mod_init(void) | ||
| 284 | { | ||
| 285 | int res = i2c_add_driver(&ami30x_mod_driver); | ||
| 286 | pr_info("%s: Probe name %s\n", __func__, "ami30x_mod"); | ||
| 287 | if (res) | ||
| 288 | pr_err("%s failed\n", __func__); | ||
| 289 | return res; | ||
| 290 | } | ||
| 291 | |||
| 292 | static void __exit ami30x_mod_exit(void) | ||
| 293 | { | ||
| 294 | pr_info("%s\n", __func__); | ||
| 295 | i2c_del_driver(&ami30x_mod_driver); | ||
| 296 | } | ||
| 297 | |||
| 298 | module_init(ami30x_mod_init); | ||
| 299 | module_exit(ami30x_mod_exit); | ||
| 300 | |||
| 301 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 302 | MODULE_DESCRIPTION("Driver to integrate AMI30X sensor with the MPU"); | ||
| 303 | MODULE_LICENSE("GPL"); | ||
| 304 | MODULE_ALIAS("ami30x_mod"); | ||
| 305 | |||
| 306 | /** | ||
| 307 | * @} | ||
| 308 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami_hw.h b/drivers/misc/inv_mpu/compass/ami_hw.h new file mode 100644 index 00000000000..32a04e91cdc --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami_hw.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 Information System Products Co.,Ltd. | ||
| 3 | * | ||
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | * you may not use this file except in compliance with the License. | ||
| 6 | * You may obtain a copy of the License at | ||
| 7 | * | ||
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | * | ||
| 10 | * Unless required by applicable law or agreed to in writing, software | ||
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | * See the License for the specific language governing permissions and | ||
| 14 | * limitations under the License. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef AMI_HW_H | ||
| 18 | #define AMI_HW_H | ||
| 19 | |||
| 20 | #define AMI_I2C_BUS_NUM 2 | ||
| 21 | |||
| 22 | #ifdef AMI304_MODEL | ||
| 23 | #define AMI_I2C_ADDRESS 0x0F | ||
| 24 | #else | ||
| 25 | #define AMI_I2C_ADDRESS 0x0E | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #define AMI_GPIO_INT 152 | ||
| 29 | #define AMI_GPIO_DRDY 153 | ||
| 30 | |||
| 31 | /* AMI-Sensor Internal Register Address | ||
| 32 | *(Please refer to AMI-Sensor Specifications) | ||
| 33 | */ | ||
| 34 | #define AMI_MOREINFO_CMDCODE 0x0d | ||
| 35 | #define AMI_WHOIAM_CMDCODE 0x0f | ||
| 36 | #define AMI_REG_DATAX 0x10 | ||
| 37 | #define AMI_REG_DATAY 0x12 | ||
| 38 | #define AMI_REG_DATAZ 0x14 | ||
| 39 | #define AMI_REG_STA1 0x18 | ||
| 40 | #define AMI_REG_CTRL1 0x1b | ||
| 41 | #define AMI_REG_CTRL2 0x1c | ||
| 42 | #define AMI_REG_CTRL3 0x1d | ||
| 43 | #define AMI_REG_B0X 0x20 | ||
| 44 | #define AMI_REG_B0Y 0x22 | ||
| 45 | #define AMI_REG_B0Z 0x24 | ||
| 46 | #define AMI_REG_CTRL5 0x40 | ||
| 47 | #define AMI_REG_CTRL4 0x5c | ||
| 48 | #define AMI_REG_TEMP 0x60 | ||
| 49 | #define AMI_REG_DELAYX 0x68 | ||
| 50 | #define AMI_REG_DELAYY 0x6e | ||
| 51 | #define AMI_REG_DELAYZ 0x74 | ||
| 52 | #define AMI_REG_OFFX 0x6c | ||
| 53 | #define AMI_REG_OFFY 0x72 | ||
| 54 | #define AMI_REG_OFFZ 0x78 | ||
| 55 | #define AMI_FINEOUTPUT_X 0x90 | ||
| 56 | #define AMI_FINEOUTPUT_Y 0x92 | ||
| 57 | #define AMI_FINEOUTPUT_Z 0x94 | ||
| 58 | #define AMI_REG_SENX 0x96 | ||
| 59 | #define AMI_REG_SENY 0x98 | ||
| 60 | #define AMI_REG_SENZ 0x9a | ||
| 61 | #define AMI_REG_GAINX 0x9c | ||
| 62 | #define AMI_REG_GAINY 0x9e | ||
| 63 | #define AMI_REG_GAINZ 0xa0 | ||
| 64 | #define AMI_GETVERSION_CMDCODE 0xe8 | ||
| 65 | #define AMI_SERIALNUMBER_CMDCODE 0xea | ||
| 66 | #define AMI_REG_B0OTPX 0xa2 | ||
| 67 | #define AMI_REG_B0OTPY 0xb8 | ||
| 68 | #define AMI_REG_B0OTPZ 0xce | ||
| 69 | #define AMI_REG_OFFOTPX 0xf8 | ||
| 70 | #define AMI_REG_OFFOTPY 0xfa | ||
| 71 | #define AMI_REG_OFFOTPZ 0xfc | ||
| 72 | |||
| 73 | /* AMI-Sensor Control Bit (Please refer to AMI-Sensor Specifications) */ | ||
| 74 | #define AMI_CTRL1_PC1 0x80 | ||
| 75 | #define AMI_CTRL1_FS1_FORCE 0x02 | ||
| 76 | #define AMI_CTRL1_ODR1 0x10 | ||
| 77 | #define AMI_CTRL2_DREN 0x08 | ||
| 78 | #define AMI_CTRL2_DRP 0x04 | ||
| 79 | #define AMI_CTRL3_FORCE_BIT 0x40 | ||
| 80 | #define AMI_CTRL3_B0_LO_BIT 0x10 | ||
| 81 | #define AMI_CTRL3_SRST_BIT 0x80 | ||
| 82 | #define AMI_CTRL4_HS 0xa07e | ||
| 83 | #define AMI_CTRL4_AB 0x0001 | ||
| 84 | #define AMI_STA1_DRDY_BIT 0x40 | ||
| 85 | #define AMI_STA1_DOR_BIT 0x20 | ||
| 86 | |||
| 87 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/ami_sensor_def.h b/drivers/misc/inv_mpu/compass/ami_sensor_def.h new file mode 100644 index 00000000000..64032e2bf1f --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami_sensor_def.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 Information System Products Co.,Ltd. | ||
| 3 | * | ||
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | * you may not use this file except in compliance with the License. | ||
| 6 | * You may obtain a copy of the License at | ||
| 7 | * | ||
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | * | ||
| 10 | * Unless required by applicable law or agreed to in writing, software | ||
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | * See the License for the specific language governing permissions and | ||
| 14 | * limitations under the License. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Definitions for ami306 compass chip. | ||
| 19 | */ | ||
| 20 | #ifndef AMI_SENSOR_DEF_H | ||
| 21 | #define AMI_SENSOR_DEF_H | ||
| 22 | |||
| 23 | /********************************************************************* | ||
| 24 | Constant | ||
| 25 | *********************************************************************/ | ||
| 26 | #define AMI_OK 0x00 /**< Normal */ | ||
| 27 | #define AMI_PARAM_ERR 0x01 /**< Parameter Error */ | ||
| 28 | #define AMI_SEQ_ERR 0x02 /**< Squence Error */ | ||
| 29 | #define AMI_SYSTEM_ERR 0x10 /**< System Error */ | ||
| 30 | #define AMI_BLOCK_ERR 0x20 /**< Block Error */ | ||
| 31 | #define AMI_ERROR 0x99 /**< other Error */ | ||
| 32 | |||
| 33 | /********************************************************************* | ||
| 34 | Struct definition | ||
| 35 | *********************************************************************/ | ||
| 36 | /** axis sensitivity(gain) calibration parameter information */ | ||
| 37 | struct ami_vector3d { | ||
| 38 | signed short x; /**< X-axis */ | ||
| 39 | signed short y; /**< Y-axis */ | ||
| 40 | signed short z; /**< Z-axis */ | ||
| 41 | }; | ||
| 42 | |||
| 43 | /** axis interference information */ | ||
| 44 | struct ami_interference { | ||
| 45 | /**< Y-axis magnetic field for X-axis correction value */ | ||
| 46 | signed short xy; | ||
| 47 | /**< Z-axis magnetic field for X-axis correction value */ | ||
| 48 | signed short xz; | ||
| 49 | /**< X-axis magnetic field for Y-axis correction value */ | ||
| 50 | signed short yx; | ||
| 51 | /**< Z-axis magnetic field for Y-axis correction value */ | ||
| 52 | signed short yz; | ||
| 53 | /**< X-axis magnetic field for Z-axis correction value */ | ||
| 54 | signed short zx; | ||
| 55 | /**< Y-axis magnetic field for Z-axis correction value */ | ||
| 56 | signed short zy; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /** sensor calibration Parameter information */ | ||
| 60 | struct ami_sensor_parametor { | ||
| 61 | /**< geomagnetic field sensor gain */ | ||
| 62 | struct ami_vector3d m_gain; | ||
| 63 | /**< geomagnetic field sensor gain correction parameter */ | ||
| 64 | struct ami_vector3d m_gain_cor; | ||
| 65 | /**< geomagnetic field sensor offset */ | ||
| 66 | struct ami_vector3d m_offset; | ||
| 67 | /**< geomagnetic field sensor axis interference parameter */ | ||
| 68 | struct ami_interference m_interference; | ||
| 69 | #ifdef AMI_6AXIS | ||
| 70 | /**< acceleration sensor gain */ | ||
| 71 | struct ami_vector3d a_gain; | ||
| 72 | /**< acceleration sensor offset */ | ||
| 73 | struct ami_vector3d a_offset; | ||
| 74 | /**< acceleration sensor deviation */ | ||
| 75 | signed short a_deviation; | ||
| 76 | #endif | ||
| 77 | }; | ||
| 78 | |||
| 79 | /** G2-Sensor measurement value (voltage ADC value ) */ | ||
| 80 | struct ami_sensor_rawvalue { | ||
| 81 | /**< geomagnetic field sensor measurement X-axis value | ||
| 82 | (mounted position/direction reference) */ | ||
| 83 | unsigned short mx; | ||
| 84 | /**< geomagnetic field sensor measurement Y-axis value | ||
| 85 | (mounted position/direction reference) */ | ||
| 86 | unsigned short my; | ||
| 87 | /**< geomagnetic field sensor measurement Z-axis value | ||
| 88 | (mounted position/direction reference) */ | ||
| 89 | unsigned short mz; | ||
| 90 | #ifdef AMI_6AXIS | ||
| 91 | /**< acceleration sensor measurement X-axis value | ||
| 92 | (mounted position/direction reference) */ | ||
| 93 | unsigned short ax; | ||
| 94 | /**< acceleration sensor measurement Y-axis value | ||
| 95 | (mounted position/direction reference) */ | ||
| 96 | unsigned short ay; | ||
| 97 | /**< acceleration sensor measurement Z-axis value | ||
| 98 | (mounted position/direction reference) */ | ||
| 99 | unsigned short az; | ||
| 100 | #endif | ||
| 101 | /**< temperature sensor measurement value */ | ||
| 102 | unsigned short temperature; | ||
| 103 | }; | ||
| 104 | |||
| 105 | /** Window function Parameter information */ | ||
| 106 | struct ami_win_parameter { | ||
| 107 | /**< current fine value */ | ||
| 108 | struct ami_vector3d m_fine; | ||
| 109 | /**< change per 1coarse */ | ||
| 110 | struct ami_vector3d m_fine_output; | ||
| 111 | /**< fine value at zero gauss */ | ||
| 112 | struct ami_vector3d m_0Gauss_fine; | ||
| 113 | #ifdef AMI304 | ||
| 114 | /**< current b0 value */ | ||
| 115 | struct ami_vector3d m_b0; | ||
| 116 | /**< current coarse value */ | ||
| 117 | struct ami_vector3d m_coar; | ||
| 118 | /**< change per 1fine */ | ||
| 119 | struct ami_vector3d m_coar_output; | ||
| 120 | /**< coarse value at zero gauss */ | ||
| 121 | struct ami_vector3d m_0Gauss_coar; | ||
| 122 | /**< delay value */ | ||
| 123 | struct ami_vector3d m_delay; | ||
| 124 | #endif | ||
| 125 | }; | ||
| 126 | |||
| 127 | /** AMI chip information ex) 1)model 2)s/n 3)ver 4)more info in the chip */ | ||
| 128 | struct ami_chipinfo { | ||
| 129 | unsigned short info; /* INFO 0x0d/0x0e reg. */ | ||
| 130 | unsigned short ver; /* VER 0xe8/0xe9 reg. */ | ||
| 131 | unsigned short sn; /* SN 0xea/0xeb reg. */ | ||
| 132 | unsigned char wia; /* WIA 0x0f reg. */ | ||
| 133 | }; | ||
| 134 | |||
| 135 | /** AMI Driver Information */ | ||
| 136 | struct ami_driverinfo { | ||
| 137 | unsigned char remarks[40]; /* Some Information */ | ||
| 138 | unsigned char datetime[30]; /* compiled date&time */ | ||
| 139 | unsigned char ver_major; /* major version */ | ||
| 140 | unsigned char ver_middle; /* middle.. */ | ||
| 141 | unsigned char ver_minor; /* minor .. */ | ||
| 142 | }; | ||
| 143 | |||
| 144 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/hmc5883.c b/drivers/misc/inv_mpu/compass/hmc5883.c new file mode 100644 index 00000000000..fdf2ac00565 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hmc5883.c | |||
| @@ -0,0 +1,391 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file hmc5883.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Honeywell | ||
| 26 | * HMC5883 compass. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | enum HMC_REG { | ||
| 49 | HMC_REG_CONF_A = 0x0, | ||
| 50 | HMC_REG_CONF_B = 0x1, | ||
| 51 | HMC_REG_MODE = 0x2, | ||
| 52 | HMC_REG_X_M = 0x3, | ||
| 53 | HMC_REG_X_L = 0x4, | ||
| 54 | HMC_REG_Z_M = 0x5, | ||
| 55 | HMC_REG_Z_L = 0x6, | ||
| 56 | HMC_REG_Y_M = 0x7, | ||
| 57 | HMC_REG_Y_L = 0x8, | ||
| 58 | HMC_REG_STATUS = 0x9, | ||
| 59 | HMC_REG_ID_A = 0xA, | ||
| 60 | HMC_REG_ID_B = 0xB, | ||
| 61 | HMC_REG_ID_C = 0xC | ||
| 62 | }; | ||
| 63 | |||
| 64 | enum HMC_CONF_A { | ||
| 65 | HMC_CONF_A_DRATE_MASK = 0x1C, | ||
| 66 | HMC_CONF_A_DRATE_0_75 = 0x00, | ||
| 67 | HMC_CONF_A_DRATE_1_5 = 0x04, | ||
| 68 | HMC_CONF_A_DRATE_3 = 0x08, | ||
| 69 | HMC_CONF_A_DRATE_7_5 = 0x0C, | ||
| 70 | HMC_CONF_A_DRATE_15 = 0x10, | ||
| 71 | HMC_CONF_A_DRATE_30 = 0x14, | ||
| 72 | HMC_CONF_A_DRATE_75 = 0x18, | ||
| 73 | HMC_CONF_A_MEAS_MASK = 0x3, | ||
| 74 | HMC_CONF_A_MEAS_NORM = 0x0, | ||
| 75 | HMC_CONF_A_MEAS_POS = 0x1, | ||
| 76 | HMC_CONF_A_MEAS_NEG = 0x2 | ||
| 77 | }; | ||
| 78 | |||
| 79 | enum HMC_CONF_B { | ||
| 80 | HMC_CONF_B_GAIN_MASK = 0xE0, | ||
| 81 | HMC_CONF_B_GAIN_0_9 = 0x00, | ||
| 82 | HMC_CONF_B_GAIN_1_2 = 0x20, | ||
| 83 | HMC_CONF_B_GAIN_1_9 = 0x40, | ||
| 84 | HMC_CONF_B_GAIN_2_5 = 0x60, | ||
| 85 | HMC_CONF_B_GAIN_4_0 = 0x80, | ||
| 86 | HMC_CONF_B_GAIN_4_6 = 0xA0, | ||
| 87 | HMC_CONF_B_GAIN_5_5 = 0xC0, | ||
| 88 | HMC_CONF_B_GAIN_7_9 = 0xE0 | ||
| 89 | }; | ||
| 90 | |||
| 91 | enum HMC_MODE { | ||
| 92 | HMC_MODE_MASK = 0x3, | ||
| 93 | HMC_MODE_CONT = 0x0, | ||
| 94 | HMC_MODE_SINGLE = 0x1, | ||
| 95 | HMC_MODE_IDLE = 0x2, | ||
| 96 | HMC_MODE_SLEEP = 0x3 | ||
| 97 | }; | ||
| 98 | |||
| 99 | /* -------------------------------------------------------------------------- */ | ||
| 100 | static int hmc5883_suspend(void *mlsl_handle, | ||
| 101 | struct ext_slave_descr *slave, | ||
| 102 | struct ext_slave_platform_data *pdata) | ||
| 103 | { | ||
| 104 | int result = INV_SUCCESS; | ||
| 105 | |||
| 106 | result = | ||
| 107 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 108 | HMC_REG_MODE, HMC_MODE_SLEEP); | ||
| 109 | if (result) { | ||
| 110 | LOG_RESULT_LOCATION(result); | ||
| 111 | return result; | ||
| 112 | } | ||
| 113 | msleep(3); | ||
| 114 | |||
| 115 | return result; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int hmc5883_resume(void *mlsl_handle, | ||
| 119 | struct ext_slave_descr *slave, | ||
| 120 | struct ext_slave_platform_data *pdata) | ||
| 121 | { | ||
| 122 | int result = INV_SUCCESS; | ||
| 123 | |||
| 124 | /* Use single measurement mode. Start at sleep state. */ | ||
| 125 | result = | ||
| 126 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 127 | HMC_REG_MODE, HMC_MODE_SLEEP); | ||
| 128 | if (result) { | ||
| 129 | LOG_RESULT_LOCATION(result); | ||
| 130 | return result; | ||
| 131 | } | ||
| 132 | /* Config normal measurement */ | ||
| 133 | result = | ||
| 134 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 135 | HMC_REG_CONF_A, 0); | ||
| 136 | if (result) { | ||
| 137 | LOG_RESULT_LOCATION(result); | ||
| 138 | return result; | ||
| 139 | } | ||
| 140 | /* Adjust gain to 307 LSB/Gauss */ | ||
| 141 | result = | ||
| 142 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 143 | HMC_REG_CONF_B, HMC_CONF_B_GAIN_5_5); | ||
| 144 | if (result) { | ||
| 145 | LOG_RESULT_LOCATION(result); | ||
| 146 | return result; | ||
| 147 | } | ||
| 148 | |||
| 149 | return result; | ||
| 150 | } | ||
| 151 | |||
| 152 | static int hmc5883_read(void *mlsl_handle, | ||
| 153 | struct ext_slave_descr *slave, | ||
| 154 | struct ext_slave_platform_data *pdata, | ||
| 155 | unsigned char *data) | ||
| 156 | { | ||
| 157 | unsigned char stat; | ||
| 158 | int result = INV_SUCCESS; | ||
| 159 | unsigned char tmp; | ||
| 160 | short axisFixed; | ||
| 161 | |||
| 162 | /* Read status reg. to check if data is ready */ | ||
| 163 | result = | ||
| 164 | inv_serial_read(mlsl_handle, pdata->address, HMC_REG_STATUS, 1, | ||
| 165 | &stat); | ||
| 166 | if (result) { | ||
| 167 | LOG_RESULT_LOCATION(result); | ||
| 168 | return result; | ||
| 169 | } | ||
| 170 | if (stat & 0x01) { | ||
| 171 | result = | ||
| 172 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 173 | HMC_REG_X_M, 6, (unsigned char *)data); | ||
| 174 | if (result) { | ||
| 175 | LOG_RESULT_LOCATION(result); | ||
| 176 | return result; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* switch YZ axis to proper position */ | ||
| 180 | tmp = data[2]; | ||
| 181 | data[2] = data[4]; | ||
| 182 | data[4] = tmp; | ||
| 183 | tmp = data[3]; | ||
| 184 | data[3] = data[5]; | ||
| 185 | data[5] = tmp; | ||
| 186 | |||
| 187 | /*drop data if overflows */ | ||
| 188 | if ((data[0] == 0xf0) || (data[2] == 0xf0) | ||
| 189 | || (data[4] == 0xf0)) { | ||
| 190 | /* trigger next measurement read */ | ||
| 191 | result = | ||
| 192 | inv_serial_single_write(mlsl_handle, | ||
| 193 | pdata->address, | ||
| 194 | HMC_REG_MODE, | ||
| 195 | HMC_MODE_SINGLE); | ||
| 196 | if (result) { | ||
| 197 | LOG_RESULT_LOCATION(result); | ||
| 198 | return result; | ||
| 199 | } | ||
| 200 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 201 | } | ||
| 202 | /* convert to fixed point and apply sensitivity correction for | ||
| 203 | Z-axis */ | ||
| 204 | axisFixed = | ||
| 205 | (short)((unsigned short)data[5] + | ||
| 206 | (unsigned short)data[4] * 256); | ||
| 207 | /* scale up by 1.125 (36/32) */ | ||
| 208 | axisFixed = (short)(axisFixed * 36); | ||
| 209 | data[4] = axisFixed >> 8; | ||
| 210 | data[5] = axisFixed & 0xFF; | ||
| 211 | |||
| 212 | axisFixed = | ||
| 213 | (short)((unsigned short)data[3] + | ||
| 214 | (unsigned short)data[2] * 256); | ||
| 215 | axisFixed = (short)(axisFixed * 32); | ||
| 216 | data[2] = axisFixed >> 8; | ||
| 217 | data[3] = axisFixed & 0xFF; | ||
| 218 | |||
| 219 | axisFixed = | ||
| 220 | (short)((unsigned short)data[1] + | ||
| 221 | (unsigned short)data[0] * 256); | ||
| 222 | axisFixed = (short)(axisFixed * 32); | ||
| 223 | data[0] = axisFixed >> 8; | ||
| 224 | data[1] = axisFixed & 0xFF; | ||
| 225 | |||
| 226 | /* trigger next measurement read */ | ||
| 227 | result = | ||
| 228 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 229 | HMC_REG_MODE, HMC_MODE_SINGLE); | ||
| 230 | if (result) { | ||
| 231 | LOG_RESULT_LOCATION(result); | ||
| 232 | return result; | ||
| 233 | } | ||
| 234 | |||
| 235 | return INV_SUCCESS; | ||
| 236 | } else { | ||
| 237 | /* trigger next measurement read */ | ||
| 238 | result = | ||
| 239 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 240 | HMC_REG_MODE, HMC_MODE_SINGLE); | ||
| 241 | if (result) { | ||
| 242 | LOG_RESULT_LOCATION(result); | ||
| 243 | return result; | ||
| 244 | } | ||
| 245 | |||
| 246 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | static struct ext_slave_descr hmc5883_descr = { | ||
| 251 | .init = NULL, | ||
| 252 | .exit = NULL, | ||
| 253 | .suspend = hmc5883_suspend, | ||
| 254 | .resume = hmc5883_resume, | ||
| 255 | .read = hmc5883_read, | ||
| 256 | .config = NULL, | ||
| 257 | .get_config = NULL, | ||
| 258 | .name = "hmc5883", | ||
| 259 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 260 | .id = COMPASS_ID_HMC5883, | ||
| 261 | .read_reg = 0x06, | ||
| 262 | .read_len = 6, | ||
| 263 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 264 | .range = {10673, 6156}, | ||
| 265 | .trigger = NULL, | ||
| 266 | }; | ||
| 267 | |||
| 268 | static | ||
| 269 | struct ext_slave_descr *hmc5883_get_slave_descr(void) | ||
| 270 | { | ||
| 271 | return &hmc5883_descr; | ||
| 272 | } | ||
| 273 | |||
| 274 | /* -------------------------------------------------------------------------- */ | ||
| 275 | struct hmc5883_mod_private_data { | ||
| 276 | struct i2c_client *client; | ||
| 277 | struct ext_slave_platform_data *pdata; | ||
| 278 | }; | ||
| 279 | |||
| 280 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 281 | |||
| 282 | static int hmc5883_mod_probe(struct i2c_client *client, | ||
| 283 | const struct i2c_device_id *devid) | ||
| 284 | { | ||
| 285 | struct ext_slave_platform_data *pdata; | ||
| 286 | struct hmc5883_mod_private_data *private_data; | ||
| 287 | int result = 0; | ||
| 288 | |||
| 289 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 290 | |||
| 291 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 292 | result = -ENODEV; | ||
| 293 | goto out_no_free; | ||
| 294 | } | ||
| 295 | |||
| 296 | pdata = client->dev.platform_data; | ||
| 297 | if (!pdata) { | ||
| 298 | dev_err(&client->adapter->dev, | ||
| 299 | "Missing platform data for slave %s\n", devid->name); | ||
| 300 | result = -EFAULT; | ||
| 301 | goto out_no_free; | ||
| 302 | } | ||
| 303 | |||
| 304 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 305 | if (!private_data) { | ||
| 306 | result = -ENOMEM; | ||
| 307 | goto out_no_free; | ||
| 308 | } | ||
| 309 | |||
| 310 | i2c_set_clientdata(client, private_data); | ||
| 311 | private_data->client = client; | ||
| 312 | private_data->pdata = pdata; | ||
| 313 | |||
| 314 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 315 | hmc5883_get_slave_descr); | ||
| 316 | if (result) { | ||
| 317 | dev_err(&client->adapter->dev, | ||
| 318 | "Slave registration failed: %s, %d\n", | ||
| 319 | devid->name, result); | ||
| 320 | goto out_free_memory; | ||
| 321 | } | ||
| 322 | |||
| 323 | return result; | ||
| 324 | |||
| 325 | out_free_memory: | ||
| 326 | kfree(private_data); | ||
| 327 | out_no_free: | ||
| 328 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 329 | return result; | ||
| 330 | |||
| 331 | } | ||
| 332 | |||
| 333 | static int hmc5883_mod_remove(struct i2c_client *client) | ||
| 334 | { | ||
| 335 | struct hmc5883_mod_private_data *private_data = | ||
| 336 | i2c_get_clientdata(client); | ||
| 337 | |||
| 338 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 339 | |||
| 340 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 341 | hmc5883_get_slave_descr); | ||
| 342 | |||
| 343 | kfree(private_data); | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | static const struct i2c_device_id hmc5883_mod_id[] = { | ||
| 348 | { "hmc5883", COMPASS_ID_HMC5883 }, | ||
| 349 | {} | ||
| 350 | }; | ||
| 351 | |||
| 352 | MODULE_DEVICE_TABLE(i2c, hmc5883_mod_id); | ||
| 353 | |||
| 354 | static struct i2c_driver hmc5883_mod_driver = { | ||
| 355 | .class = I2C_CLASS_HWMON, | ||
| 356 | .probe = hmc5883_mod_probe, | ||
| 357 | .remove = hmc5883_mod_remove, | ||
| 358 | .id_table = hmc5883_mod_id, | ||
| 359 | .driver = { | ||
| 360 | .owner = THIS_MODULE, | ||
| 361 | .name = "hmc5883_mod", | ||
| 362 | }, | ||
| 363 | .address_list = normal_i2c, | ||
| 364 | }; | ||
| 365 | |||
| 366 | static int __init hmc5883_mod_init(void) | ||
| 367 | { | ||
| 368 | int res = i2c_add_driver(&hmc5883_mod_driver); | ||
| 369 | pr_info("%s: Probe name %s\n", __func__, "hmc5883_mod"); | ||
| 370 | if (res) | ||
| 371 | pr_err("%s failed\n", __func__); | ||
| 372 | return res; | ||
| 373 | } | ||
| 374 | |||
| 375 | static void __exit hmc5883_mod_exit(void) | ||
| 376 | { | ||
| 377 | pr_info("%s\n", __func__); | ||
| 378 | i2c_del_driver(&hmc5883_mod_driver); | ||
| 379 | } | ||
| 380 | |||
| 381 | module_init(hmc5883_mod_init); | ||
| 382 | module_exit(hmc5883_mod_exit); | ||
| 383 | |||
| 384 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 385 | MODULE_DESCRIPTION("Driver to integrate HMC5883 sensor with the MPU"); | ||
| 386 | MODULE_LICENSE("GPL"); | ||
| 387 | MODULE_ALIAS("hmc5883_mod"); | ||
| 388 | |||
| 389 | /** | ||
| 390 | * @} | ||
| 391 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/hscdtd002b.c b/drivers/misc/inv_mpu/compass/hscdtd002b.c new file mode 100644 index 00000000000..4f6013cbe3d --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hscdtd002b.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file hscdtd002b.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Alps HSCDTD002B | ||
| 26 | * compass. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | #define COMPASS_HSCDTD002B_STAT (0x18) | ||
| 49 | #define COMPASS_HSCDTD002B_CTRL1 (0x1B) | ||
| 50 | #define COMPASS_HSCDTD002B_CTRL2 (0x1C) | ||
| 51 | #define COMPASS_HSCDTD002B_CTRL3 (0x1D) | ||
| 52 | #define COMPASS_HSCDTD002B_DATAX (0x10) | ||
| 53 | |||
| 54 | /* -------------------------------------------------------------------------- */ | ||
| 55 | static int hscdtd002b_suspend(void *mlsl_handle, | ||
| 56 | struct ext_slave_descr *slave, | ||
| 57 | struct ext_slave_platform_data *pdata) | ||
| 58 | { | ||
| 59 | int result = INV_SUCCESS; | ||
| 60 | |||
| 61 | /* Power mode: stand-by */ | ||
| 62 | result = | ||
| 63 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 64 | COMPASS_HSCDTD002B_CTRL1, 0x00); | ||
| 65 | if (result) { | ||
| 66 | LOG_RESULT_LOCATION(result); | ||
| 67 | return result; | ||
| 68 | } | ||
| 69 | msleep(1); /* turn-off time */ | ||
| 70 | |||
| 71 | return result; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int hscdtd002b_resume(void *mlsl_handle, | ||
| 75 | struct ext_slave_descr *slave, | ||
| 76 | struct ext_slave_platform_data *pdata) | ||
| 77 | { | ||
| 78 | int result = INV_SUCCESS; | ||
| 79 | |||
| 80 | /* Soft reset */ | ||
| 81 | result = | ||
| 82 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 83 | COMPASS_HSCDTD002B_CTRL3, 0x80); | ||
| 84 | if (result) { | ||
| 85 | LOG_RESULT_LOCATION(result); | ||
| 86 | return result; | ||
| 87 | } | ||
| 88 | /* Force state; Power mode: active */ | ||
| 89 | result = | ||
| 90 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 91 | COMPASS_HSCDTD002B_CTRL1, 0x82); | ||
| 92 | if (result) { | ||
| 93 | LOG_RESULT_LOCATION(result); | ||
| 94 | return result; | ||
| 95 | } | ||
| 96 | /* Data ready enable */ | ||
| 97 | result = | ||
| 98 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 99 | COMPASS_HSCDTD002B_CTRL2, 0x08); | ||
| 100 | if (result) { | ||
| 101 | LOG_RESULT_LOCATION(result); | ||
| 102 | return result; | ||
| 103 | } | ||
| 104 | msleep(1); /* turn-on time */ | ||
| 105 | |||
| 106 | return result; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int hscdtd002b_read(void *mlsl_handle, | ||
| 110 | struct ext_slave_descr *slave, | ||
| 111 | struct ext_slave_platform_data *pdata, | ||
| 112 | unsigned char *data) | ||
| 113 | { | ||
| 114 | unsigned char stat; | ||
| 115 | int result = INV_SUCCESS; | ||
| 116 | int status = INV_SUCCESS; | ||
| 117 | |||
| 118 | /* Read status reg. to check if data is ready */ | ||
| 119 | result = | ||
| 120 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 121 | COMPASS_HSCDTD002B_STAT, 1, &stat); | ||
| 122 | if (result) { | ||
| 123 | LOG_RESULT_LOCATION(result); | ||
| 124 | return result; | ||
| 125 | } | ||
| 126 | if (stat & 0x40) { | ||
| 127 | result = | ||
| 128 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 129 | COMPASS_HSCDTD002B_DATAX, 6, | ||
| 130 | (unsigned char *)data); | ||
| 131 | if (result) { | ||
| 132 | LOG_RESULT_LOCATION(result); | ||
| 133 | return result; | ||
| 134 | } | ||
| 135 | status = INV_SUCCESS; | ||
| 136 | } else if (stat & 0x20) { | ||
| 137 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 138 | } else { | ||
| 139 | status = INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 140 | } | ||
| 141 | /* trigger next measurement read */ | ||
| 142 | result = | ||
| 143 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 144 | COMPASS_HSCDTD002B_CTRL3, 0x40); | ||
| 145 | if (result) { | ||
| 146 | LOG_RESULT_LOCATION(result); | ||
| 147 | return result; | ||
| 148 | } | ||
| 149 | |||
| 150 | return status; | ||
| 151 | } | ||
| 152 | |||
| 153 | static struct ext_slave_descr hscdtd002b_descr = { | ||
| 154 | .init = NULL, | ||
| 155 | .exit = NULL, | ||
| 156 | .suspend = hscdtd002b_suspend, | ||
| 157 | .resume = hscdtd002b_resume, | ||
| 158 | .read = hscdtd002b_read, | ||
| 159 | .config = NULL, | ||
| 160 | .get_config = NULL, | ||
| 161 | .name = "hscdtd002b", | ||
| 162 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 163 | .id = COMPASS_ID_HSCDTD002B, | ||
| 164 | .read_reg = 0x10, | ||
| 165 | .read_len = 6, | ||
| 166 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 167 | .range = {9830, 4000}, | ||
| 168 | .trigger = NULL, | ||
| 169 | }; | ||
| 170 | |||
| 171 | static | ||
| 172 | struct ext_slave_descr *hscdtd002b_get_slave_descr(void) | ||
| 173 | { | ||
| 174 | return &hscdtd002b_descr; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* -------------------------------------------------------------------------- */ | ||
| 178 | struct hscdtd002b_mod_private_data { | ||
| 179 | struct i2c_client *client; | ||
| 180 | struct ext_slave_platform_data *pdata; | ||
| 181 | }; | ||
| 182 | |||
| 183 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 184 | |||
| 185 | static int hscdtd002b_mod_probe(struct i2c_client *client, | ||
| 186 | const struct i2c_device_id *devid) | ||
| 187 | { | ||
| 188 | struct ext_slave_platform_data *pdata; | ||
| 189 | struct hscdtd002b_mod_private_data *private_data; | ||
| 190 | int result = 0; | ||
| 191 | |||
| 192 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 193 | |||
| 194 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 195 | result = -ENODEV; | ||
| 196 | goto out_no_free; | ||
| 197 | } | ||
| 198 | |||
| 199 | pdata = client->dev.platform_data; | ||
| 200 | if (!pdata) { | ||
| 201 | dev_err(&client->adapter->dev, | ||
| 202 | "Missing platform data for slave %s\n", devid->name); | ||
| 203 | result = -EFAULT; | ||
| 204 | goto out_no_free; | ||
| 205 | } | ||
| 206 | |||
| 207 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 208 | if (!private_data) { | ||
| 209 | result = -ENOMEM; | ||
| 210 | goto out_no_free; | ||
| 211 | } | ||
| 212 | |||
| 213 | i2c_set_clientdata(client, private_data); | ||
| 214 | private_data->client = client; | ||
| 215 | private_data->pdata = pdata; | ||
| 216 | |||
| 217 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 218 | hscdtd002b_get_slave_descr); | ||
| 219 | if (result) { | ||
| 220 | dev_err(&client->adapter->dev, | ||
| 221 | "Slave registration failed: %s, %d\n", | ||
| 222 | devid->name, result); | ||
| 223 | goto out_free_memory; | ||
| 224 | } | ||
| 225 | |||
| 226 | return result; | ||
| 227 | |||
| 228 | out_free_memory: | ||
| 229 | kfree(private_data); | ||
| 230 | out_no_free: | ||
| 231 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 232 | return result; | ||
| 233 | |||
| 234 | } | ||
| 235 | |||
| 236 | static int hscdtd002b_mod_remove(struct i2c_client *client) | ||
| 237 | { | ||
| 238 | struct hscdtd002b_mod_private_data *private_data = | ||
| 239 | i2c_get_clientdata(client); | ||
| 240 | |||
| 241 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 242 | |||
| 243 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 244 | hscdtd002b_get_slave_descr); | ||
| 245 | |||
| 246 | kfree(private_data); | ||
| 247 | return 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | static const struct i2c_device_id hscdtd002b_mod_id[] = { | ||
| 251 | { "hscdtd002b", COMPASS_ID_HSCDTD002B }, | ||
| 252 | {} | ||
| 253 | }; | ||
| 254 | |||
| 255 | MODULE_DEVICE_TABLE(i2c, hscdtd002b_mod_id); | ||
| 256 | |||
| 257 | static struct i2c_driver hscdtd002b_mod_driver = { | ||
| 258 | .class = I2C_CLASS_HWMON, | ||
| 259 | .probe = hscdtd002b_mod_probe, | ||
| 260 | .remove = hscdtd002b_mod_remove, | ||
| 261 | .id_table = hscdtd002b_mod_id, | ||
| 262 | .driver = { | ||
| 263 | .owner = THIS_MODULE, | ||
| 264 | .name = "hscdtd002b_mod", | ||
| 265 | }, | ||
| 266 | .address_list = normal_i2c, | ||
| 267 | }; | ||
| 268 | |||
| 269 | static int __init hscdtd002b_mod_init(void) | ||
| 270 | { | ||
| 271 | int res = i2c_add_driver(&hscdtd002b_mod_driver); | ||
| 272 | pr_info("%s: Probe name %s\n", __func__, "hscdtd002b_mod"); | ||
| 273 | if (res) | ||
| 274 | pr_err("%s failed\n", __func__); | ||
| 275 | return res; | ||
| 276 | } | ||
| 277 | |||
| 278 | static void __exit hscdtd002b_mod_exit(void) | ||
| 279 | { | ||
| 280 | pr_info("%s\n", __func__); | ||
| 281 | i2c_del_driver(&hscdtd002b_mod_driver); | ||
| 282 | } | ||
| 283 | |||
| 284 | module_init(hscdtd002b_mod_init); | ||
| 285 | module_exit(hscdtd002b_mod_exit); | ||
| 286 | |||
| 287 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 288 | MODULE_DESCRIPTION("Driver to integrate HSCDTD002B sensor with the MPU"); | ||
| 289 | MODULE_LICENSE("GPL"); | ||
| 290 | MODULE_ALIAS("hscdtd002b_mod"); | ||
| 291 | |||
| 292 | /** | ||
| 293 | * @} | ||
| 294 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/hscdtd004a.c b/drivers/misc/inv_mpu/compass/hscdtd004a.c new file mode 100644 index 00000000000..f0915599bd2 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hscdtd004a.c | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file hscdtd004a.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Alps HSCDTD004A | ||
| 26 | * compass. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | #define COMPASS_HSCDTD004A_STAT (0x18) | ||
| 49 | #define COMPASS_HSCDTD004A_CTRL1 (0x1B) | ||
| 50 | #define COMPASS_HSCDTD004A_CTRL2 (0x1C) | ||
| 51 | #define COMPASS_HSCDTD004A_CTRL3 (0x1D) | ||
| 52 | #define COMPASS_HSCDTD004A_DATAX (0x10) | ||
| 53 | |||
| 54 | /* -------------------------------------------------------------------------- */ | ||
| 55 | |||
| 56 | static int hscdtd004a_suspend(void *mlsl_handle, | ||
| 57 | struct ext_slave_descr *slave, | ||
| 58 | struct ext_slave_platform_data *pdata) | ||
| 59 | { | ||
| 60 | int result = INV_SUCCESS; | ||
| 61 | |||
| 62 | /* Power mode: stand-by */ | ||
| 63 | result = | ||
| 64 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 65 | COMPASS_HSCDTD004A_CTRL1, 0x00); | ||
| 66 | if (result) { | ||
| 67 | LOG_RESULT_LOCATION(result); | ||
| 68 | return result; | ||
| 69 | } | ||
| 70 | msleep(1); /* turn-off time */ | ||
| 71 | |||
| 72 | return result; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int hscdtd004a_init(void *mlsl_handle, | ||
| 76 | struct ext_slave_descr *slave, | ||
| 77 | struct ext_slave_platform_data *pdata) | ||
| 78 | { | ||
| 79 | int result; | ||
| 80 | unsigned char data1, data2[2]; | ||
| 81 | |||
| 82 | result = inv_serial_read(mlsl_handle, pdata->address, 0xf, 1, &data1); | ||
| 83 | if (result) { | ||
| 84 | LOG_RESULT_LOCATION(result); | ||
| 85 | return result; | ||
| 86 | } | ||
| 87 | result = inv_serial_read(mlsl_handle, pdata->address, 0xd, 2, data2); | ||
| 88 | if (result) { | ||
| 89 | LOG_RESULT_LOCATION(result); | ||
| 90 | return result; | ||
| 91 | } | ||
| 92 | if (data1 != 0x49 || data2[0] != 0x45 || data2[1] != 0x54) { | ||
| 93 | LOG_RESULT_LOCATION(INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED); | ||
| 94 | return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED; | ||
| 95 | } | ||
| 96 | return result; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int hscdtd004a_resume(void *mlsl_handle, | ||
| 100 | struct ext_slave_descr *slave, | ||
| 101 | struct ext_slave_platform_data *pdata) | ||
| 102 | { | ||
| 103 | int result = INV_SUCCESS; | ||
| 104 | |||
| 105 | /* Soft reset */ | ||
| 106 | result = | ||
| 107 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 108 | COMPASS_HSCDTD004A_CTRL3, 0x80); | ||
| 109 | if (result) { | ||
| 110 | LOG_RESULT_LOCATION(result); | ||
| 111 | return result; | ||
| 112 | } | ||
| 113 | /* Normal state; Power mode: active */ | ||
| 114 | result = | ||
| 115 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 116 | COMPASS_HSCDTD004A_CTRL1, 0x82); | ||
| 117 | if (result) { | ||
| 118 | LOG_RESULT_LOCATION(result); | ||
| 119 | return result; | ||
| 120 | } | ||
| 121 | /* Data ready enable */ | ||
| 122 | result = | ||
| 123 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 124 | COMPASS_HSCDTD004A_CTRL2, 0x7C); | ||
| 125 | if (result) { | ||
| 126 | LOG_RESULT_LOCATION(result); | ||
| 127 | return result; | ||
| 128 | } | ||
| 129 | msleep(1); /* turn-on time */ | ||
| 130 | return result; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int hscdtd004a_read(void *mlsl_handle, | ||
| 134 | struct ext_slave_descr *slave, | ||
| 135 | struct ext_slave_platform_data *pdata, | ||
| 136 | unsigned char *data) | ||
| 137 | { | ||
| 138 | unsigned char stat; | ||
| 139 | int result = INV_SUCCESS; | ||
| 140 | int status = INV_SUCCESS; | ||
| 141 | |||
| 142 | /* Read status reg. to check if data is ready */ | ||
| 143 | result = | ||
| 144 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 145 | COMPASS_HSCDTD004A_STAT, 1, &stat); | ||
| 146 | if (result) { | ||
| 147 | LOG_RESULT_LOCATION(result); | ||
| 148 | return result; | ||
| 149 | } | ||
| 150 | if (stat & 0x48) { | ||
| 151 | result = | ||
| 152 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 153 | COMPASS_HSCDTD004A_DATAX, 6, | ||
| 154 | (unsigned char *)data); | ||
| 155 | if (result) { | ||
| 156 | LOG_RESULT_LOCATION(result); | ||
| 157 | return result; | ||
| 158 | } | ||
| 159 | status = INV_SUCCESS; | ||
| 160 | } else if (stat & 0x68) { | ||
| 161 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 162 | } else { | ||
| 163 | status = INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 164 | } | ||
| 165 | /* trigger next measurement read */ | ||
| 166 | result = | ||
| 167 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 168 | COMPASS_HSCDTD004A_CTRL3, 0x40); | ||
| 169 | if (result) { | ||
| 170 | LOG_RESULT_LOCATION(result); | ||
| 171 | return result; | ||
| 172 | } | ||
| 173 | return status; | ||
| 174 | |||
| 175 | } | ||
| 176 | |||
| 177 | static struct ext_slave_descr hscdtd004a_descr = { | ||
| 178 | .init = hscdtd004a_init, | ||
| 179 | .exit = NULL, | ||
| 180 | .suspend = hscdtd004a_suspend, | ||
| 181 | .resume = hscdtd004a_resume, | ||
| 182 | .read = hscdtd004a_read, | ||
| 183 | .config = NULL, | ||
| 184 | .get_config = NULL, | ||
| 185 | .name = "hscdtd004a", | ||
| 186 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 187 | .id = COMPASS_ID_HSCDTD004A, | ||
| 188 | .read_reg = 0x10, | ||
| 189 | .read_len = 6, | ||
| 190 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
| 191 | .range = {9830, 4000}, | ||
| 192 | .trigger = NULL, | ||
| 193 | }; | ||
| 194 | |||
| 195 | static | ||
| 196 | struct ext_slave_descr *hscdtd004a_get_slave_descr(void) | ||
| 197 | { | ||
| 198 | return &hscdtd004a_descr; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* -------------------------------------------------------------------------- */ | ||
| 202 | struct hscdtd004a_mod_private_data { | ||
| 203 | struct i2c_client *client; | ||
| 204 | struct ext_slave_platform_data *pdata; | ||
| 205 | }; | ||
| 206 | |||
| 207 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 208 | |||
| 209 | static int hscdtd004a_mod_probe(struct i2c_client *client, | ||
| 210 | const struct i2c_device_id *devid) | ||
| 211 | { | ||
| 212 | struct ext_slave_platform_data *pdata; | ||
| 213 | struct hscdtd004a_mod_private_data *private_data; | ||
| 214 | int result = 0; | ||
| 215 | |||
| 216 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 217 | |||
| 218 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 219 | result = -ENODEV; | ||
| 220 | goto out_no_free; | ||
| 221 | } | ||
| 222 | |||
| 223 | pdata = client->dev.platform_data; | ||
| 224 | if (!pdata) { | ||
| 225 | dev_err(&client->adapter->dev, | ||
| 226 | "Missing platform data for slave %s\n", devid->name); | ||
| 227 | result = -EFAULT; | ||
| 228 | goto out_no_free; | ||
| 229 | } | ||
| 230 | |||
| 231 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 232 | if (!private_data) { | ||
| 233 | result = -ENOMEM; | ||
| 234 | goto out_no_free; | ||
| 235 | } | ||
| 236 | |||
| 237 | i2c_set_clientdata(client, private_data); | ||
| 238 | private_data->client = client; | ||
| 239 | private_data->pdata = pdata; | ||
| 240 | |||
| 241 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 242 | hscdtd004a_get_slave_descr); | ||
| 243 | if (result) { | ||
| 244 | dev_err(&client->adapter->dev, | ||
| 245 | "Slave registration failed: %s, %d\n", | ||
| 246 | devid->name, result); | ||
| 247 | goto out_free_memory; | ||
| 248 | } | ||
| 249 | |||
| 250 | return result; | ||
| 251 | |||
| 252 | out_free_memory: | ||
| 253 | kfree(private_data); | ||
| 254 | out_no_free: | ||
| 255 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 256 | return result; | ||
| 257 | |||
| 258 | } | ||
| 259 | |||
| 260 | static int hscdtd004a_mod_remove(struct i2c_client *client) | ||
| 261 | { | ||
| 262 | struct hscdtd004a_mod_private_data *private_data = | ||
| 263 | i2c_get_clientdata(client); | ||
| 264 | |||
| 265 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 266 | |||
| 267 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 268 | hscdtd004a_get_slave_descr); | ||
| 269 | |||
| 270 | kfree(private_data); | ||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static const struct i2c_device_id hscdtd004a_mod_id[] = { | ||
| 275 | { "hscdtd004a", COMPASS_ID_HSCDTD004A }, | ||
| 276 | {} | ||
| 277 | }; | ||
| 278 | |||
| 279 | MODULE_DEVICE_TABLE(i2c, hscdtd004a_mod_id); | ||
| 280 | |||
| 281 | static struct i2c_driver hscdtd004a_mod_driver = { | ||
| 282 | .class = I2C_CLASS_HWMON, | ||
| 283 | .probe = hscdtd004a_mod_probe, | ||
| 284 | .remove = hscdtd004a_mod_remove, | ||
| 285 | .id_table = hscdtd004a_mod_id, | ||
| 286 | .driver = { | ||
| 287 | .owner = THIS_MODULE, | ||
| 288 | .name = "hscdtd004a_mod", | ||
| 289 | }, | ||
| 290 | .address_list = normal_i2c, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static int __init hscdtd004a_mod_init(void) | ||
| 294 | { | ||
| 295 | int res = i2c_add_driver(&hscdtd004a_mod_driver); | ||
| 296 | pr_info("%s: Probe name %s\n", __func__, "hscdtd004a_mod"); | ||
| 297 | if (res) | ||
| 298 | pr_err("%s failed\n", __func__); | ||
| 299 | return res; | ||
| 300 | } | ||
| 301 | |||
| 302 | static void __exit hscdtd004a_mod_exit(void) | ||
| 303 | { | ||
| 304 | pr_info("%s\n", __func__); | ||
| 305 | i2c_del_driver(&hscdtd004a_mod_driver); | ||
| 306 | } | ||
| 307 | |||
| 308 | module_init(hscdtd004a_mod_init); | ||
| 309 | module_exit(hscdtd004a_mod_exit); | ||
| 310 | |||
| 311 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 312 | MODULE_DESCRIPTION("Driver to integrate HSCDTD004A sensor with the MPU"); | ||
| 313 | MODULE_LICENSE("GPL"); | ||
| 314 | MODULE_ALIAS("hscdtd004a_mod"); | ||
| 315 | |||
| 316 | /** | ||
| 317 | * @} | ||
| 318 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/lsm303dlx_m.c b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c new file mode 100644 index 00000000000..32f8cdddb00 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file lsm303dlx_m.c | ||
| 25 | * @brief Magnetometer setup and handling methods for ST LSM303 | ||
| 26 | * compass. | ||
| 27 | * This magnetometer device is part of a combo chip with the | ||
| 28 | * ST LIS331DLH accelerometer and the logic in entirely based | ||
| 29 | * on the Honeywell HMC5883 magnetometer. | ||
| 30 | */ | ||
| 31 | |||
| 32 | /* -------------------------------------------------------------------------- */ | ||
| 33 | |||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/moduleparam.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/delay.h> | ||
| 41 | #include "mpu-dev.h" | ||
| 42 | |||
| 43 | #include <log.h> | ||
| 44 | #include <linux/mpu.h> | ||
| 45 | #include "mlsl.h" | ||
| 46 | #include "mldl_cfg.h" | ||
| 47 | #undef MPL_LOG_TAG | ||
| 48 | #define MPL_LOG_TAG "MPL-compass" | ||
| 49 | |||
| 50 | /* -------------------------------------------------------------------------- */ | ||
| 51 | enum LSM_REG { | ||
| 52 | LSM_REG_CONF_A = 0x0, | ||
| 53 | LSM_REG_CONF_B = 0x1, | ||
| 54 | LSM_REG_MODE = 0x2, | ||
| 55 | LSM_REG_X_M = 0x3, | ||
| 56 | LSM_REG_X_L = 0x4, | ||
| 57 | LSM_REG_Z_M = 0x5, | ||
| 58 | LSM_REG_Z_L = 0x6, | ||
| 59 | LSM_REG_Y_M = 0x7, | ||
| 60 | LSM_REG_Y_L = 0x8, | ||
| 61 | LSM_REG_STATUS = 0x9, | ||
| 62 | LSM_REG_ID_A = 0xA, | ||
| 63 | LSM_REG_ID_B = 0xB, | ||
| 64 | LSM_REG_ID_C = 0xC | ||
| 65 | }; | ||
| 66 | |||
| 67 | enum LSM_CONF_A { | ||
| 68 | LSM_CONF_A_DRATE_MASK = 0x1C, | ||
| 69 | LSM_CONF_A_DRATE_0_75 = 0x00, | ||
| 70 | LSM_CONF_A_DRATE_1_5 = 0x04, | ||
| 71 | LSM_CONF_A_DRATE_3 = 0x08, | ||
| 72 | LSM_CONF_A_DRATE_7_5 = 0x0C, | ||
| 73 | LSM_CONF_A_DRATE_15 = 0x10, | ||
| 74 | LSM_CONF_A_DRATE_30 = 0x14, | ||
| 75 | LSM_CONF_A_DRATE_75 = 0x18, | ||
| 76 | LSM_CONF_A_MEAS_MASK = 0x3, | ||
| 77 | LSM_CONF_A_MEAS_NORM = 0x0, | ||
| 78 | LSM_CONF_A_MEAS_POS = 0x1, | ||
| 79 | LSM_CONF_A_MEAS_NEG = 0x2 | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum LSM_CONF_B { | ||
| 83 | LSM_CONF_B_GAIN_MASK = 0xE0, | ||
| 84 | LSM_CONF_B_GAIN_0_9 = 0x00, | ||
| 85 | LSM_CONF_B_GAIN_1_2 = 0x20, | ||
| 86 | LSM_CONF_B_GAIN_1_9 = 0x40, | ||
| 87 | LSM_CONF_B_GAIN_2_5 = 0x60, | ||
| 88 | LSM_CONF_B_GAIN_4_0 = 0x80, | ||
| 89 | LSM_CONF_B_GAIN_4_6 = 0xA0, | ||
| 90 | LSM_CONF_B_GAIN_5_5 = 0xC0, | ||
| 91 | LSM_CONF_B_GAIN_7_9 = 0xE0 | ||
| 92 | }; | ||
| 93 | |||
| 94 | enum LSM_MODE { | ||
| 95 | LSM_MODE_MASK = 0x3, | ||
| 96 | LSM_MODE_CONT = 0x0, | ||
| 97 | LSM_MODE_SINGLE = 0x1, | ||
| 98 | LSM_MODE_IDLE = 0x2, | ||
| 99 | LSM_MODE_SLEEP = 0x3 | ||
| 100 | }; | ||
| 101 | |||
| 102 | /* -------------------------------------------------------------------------- */ | ||
| 103 | |||
| 104 | static int lsm303dlx_m_suspend(void *mlsl_handle, | ||
| 105 | struct ext_slave_descr *slave, | ||
| 106 | struct ext_slave_platform_data *pdata) | ||
| 107 | { | ||
| 108 | int result = INV_SUCCESS; | ||
| 109 | |||
| 110 | result = | ||
| 111 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 112 | LSM_REG_MODE, LSM_MODE_SLEEP); | ||
| 113 | if (result) { | ||
| 114 | LOG_RESULT_LOCATION(result); | ||
| 115 | return result; | ||
| 116 | } | ||
| 117 | msleep(3); | ||
| 118 | |||
| 119 | return result; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int lsm303dlx_m_resume(void *mlsl_handle, | ||
| 123 | struct ext_slave_descr *slave, | ||
| 124 | struct ext_slave_platform_data *pdata) | ||
| 125 | { | ||
| 126 | int result = INV_SUCCESS; | ||
| 127 | |||
| 128 | /* Use single measurement mode. Start at sleep state. */ | ||
| 129 | result = | ||
| 130 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 131 | LSM_REG_MODE, LSM_MODE_SLEEP); | ||
| 132 | if (result) { | ||
| 133 | LOG_RESULT_LOCATION(result); | ||
| 134 | return result; | ||
| 135 | } | ||
| 136 | /* Config normal measurement */ | ||
| 137 | result = | ||
| 138 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 139 | LSM_REG_CONF_A, 0); | ||
| 140 | if (result) { | ||
| 141 | LOG_RESULT_LOCATION(result); | ||
| 142 | return result; | ||
| 143 | } | ||
| 144 | /* Adjust gain to 320 LSB/Gauss */ | ||
| 145 | result = | ||
| 146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 147 | LSM_REG_CONF_B, LSM_CONF_B_GAIN_5_5); | ||
| 148 | if (result) { | ||
| 149 | LOG_RESULT_LOCATION(result); | ||
| 150 | return result; | ||
| 151 | } | ||
| 152 | |||
| 153 | return result; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int lsm303dlx_m_read(void *mlsl_handle, | ||
| 157 | struct ext_slave_descr *slave, | ||
| 158 | struct ext_slave_platform_data *pdata, | ||
| 159 | unsigned char *data) | ||
| 160 | { | ||
| 161 | unsigned char stat; | ||
| 162 | int result = INV_SUCCESS; | ||
| 163 | short axis_fixed; | ||
| 164 | |||
| 165 | /* Read status reg. to check if data is ready */ | ||
| 166 | result = | ||
| 167 | inv_serial_read(mlsl_handle, pdata->address, LSM_REG_STATUS, 1, | ||
| 168 | &stat); | ||
| 169 | if (result) { | ||
| 170 | LOG_RESULT_LOCATION(result); | ||
| 171 | return result; | ||
| 172 | } | ||
| 173 | if (stat & 0x01) { | ||
| 174 | result = | ||
| 175 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 176 | LSM_REG_X_M, 6, (unsigned char *)data); | ||
| 177 | if (result) { | ||
| 178 | LOG_RESULT_LOCATION(result); | ||
| 179 | return result; | ||
| 180 | } | ||
| 181 | |||
| 182 | /*drop data if overflows */ | ||
| 183 | if ((data[0] == 0xf0) || (data[2] == 0xf0) | ||
| 184 | || (data[4] == 0xf0)) { | ||
| 185 | /* trigger next measurement read */ | ||
| 186 | result = | ||
| 187 | inv_serial_single_write(mlsl_handle, | ||
| 188 | pdata->address, | ||
| 189 | LSM_REG_MODE, | ||
| 190 | LSM_MODE_SINGLE); | ||
| 191 | if (result) { | ||
| 192 | LOG_RESULT_LOCATION(result); | ||
| 193 | return result; | ||
| 194 | } | ||
| 195 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 196 | } | ||
| 197 | /* convert to fixed point and apply sensitivity correction for | ||
| 198 | Z-axis */ | ||
| 199 | axis_fixed = | ||
| 200 | (short)((unsigned short)data[5] + | ||
| 201 | (unsigned short)data[4] * 256); | ||
| 202 | /* scale up by 1.125 (36/32) approximate of 1.122 (320/285) */ | ||
| 203 | if (slave->id == COMPASS_ID_LSM303DLM) { | ||
| 204 | /* NOTE/IMPORTANT: | ||
| 205 | lsm303dlm compass axis definition doesn't | ||
| 206 | respect the right hand rule. We invert | ||
| 207 | the sign of the Z axis to fix that. */ | ||
| 208 | axis_fixed = (short)(-1 * axis_fixed * 36); | ||
| 209 | } else { | ||
| 210 | axis_fixed = (short)(axis_fixed * 36); | ||
| 211 | } | ||
| 212 | data[4] = axis_fixed >> 8; | ||
| 213 | data[5] = axis_fixed & 0xFF; | ||
| 214 | |||
| 215 | axis_fixed = | ||
| 216 | (short)((unsigned short)data[3] + | ||
| 217 | (unsigned short)data[2] * 256); | ||
| 218 | axis_fixed = (short)(axis_fixed * 32); | ||
| 219 | data[2] = axis_fixed >> 8; | ||
| 220 | data[3] = axis_fixed & 0xFF; | ||
| 221 | |||
| 222 | axis_fixed = | ||
| 223 | (short)((unsigned short)data[1] + | ||
| 224 | (unsigned short)data[0] * 256); | ||
| 225 | axis_fixed = (short)(axis_fixed * 32); | ||
| 226 | data[0] = axis_fixed >> 8; | ||
| 227 | data[1] = axis_fixed & 0xFF; | ||
| 228 | |||
| 229 | /* trigger next measurement read */ | ||
| 230 | result = | ||
| 231 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 232 | LSM_REG_MODE, LSM_MODE_SINGLE); | ||
| 233 | if (result) { | ||
| 234 | LOG_RESULT_LOCATION(result); | ||
| 235 | return result; | ||
| 236 | } | ||
| 237 | |||
| 238 | return INV_SUCCESS; | ||
| 239 | } else { | ||
| 240 | /* trigger next measurement read */ | ||
| 241 | result = | ||
| 242 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 243 | LSM_REG_MODE, LSM_MODE_SINGLE); | ||
| 244 | if (result) { | ||
| 245 | LOG_RESULT_LOCATION(result); | ||
| 246 | return result; | ||
| 247 | } | ||
| 248 | |||
| 249 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | static struct ext_slave_descr lsm303dlx_m_descr = { | ||
| 254 | .init = NULL, | ||
| 255 | .exit = NULL, | ||
| 256 | .suspend = lsm303dlx_m_suspend, | ||
| 257 | .resume = lsm303dlx_m_resume, | ||
| 258 | .read = lsm303dlx_m_read, | ||
| 259 | .config = NULL, | ||
| 260 | .get_config = NULL, | ||
| 261 | .name = "lsm303dlx_m", | ||
| 262 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 263 | .id = ID_INVALID, | ||
| 264 | .read_reg = 0x06, | ||
| 265 | .read_len = 6, | ||
| 266 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 267 | .range = {10240, 0}, | ||
| 268 | .trigger = NULL, | ||
| 269 | }; | ||
| 270 | |||
| 271 | static | ||
| 272 | struct ext_slave_descr *lsm303dlx_m_get_slave_descr(void) | ||
| 273 | { | ||
| 274 | return &lsm303dlx_m_descr; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* -------------------------------------------------------------------------- */ | ||
| 278 | struct lsm303dlx_m_mod_private_data { | ||
| 279 | struct i2c_client *client; | ||
| 280 | struct ext_slave_platform_data *pdata; | ||
| 281 | }; | ||
| 282 | |||
| 283 | static const struct i2c_device_id lsm303dlx_m_mod_id[] = { | ||
| 284 | { "lsm303dlh", COMPASS_ID_LSM303DLH }, | ||
| 285 | { "lsm303dlm", COMPASS_ID_LSM303DLM }, | ||
| 286 | {} | ||
| 287 | }; | ||
| 288 | MODULE_DEVICE_TABLE(i2c, lsm303dlx_m_mod_id); | ||
| 289 | |||
| 290 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 291 | |||
| 292 | static int lsm303dlx_m_mod_probe(struct i2c_client *client, | ||
| 293 | const struct i2c_device_id *devid) | ||
| 294 | { | ||
| 295 | struct ext_slave_platform_data *pdata; | ||
| 296 | struct lsm303dlx_m_mod_private_data *private_data; | ||
| 297 | int result = 0; | ||
| 298 | |||
| 299 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 300 | lsm303dlx_m_descr.id = devid->driver_data; | ||
| 301 | |||
| 302 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 303 | result = -ENODEV; | ||
| 304 | goto out_no_free; | ||
| 305 | } | ||
| 306 | |||
| 307 | pdata = client->dev.platform_data; | ||
| 308 | if (!pdata) { | ||
| 309 | dev_err(&client->adapter->dev, | ||
| 310 | "Missing platform data for slave %s\n", devid->name); | ||
| 311 | result = -EFAULT; | ||
| 312 | goto out_no_free; | ||
| 313 | } | ||
| 314 | |||
| 315 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 316 | if (!private_data) { | ||
| 317 | result = -ENOMEM; | ||
| 318 | goto out_no_free; | ||
| 319 | } | ||
| 320 | |||
| 321 | i2c_set_clientdata(client, private_data); | ||
| 322 | private_data->client = client; | ||
| 323 | private_data->pdata = pdata; | ||
| 324 | |||
| 325 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 326 | lsm303dlx_m_get_slave_descr); | ||
| 327 | if (result) { | ||
| 328 | dev_err(&client->adapter->dev, | ||
| 329 | "Slave registration failed: %s, %d\n", | ||
| 330 | devid->name, result); | ||
| 331 | goto out_free_memory; | ||
| 332 | } | ||
| 333 | |||
| 334 | return result; | ||
| 335 | |||
| 336 | out_free_memory: | ||
| 337 | kfree(private_data); | ||
| 338 | out_no_free: | ||
| 339 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 340 | return result; | ||
| 341 | |||
| 342 | } | ||
| 343 | |||
| 344 | static int lsm303dlx_m_mod_remove(struct i2c_client *client) | ||
| 345 | { | ||
| 346 | struct lsm303dlx_m_mod_private_data *private_data = | ||
| 347 | i2c_get_clientdata(client); | ||
| 348 | |||
| 349 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 350 | |||
| 351 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 352 | lsm303dlx_m_get_slave_descr); | ||
| 353 | |||
| 354 | kfree(private_data); | ||
| 355 | return 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | static struct i2c_driver lsm303dlx_m_mod_driver = { | ||
| 359 | .class = I2C_CLASS_HWMON, | ||
| 360 | .probe = lsm303dlx_m_mod_probe, | ||
| 361 | .remove = lsm303dlx_m_mod_remove, | ||
| 362 | .id_table = lsm303dlx_m_mod_id, | ||
| 363 | .driver = { | ||
| 364 | .owner = THIS_MODULE, | ||
| 365 | .name = "lsm303dlx_m_mod", | ||
| 366 | }, | ||
| 367 | .address_list = normal_i2c, | ||
| 368 | }; | ||
| 369 | |||
| 370 | static int __init lsm303dlx_m_mod_init(void) | ||
| 371 | { | ||
| 372 | int res = i2c_add_driver(&lsm303dlx_m_mod_driver); | ||
| 373 | pr_info("%s: Probe name %s\n", __func__, "lsm303dlx_m_mod"); | ||
| 374 | if (res) | ||
| 375 | pr_err("%s failed\n", __func__); | ||
| 376 | return res; | ||
| 377 | } | ||
| 378 | |||
| 379 | static void __exit lsm303dlx_m_mod_exit(void) | ||
| 380 | { | ||
| 381 | pr_info("%s\n", __func__); | ||
| 382 | i2c_del_driver(&lsm303dlx_m_mod_driver); | ||
| 383 | } | ||
| 384 | |||
| 385 | module_init(lsm303dlx_m_mod_init); | ||
| 386 | module_exit(lsm303dlx_m_mod_exit); | ||
| 387 | |||
| 388 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 389 | MODULE_DESCRIPTION("Driver to integrate lsm303dlx_m sensor with the MPU"); | ||
| 390 | MODULE_LICENSE("GPL"); | ||
| 391 | MODULE_ALIAS("lsm303dlx_m_mod"); | ||
| 392 | |||
| 393 | /** | ||
| 394 | * @} | ||
| 395 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/mmc314x.c b/drivers/misc/inv_mpu/compass/mmc314x.c new file mode 100644 index 00000000000..786fadcc3e4 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/mmc314x.c | |||
| @@ -0,0 +1,313 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mmc314x.c | ||
| 25 | * @brief Magnetometer setup and handling methods for the | ||
| 26 | * MEMSIC MMC314x compass. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include "mpu-dev.h" | ||
| 39 | |||
| 40 | #include <log.h> | ||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mlsl.h" | ||
| 43 | #include "mldl_cfg.h" | ||
| 44 | #undef MPL_LOG_TAG | ||
| 45 | #define MPL_LOG_TAG "MPL-compass" | ||
| 46 | |||
| 47 | /* -------------------------------------------------------------------------- */ | ||
| 48 | |||
| 49 | static int reset_int = 1000; | ||
| 50 | static int read_count = 1; | ||
| 51 | static char reset_mode; /* in Z-init section */ | ||
| 52 | |||
| 53 | /* -------------------------------------------------------------------------- */ | ||
| 54 | #define MMC314X_REG_ST (0x00) | ||
| 55 | #define MMC314X_REG_X_MSB (0x01) | ||
| 56 | |||
| 57 | #define MMC314X_CNTL_MODE_WAKE_UP (0x01) | ||
| 58 | #define MMC314X_CNTL_MODE_SET (0x02) | ||
| 59 | #define MMC314X_CNTL_MODE_RESET (0x04) | ||
| 60 | |||
| 61 | /* -------------------------------------------------------------------------- */ | ||
| 62 | |||
| 63 | static int mmc314x_suspend(void *mlsl_handle, | ||
| 64 | struct ext_slave_descr *slave, | ||
| 65 | struct ext_slave_platform_data *pdata) | ||
| 66 | { | ||
| 67 | int result = INV_SUCCESS; | ||
| 68 | |||
| 69 | return result; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int mmc314x_resume(void *mlsl_handle, | ||
| 73 | struct ext_slave_descr *slave, | ||
| 74 | struct ext_slave_platform_data *pdata) | ||
| 75 | { | ||
| 76 | |||
| 77 | int result; | ||
| 78 | result = | ||
| 79 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 80 | MMC314X_REG_ST, MMC314X_CNTL_MODE_RESET); | ||
| 81 | if (result) { | ||
| 82 | LOG_RESULT_LOCATION(result); | ||
| 83 | return result; | ||
| 84 | } | ||
| 85 | msleep(10); | ||
| 86 | result = | ||
| 87 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 88 | MMC314X_REG_ST, MMC314X_CNTL_MODE_SET); | ||
| 89 | if (result) { | ||
| 90 | LOG_RESULT_LOCATION(result); | ||
| 91 | return result; | ||
| 92 | } | ||
| 93 | msleep(10); | ||
| 94 | read_count = 1; | ||
| 95 | return INV_SUCCESS; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int mmc314x_read(void *mlsl_handle, | ||
| 99 | struct ext_slave_descr *slave, | ||
| 100 | struct ext_slave_platform_data *pdata, | ||
| 101 | unsigned char *data) | ||
| 102 | { | ||
| 103 | int result, ii; | ||
| 104 | short tmp[3]; | ||
| 105 | unsigned char tmpdata[6]; | ||
| 106 | |||
| 107 | if (read_count > 1000) | ||
| 108 | read_count = 1; | ||
| 109 | |||
| 110 | result = | ||
| 111 | inv_serial_read(mlsl_handle, pdata->address, MMC314X_REG_X_MSB, | ||
| 112 | 6, (unsigned char *)data); | ||
| 113 | if (result) { | ||
| 114 | LOG_RESULT_LOCATION(result); | ||
| 115 | return result; | ||
| 116 | } | ||
| 117 | |||
| 118 | for (ii = 0; ii < 6; ii++) | ||
| 119 | tmpdata[ii] = data[ii]; | ||
| 120 | |||
| 121 | for (ii = 0; ii < 3; ii++) { | ||
| 122 | tmp[ii] = (short)((tmpdata[2 * ii] << 8) + tmpdata[2 * ii + 1]); | ||
| 123 | tmp[ii] = tmp[ii] - 4096; | ||
| 124 | tmp[ii] = tmp[ii] * 16; | ||
| 125 | } | ||
| 126 | |||
| 127 | for (ii = 0; ii < 3; ii++) { | ||
| 128 | data[2 * ii] = (unsigned char)(tmp[ii] >> 8); | ||
| 129 | data[2 * ii + 1] = (unsigned char)(tmp[ii]); | ||
| 130 | } | ||
| 131 | |||
| 132 | if (read_count % reset_int == 0) { | ||
| 133 | if (reset_mode) { | ||
| 134 | result = | ||
| 135 | inv_serial_single_write(mlsl_handle, | ||
| 136 | pdata->address, | ||
| 137 | MMC314X_REG_ST, | ||
| 138 | MMC314X_CNTL_MODE_RESET); | ||
| 139 | if (result) { | ||
| 140 | LOG_RESULT_LOCATION(result); | ||
| 141 | return result; | ||
| 142 | } | ||
| 143 | reset_mode = 0; | ||
| 144 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 145 | } else { | ||
| 146 | result = | ||
| 147 | inv_serial_single_write(mlsl_handle, | ||
| 148 | pdata->address, | ||
| 149 | MMC314X_REG_ST, | ||
| 150 | MMC314X_CNTL_MODE_SET); | ||
| 151 | if (result) { | ||
| 152 | LOG_RESULT_LOCATION(result); | ||
| 153 | return result; | ||
| 154 | } | ||
| 155 | reset_mode = 1; | ||
| 156 | read_count++; | ||
| 157 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | result = | ||
| 161 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 162 | MMC314X_REG_ST, MMC314X_CNTL_MODE_WAKE_UP); | ||
| 163 | if (result) { | ||
| 164 | LOG_RESULT_LOCATION(result); | ||
| 165 | return result; | ||
| 166 | } | ||
| 167 | read_count++; | ||
| 168 | |||
| 169 | return INV_SUCCESS; | ||
| 170 | } | ||
| 171 | |||
| 172 | static struct ext_slave_descr mmc314x_descr = { | ||
| 173 | .init = NULL, | ||
| 174 | .exit = NULL, | ||
| 175 | .suspend = mmc314x_suspend, | ||
| 176 | .resume = mmc314x_resume, | ||
| 177 | .read = mmc314x_read, | ||
| 178 | .config = NULL, | ||
| 179 | .get_config = NULL, | ||
| 180 | .name = "mmc314x", | ||
| 181 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 182 | .id = COMPASS_ID_MMC314X, | ||
| 183 | .read_reg = 0x01, | ||
| 184 | .read_len = 6, | ||
| 185 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 186 | .range = {400, 0}, | ||
| 187 | .trigger = NULL, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static | ||
| 191 | struct ext_slave_descr *mmc314x_get_slave_descr(void) | ||
| 192 | { | ||
| 193 | return &mmc314x_descr; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* -------------------------------------------------------------------------- */ | ||
| 197 | struct mmc314x_mod_private_data { | ||
| 198 | struct i2c_client *client; | ||
| 199 | struct ext_slave_platform_data *pdata; | ||
| 200 | }; | ||
| 201 | |||
| 202 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 203 | |||
| 204 | static int mmc314x_mod_probe(struct i2c_client *client, | ||
| 205 | const struct i2c_device_id *devid) | ||
| 206 | { | ||
| 207 | struct ext_slave_platform_data *pdata; | ||
| 208 | struct mmc314x_mod_private_data *private_data; | ||
| 209 | int result = 0; | ||
| 210 | |||
| 211 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 212 | |||
| 213 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 214 | result = -ENODEV; | ||
| 215 | goto out_no_free; | ||
| 216 | } | ||
| 217 | |||
| 218 | pdata = client->dev.platform_data; | ||
| 219 | if (!pdata) { | ||
| 220 | dev_err(&client->adapter->dev, | ||
| 221 | "Missing platform data for slave %s\n", devid->name); | ||
| 222 | result = -EFAULT; | ||
| 223 | goto out_no_free; | ||
| 224 | } | ||
| 225 | |||
| 226 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 227 | if (!private_data) { | ||
| 228 | result = -ENOMEM; | ||
| 229 | goto out_no_free; | ||
| 230 | } | ||
| 231 | |||
| 232 | i2c_set_clientdata(client, private_data); | ||
| 233 | private_data->client = client; | ||
| 234 | private_data->pdata = pdata; | ||
| 235 | |||
| 236 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 237 | mmc314x_get_slave_descr); | ||
| 238 | if (result) { | ||
| 239 | dev_err(&client->adapter->dev, | ||
| 240 | "Slave registration failed: %s, %d\n", | ||
| 241 | devid->name, result); | ||
| 242 | goto out_free_memory; | ||
| 243 | } | ||
| 244 | |||
| 245 | return result; | ||
| 246 | |||
| 247 | out_free_memory: | ||
| 248 | kfree(private_data); | ||
| 249 | out_no_free: | ||
| 250 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 251 | return result; | ||
| 252 | |||
| 253 | } | ||
| 254 | |||
| 255 | static int mmc314x_mod_remove(struct i2c_client *client) | ||
| 256 | { | ||
| 257 | struct mmc314x_mod_private_data *private_data = | ||
| 258 | i2c_get_clientdata(client); | ||
| 259 | |||
| 260 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 261 | |||
| 262 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 263 | mmc314x_get_slave_descr); | ||
| 264 | |||
| 265 | kfree(private_data); | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | static const struct i2c_device_id mmc314x_mod_id[] = { | ||
| 270 | { "mmc314x", COMPASS_ID_MMC314X }, | ||
| 271 | {} | ||
| 272 | }; | ||
| 273 | |||
| 274 | MODULE_DEVICE_TABLE(i2c, mmc314x_mod_id); | ||
| 275 | |||
| 276 | static struct i2c_driver mmc314x_mod_driver = { | ||
| 277 | .class = I2C_CLASS_HWMON, | ||
| 278 | .probe = mmc314x_mod_probe, | ||
| 279 | .remove = mmc314x_mod_remove, | ||
| 280 | .id_table = mmc314x_mod_id, | ||
| 281 | .driver = { | ||
| 282 | .owner = THIS_MODULE, | ||
| 283 | .name = "mmc314x_mod", | ||
| 284 | }, | ||
| 285 | .address_list = normal_i2c, | ||
| 286 | }; | ||
| 287 | |||
| 288 | static int __init mmc314x_mod_init(void) | ||
| 289 | { | ||
| 290 | int res = i2c_add_driver(&mmc314x_mod_driver); | ||
| 291 | pr_info("%s: Probe name %s\n", __func__, "mmc314x_mod"); | ||
| 292 | if (res) | ||
| 293 | pr_err("%s failed\n", __func__); | ||
| 294 | return res; | ||
| 295 | } | ||
| 296 | |||
| 297 | static void __exit mmc314x_mod_exit(void) | ||
| 298 | { | ||
| 299 | pr_info("%s\n", __func__); | ||
| 300 | i2c_del_driver(&mmc314x_mod_driver); | ||
| 301 | } | ||
| 302 | |||
| 303 | module_init(mmc314x_mod_init); | ||
| 304 | module_exit(mmc314x_mod_exit); | ||
| 305 | |||
| 306 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 307 | MODULE_DESCRIPTION("Driver to integrate MMC314X sensor with the MPU"); | ||
| 308 | MODULE_LICENSE("GPL"); | ||
| 309 | MODULE_ALIAS("mmc314x_mod"); | ||
| 310 | |||
| 311 | /** | ||
| 312 | * @} | ||
| 313 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/yas529-kernel.c b/drivers/misc/inv_mpu/compass/yas529-kernel.c new file mode 100644 index 00000000000..f53223fba64 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/yas529-kernel.c | |||
| @@ -0,0 +1,611 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* -------------------------------------------------------------------------- */ | ||
| 21 | |||
| 22 | #include <linux/i2c.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/errno.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include "mpu-dev.h" | ||
| 30 | |||
| 31 | #include <log.h> | ||
| 32 | #include <linux/mpu.h> | ||
| 33 | #include "mlsl.h" | ||
| 34 | #include "mldl_cfg.h" | ||
| 35 | #undef MPL_LOG_TAG | ||
| 36 | #define MPL_LOG_TAG "MPL-acc" | ||
| 37 | |||
| 38 | /*----- YAMAHA YAS529 Registers ------*/ | ||
| 39 | enum YAS_REG { | ||
| 40 | YAS_REG_CMDR = 0x00, /* 000 < 5 */ | ||
| 41 | YAS_REG_XOFFSETR = 0x20, /* 001 < 5 */ | ||
| 42 | YAS_REG_Y1OFFSETR = 0x40, /* 010 < 5 */ | ||
| 43 | YAS_REG_Y2OFFSETR = 0x60, /* 011 < 5 */ | ||
| 44 | YAS_REG_ICOILR = 0x80, /* 100 < 5 */ | ||
| 45 | YAS_REG_CAL = 0xA0, /* 101 < 5 */ | ||
| 46 | YAS_REG_CONFR = 0xC0, /* 110 < 5 */ | ||
| 47 | YAS_REG_DOUTR = 0xE0 /* 111 < 5 */ | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* -------------------------------------------------------------------------- */ | ||
| 51 | |||
| 52 | static long a1; | ||
| 53 | static long a2; | ||
| 54 | static long a3; | ||
| 55 | static long a4; | ||
| 56 | static long a5; | ||
| 57 | static long a6; | ||
| 58 | static long a7; | ||
| 59 | static long a8; | ||
| 60 | static long a9; | ||
| 61 | |||
| 62 | /* -------------------------------------------------------------------------- */ | ||
| 63 | static int yas529_sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
| 64 | unsigned char address, | ||
| 65 | unsigned int len, unsigned char *data) | ||
| 66 | { | ||
| 67 | struct i2c_msg msgs[1]; | ||
| 68 | int res; | ||
| 69 | |||
| 70 | if (NULL == data || NULL == i2c_adap) | ||
| 71 | return -EINVAL; | ||
| 72 | |||
| 73 | msgs[0].addr = address; | ||
| 74 | msgs[0].flags = 0; /* write */ | ||
| 75 | msgs[0].buf = (unsigned char *)data; | ||
| 76 | msgs[0].len = len; | ||
| 77 | |||
| 78 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
| 79 | if (res < 1) | ||
| 80 | return res; | ||
| 81 | else | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | static int yas529_sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
| 86 | unsigned char address, | ||
| 87 | unsigned char reg, | ||
| 88 | unsigned int len, unsigned char *data) | ||
| 89 | { | ||
| 90 | struct i2c_msg msgs[2]; | ||
| 91 | int res; | ||
| 92 | |||
| 93 | if (NULL == data || NULL == i2c_adap) | ||
| 94 | return -EINVAL; | ||
| 95 | |||
| 96 | msgs[0].addr = address; | ||
| 97 | msgs[0].flags = I2C_M_RD; | ||
| 98 | msgs[0].buf = data; | ||
| 99 | msgs[0].len = len; | ||
| 100 | |||
| 101 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
| 102 | if (res < 1) | ||
| 103 | return res; | ||
| 104 | else | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | static int yas529_suspend(void *mlsl_handle, | ||
| 109 | struct ext_slave_descr *slave, | ||
| 110 | struct ext_slave_platform_data *pdata) | ||
| 111 | { | ||
| 112 | int result = INV_SUCCESS; | ||
| 113 | |||
| 114 | return result; | ||
| 115 | } | ||
| 116 | |||
| 117 | static int yas529_resume(void *mlsl_handle, | ||
| 118 | struct ext_slave_descr *slave, | ||
| 119 | struct ext_slave_platform_data *pdata) | ||
| 120 | { | ||
| 121 | int result = INV_SUCCESS; | ||
| 122 | |||
| 123 | unsigned char dummyData[1] = { 0 }; | ||
| 124 | unsigned char dummyRegister = 0; | ||
| 125 | unsigned char rawData[6]; | ||
| 126 | unsigned char calData[9]; | ||
| 127 | |||
| 128 | short xoffset, y1offset, y2offset; | ||
| 129 | short d2, d3, d4, d5, d6, d7, d8, d9; | ||
| 130 | |||
| 131 | /* YAS529 Application Manual MS-3C - Section 4.4.5 */ | ||
| 132 | /* =============================================== */ | ||
| 133 | /* Step 1 - register initialization */ | ||
| 134 | /* zero initialization coil register - "100 00 000" */ | ||
| 135 | dummyData[0] = YAS_REG_ICOILR | 0x00; | ||
| 136 | result = | ||
| 137 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 138 | if (result) { | ||
| 139 | LOG_RESULT_LOCATION(result); | ||
| 140 | return result; | ||
| 141 | } | ||
| 142 | /* zero config register - "110 00 000" */ | ||
| 143 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
| 144 | result = | ||
| 145 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 146 | if (result) { | ||
| 147 | LOG_RESULT_LOCATION(result); | ||
| 148 | return result; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Step 2 - initialization coil operation */ | ||
| 152 | dummyData[0] = YAS_REG_ICOILR | 0x11; | ||
| 153 | result = | ||
| 154 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 155 | if (result) { | ||
| 156 | LOG_RESULT_LOCATION(result); | ||
| 157 | return result; | ||
| 158 | } | ||
| 159 | dummyData[0] = YAS_REG_ICOILR | 0x01; | ||
| 160 | result = | ||
| 161 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 162 | if (result) { | ||
| 163 | LOG_RESULT_LOCATION(result); | ||
| 164 | return result; | ||
| 165 | } | ||
| 166 | dummyData[0] = YAS_REG_ICOILR | 0x12; | ||
| 167 | result = | ||
| 168 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 169 | if (result) { | ||
| 170 | LOG_RESULT_LOCATION(result); | ||
| 171 | return result; | ||
| 172 | } | ||
| 173 | dummyData[0] = YAS_REG_ICOILR | 0x02; | ||
| 174 | result = | ||
| 175 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 176 | if (result) { | ||
| 177 | LOG_RESULT_LOCATION(result); | ||
| 178 | return result; | ||
| 179 | } | ||
| 180 | dummyData[0] = YAS_REG_ICOILR | 0x13; | ||
| 181 | result = | ||
| 182 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 183 | if (result) { | ||
| 184 | LOG_RESULT_LOCATION(result); | ||
| 185 | return result; | ||
| 186 | } | ||
| 187 | dummyData[0] = YAS_REG_ICOILR | 0x03; | ||
| 188 | result = | ||
| 189 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 190 | if (result) { | ||
| 191 | LOG_RESULT_LOCATION(result); | ||
| 192 | return result; | ||
| 193 | } | ||
| 194 | dummyData[0] = YAS_REG_ICOILR | 0x14; | ||
| 195 | result = | ||
| 196 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 197 | if (result) { | ||
| 198 | LOG_RESULT_LOCATION(result); | ||
| 199 | return result; | ||
| 200 | } | ||
| 201 | dummyData[0] = YAS_REG_ICOILR | 0x04; | ||
| 202 | result = | ||
| 203 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 204 | if (result) { | ||
| 205 | LOG_RESULT_LOCATION(result); | ||
| 206 | return result; | ||
| 207 | } | ||
| 208 | dummyData[0] = YAS_REG_ICOILR | 0x15; | ||
| 209 | result = | ||
| 210 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 211 | if (result) { | ||
| 212 | LOG_RESULT_LOCATION(result); | ||
| 213 | return result; | ||
| 214 | } | ||
| 215 | dummyData[0] = YAS_REG_ICOILR | 0x05; | ||
| 216 | result = | ||
| 217 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 218 | if (result) { | ||
| 219 | LOG_RESULT_LOCATION(result); | ||
| 220 | return result; | ||
| 221 | } | ||
| 222 | dummyData[0] = YAS_REG_ICOILR | 0x16; | ||
| 223 | result = | ||
| 224 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 225 | if (result) { | ||
| 226 | LOG_RESULT_LOCATION(result); | ||
| 227 | return result; | ||
| 228 | } | ||
| 229 | dummyData[0] = YAS_REG_ICOILR | 0x06; | ||
| 230 | result = | ||
| 231 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 232 | if (result) { | ||
| 233 | LOG_RESULT_LOCATION(result); | ||
| 234 | return result; | ||
| 235 | } | ||
| 236 | dummyData[0] = YAS_REG_ICOILR | 0x17; | ||
| 237 | result = | ||
| 238 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 239 | if (result) { | ||
| 240 | LOG_RESULT_LOCATION(result); | ||
| 241 | return result; | ||
| 242 | } | ||
| 243 | dummyData[0] = YAS_REG_ICOILR | 0x07; | ||
| 244 | result = | ||
| 245 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 246 | if (result) { | ||
| 247 | LOG_RESULT_LOCATION(result); | ||
| 248 | return result; | ||
| 249 | } | ||
| 250 | dummyData[0] = YAS_REG_ICOILR | 0x10; | ||
| 251 | result = | ||
| 252 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 253 | if (result) { | ||
| 254 | LOG_RESULT_LOCATION(result); | ||
| 255 | return result; | ||
| 256 | } | ||
| 257 | dummyData[0] = YAS_REG_ICOILR | 0x00; | ||
| 258 | result = | ||
| 259 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 260 | if (result) { | ||
| 261 | LOG_RESULT_LOCATION(result); | ||
| 262 | return result; | ||
| 263 | } | ||
| 264 | |||
| 265 | /* Step 3 - rough offset measurement */ | ||
| 266 | /* Config register - Measurements results - "110 00 000" */ | ||
| 267 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
| 268 | result = | ||
| 269 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 270 | if (result) { | ||
| 271 | LOG_RESULT_LOCATION(result); | ||
| 272 | return result; | ||
| 273 | } | ||
| 274 | /* Measurements command register - Rough offset measurement - | ||
| 275 | "000 00001" */ | ||
| 276 | dummyData[0] = YAS_REG_CMDR | 0x01; | ||
| 277 | result = | ||
| 278 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 279 | if (result) { | ||
| 280 | LOG_RESULT_LOCATION(result); | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | msleep(2); /* wait at least 1.5ms */ | ||
| 284 | |||
| 285 | /* Measurement data read */ | ||
| 286 | result = | ||
| 287 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
| 288 | dummyRegister, 6, rawData); | ||
| 289 | if (result) { | ||
| 290 | LOG_RESULT_LOCATION(result); | ||
| 291 | return result; | ||
| 292 | } | ||
| 293 | xoffset = | ||
| 294 | (short)((unsigned short)rawData[5] + | ||
| 295 | ((unsigned short)rawData[4] & 0x7) * 256) - 5; | ||
| 296 | if (xoffset < 0) | ||
| 297 | xoffset = 0; | ||
| 298 | y1offset = | ||
| 299 | (short)((unsigned short)rawData[3] + | ||
| 300 | ((unsigned short)rawData[2] & 0x7) * 256) - 5; | ||
| 301 | if (y1offset < 0) | ||
| 302 | y1offset = 0; | ||
| 303 | y2offset = | ||
| 304 | (short)((unsigned short)rawData[1] + | ||
| 305 | ((unsigned short)rawData[0] & 0x7) * 256) - 5; | ||
| 306 | if (y2offset < 0) | ||
| 307 | y2offset = 0; | ||
| 308 | |||
| 309 | /* Step 4 - rough offset setting */ | ||
| 310 | /* Set rough offset register values */ | ||
| 311 | dummyData[0] = YAS_REG_XOFFSETR | xoffset; | ||
| 312 | result = | ||
| 313 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 314 | if (result) { | ||
| 315 | LOG_RESULT_LOCATION(result); | ||
| 316 | return result; | ||
| 317 | } | ||
| 318 | dummyData[0] = YAS_REG_Y1OFFSETR | y1offset; | ||
| 319 | result = | ||
| 320 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 321 | if (result) { | ||
| 322 | LOG_RESULT_LOCATION(result); | ||
| 323 | return result; | ||
| 324 | } | ||
| 325 | dummyData[0] = YAS_REG_Y2OFFSETR | y2offset; | ||
| 326 | result = | ||
| 327 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 328 | if (result) { | ||
| 329 | LOG_RESULT_LOCATION(result); | ||
| 330 | return result; | ||
| 331 | } | ||
| 332 | |||
| 333 | /* CAL matrix read (first read is invalid) */ | ||
| 334 | /* Config register - CAL register read - "110 01 000" */ | ||
| 335 | dummyData[0] = YAS_REG_CONFR | 0x08; | ||
| 336 | result = | ||
| 337 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 338 | if (result) { | ||
| 339 | LOG_RESULT_LOCATION(result); | ||
| 340 | return result; | ||
| 341 | } | ||
| 342 | /* CAL data read */ | ||
| 343 | result = | ||
| 344 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
| 345 | dummyRegister, 9, calData); | ||
| 346 | if (result) { | ||
| 347 | LOG_RESULT_LOCATION(result); | ||
| 348 | return result; | ||
| 349 | } | ||
| 350 | /* Config register - CAL register read - "110 01 000" */ | ||
| 351 | dummyData[0] = YAS_REG_CONFR | 0x08; | ||
| 352 | result = | ||
| 353 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 354 | if (result) { | ||
| 355 | LOG_RESULT_LOCATION(result); | ||
| 356 | return result; | ||
| 357 | } | ||
| 358 | /* CAL data read */ | ||
| 359 | result = | ||
| 360 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
| 361 | dummyRegister, 9, calData); | ||
| 362 | if (result) { | ||
| 363 | LOG_RESULT_LOCATION(result); | ||
| 364 | return result; | ||
| 365 | } | ||
| 366 | |||
| 367 | /* Calculate coefficients of the sensitivity correction matrix */ | ||
| 368 | a1 = 100; | ||
| 369 | d2 = (calData[0] & 0xFC) >> 2; /* [71..66] 6bit */ | ||
| 370 | a2 = (short)(d2 - 32); | ||
| 371 | /* [65..62] 4bit */ | ||
| 372 | d3 = ((calData[0] & 0x03) << 2) | ((calData[1] & 0xC0) >> 6); | ||
| 373 | a3 = (short)(d3 - 8); | ||
| 374 | d4 = (calData[1] & 0x3F); /* [61..56] 6bit */ | ||
| 375 | a4 = (short)(d4 - 32); | ||
| 376 | d5 = (calData[2] & 0xFC) >> 2; /* [55..50] 6bit */ | ||
| 377 | a5 = (short)(d5 - 32) + 70; | ||
| 378 | /* [49..44] 6bit */ | ||
| 379 | d6 = ((calData[2] & 0x03) << 4) | ((calData[3] & 0xF0) >> 4); | ||
| 380 | a6 = (short)(d6 - 32); | ||
| 381 | /* [43..38] 6bit */ | ||
| 382 | d7 = ((calData[3] & 0x0F) << 2) | ((calData[4] & 0xC0) >> 6); | ||
| 383 | a7 = (short)(d7 - 32); | ||
| 384 | d8 = (calData[4] & 0x3F); /* [37..32] 6bit */ | ||
| 385 | a8 = (short)(d8 - 32); | ||
| 386 | d9 = (calData[5] & 0xFE) >> 1; /* [31..25] 7bit */ | ||
| 387 | a9 = (short)(d9 - 64) + 130; | ||
| 388 | |||
| 389 | return result; | ||
| 390 | } | ||
| 391 | |||
| 392 | static int yas529_read(void *mlsl_handle, | ||
| 393 | struct ext_slave_descr *slave, | ||
| 394 | struct ext_slave_platform_data *pdata, | ||
| 395 | unsigned char *data) | ||
| 396 | { | ||
| 397 | unsigned char stat; | ||
| 398 | unsigned char rawData[6]; | ||
| 399 | unsigned char dummyData[1] = { 0 }; | ||
| 400 | unsigned char dummyRegister = 0; | ||
| 401 | int result = INV_SUCCESS; | ||
| 402 | short SX, SY1, SY2, SY, SZ; | ||
| 403 | short row1fixed, row2fixed, row3fixed; | ||
| 404 | |||
| 405 | /* Config register - Measurements results - "110 00 000" */ | ||
| 406 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
| 407 | result = | ||
| 408 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 409 | if (result) { | ||
| 410 | LOG_RESULT_LOCATION(result); | ||
| 411 | return result; | ||
| 412 | } | ||
| 413 | /* Measurements command register - Normal magnetic field measurement - | ||
| 414 | "000 00000" */ | ||
| 415 | dummyData[0] = YAS_REG_CMDR | 0x00; | ||
| 416 | result = | ||
| 417 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
| 418 | if (result) { | ||
| 419 | LOG_RESULT_LOCATION(result); | ||
| 420 | return result; | ||
| 421 | } | ||
| 422 | msleep(10); | ||
| 423 | /* Measurement data read */ | ||
| 424 | result = | ||
| 425 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
| 426 | dummyRegister, 6, (unsigned char *)&rawData); | ||
| 427 | if (result) { | ||
| 428 | LOG_RESULT_LOCATION(result); | ||
| 429 | return result; | ||
| 430 | } | ||
| 431 | |||
| 432 | stat = rawData[0] & 0x80; | ||
| 433 | if (stat == 0x00) { | ||
| 434 | /* Extract raw data */ | ||
| 435 | SX = (short)((unsigned short)rawData[5] + | ||
| 436 | ((unsigned short)rawData[4] & 0x7) * 256); | ||
| 437 | SY1 = | ||
| 438 | (short)((unsigned short)rawData[3] + | ||
| 439 | ((unsigned short)rawData[2] & 0x7) * 256); | ||
| 440 | SY2 = | ||
| 441 | (short)((unsigned short)rawData[1] + | ||
| 442 | ((unsigned short)rawData[0] & 0x7) * 256); | ||
| 443 | if ((SX <= 1) || (SY1 <= 1) || (SY2 <= 1)) | ||
| 444 | return INV_ERROR_COMPASS_DATA_UNDERFLOW; | ||
| 445 | if ((SX >= 1024) || (SY1 >= 1024) || (SY2 >= 1024)) | ||
| 446 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
| 447 | /* Convert to XYZ axis */ | ||
| 448 | SX = -1 * SX; | ||
| 449 | SY = SY2 - SY1; | ||
| 450 | SZ = SY1 + SY2; | ||
| 451 | |||
| 452 | /* Apply sensitivity correction matrix */ | ||
| 453 | row1fixed = (short)((a1 * SX + a2 * SY + a3 * SZ) >> 7) * 41; | ||
| 454 | row2fixed = (short)((a4 * SX + a5 * SY + a6 * SZ) >> 7) * 41; | ||
| 455 | row3fixed = (short)((a7 * SX + a8 * SY + a9 * SZ) >> 7) * 41; | ||
| 456 | |||
| 457 | data[0] = row1fixed >> 8; | ||
| 458 | data[1] = row1fixed & 0xFF; | ||
| 459 | data[2] = row2fixed >> 8; | ||
| 460 | data[3] = row2fixed & 0xFF; | ||
| 461 | data[4] = row3fixed >> 8; | ||
| 462 | data[5] = row3fixed & 0xFF; | ||
| 463 | |||
| 464 | return INV_SUCCESS; | ||
| 465 | } else { | ||
| 466 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | static struct ext_slave_descr yas529_descr = { | ||
| 471 | .init = NULL, | ||
| 472 | .exit = NULL, | ||
| 473 | .suspend = yas529_suspend, | ||
| 474 | .resume = yas529_resume, | ||
| 475 | .read = yas529_read, | ||
| 476 | .config = NULL, | ||
| 477 | .get_config = NULL, | ||
| 478 | .name = "yas529", | ||
| 479 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 480 | .id = COMPASS_ID_YAS529, | ||
| 481 | .read_reg = 0x06, | ||
| 482 | .read_len = 6, | ||
| 483 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 484 | .range = {19660, 8000}, | ||
| 485 | .trigger = NULL, | ||
| 486 | }; | ||
| 487 | |||
| 488 | static | ||
| 489 | struct ext_slave_descr *yas529_get_slave_descr(void) | ||
| 490 | { | ||
| 491 | return &yas529_descr; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* -------------------------------------------------------------------------- */ | ||
| 495 | struct yas529_mod_private_data { | ||
| 496 | struct i2c_client *client; | ||
| 497 | struct ext_slave_platform_data *pdata; | ||
| 498 | }; | ||
| 499 | |||
| 500 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 501 | |||
| 502 | static int yas529_mod_probe(struct i2c_client *client, | ||
| 503 | const struct i2c_device_id *devid) | ||
| 504 | { | ||
| 505 | struct ext_slave_platform_data *pdata; | ||
| 506 | struct yas529_mod_private_data *private_data; | ||
| 507 | int result = 0; | ||
| 508 | |||
| 509 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 510 | |||
| 511 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 512 | result = -ENODEV; | ||
| 513 | goto out_no_free; | ||
| 514 | } | ||
| 515 | |||
| 516 | pdata = client->dev.platform_data; | ||
| 517 | if (!pdata) { | ||
| 518 | dev_err(&client->adapter->dev, | ||
| 519 | "Missing platform data for slave %s\n", devid->name); | ||
| 520 | result = -EFAULT; | ||
| 521 | goto out_no_free; | ||
| 522 | } | ||
| 523 | |||
| 524 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 525 | if (!private_data) { | ||
| 526 | result = -ENOMEM; | ||
| 527 | goto out_no_free; | ||
| 528 | } | ||
| 529 | |||
| 530 | i2c_set_clientdata(client, private_data); | ||
| 531 | private_data->client = client; | ||
| 532 | private_data->pdata = pdata; | ||
| 533 | |||
| 534 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 535 | yas529_get_slave_descr); | ||
| 536 | if (result) { | ||
| 537 | dev_err(&client->adapter->dev, | ||
| 538 | "Slave registration failed: %s, %d\n", | ||
| 539 | devid->name, result); | ||
| 540 | goto out_free_memory; | ||
| 541 | } | ||
| 542 | |||
| 543 | return result; | ||
| 544 | |||
| 545 | out_free_memory: | ||
| 546 | kfree(private_data); | ||
| 547 | out_no_free: | ||
| 548 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 549 | return result; | ||
| 550 | |||
| 551 | } | ||
| 552 | |||
| 553 | static int yas529_mod_remove(struct i2c_client *client) | ||
| 554 | { | ||
| 555 | struct yas529_mod_private_data *private_data = | ||
| 556 | i2c_get_clientdata(client); | ||
| 557 | |||
| 558 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 559 | |||
| 560 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 561 | yas529_get_slave_descr); | ||
| 562 | |||
| 563 | kfree(private_data); | ||
| 564 | return 0; | ||
| 565 | } | ||
| 566 | |||
| 567 | static const struct i2c_device_id yas529_mod_id[] = { | ||
| 568 | { "yas529", COMPASS_ID_YAS529 }, | ||
| 569 | {} | ||
| 570 | }; | ||
| 571 | |||
| 572 | MODULE_DEVICE_TABLE(i2c, yas529_mod_id); | ||
| 573 | |||
| 574 | static struct i2c_driver yas529_mod_driver = { | ||
| 575 | .class = I2C_CLASS_HWMON, | ||
| 576 | .probe = yas529_mod_probe, | ||
| 577 | .remove = yas529_mod_remove, | ||
| 578 | .id_table = yas529_mod_id, | ||
| 579 | .driver = { | ||
| 580 | .owner = THIS_MODULE, | ||
| 581 | .name = "yas529_mod", | ||
| 582 | }, | ||
| 583 | .address_list = normal_i2c, | ||
| 584 | }; | ||
| 585 | |||
| 586 | static int __init yas529_mod_init(void) | ||
| 587 | { | ||
| 588 | int res = i2c_add_driver(&yas529_mod_driver); | ||
| 589 | pr_info("%s: Probe name %s\n", __func__, "yas529_mod"); | ||
| 590 | if (res) | ||
| 591 | pr_err("%s failed\n", __func__); | ||
| 592 | return res; | ||
| 593 | } | ||
| 594 | |||
| 595 | static void __exit yas529_mod_exit(void) | ||
| 596 | { | ||
| 597 | pr_info("%s\n", __func__); | ||
| 598 | i2c_del_driver(&yas529_mod_driver); | ||
| 599 | } | ||
| 600 | |||
| 601 | module_init(yas529_mod_init); | ||
| 602 | module_exit(yas529_mod_exit); | ||
| 603 | |||
| 604 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 605 | MODULE_DESCRIPTION("Driver to integrate YAS529 sensor with the MPU"); | ||
| 606 | MODULE_LICENSE("GPL"); | ||
| 607 | MODULE_ALIAS("yas529_mod"); | ||
| 608 | |||
| 609 | /** | ||
| 610 | * @} | ||
| 611 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/yas530.c b/drivers/misc/inv_mpu/compass/yas530.c new file mode 100644 index 00000000000..fdca05ba8e5 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/yas530.c | |||
| @@ -0,0 +1,580 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup COMPASSDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file yas530.c | ||
| 25 | * @brief Magnetometer setup and handling methods for Yamaha YAS530 | ||
| 26 | * compass when used in a user-space solution (no kernel driver). | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* -------------------------------------------------------------------------- */ | ||
| 30 | |||
| 31 | #include <linux/i2c.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/errno.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | |||
| 38 | #include <linux/module.h> | ||
| 39 | #include <linux/delay.h> | ||
| 40 | #include "mpu-dev.h" | ||
| 41 | |||
| 42 | #include "log.h" | ||
| 43 | #include <linux/mpu.h> | ||
| 44 | #include "mlsl.h" | ||
| 45 | #include "mldl_cfg.h" | ||
| 46 | #undef MPL_LOG_TAG | ||
| 47 | #define MPL_LOG_TAG "MPL-compass" | ||
| 48 | |||
| 49 | /* -------------------------------------------------------------------------- */ | ||
| 50 | #define YAS530_REGADDR_DEVICE_ID (0x80) | ||
| 51 | #define YAS530_REGADDR_ACTUATE_INIT_COIL (0x81) | ||
| 52 | #define YAS530_REGADDR_MEASURE_COMMAND (0x82) | ||
| 53 | #define YAS530_REGADDR_CONFIG (0x83) | ||
| 54 | #define YAS530_REGADDR_MEASURE_INTERVAL (0x84) | ||
| 55 | #define YAS530_REGADDR_OFFSET_X (0x85) | ||
| 56 | #define YAS530_REGADDR_OFFSET_Y1 (0x86) | ||
| 57 | #define YAS530_REGADDR_OFFSET_Y2 (0x87) | ||
| 58 | #define YAS530_REGADDR_TEST1 (0x88) | ||
| 59 | #define YAS530_REGADDR_TEST2 (0x89) | ||
| 60 | #define YAS530_REGADDR_CAL (0x90) | ||
| 61 | #define YAS530_REGADDR_MEASURE_DATA (0xb0) | ||
| 62 | |||
| 63 | /* -------------------------------------------------------------------------- */ | ||
| 64 | static int Cx, Cy1, Cy2; | ||
| 65 | static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9; | ||
| 66 | static int k; | ||
| 67 | |||
| 68 | static unsigned char dx, dy1, dy2; | ||
| 69 | static unsigned char d2, d3, d4, d5, d6, d7, d8, d9, d0; | ||
| 70 | static unsigned char dck; | ||
| 71 | |||
| 72 | /* -------------------------------------------------------------------------- */ | ||
| 73 | |||
| 74 | static int set_hardware_offset(void *mlsl_handle, | ||
| 75 | struct ext_slave_descr *slave, | ||
| 76 | struct ext_slave_platform_data *pdata, | ||
| 77 | char offset_x, char offset_y1, char offset_y2) | ||
| 78 | { | ||
| 79 | char data; | ||
| 80 | int result = INV_SUCCESS; | ||
| 81 | |||
| 82 | data = offset_x & 0x3f; | ||
| 83 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 84 | YAS530_REGADDR_OFFSET_X, data); | ||
| 85 | if (result) { | ||
| 86 | LOG_RESULT_LOCATION(result); | ||
| 87 | return result; | ||
| 88 | } | ||
| 89 | |||
| 90 | data = offset_y1 & 0x3f; | ||
| 91 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 92 | YAS530_REGADDR_OFFSET_Y1, data); | ||
| 93 | if (result) { | ||
| 94 | LOG_RESULT_LOCATION(result); | ||
| 95 | return result; | ||
| 96 | } | ||
| 97 | |||
| 98 | data = offset_y2 & 0x3f; | ||
| 99 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 100 | YAS530_REGADDR_OFFSET_Y2, data); | ||
| 101 | if (result) { | ||
| 102 | LOG_RESULT_LOCATION(result); | ||
| 103 | return result; | ||
| 104 | } | ||
| 105 | |||
| 106 | return result; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int set_measure_command(void *mlsl_handle, | ||
| 110 | struct ext_slave_descr *slave, | ||
| 111 | struct ext_slave_platform_data *pdata, | ||
| 112 | int ldtc, int fors, int dlymes) | ||
| 113 | { | ||
| 114 | int result = INV_SUCCESS; | ||
| 115 | |||
| 116 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 117 | YAS530_REGADDR_MEASURE_COMMAND, 0x01); | ||
| 118 | if (result) { | ||
| 119 | LOG_RESULT_LOCATION(result); | ||
| 120 | return result; | ||
| 121 | } | ||
| 122 | |||
| 123 | return result; | ||
| 124 | } | ||
| 125 | |||
| 126 | static int measure_normal(void *mlsl_handle, | ||
| 127 | struct ext_slave_descr *slave, | ||
| 128 | struct ext_slave_platform_data *pdata, | ||
| 129 | int *busy, unsigned short *t, | ||
| 130 | unsigned short *x, unsigned short *y1, | ||
| 131 | unsigned short *y2) | ||
| 132 | { | ||
| 133 | unsigned char data[8]; | ||
| 134 | unsigned short b, to, xo, y1o, y2o; | ||
| 135 | int result; | ||
| 136 | ktime_t sleeptime; | ||
| 137 | result = set_measure_command(mlsl_handle, slave, pdata, 0, 0, 0); | ||
| 138 | sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC); | ||
| 139 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 140 | schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL); | ||
| 141 | |||
| 142 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 143 | YAS530_REGADDR_MEASURE_DATA, 8, data); | ||
| 144 | if (result) { | ||
| 145 | LOG_RESULT_LOCATION(result); | ||
| 146 | return result; | ||
| 147 | } | ||
| 148 | |||
| 149 | b = (data[0] >> 7) & 0x01; | ||
| 150 | to = ((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03); | ||
| 151 | xo = ((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f); | ||
| 152 | y1o = ((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f); | ||
| 153 | y2o = ((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f); | ||
| 154 | |||
| 155 | *busy = b; | ||
| 156 | *t = to; | ||
| 157 | *x = xo; | ||
| 158 | *y1 = y1o; | ||
| 159 | *y2 = y2o; | ||
| 160 | |||
| 161 | return result; | ||
| 162 | } | ||
| 163 | |||
| 164 | static int check_offset(void *mlsl_handle, | ||
| 165 | struct ext_slave_descr *slave, | ||
| 166 | struct ext_slave_platform_data *pdata, | ||
| 167 | char offset_x, char offset_y1, char offset_y2, | ||
| 168 | int *flag_x, int *flag_y1, int *flag_y2) | ||
| 169 | { | ||
| 170 | int result; | ||
| 171 | int busy; | ||
| 172 | short t, x, y1, y2; | ||
| 173 | |||
| 174 | result = set_hardware_offset(mlsl_handle, slave, pdata, | ||
| 175 | offset_x, offset_y1, offset_y2); | ||
| 176 | if (result) { | ||
| 177 | LOG_RESULT_LOCATION(result); | ||
| 178 | return result; | ||
| 179 | } | ||
| 180 | |||
| 181 | result = measure_normal(mlsl_handle, slave, pdata, | ||
| 182 | &busy, &t, &x, &y1, &y2); | ||
| 183 | if (result) { | ||
| 184 | LOG_RESULT_LOCATION(result); | ||
| 185 | return result; | ||
| 186 | } | ||
| 187 | |||
| 188 | *flag_x = 0; | ||
| 189 | *flag_y1 = 0; | ||
| 190 | *flag_y2 = 0; | ||
| 191 | |||
| 192 | if (x > 2048) | ||
| 193 | *flag_x = 1; | ||
| 194 | if (y1 > 2048) | ||
| 195 | *flag_y1 = 1; | ||
| 196 | if (y2 > 2048) | ||
| 197 | *flag_y2 = 1; | ||
| 198 | if (x < 2048) | ||
| 199 | *flag_x = -1; | ||
| 200 | if (y1 < 2048) | ||
| 201 | *flag_y1 = -1; | ||
| 202 | if (y2 < 2048) | ||
| 203 | *flag_y2 = -1; | ||
| 204 | |||
| 205 | return result; | ||
| 206 | } | ||
| 207 | |||
| 208 | static int measure_and_set_offset(void *mlsl_handle, | ||
| 209 | struct ext_slave_descr *slave, | ||
| 210 | struct ext_slave_platform_data *pdata, | ||
| 211 | char *offset) | ||
| 212 | { | ||
| 213 | int i; | ||
| 214 | int result = INV_SUCCESS; | ||
| 215 | char offset_x = 0, offset_y1 = 0, offset_y2 = 0; | ||
| 216 | int flag_x = 0, flag_y1 = 0, flag_y2 = 0; | ||
| 217 | static const int correct[5] = { 16, 8, 4, 2, 1 }; | ||
| 218 | |||
| 219 | for (i = 0; i < 5; i++) { | ||
| 220 | result = check_offset(mlsl_handle, slave, pdata, | ||
| 221 | offset_x, offset_y1, offset_y2, | ||
| 222 | &flag_x, &flag_y1, &flag_y2); | ||
| 223 | if (result) { | ||
| 224 | LOG_RESULT_LOCATION(result); | ||
| 225 | return result; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (flag_x) | ||
| 229 | offset_x += flag_x * correct[i]; | ||
| 230 | if (flag_y1) | ||
| 231 | offset_y1 += flag_y1 * correct[i]; | ||
| 232 | if (flag_y2) | ||
| 233 | offset_y2 += flag_y2 * correct[i]; | ||
| 234 | } | ||
| 235 | |||
| 236 | result = set_hardware_offset(mlsl_handle, slave, pdata, | ||
| 237 | offset_x, offset_y1, offset_y2); | ||
| 238 | if (result) { | ||
| 239 | LOG_RESULT_LOCATION(result); | ||
| 240 | return result; | ||
| 241 | } | ||
| 242 | |||
| 243 | offset[0] = offset_x; | ||
| 244 | offset[1] = offset_y1; | ||
| 245 | offset[2] = offset_y2; | ||
| 246 | |||
| 247 | return result; | ||
| 248 | } | ||
| 249 | |||
| 250 | static void coordinate_conversion(short x, short y1, short y2, short t, | ||
| 251 | int32_t *xo, int32_t *yo, int32_t *zo) | ||
| 252 | { | ||
| 253 | int32_t sx, sy1, sy2, sy, sz; | ||
| 254 | int32_t hx, hy, hz; | ||
| 255 | |||
| 256 | sx = x - (Cx * t) / 100; | ||
| 257 | sy1 = y1 - (Cy1 * t) / 100; | ||
| 258 | sy2 = y2 - (Cy2 * t) / 100; | ||
| 259 | |||
| 260 | sy = sy1 - sy2; | ||
| 261 | sz = -sy1 - sy2; | ||
| 262 | |||
| 263 | hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10); | ||
| 264 | hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10); | ||
| 265 | hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10); | ||
| 266 | |||
| 267 | *xo = hx; | ||
| 268 | *yo = hy; | ||
| 269 | *zo = hz; | ||
| 270 | } | ||
| 271 | |||
| 272 | static int yas530_suspend(void *mlsl_handle, | ||
| 273 | struct ext_slave_descr *slave, | ||
| 274 | struct ext_slave_platform_data *pdata) | ||
| 275 | { | ||
| 276 | int result = INV_SUCCESS; | ||
| 277 | |||
| 278 | return result; | ||
| 279 | } | ||
| 280 | |||
| 281 | static int yas530_resume(void *mlsl_handle, | ||
| 282 | struct ext_slave_descr *slave, | ||
| 283 | struct ext_slave_platform_data *pdata) | ||
| 284 | { | ||
| 285 | int result = INV_SUCCESS; | ||
| 286 | |||
| 287 | unsigned char dummyData = 0x00; | ||
| 288 | char offset[3] = { 0, 0, 0 }; | ||
| 289 | unsigned char data[16]; | ||
| 290 | unsigned char read_reg[1]; | ||
| 291 | |||
| 292 | /* =============================================== */ | ||
| 293 | |||
| 294 | /* Step 1 - Test register initialization */ | ||
| 295 | dummyData = 0x00; | ||
| 296 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 297 | YAS530_REGADDR_TEST1, dummyData); | ||
| 298 | if (result) { | ||
| 299 | LOG_RESULT_LOCATION(result); | ||
| 300 | return result; | ||
| 301 | } | ||
| 302 | |||
| 303 | result = | ||
| 304 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 305 | YAS530_REGADDR_TEST2, dummyData); | ||
| 306 | if (result) { | ||
| 307 | LOG_RESULT_LOCATION(result); | ||
| 308 | return result; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Device ID read */ | ||
| 312 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 313 | YAS530_REGADDR_DEVICE_ID, 1, read_reg); | ||
| 314 | |||
| 315 | /*Step 2 Read the CAL register */ | ||
| 316 | /* CAL data read */ | ||
| 317 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 318 | YAS530_REGADDR_CAL, 16, data); | ||
| 319 | if (result) { | ||
| 320 | LOG_RESULT_LOCATION(result); | ||
| 321 | return result; | ||
| 322 | } | ||
| 323 | /* CAL data Second Read */ | ||
| 324 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
| 325 | YAS530_REGADDR_CAL, 16, data); | ||
| 326 | if (result) { | ||
| 327 | LOG_RESULT_LOCATION(result); | ||
| 328 | return result; | ||
| 329 | } | ||
| 330 | |||
| 331 | /*Cal data */ | ||
| 332 | dx = data[0]; | ||
| 333 | dy1 = data[1]; | ||
| 334 | dy2 = data[2]; | ||
| 335 | d2 = (data[3] >> 2) & 0x03f; | ||
| 336 | d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03); | ||
| 337 | d4 = data[4] & 0x3f; | ||
| 338 | d5 = (data[5] >> 2) & 0x3f; | ||
| 339 | d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f); | ||
| 340 | d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07); | ||
| 341 | d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01); | ||
| 342 | d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01); | ||
| 343 | d0 = (data[9] >> 2) & 0x1f; | ||
| 344 | dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01); | ||
| 345 | |||
| 346 | /*Correction Data */ | ||
| 347 | Cx = (int)dx * 6 - 768; | ||
| 348 | Cy1 = (int)dy1 * 6 - 768; | ||
| 349 | Cy2 = (int)dy2 * 6 - 768; | ||
| 350 | a2 = (int)d2 - 32; | ||
| 351 | a3 = (int)d3 - 8; | ||
| 352 | a4 = (int)d4 - 32; | ||
| 353 | a5 = (int)d5 + 38; | ||
| 354 | a6 = (int)d6 - 32; | ||
| 355 | a7 = (int)d7 - 64; | ||
| 356 | a8 = (int)d8 - 32; | ||
| 357 | a9 = (int)d9; | ||
| 358 | k = (int)d0 + 10; | ||
| 359 | |||
| 360 | /*Obtain the [49:47] bits */ | ||
| 361 | dck &= 0x07; | ||
| 362 | |||
| 363 | /*Step 3 : Storing the CONFIG with the CLK value */ | ||
| 364 | dummyData = 0x00 | (dck << 2); | ||
| 365 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 366 | YAS530_REGADDR_CONFIG, dummyData); | ||
| 367 | if (result) { | ||
| 368 | LOG_RESULT_LOCATION(result); | ||
| 369 | return result; | ||
| 370 | } | ||
| 371 | |||
| 372 | /*Step 4 : Set Acquisition Interval Register */ | ||
| 373 | dummyData = 0x00; | ||
| 374 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 375 | YAS530_REGADDR_MEASURE_INTERVAL, | ||
| 376 | dummyData); | ||
| 377 | if (result) { | ||
| 378 | LOG_RESULT_LOCATION(result); | ||
| 379 | return result; | ||
| 380 | } | ||
| 381 | |||
| 382 | /*Step 5 : Reset Coil */ | ||
| 383 | dummyData = 0x00; | ||
| 384 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 385 | YAS530_REGADDR_ACTUATE_INIT_COIL, | ||
| 386 | dummyData); | ||
| 387 | if (result) { | ||
| 388 | LOG_RESULT_LOCATION(result); | ||
| 389 | return result; | ||
| 390 | } | ||
| 391 | |||
| 392 | /* Offset Measurement and Set */ | ||
| 393 | result = measure_and_set_offset(mlsl_handle, slave, pdata, offset); | ||
| 394 | if (result) { | ||
| 395 | LOG_RESULT_LOCATION(result); | ||
| 396 | return result; | ||
| 397 | } | ||
| 398 | |||
| 399 | return result; | ||
| 400 | } | ||
| 401 | |||
| 402 | static int yas530_read(void *mlsl_handle, | ||
| 403 | struct ext_slave_descr *slave, | ||
| 404 | struct ext_slave_platform_data *pdata, | ||
| 405 | unsigned char *data) | ||
| 406 | { | ||
| 407 | int result = INV_SUCCESS; | ||
| 408 | |||
| 409 | int busy; | ||
| 410 | short t, x, y1, y2; | ||
| 411 | int32_t xyz[3]; | ||
| 412 | short rawfixed[3]; | ||
| 413 | |||
| 414 | result = measure_normal(mlsl_handle, slave, pdata, | ||
| 415 | &busy, &t, &x, &y1, &y2); | ||
| 416 | if (result) { | ||
| 417 | LOG_RESULT_LOCATION(result); | ||
| 418 | return result; | ||
| 419 | } | ||
| 420 | |||
| 421 | coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]); | ||
| 422 | |||
| 423 | rawfixed[0] = (short)(xyz[0] / 100); | ||
| 424 | rawfixed[1] = (short)(xyz[1] / 100); | ||
| 425 | rawfixed[2] = (short)(xyz[2] / 100); | ||
| 426 | |||
| 427 | data[0] = rawfixed[0] >> 8; | ||
| 428 | data[1] = rawfixed[0] & 0xFF; | ||
| 429 | data[2] = rawfixed[1] >> 8; | ||
| 430 | data[3] = rawfixed[1] & 0xFF; | ||
| 431 | data[4] = rawfixed[2] >> 8; | ||
| 432 | data[5] = rawfixed[2] & 0xFF; | ||
| 433 | |||
| 434 | if (busy) | ||
| 435 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
| 436 | return result; | ||
| 437 | } | ||
| 438 | |||
| 439 | static struct ext_slave_descr yas530_descr = { | ||
| 440 | .init = NULL, | ||
| 441 | .exit = NULL, | ||
| 442 | .suspend = yas530_suspend, | ||
| 443 | .resume = yas530_resume, | ||
| 444 | .read = yas530_read, | ||
| 445 | .config = NULL, | ||
| 446 | .get_config = NULL, | ||
| 447 | .name = "yas530", | ||
| 448 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
| 449 | .id = COMPASS_ID_YAS530, | ||
| 450 | .read_reg = 0x06, | ||
| 451 | .read_len = 6, | ||
| 452 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 453 | .range = {3276, 8001}, | ||
| 454 | .trigger = NULL, | ||
| 455 | }; | ||
| 456 | |||
| 457 | static | ||
| 458 | struct ext_slave_descr *yas530_get_slave_descr(void) | ||
| 459 | { | ||
| 460 | return &yas530_descr; | ||
| 461 | } | ||
| 462 | |||
| 463 | /* -------------------------------------------------------------------------- */ | ||
| 464 | struct yas530_mod_private_data { | ||
| 465 | struct i2c_client *client; | ||
| 466 | struct ext_slave_platform_data *pdata; | ||
| 467 | }; | ||
| 468 | |||
| 469 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 470 | |||
| 471 | static int yas530_mod_probe(struct i2c_client *client, | ||
| 472 | const struct i2c_device_id *devid) | ||
| 473 | { | ||
| 474 | struct ext_slave_platform_data *pdata; | ||
| 475 | struct yas530_mod_private_data *private_data; | ||
| 476 | int result = 0; | ||
| 477 | |||
| 478 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 479 | |||
| 480 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 481 | result = -ENODEV; | ||
| 482 | goto out_no_free; | ||
| 483 | } | ||
| 484 | |||
| 485 | pdata = client->dev.platform_data; | ||
| 486 | if (!pdata) { | ||
| 487 | dev_err(&client->adapter->dev, | ||
| 488 | "Missing platform data for slave %s\n", devid->name); | ||
| 489 | result = -EFAULT; | ||
| 490 | goto out_no_free; | ||
| 491 | } | ||
| 492 | |||
| 493 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 494 | if (!private_data) { | ||
| 495 | result = -ENOMEM; | ||
| 496 | goto out_no_free; | ||
| 497 | } | ||
| 498 | |||
| 499 | i2c_set_clientdata(client, private_data); | ||
| 500 | private_data->client = client; | ||
| 501 | private_data->pdata = pdata; | ||
| 502 | |||
| 503 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 504 | yas530_get_slave_descr); | ||
| 505 | if (result) { | ||
| 506 | dev_err(&client->adapter->dev, | ||
| 507 | "Slave registration failed: %s, %d\n", | ||
| 508 | devid->name, result); | ||
| 509 | goto out_free_memory; | ||
| 510 | } | ||
| 511 | |||
| 512 | return result; | ||
| 513 | |||
| 514 | out_free_memory: | ||
| 515 | kfree(private_data); | ||
| 516 | out_no_free: | ||
| 517 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 518 | return result; | ||
| 519 | |||
| 520 | } | ||
| 521 | |||
| 522 | static int yas530_mod_remove(struct i2c_client *client) | ||
| 523 | { | ||
| 524 | struct yas530_mod_private_data *private_data = | ||
| 525 | i2c_get_clientdata(client); | ||
| 526 | |||
| 527 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 528 | |||
| 529 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 530 | yas530_get_slave_descr); | ||
| 531 | |||
| 532 | kfree(private_data); | ||
| 533 | return 0; | ||
| 534 | } | ||
| 535 | |||
| 536 | static const struct i2c_device_id yas530_mod_id[] = { | ||
| 537 | { "yas530", COMPASS_ID_YAS530 }, | ||
| 538 | {} | ||
| 539 | }; | ||
| 540 | |||
| 541 | MODULE_DEVICE_TABLE(i2c, yas530_mod_id); | ||
| 542 | |||
| 543 | static struct i2c_driver yas530_mod_driver = { | ||
| 544 | .class = I2C_CLASS_HWMON, | ||
| 545 | .probe = yas530_mod_probe, | ||
| 546 | .remove = yas530_mod_remove, | ||
| 547 | .id_table = yas530_mod_id, | ||
| 548 | .driver = { | ||
| 549 | .owner = THIS_MODULE, | ||
| 550 | .name = "yas530_mod", | ||
| 551 | }, | ||
| 552 | .address_list = normal_i2c, | ||
| 553 | }; | ||
| 554 | |||
| 555 | static int __init yas530_mod_init(void) | ||
| 556 | { | ||
| 557 | int res = i2c_add_driver(&yas530_mod_driver); | ||
| 558 | pr_info("%s: Probe name %s\n", __func__, "yas530_mod"); | ||
| 559 | if (res) | ||
| 560 | pr_err("%s failed\n", __func__); | ||
| 561 | return res; | ||
| 562 | } | ||
| 563 | |||
| 564 | static void __exit yas530_mod_exit(void) | ||
| 565 | { | ||
| 566 | pr_info("%s\n", __func__); | ||
| 567 | i2c_del_driver(&yas530_mod_driver); | ||
| 568 | } | ||
| 569 | |||
| 570 | module_init(yas530_mod_init); | ||
| 571 | module_exit(yas530_mod_exit); | ||
| 572 | |||
| 573 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 574 | MODULE_DESCRIPTION("Driver to integrate YAS530 sensor with the MPU"); | ||
| 575 | MODULE_LICENSE("GPL"); | ||
| 576 | MODULE_ALIAS("yas530_mod"); | ||
| 577 | |||
| 578 | /** | ||
| 579 | * @} | ||
| 580 | */ | ||
diff --git a/drivers/misc/inv_mpu/log.h b/drivers/misc/inv_mpu/log.h new file mode 100644 index 00000000000..5630602e3ef --- /dev/null +++ b/drivers/misc/inv_mpu/log.h | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* | ||
| 21 | * This file incorporates work covered by the following copyright and | ||
| 22 | * permission notice: | ||
| 23 | * | ||
| 24 | * Copyright (C) 2005 The Android Open Source Project | ||
| 25 | * | ||
| 26 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 27 | * you may not use this file except in compliance with the License. | ||
| 28 | * You may obtain a copy of the License at | ||
| 29 | * | ||
| 30 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 31 | * | ||
| 32 | * Unless required by applicable law or agreed to in writing, software | ||
| 33 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 35 | * See the License for the specific language governing permissions and | ||
| 36 | * limitations under the License. | ||
| 37 | */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | * C/C++ logging functions. See the logging documentation for API details. | ||
| 41 | * | ||
| 42 | * We'd like these to be available from C code (in case we import some from | ||
| 43 | * somewhere), so this has a C interface. | ||
| 44 | * | ||
| 45 | * The output will be correct when the log file is shared between multiple | ||
| 46 | * threads and/or multiple processes so long as the operating system | ||
| 47 | * supports O_APPEND. These calls have mutex-protected data structures | ||
| 48 | * and so are NOT reentrant. Do not use MPL_LOG in a signal handler. | ||
| 49 | */ | ||
| 50 | #ifndef _LIBS_CUTILS_MPL_LOG_H | ||
| 51 | #define _LIBS_CUTILS_MPL_LOG_H | ||
| 52 | |||
| 53 | #include "mltypes.h" | ||
| 54 | #include <stdarg.h> | ||
| 55 | |||
| 56 | |||
| 57 | #include <linux/kernel.h> | ||
| 58 | |||
| 59 | |||
| 60 | /* --------------------------------------------------------------------- */ | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Normally we strip MPL_LOGV (VERBOSE messages) from release builds. | ||
| 64 | * You can modify this (for example with "#define MPL_LOG_NDEBUG 0" | ||
| 65 | * at the top of your source file) to change that behavior. | ||
| 66 | */ | ||
| 67 | #ifndef MPL_LOG_NDEBUG | ||
| 68 | #ifdef NDEBUG | ||
| 69 | #define MPL_LOG_NDEBUG 1 | ||
| 70 | #else | ||
| 71 | #define MPL_LOG_NDEBUG 0 | ||
| 72 | #endif | ||
| 73 | #endif | ||
| 74 | |||
| 75 | #define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE | ||
| 76 | #define MPL_LOG_DEFAULT KERN_DEFAULT | ||
| 77 | #define MPL_LOG_VERBOSE KERN_CONT | ||
| 78 | #define MPL_LOG_DEBUG KERN_NOTICE | ||
| 79 | #define MPL_LOG_INFO KERN_INFO | ||
| 80 | #define MPL_LOG_WARN KERN_WARNING | ||
| 81 | #define MPL_LOG_ERROR KERN_ERR | ||
| 82 | #define MPL_LOG_SILENT MPL_LOG_VERBOSE | ||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | /* | ||
| 87 | * This is the local tag used for the following simplified | ||
| 88 | * logging macros. You can change this preprocessor definition | ||
| 89 | * before using the other macros to change the tag. | ||
| 90 | */ | ||
| 91 | #ifndef MPL_LOG_TAG | ||
| 92 | #define MPL_LOG_TAG | ||
| 93 | #endif | ||
| 94 | |||
| 95 | /* --------------------------------------------------------------------- */ | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Simplified macro to send a verbose log message using the current MPL_LOG_TAG. | ||
| 99 | */ | ||
| 100 | #ifndef MPL_LOGV | ||
| 101 | #if MPL_LOG_NDEBUG | ||
| 102 | #define MPL_LOGV(fmt, ...) \ | ||
| 103 | do { \ | ||
| 104 | if (0) \ | ||
| 105 | MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\ | ||
| 106 | } while (0) | ||
| 107 | #else | ||
| 108 | #define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 109 | #endif | ||
| 110 | #endif | ||
| 111 | |||
| 112 | #ifndef CONDITION | ||
| 113 | #define CONDITION(cond) ((cond) != 0) | ||
| 114 | #endif | ||
| 115 | |||
| 116 | #ifndef MPL_LOGV_IF | ||
| 117 | #if MPL_LOG_NDEBUG | ||
| 118 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
| 119 | do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0) | ||
| 120 | #else | ||
| 121 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
| 122 | ((CONDITION(cond)) \ | ||
| 123 | ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 124 | : (void)0) | ||
| 125 | #endif | ||
| 126 | #endif | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Simplified macro to send a debug log message using the current MPL_LOG_TAG. | ||
| 130 | */ | ||
| 131 | #ifndef MPL_LOGD | ||
| 132 | #define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
| 133 | #endif | ||
| 134 | |||
| 135 | #ifndef MPL_LOGD_IF | ||
| 136 | #define MPL_LOGD_IF(cond, fmt, ...) \ | ||
| 137 | ((CONDITION(cond)) \ | ||
| 138 | ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 139 | : (void)0) | ||
| 140 | #endif | ||
| 141 | |||
| 142 | /* | ||
| 143 | * Simplified macro to send an info log message using the current MPL_LOG_TAG. | ||
| 144 | */ | ||
| 145 | #ifndef MPL_LOGI | ||
| 146 | #define MPL_LOGI(fmt, ...) pr_info(KERN_INFO MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
| 147 | #endif | ||
| 148 | |||
| 149 | #ifndef MPL_LOGI_IF | ||
| 150 | #define MPL_LOGI_IF(cond, fmt, ...) \ | ||
| 151 | ((CONDITION(cond)) \ | ||
| 152 | ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 153 | : (void)0) | ||
| 154 | #endif | ||
| 155 | |||
| 156 | /* | ||
| 157 | * Simplified macro to send a warning log message using the current MPL_LOG_TAG. | ||
| 158 | */ | ||
| 159 | #ifndef MPL_LOGW | ||
| 160 | #define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
| 161 | #endif | ||
| 162 | |||
| 163 | #ifndef MPL_LOGW_IF | ||
| 164 | #define MPL_LOGW_IF(cond, fmt, ...) \ | ||
| 165 | ((CONDITION(cond)) \ | ||
| 166 | ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 167 | : (void)0) | ||
| 168 | #endif | ||
| 169 | |||
| 170 | /* | ||
| 171 | * Simplified macro to send an error log message using the current MPL_LOG_TAG. | ||
| 172 | */ | ||
| 173 | #ifndef MPL_LOGE | ||
| 174 | #define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
| 175 | #endif | ||
| 176 | |||
| 177 | #ifndef MPL_LOGE_IF | ||
| 178 | #define MPL_LOGE_IF(cond, fmt, ...) \ | ||
| 179 | ((CONDITION(cond)) \ | ||
| 180 | ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
| 181 | : (void)0) | ||
| 182 | #endif | ||
| 183 | |||
| 184 | /* --------------------------------------------------------------------- */ | ||
| 185 | |||
| 186 | /* | ||
| 187 | * Log a fatal error. If the given condition fails, this stops program | ||
| 188 | * execution like a normal assertion, but also generating the given message. | ||
| 189 | * It is NOT stripped from release builds. Note that the condition test | ||
| 190 | * is -inverted- from the normal assert() semantics. | ||
| 191 | */ | ||
| 192 | #define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \ | ||
| 193 | ((CONDITION(cond)) \ | ||
| 194 | ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \ | ||
| 195 | fmt, ##__VA_ARGS__)) \ | ||
| 196 | : (void)0) | ||
| 197 | |||
| 198 | #define MPL_LOG_ALWAYS_FATAL(fmt, ...) \ | ||
| 199 | (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__))) | ||
| 200 | |||
| 201 | /* | ||
| 202 | * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that | ||
| 203 | * are stripped out of release builds. | ||
| 204 | */ | ||
| 205 | #if MPL_LOG_NDEBUG | ||
| 206 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
| 207 | do { \ | ||
| 208 | if (0) \ | ||
| 209 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \ | ||
| 210 | } while (0) | ||
| 211 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
| 212 | do { \ | ||
| 213 | if (0) \ | ||
| 214 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \ | ||
| 215 | } while (0) | ||
| 216 | #else | ||
| 217 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
| 218 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__) | ||
| 219 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
| 220 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) | ||
| 221 | #endif | ||
| 222 | |||
| 223 | /* | ||
| 224 | * Assertion that generates a log message when the assertion fails. | ||
| 225 | * Stripped out of release builds. Uses the current MPL_LOG_TAG. | ||
| 226 | */ | ||
| 227 | #define MPL_LOG_ASSERT(cond, fmt, ...) \ | ||
| 228 | MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__) | ||
| 229 | |||
| 230 | /* --------------------------------------------------------------------- */ | ||
| 231 | |||
| 232 | /* | ||
| 233 | * Basic log message macro. | ||
| 234 | * | ||
| 235 | * Example: | ||
| 236 | * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno); | ||
| 237 | * | ||
| 238 | * The second argument may be NULL or "" to indicate the "global" tag. | ||
| 239 | */ | ||
| 240 | #ifndef MPL_LOG | ||
| 241 | #define MPL_LOG(priority, tag, fmt, ...) \ | ||
| 242 | MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__) | ||
| 243 | #endif | ||
| 244 | |||
| 245 | /* | ||
| 246 | * Log macro that allows you to specify a number for the priority. | ||
| 247 | */ | ||
| 248 | #ifndef MPL_LOG_PRI | ||
| 249 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
| 250 | pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__) | ||
| 251 | #endif | ||
| 252 | |||
| 253 | /* | ||
| 254 | * Log macro that allows you to pass in a varargs ("args" is a va_list). | ||
| 255 | */ | ||
| 256 | #ifndef MPL_LOG_PRI_VA | ||
| 257 | /* not allowed in the Kernel because there is no dev_dbg that takes a va_list */ | ||
| 258 | #endif | ||
| 259 | |||
| 260 | /* --------------------------------------------------------------------- */ | ||
| 261 | |||
| 262 | /* | ||
| 263 | * =========================================================================== | ||
| 264 | * | ||
| 265 | * The stuff in the rest of this file should not be used directly. | ||
| 266 | */ | ||
| 267 | |||
| 268 | int _MLPrintLog(int priority, const char *tag, const char *fmt, ...); | ||
| 269 | int _MLPrintVaLog(int priority, const char *tag, const char *fmt, va_list args); | ||
| 270 | /* Final implementation of actual writing to a character device */ | ||
| 271 | int _MLWriteLog(const char *buf, int buflen); | ||
| 272 | |||
| 273 | static inline void __print_result_location(int result, | ||
| 274 | const char *file, | ||
| 275 | const char *func, int line) | ||
| 276 | { | ||
| 277 | MPL_LOGE("%s|%s|%d returning %d\n", file, func, line, result); | ||
| 278 | } | ||
| 279 | |||
| 280 | #define LOG_RESULT_LOCATION(condition) \ | ||
| 281 | do { \ | ||
| 282 | __print_result_location((int)(condition), __FILE__, \ | ||
| 283 | __func__, __LINE__); \ | ||
| 284 | } while (0) | ||
| 285 | |||
| 286 | |||
| 287 | #endif /* _LIBS_CUTILS_MPL_LOG_H */ | ||
diff --git a/drivers/misc/inv_mpu/mldl_cfg.h b/drivers/misc/inv_mpu/mldl_cfg.h new file mode 100644 index 00000000000..2b81b89179d --- /dev/null +++ b/drivers/misc/inv_mpu/mldl_cfg.h | |||
| @@ -0,0 +1,384 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_cfg.h | ||
| 25 | * @brief The Motion Library Driver Layer Configuration header file. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #ifndef __MLDL_CFG_H__ | ||
| 29 | #define __MLDL_CFG_H__ | ||
| 30 | |||
| 31 | #include "mltypes.h" | ||
| 32 | #include "mlsl.h" | ||
| 33 | #include <linux/mpu.h> | ||
| 34 | #ifdef MPU_CURRENT_BUILD_MPU6050B1 | ||
| 35 | #include "mpu6050b1.h" | ||
| 36 | #elif defined(MPU_CURRENT_BUILD_MPU3050) | ||
| 37 | #include "mpu3050.h" | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #include "log.h" | ||
| 41 | |||
| 42 | /************************************************************************* | ||
| 43 | * Sensors Bit definitions | ||
| 44 | *************************************************************************/ | ||
| 45 | |||
| 46 | #define INV_X_GYRO (0x0001) | ||
| 47 | #define INV_Y_GYRO (0x0002) | ||
| 48 | #define INV_Z_GYRO (0x0004) | ||
| 49 | #define INV_DMP_PROCESSOR (0x0008) | ||
| 50 | |||
| 51 | #define INV_X_ACCEL (0x0010) | ||
| 52 | #define INV_Y_ACCEL (0x0020) | ||
| 53 | #define INV_Z_ACCEL (0x0040) | ||
| 54 | |||
| 55 | #define INV_X_COMPASS (0x0080) | ||
| 56 | #define INV_Y_COMPASS (0x0100) | ||
| 57 | #define INV_Z_COMPASS (0x0200) | ||
| 58 | |||
| 59 | #define INV_X_PRESSURE (0x0300) | ||
| 60 | #define INV_Y_PRESSURE (0x0800) | ||
| 61 | #define INV_Z_PRESSURE (0x1000) | ||
| 62 | |||
| 63 | #define INV_TEMPERATURE (0x2000) | ||
| 64 | #define INV_TIME (0x4000) | ||
| 65 | |||
| 66 | #define INV_THREE_AXIS_GYRO (0x000F) | ||
| 67 | #define INV_THREE_AXIS_ACCEL (0x0070) | ||
| 68 | #define INV_THREE_AXIS_COMPASS (0x0380) | ||
| 69 | #define INV_THREE_AXIS_PRESSURE (0x1C00) | ||
| 70 | |||
| 71 | #define INV_FIVE_AXIS (0x007B) | ||
| 72 | #define INV_SIX_AXIS_GYRO_ACCEL (0x007F) | ||
| 73 | #define INV_SIX_AXIS_ACCEL_COMPASS (0x03F0) | ||
| 74 | #define INV_NINE_AXIS (0x03FF) | ||
| 75 | #define INV_ALL_SENSORS (0x7FFF) | ||
| 76 | |||
| 77 | #define MPL_PROD_KEY(ver, rev) (ver * 100 + rev) | ||
| 78 | |||
| 79 | /* -------------------------------------------------------------------------- */ | ||
| 80 | struct mpu_ram { | ||
| 81 | __u16 length; | ||
| 82 | __u8 *ram; | ||
| 83 | }; | ||
| 84 | |||
| 85 | struct mpu_gyro_cfg { | ||
| 86 | __u8 int_config; | ||
| 87 | __u8 ext_sync; | ||
| 88 | __u8 full_scale; | ||
| 89 | __u8 lpf; | ||
| 90 | __u8 clk_src; | ||
| 91 | __u8 divider; | ||
| 92 | __u8 dmp_enable; | ||
| 93 | __u8 fifo_enable; | ||
| 94 | __u8 dmp_cfg1; | ||
| 95 | __u8 dmp_cfg2; | ||
| 96 | }; | ||
| 97 | |||
| 98 | /* Offset registers that can be calibrated */ | ||
| 99 | struct mpu_offsets { | ||
| 100 | __u8 tc[GYRO_NUM_AXES]; | ||
| 101 | __u16 gyro[GYRO_NUM_AXES]; | ||
| 102 | }; | ||
| 103 | |||
| 104 | /* Chip related information that can be read and verified */ | ||
| 105 | struct mpu_chip_info { | ||
| 106 | __u8 addr; | ||
| 107 | __u8 product_revision; | ||
| 108 | __u8 silicon_revision; | ||
| 109 | __u8 product_id; | ||
| 110 | __u16 gyro_sens_trim; | ||
| 111 | /* Only used for MPU6050 */ | ||
| 112 | __u16 accel_sens_trim; | ||
| 113 | }; | ||
| 114 | |||
| 115 | |||
| 116 | struct inv_mpu_cfg { | ||
| 117 | __u32 requested_sensors; | ||
| 118 | __u8 ignore_system_suspend; | ||
| 119 | }; | ||
| 120 | |||
| 121 | /* Driver related state information */ | ||
| 122 | struct inv_mpu_state { | ||
| 123 | #define MPU_GYRO_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_GYROSCOPE) | ||
| 124 | #define MPU_ACCEL_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_ACCEL) | ||
| 125 | #define MPU_COMPASS_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_COMPASS) | ||
| 126 | #define MPU_PRESSURE_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_PRESSURE) | ||
| 127 | #define MPU_GYRO_IS_BYPASSED (0x10) | ||
| 128 | #define MPU_DMP_IS_SUSPENDED (0x20) | ||
| 129 | #define MPU_GYRO_NEEDS_CONFIG (0x40) | ||
| 130 | #define MPU_DEVICE_IS_SUSPENDED (0x80) | ||
| 131 | __u8 status; | ||
| 132 | /* 0-1 for 3050, bitfield of BIT_SLVx_DLY_EN, x = [0..4] */ | ||
| 133 | __u8 i2c_slaves_enabled; | ||
| 134 | }; | ||
| 135 | |||
| 136 | /* Platform data for the MPU */ | ||
| 137 | struct mldl_cfg { | ||
| 138 | struct mpu_ram *mpu_ram; | ||
| 139 | struct mpu_gyro_cfg *mpu_gyro_cfg; | ||
| 140 | struct mpu_offsets *mpu_offsets; | ||
| 141 | struct mpu_chip_info *mpu_chip_info; | ||
| 142 | |||
| 143 | /* MPU Related stored status and info */ | ||
| 144 | struct inv_mpu_cfg *inv_mpu_cfg; | ||
| 145 | struct inv_mpu_state *inv_mpu_state; | ||
| 146 | |||
| 147 | /* Slave related information */ | ||
| 148 | struct ext_slave_descr *slave[EXT_SLAVE_NUM_TYPES]; | ||
| 149 | /* Platform Data */ | ||
| 150 | struct mpu_platform_data *pdata; | ||
| 151 | struct ext_slave_platform_data *pdata_slave[EXT_SLAVE_NUM_TYPES]; | ||
| 152 | }; | ||
| 153 | |||
| 154 | /* -------------------------------------------------------------------------- */ | ||
| 155 | |||
| 156 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
| 157 | void *mlsl_handle, | ||
| 158 | void *accel_handle, | ||
| 159 | void *compass_handle, | ||
| 160 | void *pressure_handle); | ||
| 161 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
| 162 | void *mlsl_handle, | ||
| 163 | void *accel_handle, | ||
| 164 | void *compass_handle, | ||
| 165 | void *pressure_handle); | ||
| 166 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
| 167 | void *gyro_handle, | ||
| 168 | void *accel_handle, | ||
| 169 | void *compass_handle, | ||
| 170 | void *pressure_handle, | ||
| 171 | unsigned long sensors); | ||
| 172 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
| 173 | void *gyro_handle, | ||
| 174 | void *accel_handle, | ||
| 175 | void *compass_handle, | ||
| 176 | void *pressure_handle, | ||
| 177 | unsigned long sensors); | ||
| 178 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, | ||
| 179 | void *mlsl_handle, | ||
| 180 | const unsigned char *data, | ||
| 181 | int size); | ||
| 182 | |||
| 183 | /* -------------------------------------------------------------------------- */ | ||
| 184 | /* Slave Read functions */ | ||
| 185 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
| 186 | void *gyro_handle, | ||
| 187 | void *slave_handle, | ||
| 188 | struct ext_slave_descr *slave, | ||
| 189 | struct ext_slave_platform_data *pdata, | ||
| 190 | unsigned char *data); | ||
| 191 | static inline int inv_mpu_read_accel(struct mldl_cfg *mldl_cfg, | ||
| 192 | void *gyro_handle, | ||
| 193 | void *accel_handle, unsigned char *data) | ||
| 194 | { | ||
| 195 | if (!mldl_cfg) { | ||
| 196 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 197 | return INV_ERROR_INVALID_PARAMETER; | ||
| 198 | } | ||
| 199 | |||
| 200 | return inv_mpu_slave_read( | ||
| 201 | mldl_cfg, gyro_handle, accel_handle, | ||
| 202 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 203 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 204 | data); | ||
| 205 | } | ||
| 206 | |||
| 207 | static inline int inv_mpu_read_compass(struct mldl_cfg *mldl_cfg, | ||
| 208 | void *gyro_handle, | ||
| 209 | void *compass_handle, | ||
| 210 | unsigned char *data) | ||
| 211 | { | ||
| 212 | if (!mldl_cfg) { | ||
| 213 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 214 | return INV_ERROR_INVALID_PARAMETER; | ||
| 215 | } | ||
| 216 | |||
| 217 | return inv_mpu_slave_read( | ||
| 218 | mldl_cfg, gyro_handle, compass_handle, | ||
| 219 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 220 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 221 | data); | ||
| 222 | } | ||
| 223 | |||
| 224 | static inline int inv_mpu_read_pressure(struct mldl_cfg *mldl_cfg, | ||
| 225 | void *gyro_handle, | ||
| 226 | void *pressure_handle, | ||
| 227 | unsigned char *data) | ||
| 228 | { | ||
| 229 | if (!mldl_cfg) { | ||
| 230 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 231 | return INV_ERROR_INVALID_PARAMETER; | ||
| 232 | } | ||
| 233 | |||
| 234 | return inv_mpu_slave_read( | ||
| 235 | mldl_cfg, gyro_handle, pressure_handle, | ||
| 236 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 237 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 238 | data); | ||
| 239 | } | ||
| 240 | |||
| 241 | int gyro_config(void *mlsl_handle, | ||
| 242 | struct mldl_cfg *mldl_cfg, | ||
| 243 | struct ext_slave_config *data); | ||
| 244 | |||
| 245 | /* Slave Config functions */ | ||
| 246 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 247 | void *gyro_handle, | ||
| 248 | void *slave_handle, | ||
| 249 | struct ext_slave_config *data, | ||
| 250 | struct ext_slave_descr *slave, | ||
| 251 | struct ext_slave_platform_data *pdata); | ||
| 252 | static inline int inv_mpu_config_accel(struct mldl_cfg *mldl_cfg, | ||
| 253 | void *gyro_handle, | ||
| 254 | void *accel_handle, | ||
| 255 | struct ext_slave_config *data) | ||
| 256 | { | ||
| 257 | if (!mldl_cfg) { | ||
| 258 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 259 | return INV_ERROR_INVALID_PARAMETER; | ||
| 260 | } | ||
| 261 | |||
| 262 | return inv_mpu_slave_config( | ||
| 263 | mldl_cfg, gyro_handle, accel_handle, data, | ||
| 264 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 265 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]); | ||
| 266 | } | ||
| 267 | |||
| 268 | static inline int inv_mpu_config_compass(struct mldl_cfg *mldl_cfg, | ||
| 269 | void *gyro_handle, | ||
| 270 | void *compass_handle, | ||
| 271 | struct ext_slave_config *data) | ||
| 272 | { | ||
| 273 | if (!mldl_cfg) { | ||
| 274 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 275 | return INV_ERROR_INVALID_PARAMETER; | ||
| 276 | } | ||
| 277 | |||
| 278 | return inv_mpu_slave_config( | ||
| 279 | mldl_cfg, gyro_handle, compass_handle, data, | ||
| 280 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 281 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]); | ||
| 282 | } | ||
| 283 | |||
| 284 | static inline int inv_mpu_config_pressure(struct mldl_cfg *mldl_cfg, | ||
| 285 | void *gyro_handle, | ||
| 286 | void *pressure_handle, | ||
| 287 | struct ext_slave_config *data) | ||
| 288 | { | ||
| 289 | if (!mldl_cfg) { | ||
| 290 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 291 | return INV_ERROR_INVALID_PARAMETER; | ||
| 292 | } | ||
| 293 | |||
| 294 | return inv_mpu_slave_config( | ||
| 295 | mldl_cfg, gyro_handle, pressure_handle, data, | ||
| 296 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 297 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]); | ||
| 298 | } | ||
| 299 | |||
| 300 | int gyro_get_config(void *mlsl_handle, | ||
| 301 | struct mldl_cfg *mldl_cfg, | ||
| 302 | struct ext_slave_config *data); | ||
| 303 | |||
| 304 | /* Slave get config functions */ | ||
| 305 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 306 | void *gyro_handle, | ||
| 307 | void *slave_handle, | ||
| 308 | struct ext_slave_config *data, | ||
| 309 | struct ext_slave_descr *slave, | ||
| 310 | struct ext_slave_platform_data *pdata); | ||
| 311 | |||
| 312 | static inline int inv_mpu_get_accel_config(struct mldl_cfg *mldl_cfg, | ||
| 313 | void *gyro_handle, | ||
| 314 | void *accel_handle, | ||
| 315 | struct ext_slave_config *data) | ||
| 316 | { | ||
| 317 | if (!mldl_cfg) { | ||
| 318 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 319 | return INV_ERROR_INVALID_PARAMETER; | ||
| 320 | } | ||
| 321 | |||
| 322 | return inv_mpu_get_slave_config( | ||
| 323 | mldl_cfg, gyro_handle, accel_handle, data, | ||
| 324 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 325 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]); | ||
| 326 | } | ||
| 327 | |||
| 328 | static inline int inv_mpu_get_compass_config(struct mldl_cfg *mldl_cfg, | ||
| 329 | void *gyro_handle, | ||
| 330 | void *compass_handle, | ||
| 331 | struct ext_slave_config *data) | ||
| 332 | { | ||
| 333 | if (!mldl_cfg || !(mldl_cfg->pdata)) { | ||
| 334 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 335 | return INV_ERROR_INVALID_PARAMETER; | ||
| 336 | } | ||
| 337 | |||
| 338 | return inv_mpu_get_slave_config( | ||
| 339 | mldl_cfg, gyro_handle, compass_handle, data, | ||
| 340 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 341 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]); | ||
| 342 | } | ||
| 343 | |||
| 344 | static inline int inv_mpu_get_pressure_config(struct mldl_cfg *mldl_cfg, | ||
| 345 | void *gyro_handle, | ||
| 346 | void *pressure_handle, | ||
| 347 | struct ext_slave_config *data) | ||
| 348 | { | ||
| 349 | if (!mldl_cfg || !(mldl_cfg->pdata)) { | ||
| 350 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 351 | return INV_ERROR_INVALID_PARAMETER; | ||
| 352 | } | ||
| 353 | |||
| 354 | return inv_mpu_get_slave_config( | ||
| 355 | mldl_cfg, gyro_handle, pressure_handle, data, | ||
| 356 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 357 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]); | ||
| 358 | } | ||
| 359 | |||
| 360 | /* -------------------------------------------------------------------------- */ | ||
| 361 | |||
| 362 | static inline | ||
| 363 | long inv_mpu_get_sampling_rate_hz(struct mpu_gyro_cfg *gyro_cfg) | ||
| 364 | { | ||
| 365 | if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7)) | ||
| 366 | return 8000L / (gyro_cfg->divider + 1); | ||
| 367 | else | ||
| 368 | return 1000L / (gyro_cfg->divider + 1); | ||
| 369 | } | ||
| 370 | |||
| 371 | static inline | ||
| 372 | long inv_mpu_get_sampling_period_us(struct mpu_gyro_cfg *gyro_cfg) | ||
| 373 | { | ||
| 374 | if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7)) | ||
| 375 | return (long) (1000000L * (gyro_cfg->divider + 1)) / 8000L; | ||
| 376 | else | ||
| 377 | return (long) (1000000L * (gyro_cfg->divider + 1)) / 1000L; | ||
| 378 | } | ||
| 379 | |||
| 380 | #endif /* __MLDL_CFG_H__ */ | ||
| 381 | |||
| 382 | /** | ||
| 383 | * @} | ||
| 384 | */ | ||
diff --git a/drivers/misc/inv_mpu/mldl_print_cfg.h b/drivers/misc/inv_mpu/mldl_print_cfg.h new file mode 100644 index 00000000000..2e1911440cc --- /dev/null +++ b/drivers/misc/inv_mpu/mldl_print_cfg.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup | ||
| 22 | * @brief | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mldl_print_cfg.h | ||
| 26 | * @brief | ||
| 27 | * | ||
| 28 | * | ||
| 29 | */ | ||
| 30 | #ifndef __MLDL_PRINT_CFG__ | ||
| 31 | #define __MLDL_PRINT_CFG__ | ||
| 32 | |||
| 33 | #include "mldl_cfg.h" | ||
| 34 | |||
| 35 | |||
| 36 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg); | ||
| 37 | |||
| 38 | #endif /* __MLDL_PRINT_CFG__ */ | ||
diff --git a/drivers/misc/inv_mpu/mlsl.h b/drivers/misc/inv_mpu/mlsl.h new file mode 100644 index 00000000000..204baedc1e2 --- /dev/null +++ b/drivers/misc/inv_mpu/mlsl.h | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MLSL_H__ | ||
| 21 | #define __MLSL_H__ | ||
| 22 | |||
| 23 | /** | ||
| 24 | * @defgroup MLSL | ||
| 25 | * @brief Motion Library - Serial Layer. | ||
| 26 | * The Motion Library System Layer provides the Motion Library | ||
| 27 | * with the communication interface to the hardware. | ||
| 28 | * | ||
| 29 | * The communication interface is assumed to support serial | ||
| 30 | * transfers in burst of variable length up to | ||
| 31 | * SERIAL_MAX_TRANSFER_SIZE. | ||
| 32 | * The default value for SERIAL_MAX_TRANSFER_SIZE is 128 bytes. | ||
| 33 | * Transfers of length greater than SERIAL_MAX_TRANSFER_SIZE, will | ||
| 34 | * be subdivided in smaller transfers of length <= | ||
| 35 | * SERIAL_MAX_TRANSFER_SIZE. | ||
| 36 | * The SERIAL_MAX_TRANSFER_SIZE definition can be modified to | ||
| 37 | * overcome any host processor transfer size limitation down to | ||
| 38 | * 1 B, the minimum. | ||
| 39 | * An higher value for SERIAL_MAX_TRANSFER_SIZE will favor | ||
| 40 | * performance and efficiency while requiring higher resource usage | ||
| 41 | * (mostly buffering). A smaller value will increase overhead and | ||
| 42 | * decrease efficiency but allows to operate with more resource | ||
| 43 | * constrained processor and master serial controllers. | ||
| 44 | * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the | ||
| 45 | * mlsl.h header file and master serial controllers. | ||
| 46 | * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the | ||
| 47 | * mlsl.h header file. | ||
| 48 | * | ||
| 49 | * @{ | ||
| 50 | * @file mlsl.h | ||
| 51 | * @brief The Motion Library System Layer. | ||
| 52 | * | ||
| 53 | */ | ||
| 54 | |||
| 55 | #include "mltypes.h" | ||
| 56 | #include <linux/mpu.h> | ||
| 57 | |||
| 58 | |||
| 59 | /* | ||
| 60 | * NOTE : to properly support Yamaha compass reads, | ||
| 61 | * the max transfer size should be at least 9 B. | ||
| 62 | * Length in bytes, typically a power of 2 >= 2 | ||
| 63 | */ | ||
| 64 | #define SERIAL_MAX_TRANSFER_SIZE 128 | ||
| 65 | |||
| 66 | |||
| 67 | /** | ||
| 68 | * inv_serial_single_write() - used to write a single byte of data. | ||
| 69 | * @sl_handle pointer to the serial device used for the communication. | ||
| 70 | * @slave_addr I2C slave address of device. | ||
| 71 | * @register_addr Register address to write. | ||
| 72 | * @data Single byte of data to write. | ||
| 73 | * | ||
| 74 | * It is called by the MPL to write a single byte of data to the MPU. | ||
| 75 | * | ||
| 76 | * returns INV_SUCCESS if successful, a non-zero error code otherwise. | ||
| 77 | */ | ||
| 78 | int inv_serial_single_write( | ||
| 79 | void *sl_handle, | ||
| 80 | unsigned char slave_addr, | ||
| 81 | unsigned char register_addr, | ||
| 82 | unsigned char data); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * inv_serial_write() - used to write multiple bytes of data to registers. | ||
| 86 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 87 | * @slave_addr I2C slave address of device. | ||
| 88 | * @register_addr Register address to write. | ||
| 89 | * @length Length of burst of data. | ||
| 90 | * @data Pointer to block of data. | ||
| 91 | * | ||
| 92 | * returns INV_SUCCESS if successful, a non-zero error code otherwise. | ||
| 93 | */ | ||
| 94 | int inv_serial_write( | ||
| 95 | void *sl_handle, | ||
| 96 | unsigned char slave_addr, | ||
| 97 | unsigned short length, | ||
| 98 | unsigned char const *data); | ||
| 99 | |||
| 100 | /** | ||
| 101 | * inv_serial_read() - used to read multiple bytes of data from registers. | ||
| 102 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 103 | * @slave_addr I2C slave address of device. | ||
| 104 | * @register_addr Register address to read. | ||
| 105 | * @length Length of burst of data. | ||
| 106 | * @data Pointer to block of data. | ||
| 107 | * | ||
| 108 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
| 109 | */ | ||
| 110 | int inv_serial_read( | ||
| 111 | void *sl_handle, | ||
| 112 | unsigned char slave_addr, | ||
| 113 | unsigned char register_addr, | ||
| 114 | unsigned short length, | ||
| 115 | unsigned char *data); | ||
| 116 | |||
| 117 | /** | ||
| 118 | * inv_serial_read_mem() - used to read multiple bytes of data from the memory. | ||
| 119 | * This should be sent by I2C or SPI. | ||
| 120 | * | ||
| 121 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 122 | * @slave_addr I2C slave address of device. | ||
| 123 | * @mem_addr The location in the memory to read from. | ||
| 124 | * @length Length of burst data. | ||
| 125 | * @data Pointer to block of data. | ||
| 126 | * | ||
| 127 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
| 128 | */ | ||
| 129 | int inv_serial_read_mem( | ||
| 130 | void *sl_handle, | ||
| 131 | unsigned char slave_addr, | ||
| 132 | unsigned short mem_addr, | ||
| 133 | unsigned short length, | ||
| 134 | unsigned char *data); | ||
| 135 | |||
| 136 | /** | ||
| 137 | * inv_serial_write_mem() - used to write multiple bytes of data to the memory. | ||
| 138 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 139 | * @slave_addr I2C slave address of device. | ||
| 140 | * @mem_addr The location in the memory to write to. | ||
| 141 | * @length Length of burst data. | ||
| 142 | * @data Pointer to block of data. | ||
| 143 | * | ||
| 144 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
| 145 | */ | ||
| 146 | int inv_serial_write_mem( | ||
| 147 | void *sl_handle, | ||
| 148 | unsigned char slave_addr, | ||
| 149 | unsigned short mem_addr, | ||
| 150 | unsigned short length, | ||
| 151 | unsigned char const *data); | ||
| 152 | |||
| 153 | /** | ||
| 154 | * inv_serial_read_fifo() - used to read multiple bytes of data from the fifo. | ||
| 155 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 156 | * @slave_addr I2C slave address of device. | ||
| 157 | * @length Length of burst of data. | ||
| 158 | * @data Pointer to block of data. | ||
| 159 | * | ||
| 160 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
| 161 | */ | ||
| 162 | int inv_serial_read_fifo( | ||
| 163 | void *sl_handle, | ||
| 164 | unsigned char slave_addr, | ||
| 165 | unsigned short length, | ||
| 166 | unsigned char *data); | ||
| 167 | |||
| 168 | /** | ||
| 169 | * inv_serial_write_fifo() - used to write multiple bytes of data to the fifo. | ||
| 170 | * @sl_handle a file handle to the serial device used for the communication. | ||
| 171 | * @slave_addr I2C slave address of device. | ||
| 172 | * @length Length of burst of data. | ||
| 173 | * @data Pointer to block of data. | ||
| 174 | * | ||
| 175 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
| 176 | */ | ||
| 177 | int inv_serial_write_fifo( | ||
| 178 | void *sl_handle, | ||
| 179 | unsigned char slave_addr, | ||
| 180 | unsigned short length, | ||
| 181 | unsigned char const *data); | ||
| 182 | |||
| 183 | /** | ||
| 184 | * @} | ||
| 185 | */ | ||
| 186 | #endif /* __MLSL_H__ */ | ||
diff --git a/drivers/misc/inv_mpu/mltypes.h b/drivers/misc/inv_mpu/mltypes.h new file mode 100644 index 00000000000..a249f93be3e --- /dev/null +++ b/drivers/misc/inv_mpu/mltypes.h | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup MLERROR | ||
| 22 | * @brief Definition of the error codes used within the MPL and | ||
| 23 | * returned to the user. | ||
| 24 | * Every function tries to return a meaningful error code basing | ||
| 25 | * on the occuring error condition. The error code is numeric. | ||
| 26 | * | ||
| 27 | * The available error codes and their associated values are: | ||
| 28 | * - (0) INV_SUCCESS | ||
| 29 | * - (32) INV_ERROR | ||
| 30 | * - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER | ||
| 31 | * - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED | ||
| 32 | * - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED | ||
| 33 | * - (38) INV_ERROR_DMP_NOT_STARTED | ||
| 34 | * - (39) INV_ERROR_DMP_STARTED | ||
| 35 | * - (40) INV_ERROR_NOT_OPENED | ||
| 36 | * - (41) INV_ERROR_OPENED | ||
| 37 | * - (19 / ENODEV) INV_ERROR_INVALID_MODULE | ||
| 38 | * - (12 / ENOMEM) INV_ERROR_MEMORY_EXAUSTED | ||
| 39 | * - (44) INV_ERROR_DIVIDE_BY_ZERO | ||
| 40 | * - (45) INV_ERROR_ASSERTION_FAILURE | ||
| 41 | * - (46) INV_ERROR_FILE_OPEN | ||
| 42 | * - (47) INV_ERROR_FILE_READ | ||
| 43 | * - (48) INV_ERROR_FILE_WRITE | ||
| 44 | * - (49) INV_ERROR_INVALID_CONFIGURATION | ||
| 45 | * - (52) INV_ERROR_SERIAL_CLOSED | ||
| 46 | * - (53) INV_ERROR_SERIAL_OPEN_ERROR | ||
| 47 | * - (54) INV_ERROR_SERIAL_READ | ||
| 48 | * - (55) INV_ERROR_SERIAL_WRITE | ||
| 49 | * - (56) INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
| 50 | * - (57) INV_ERROR_SM_TRANSITION | ||
| 51 | * - (58) INV_ERROR_SM_IMPROPER_STATE | ||
| 52 | * - (62) INV_ERROR_FIFO_OVERFLOW | ||
| 53 | * - (63) INV_ERROR_FIFO_FOOTER | ||
| 54 | * - (64) INV_ERROR_FIFO_READ_COUNT | ||
| 55 | * - (65) INV_ERROR_FIFO_READ_DATA | ||
| 56 | * - (72) INV_ERROR_MEMORY_SET | ||
| 57 | * - (82) INV_ERROR_LOG_MEMORY_ERROR | ||
| 58 | * - (83) INV_ERROR_LOG_OUTPUT_ERROR | ||
| 59 | * - (92) INV_ERROR_OS_BAD_PTR | ||
| 60 | * - (93) INV_ERROR_OS_BAD_HANDLE | ||
| 61 | * - (94) INV_ERROR_OS_CREATE_FAILED | ||
| 62 | * - (95) INV_ERROR_OS_LOCK_FAILED | ||
| 63 | * - (102) INV_ERROR_COMPASS_DATA_OVERFLOW | ||
| 64 | * - (103) INV_ERROR_COMPASS_DATA_UNDERFLOW | ||
| 65 | * - (104) INV_ERROR_COMPASS_DATA_NOT_READY | ||
| 66 | * - (105) INV_ERROR_COMPASS_DATA_ERROR | ||
| 67 | * - (107) INV_ERROR_CALIBRATION_LOAD | ||
| 68 | * - (108) INV_ERROR_CALIBRATION_STORE | ||
| 69 | * - (109) INV_ERROR_CALIBRATION_LEN | ||
| 70 | * - (110) INV_ERROR_CALIBRATION_CHECKSUM | ||
| 71 | * - (111) INV_ERROR_ACCEL_DATA_OVERFLOW | ||
| 72 | * - (112) INV_ERROR_ACCEL_DATA_UNDERFLOW | ||
| 73 | * - (113) INV_ERROR_ACCEL_DATA_NOT_READY | ||
| 74 | * - (114) INV_ERROR_ACCEL_DATA_ERROR | ||
| 75 | * | ||
| 76 | * The available warning codes and their associated values are: | ||
| 77 | * - (115) INV_WARNING_MOTION_RACE | ||
| 78 | * - (116) INV_WARNING_QUAT_TRASHED | ||
| 79 | * | ||
| 80 | * @{ | ||
| 81 | * @file mltypes.h | ||
| 82 | * @} | ||
| 83 | */ | ||
| 84 | |||
| 85 | #ifndef MLTYPES_H | ||
| 86 | #define MLTYPES_H | ||
| 87 | |||
| 88 | #include <linux/types.h> | ||
| 89 | #include <asm-generic/errno-base.h> | ||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | /*--------------------------- | ||
| 95 | * ML Defines | ||
| 96 | *--------------------------*/ | ||
| 97 | |||
| 98 | #ifndef NULL | ||
| 99 | #define NULL 0 | ||
| 100 | #endif | ||
| 101 | |||
| 102 | /* - ML Errors. - */ | ||
| 103 | #define ERROR_NAME(x) (#x) | ||
| 104 | #define ERROR_CHECK_FIRST(first, x) \ | ||
| 105 | { if (INV_SUCCESS == first) first = x; } | ||
| 106 | |||
| 107 | #define INV_SUCCESS (0) | ||
| 108 | /* Generic Error code. Proprietary Error Codes only */ | ||
| 109 | #define INV_ERROR_BASE (0x20) | ||
| 110 | #define INV_ERROR (INV_ERROR_BASE) | ||
| 111 | |||
| 112 | /* Compatibility and other generic error codes */ | ||
| 113 | #define INV_ERROR_INVALID_PARAMETER (EINVAL) | ||
| 114 | #define INV_ERROR_FEATURE_NOT_ENABLED (EPERM) | ||
| 115 | #define INV_ERROR_FEATURE_NOT_IMPLEMENTED (INV_ERROR_BASE + 4) | ||
| 116 | #define INV_ERROR_DMP_NOT_STARTED (INV_ERROR_BASE + 6) | ||
| 117 | #define INV_ERROR_DMP_STARTED (INV_ERROR_BASE + 7) | ||
| 118 | #define INV_ERROR_NOT_OPENED (INV_ERROR_BASE + 8) | ||
| 119 | #define INV_ERROR_OPENED (INV_ERROR_BASE + 9) | ||
| 120 | #define INV_ERROR_INVALID_MODULE (ENODEV) | ||
| 121 | #define INV_ERROR_MEMORY_EXAUSTED (ENOMEM) | ||
| 122 | #define INV_ERROR_DIVIDE_BY_ZERO (INV_ERROR_BASE + 12) | ||
| 123 | #define INV_ERROR_ASSERTION_FAILURE (INV_ERROR_BASE + 13) | ||
| 124 | #define INV_ERROR_FILE_OPEN (INV_ERROR_BASE + 14) | ||
| 125 | #define INV_ERROR_FILE_READ (INV_ERROR_BASE + 15) | ||
| 126 | #define INV_ERROR_FILE_WRITE (INV_ERROR_BASE + 16) | ||
| 127 | #define INV_ERROR_INVALID_CONFIGURATION (INV_ERROR_BASE + 17) | ||
| 128 | |||
| 129 | /* Serial Communication */ | ||
| 130 | #define INV_ERROR_SERIAL_CLOSED (INV_ERROR_BASE + 20) | ||
| 131 | #define INV_ERROR_SERIAL_OPEN_ERROR (INV_ERROR_BASE + 21) | ||
| 132 | #define INV_ERROR_SERIAL_READ (INV_ERROR_BASE + 22) | ||
| 133 | #define INV_ERROR_SERIAL_WRITE (INV_ERROR_BASE + 23) | ||
| 134 | #define INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (INV_ERROR_BASE + 24) | ||
| 135 | |||
| 136 | /* SM = State Machine */ | ||
| 137 | #define INV_ERROR_SM_TRANSITION (INV_ERROR_BASE + 25) | ||
| 138 | #define INV_ERROR_SM_IMPROPER_STATE (INV_ERROR_BASE + 26) | ||
| 139 | |||
| 140 | /* Fifo */ | ||
| 141 | #define INV_ERROR_FIFO_OVERFLOW (INV_ERROR_BASE + 30) | ||
| 142 | #define INV_ERROR_FIFO_FOOTER (INV_ERROR_BASE + 31) | ||
| 143 | #define INV_ERROR_FIFO_READ_COUNT (INV_ERROR_BASE + 32) | ||
| 144 | #define INV_ERROR_FIFO_READ_DATA (INV_ERROR_BASE + 33) | ||
| 145 | |||
| 146 | /* Memory & Registers, Set & Get */ | ||
| 147 | #define INV_ERROR_MEMORY_SET (INV_ERROR_BASE + 40) | ||
| 148 | |||
| 149 | #define INV_ERROR_LOG_MEMORY_ERROR (INV_ERROR_BASE + 50) | ||
| 150 | #define INV_ERROR_LOG_OUTPUT_ERROR (INV_ERROR_BASE + 51) | ||
| 151 | |||
| 152 | /* OS interface errors */ | ||
| 153 | #define INV_ERROR_OS_BAD_PTR (INV_ERROR_BASE + 60) | ||
| 154 | #define INV_ERROR_OS_BAD_HANDLE (INV_ERROR_BASE + 61) | ||
| 155 | #define INV_ERROR_OS_CREATE_FAILED (INV_ERROR_BASE + 62) | ||
| 156 | #define INV_ERROR_OS_LOCK_FAILED (INV_ERROR_BASE + 63) | ||
| 157 | |||
| 158 | /* Compass errors */ | ||
| 159 | #define INV_ERROR_COMPASS_DATA_OVERFLOW (INV_ERROR_BASE + 70) | ||
| 160 | #define INV_ERROR_COMPASS_DATA_UNDERFLOW (INV_ERROR_BASE + 71) | ||
| 161 | #define INV_ERROR_COMPASS_DATA_NOT_READY (INV_ERROR_BASE + 72) | ||
| 162 | #define INV_ERROR_COMPASS_DATA_ERROR (INV_ERROR_BASE + 73) | ||
| 163 | |||
| 164 | /* Load/Store calibration */ | ||
| 165 | #define INV_ERROR_CALIBRATION_LOAD (INV_ERROR_BASE + 75) | ||
| 166 | #define INV_ERROR_CALIBRATION_STORE (INV_ERROR_BASE + 76) | ||
| 167 | #define INV_ERROR_CALIBRATION_LEN (INV_ERROR_BASE + 77) | ||
| 168 | #define INV_ERROR_CALIBRATION_CHECKSUM (INV_ERROR_BASE + 78) | ||
| 169 | |||
| 170 | /* Accel errors */ | ||
| 171 | #define INV_ERROR_ACCEL_DATA_OVERFLOW (INV_ERROR_BASE + 79) | ||
| 172 | #define INV_ERROR_ACCEL_DATA_UNDERFLOW (INV_ERROR_BASE + 80) | ||
| 173 | #define INV_ERROR_ACCEL_DATA_NOT_READY (INV_ERROR_BASE + 81) | ||
| 174 | #define INV_ERROR_ACCEL_DATA_ERROR (INV_ERROR_BASE + 82) | ||
| 175 | |||
| 176 | /* No Motion Warning States */ | ||
| 177 | #define INV_WARNING_MOTION_RACE (INV_ERROR_BASE + 83) | ||
| 178 | #define INV_WARNING_QUAT_TRASHED (INV_ERROR_BASE + 84) | ||
| 179 | #define INV_WARNING_GYRO_MAG (INV_ERROR_BASE + 85) | ||
| 180 | |||
| 181 | #ifdef INV_USE_LEGACY_NAMES | ||
| 182 | #define ML_SUCCESS INV_SUCCESS | ||
| 183 | #define ML_ERROR INV_ERROR | ||
| 184 | #define ML_ERROR_INVALID_PARAMETER INV_ERROR_INVALID_PARAMETER | ||
| 185 | #define ML_ERROR_FEATURE_NOT_ENABLED INV_ERROR_FEATURE_NOT_ENABLED | ||
| 186 | #define ML_ERROR_FEATURE_NOT_IMPLEMENTED INV_ERROR_FEATURE_NOT_IMPLEMENTED | ||
| 187 | #define ML_ERROR_DMP_NOT_STARTED INV_ERROR_DMP_NOT_STARTED | ||
| 188 | #define ML_ERROR_DMP_STARTED INV_ERROR_DMP_STARTED | ||
| 189 | #define ML_ERROR_NOT_OPENED INV_ERROR_NOT_OPENED | ||
| 190 | #define ML_ERROR_OPENED INV_ERROR_OPENED | ||
| 191 | #define ML_ERROR_INVALID_MODULE INV_ERROR_INVALID_MODULE | ||
| 192 | #define ML_ERROR_MEMORY_EXAUSTED INV_ERROR_MEMORY_EXAUSTED | ||
| 193 | #define ML_ERROR_DIVIDE_BY_ZERO INV_ERROR_DIVIDE_BY_ZERO | ||
| 194 | #define ML_ERROR_ASSERTION_FAILURE INV_ERROR_ASSERTION_FAILURE | ||
| 195 | #define ML_ERROR_FILE_OPEN INV_ERROR_FILE_OPEN | ||
| 196 | #define ML_ERROR_FILE_READ INV_ERROR_FILE_READ | ||
| 197 | #define ML_ERROR_FILE_WRITE INV_ERROR_FILE_WRITE | ||
| 198 | #define ML_ERROR_INVALID_CONFIGURATION INV_ERROR_INVALID_CONFIGURATION | ||
| 199 | #define ML_ERROR_SERIAL_CLOSED INV_ERROR_SERIAL_CLOSED | ||
| 200 | #define ML_ERROR_SERIAL_OPEN_ERROR INV_ERROR_SERIAL_OPEN_ERROR | ||
| 201 | #define ML_ERROR_SERIAL_READ INV_ERROR_SERIAL_READ | ||
| 202 | #define ML_ERROR_SERIAL_WRITE INV_ERROR_SERIAL_WRITE | ||
| 203 | #define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED \ | ||
| 204 | INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
| 205 | #define ML_ERROR_SM_TRANSITION INV_ERROR_SM_TRANSITION | ||
| 206 | #define ML_ERROR_SM_IMPROPER_STATE INV_ERROR_SM_IMPROPER_STATE | ||
| 207 | #define ML_ERROR_FIFO_OVERFLOW INV_ERROR_FIFO_OVERFLOW | ||
| 208 | #define ML_ERROR_FIFO_FOOTER INV_ERROR_FIFO_FOOTER | ||
| 209 | #define ML_ERROR_FIFO_READ_COUNT INV_ERROR_FIFO_READ_COUNT | ||
| 210 | #define ML_ERROR_FIFO_READ_DATA INV_ERROR_FIFO_READ_DATA | ||
| 211 | #define ML_ERROR_MEMORY_SET INV_ERROR_MEMORY_SET | ||
| 212 | #define ML_ERROR_LOG_MEMORY_ERROR INV_ERROR_LOG_MEMORY_ERROR | ||
| 213 | #define ML_ERROR_LOG_OUTPUT_ERROR INV_ERROR_LOG_OUTPUT_ERROR | ||
| 214 | #define ML_ERROR_OS_BAD_PTR INV_ERROR_OS_BAD_PTR | ||
| 215 | #define ML_ERROR_OS_BAD_HANDLE INV_ERROR_OS_BAD_HANDLE | ||
| 216 | #define ML_ERROR_OS_CREATE_FAILED INV_ERROR_OS_CREATE_FAILED | ||
| 217 | #define ML_ERROR_OS_LOCK_FAILED INV_ERROR_OS_LOCK_FAILED | ||
| 218 | #define ML_ERROR_COMPASS_DATA_OVERFLOW INV_ERROR_COMPASS_DATA_OVERFLOW | ||
| 219 | #define ML_ERROR_COMPASS_DATA_UNDERFLOW INV_ERROR_COMPASS_DATA_UNDERFLOW | ||
| 220 | #define ML_ERROR_COMPASS_DATA_NOT_READY INV_ERROR_COMPASS_DATA_NOT_READY | ||
| 221 | #define ML_ERROR_COMPASS_DATA_ERROR INV_ERROR_COMPASS_DATA_ERROR | ||
| 222 | #define ML_ERROR_CALIBRATION_LOAD INV_ERROR_CALIBRATION_LOAD | ||
| 223 | #define ML_ERROR_CALIBRATION_STORE INV_ERROR_CALIBRATION_STORE | ||
| 224 | #define ML_ERROR_CALIBRATION_LEN INV_ERROR_CALIBRATION_LEN | ||
| 225 | #define ML_ERROR_CALIBRATION_CHECKSUM INV_ERROR_CALIBRATION_CHECKSUM | ||
| 226 | #define ML_ERROR_ACCEL_DATA_OVERFLOW INV_ERROR_ACCEL_DATA_OVERFLOW | ||
| 227 | #define ML_ERROR_ACCEL_DATA_UNDERFLOW INV_ERROR_ACCEL_DATA_UNDERFLOW | ||
| 228 | #define ML_ERROR_ACCEL_DATA_NOT_READY INV_ERROR_ACCEL_DATA_NOT_READY | ||
| 229 | #define ML_ERROR_ACCEL_DATA_ERROR INV_ERROR_ACCEL_DATA_ERROR | ||
| 230 | #endif | ||
| 231 | |||
| 232 | /* For Linux coding compliance */ | ||
| 233 | |||
| 234 | #endif /* MLTYPES_H */ | ||
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h new file mode 100644 index 00000000000..b6a4fcfac58 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu-dev.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | |||
| 21 | #ifndef __MPU_DEV_H__ | ||
| 22 | #define __MPU_DEV_H__ | ||
| 23 | |||
| 24 | #include <linux/i2c.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/mpu.h> | ||
| 27 | |||
| 28 | int inv_mpu_register_slave(struct module *slave_module, | ||
| 29 | struct i2c_client *client, | ||
| 30 | struct ext_slave_platform_data *pdata, | ||
| 31 | struct ext_slave_descr *(*slave_descr)(void)); | ||
| 32 | |||
| 33 | void inv_mpu_unregister_slave(struct i2c_client *client, | ||
| 34 | struct ext_slave_platform_data *pdata, | ||
| 35 | struct ext_slave_descr *(*slave_descr)(void)); | ||
| 36 | #endif | ||
diff --git a/drivers/misc/inv_mpu/mpu3050.h b/drivers/misc/inv_mpu/mpu3050.h new file mode 100644 index 00000000000..02af16ed121 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050.h | |||
| @@ -0,0 +1,251 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MPU_H_ | ||
| 21 | #error Do not include this file directly. Include mpu.h instead. | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #ifndef __MPU3050_H_ | ||
| 25 | #define __MPU3050_H_ | ||
| 26 | |||
| 27 | #include <linux/types.h> | ||
| 28 | |||
| 29 | |||
| 30 | #define MPU_NAME "mpu3050" | ||
| 31 | #define DEFAULT_MPU_SLAVEADDR 0x68 | ||
| 32 | |||
| 33 | /*==== MPU REGISTER SET ====*/ | ||
| 34 | enum mpu_register { | ||
| 35 | MPUREG_WHO_AM_I = 0, /* 00 0x00 */ | ||
| 36 | MPUREG_PRODUCT_ID, /* 01 0x01 */ | ||
| 37 | MPUREG_02_RSVD, /* 02 0x02 */ | ||
| 38 | MPUREG_03_RSVD, /* 03 0x03 */ | ||
| 39 | MPUREG_04_RSVD, /* 04 0x04 */ | ||
| 40 | MPUREG_XG_OFFS_TC, /* 05 0x05 */ | ||
| 41 | MPUREG_06_RSVD, /* 06 0x06 */ | ||
| 42 | MPUREG_07_RSVD, /* 07 0x07 */ | ||
| 43 | MPUREG_YG_OFFS_TC, /* 08 0x08 */ | ||
| 44 | MPUREG_09_RSVD, /* 09 0x09 */ | ||
| 45 | MPUREG_0A_RSVD, /* 10 0x0a */ | ||
| 46 | MPUREG_ZG_OFFS_TC, /* 11 0x0b */ | ||
| 47 | MPUREG_X_OFFS_USRH, /* 12 0x0c */ | ||
| 48 | MPUREG_X_OFFS_USRL, /* 13 0x0d */ | ||
| 49 | MPUREG_Y_OFFS_USRH, /* 14 0x0e */ | ||
| 50 | MPUREG_Y_OFFS_USRL, /* 15 0x0f */ | ||
| 51 | MPUREG_Z_OFFS_USRH, /* 16 0x10 */ | ||
| 52 | MPUREG_Z_OFFS_USRL, /* 17 0x11 */ | ||
| 53 | MPUREG_FIFO_EN1, /* 18 0x12 */ | ||
| 54 | MPUREG_FIFO_EN2, /* 19 0x13 */ | ||
| 55 | MPUREG_AUX_SLV_ADDR, /* 20 0x14 */ | ||
| 56 | MPUREG_SMPLRT_DIV, /* 21 0x15 */ | ||
| 57 | MPUREG_DLPF_FS_SYNC, /* 22 0x16 */ | ||
| 58 | MPUREG_INT_CFG, /* 23 0x17 */ | ||
| 59 | MPUREG_ACCEL_BURST_ADDR,/* 24 0x18 */ | ||
| 60 | MPUREG_19_RSVD, /* 25 0x19 */ | ||
| 61 | MPUREG_INT_STATUS, /* 26 0x1a */ | ||
| 62 | MPUREG_TEMP_OUT_H, /* 27 0x1b */ | ||
| 63 | MPUREG_TEMP_OUT_L, /* 28 0x1c */ | ||
| 64 | MPUREG_GYRO_XOUT_H, /* 29 0x1d */ | ||
| 65 | MPUREG_GYRO_XOUT_L, /* 30 0x1e */ | ||
| 66 | MPUREG_GYRO_YOUT_H, /* 31 0x1f */ | ||
| 67 | MPUREG_GYRO_YOUT_L, /* 32 0x20 */ | ||
| 68 | MPUREG_GYRO_ZOUT_H, /* 33 0x21 */ | ||
| 69 | MPUREG_GYRO_ZOUT_L, /* 34 0x22 */ | ||
| 70 | MPUREG_23_RSVD, /* 35 0x23 */ | ||
| 71 | MPUREG_24_RSVD, /* 36 0x24 */ | ||
| 72 | MPUREG_25_RSVD, /* 37 0x25 */ | ||
| 73 | MPUREG_26_RSVD, /* 38 0x26 */ | ||
| 74 | MPUREG_27_RSVD, /* 39 0x27 */ | ||
| 75 | MPUREG_28_RSVD, /* 40 0x28 */ | ||
| 76 | MPUREG_29_RSVD, /* 41 0x29 */ | ||
| 77 | MPUREG_2A_RSVD, /* 42 0x2a */ | ||
| 78 | MPUREG_2B_RSVD, /* 43 0x2b */ | ||
| 79 | MPUREG_2C_RSVD, /* 44 0x2c */ | ||
| 80 | MPUREG_2D_RSVD, /* 45 0x2d */ | ||
| 81 | MPUREG_2E_RSVD, /* 46 0x2e */ | ||
| 82 | MPUREG_2F_RSVD, /* 47 0x2f */ | ||
| 83 | MPUREG_30_RSVD, /* 48 0x30 */ | ||
| 84 | MPUREG_31_RSVD, /* 49 0x31 */ | ||
| 85 | MPUREG_32_RSVD, /* 50 0x32 */ | ||
| 86 | MPUREG_33_RSVD, /* 51 0x33 */ | ||
| 87 | MPUREG_34_RSVD, /* 52 0x34 */ | ||
| 88 | MPUREG_DMP_CFG_1, /* 53 0x35 */ | ||
| 89 | MPUREG_DMP_CFG_2, /* 54 0x36 */ | ||
| 90 | MPUREG_BANK_SEL, /* 55 0x37 */ | ||
| 91 | MPUREG_MEM_START_ADDR, /* 56 0x38 */ | ||
| 92 | MPUREG_MEM_R_W, /* 57 0x39 */ | ||
| 93 | MPUREG_FIFO_COUNTH, /* 58 0x3a */ | ||
| 94 | MPUREG_FIFO_COUNTL, /* 59 0x3b */ | ||
| 95 | MPUREG_FIFO_R_W, /* 60 0x3c */ | ||
| 96 | MPUREG_USER_CTRL, /* 61 0x3d */ | ||
| 97 | MPUREG_PWR_MGM, /* 62 0x3e */ | ||
| 98 | MPUREG_3F_RSVD, /* 63 0x3f */ | ||
| 99 | NUM_OF_MPU_REGISTERS /* 64 0x40 */ | ||
| 100 | }; | ||
| 101 | |||
| 102 | /*==== BITS FOR MPU ====*/ | ||
| 103 | |||
| 104 | /*---- MPU 'FIFO_EN1' register (12) ----*/ | ||
| 105 | #define BIT_TEMP_OUT 0x80 | ||
| 106 | #define BIT_GYRO_XOUT 0x40 | ||
| 107 | #define BIT_GYRO_YOUT 0x20 | ||
| 108 | #define BIT_GYRO_ZOUT 0x10 | ||
| 109 | #define BIT_ACCEL_XOUT 0x08 | ||
| 110 | #define BIT_ACCEL_YOUT 0x04 | ||
| 111 | #define BIT_ACCEL_ZOUT 0x02 | ||
| 112 | #define BIT_AUX_1OUT 0x01 | ||
| 113 | /*---- MPU 'FIFO_EN2' register (13) ----*/ | ||
| 114 | #define BIT_AUX_2OUT 0x02 | ||
| 115 | #define BIT_AUX_3OUT 0x01 | ||
| 116 | /*---- MPU 'DLPF_FS_SYNC' register (16) ----*/ | ||
| 117 | #define BITS_EXT_SYNC_NONE 0x00 | ||
| 118 | #define BITS_EXT_SYNC_TEMP 0x20 | ||
| 119 | #define BITS_EXT_SYNC_GYROX 0x40 | ||
| 120 | #define BITS_EXT_SYNC_GYROY 0x60 | ||
| 121 | #define BITS_EXT_SYNC_GYROZ 0x80 | ||
| 122 | #define BITS_EXT_SYNC_ACCELX 0xA0 | ||
| 123 | #define BITS_EXT_SYNC_ACCELY 0xC0 | ||
| 124 | #define BITS_EXT_SYNC_ACCELZ 0xE0 | ||
| 125 | #define BITS_EXT_SYNC_MASK 0xE0 | ||
| 126 | #define BITS_FS_250DPS 0x00 | ||
| 127 | #define BITS_FS_500DPS 0x08 | ||
| 128 | #define BITS_FS_1000DPS 0x10 | ||
| 129 | #define BITS_FS_2000DPS 0x18 | ||
| 130 | #define BITS_FS_MASK 0x18 | ||
| 131 | #define BITS_DLPF_CFG_256HZ_NOLPF2 0x00 | ||
| 132 | #define BITS_DLPF_CFG_188HZ 0x01 | ||
| 133 | #define BITS_DLPF_CFG_98HZ 0x02 | ||
| 134 | #define BITS_DLPF_CFG_42HZ 0x03 | ||
| 135 | #define BITS_DLPF_CFG_20HZ 0x04 | ||
| 136 | #define BITS_DLPF_CFG_10HZ 0x05 | ||
| 137 | #define BITS_DLPF_CFG_5HZ 0x06 | ||
| 138 | #define BITS_DLPF_CFG_2100HZ_NOLPF 0x07 | ||
| 139 | #define BITS_DLPF_CFG_MASK 0x07 | ||
| 140 | /*---- MPU 'INT_CFG' register (17) ----*/ | ||
| 141 | #define BIT_ACTL 0x80 | ||
| 142 | #define BIT_ACTL_LOW 0x80 | ||
| 143 | #define BIT_ACTL_HIGH 0x00 | ||
| 144 | #define BIT_OPEN 0x40 | ||
| 145 | #define BIT_OPEN_DRAIN 0x40 | ||
| 146 | #define BIT_PUSH_PULL 0x00 | ||
| 147 | #define BIT_LATCH_INT_EN 0x20 | ||
| 148 | #define BIT_INT_PULSE_WIDTH_50US 0x00 | ||
| 149 | #define BIT_INT_ANYRD_2CLEAR 0x10 | ||
| 150 | #define BIT_INT_STAT_READ_2CLEAR 0x00 | ||
| 151 | #define BIT_MPU_RDY_EN 0x04 | ||
| 152 | #define BIT_DMP_INT_EN 0x02 | ||
| 153 | #define BIT_RAW_RDY_EN 0x01 | ||
| 154 | /*---- MPU 'INT_STATUS' register (1A) ----*/ | ||
| 155 | #define BIT_INT_STATUS_FIFO_OVERLOW 0x80 | ||
| 156 | #define BIT_MPU_RDY 0x04 | ||
| 157 | #define BIT_DMP_INT 0x02 | ||
| 158 | #define BIT_RAW_RDY 0x01 | ||
| 159 | /*---- MPU 'BANK_SEL' register (37) ----*/ | ||
| 160 | #define BIT_PRFTCH_EN 0x20 | ||
| 161 | #define BIT_CFG_USER_BANK 0x10 | ||
| 162 | #define BITS_MEM_SEL 0x0f | ||
| 163 | /*---- MPU 'USER_CTRL' register (3D) ----*/ | ||
| 164 | #define BIT_DMP_EN 0x80 | ||
| 165 | #define BIT_FIFO_EN 0x40 | ||
| 166 | #define BIT_AUX_IF_EN 0x20 | ||
| 167 | #define BIT_AUX_RD_LENG 0x10 | ||
| 168 | #define BIT_AUX_IF_RST 0x08 | ||
| 169 | #define BIT_DMP_RST 0x04 | ||
| 170 | #define BIT_FIFO_RST 0x02 | ||
| 171 | #define BIT_GYRO_RST 0x01 | ||
| 172 | /*---- MPU 'PWR_MGM' register (3E) ----*/ | ||
| 173 | #define BIT_H_RESET 0x80 | ||
| 174 | #define BIT_SLEEP 0x40 | ||
| 175 | #define BIT_STBY_XG 0x20 | ||
| 176 | #define BIT_STBY_YG 0x10 | ||
| 177 | #define BIT_STBY_ZG 0x08 | ||
| 178 | #define BITS_CLKSEL 0x07 | ||
| 179 | |||
| 180 | /*---- MPU Silicon Revision ----*/ | ||
| 181 | #define MPU_SILICON_REV_A4 1 /* MPU A4 Device */ | ||
| 182 | #define MPU_SILICON_REV_B1 2 /* MPU B1 Device */ | ||
| 183 | #define MPU_SILICON_REV_B4 3 /* MPU B4 Device */ | ||
| 184 | #define MPU_SILICON_REV_B6 4 /* MPU B6 Device */ | ||
| 185 | |||
| 186 | /*---- MPU Memory ----*/ | ||
| 187 | #define MPU_MEM_BANK_SIZE (256) | ||
| 188 | #define FIFO_HW_SIZE (512) | ||
| 189 | |||
| 190 | enum MPU_MEMORY_BANKS { | ||
| 191 | MPU_MEM_RAM_BANK_0 = 0, | ||
| 192 | MPU_MEM_RAM_BANK_1, | ||
| 193 | MPU_MEM_RAM_BANK_2, | ||
| 194 | MPU_MEM_RAM_BANK_3, | ||
| 195 | MPU_MEM_NUM_RAM_BANKS, | ||
| 196 | MPU_MEM_OTP_BANK_0 = MPU_MEM_NUM_RAM_BANKS, | ||
| 197 | /* This one is always last */ | ||
| 198 | MPU_MEM_NUM_BANKS | ||
| 199 | }; | ||
| 200 | |||
| 201 | /*---- structure containing control variables used by MLDL ----*/ | ||
| 202 | /*---- MPU clock source settings ----*/ | ||
| 203 | /*---- MPU filter selections ----*/ | ||
| 204 | enum mpu_filter { | ||
| 205 | MPU_FILTER_256HZ_NOLPF2 = 0, | ||
| 206 | MPU_FILTER_188HZ, | ||
| 207 | MPU_FILTER_98HZ, | ||
| 208 | MPU_FILTER_42HZ, | ||
| 209 | MPU_FILTER_20HZ, | ||
| 210 | MPU_FILTER_10HZ, | ||
| 211 | MPU_FILTER_5HZ, | ||
| 212 | MPU_FILTER_2100HZ_NOLPF, | ||
| 213 | NUM_MPU_FILTER | ||
| 214 | }; | ||
| 215 | |||
| 216 | enum mpu_fullscale { | ||
| 217 | MPU_FS_250DPS = 0, | ||
| 218 | MPU_FS_500DPS, | ||
| 219 | MPU_FS_1000DPS, | ||
| 220 | MPU_FS_2000DPS, | ||
| 221 | NUM_MPU_FS | ||
| 222 | }; | ||
| 223 | |||
| 224 | enum mpu_clock_sel { | ||
| 225 | MPU_CLK_SEL_INTERNAL = 0, | ||
| 226 | MPU_CLK_SEL_PLLGYROX, | ||
| 227 | MPU_CLK_SEL_PLLGYROY, | ||
| 228 | MPU_CLK_SEL_PLLGYROZ, | ||
| 229 | MPU_CLK_SEL_PLLEXT32K, | ||
| 230 | MPU_CLK_SEL_PLLEXT19M, | ||
| 231 | MPU_CLK_SEL_RESERVED, | ||
| 232 | MPU_CLK_SEL_STOP, | ||
| 233 | NUM_CLK_SEL | ||
| 234 | }; | ||
| 235 | |||
| 236 | enum mpu_ext_sync { | ||
| 237 | MPU_EXT_SYNC_NONE = 0, | ||
| 238 | MPU_EXT_SYNC_TEMP, | ||
| 239 | MPU_EXT_SYNC_GYROX, | ||
| 240 | MPU_EXT_SYNC_GYROY, | ||
| 241 | MPU_EXT_SYNC_GYROZ, | ||
| 242 | MPU_EXT_SYNC_ACCELX, | ||
| 243 | MPU_EXT_SYNC_ACCELY, | ||
| 244 | MPU_EXT_SYNC_ACCELZ, | ||
| 245 | NUM_MPU_EXT_SYNC | ||
| 246 | }; | ||
| 247 | |||
| 248 | #define DLPF_FS_SYNC_VALUE(ext_sync, full_scale, lpf) \ | ||
| 249 | ((ext_sync << 5) | (full_scale << 3) | lpf) | ||
| 250 | |||
| 251 | #endif /* __MPU3050_H_ */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/Makefile b/drivers/misc/inv_mpu/mpu3050/Makefile new file mode 100644 index 00000000000..9e573930238 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/Makefile | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | |||
| 2 | # Kernel makefile for motions sensors | ||
| 3 | # | ||
| 4 | # | ||
| 5 | |||
| 6 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o | ||
| 7 | |||
| 8 | ccflags-y := -DMPU_CURRENT_BUILD_MPU3050 | ||
| 9 | |||
| 10 | mpu3050-objs += mldl_cfg.o | ||
| 11 | mpu3050-objs += mldl_print_cfg.o | ||
| 12 | mpu3050-objs += mlsl-kernel.o | ||
| 13 | mpu3050-objs += mpu-dev.o | ||
| 14 | |||
| 15 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 16 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
| 17 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c new file mode 100644 index 00000000000..ccacc8ec0b5 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c | |||
| @@ -0,0 +1,1765 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_cfg.c | ||
| 25 | * @brief The Motion Library Driver Layer. | ||
| 26 | */ | ||
| 27 | |||
| 28 | /* -------------------------------------------------------------------------- */ | ||
| 29 | #include <linux/delay.h> | ||
| 30 | #include <linux/slab.h> | ||
| 31 | |||
| 32 | #include <stddef.h> | ||
| 33 | |||
| 34 | #include "mldl_cfg.h" | ||
| 35 | #include <linux/mpu.h> | ||
| 36 | #include "mpu3050.h" | ||
| 37 | |||
| 38 | #include "mlsl.h" | ||
| 39 | #include "mldl_print_cfg.h" | ||
| 40 | #include "log.h" | ||
| 41 | #undef MPL_LOG_TAG | ||
| 42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
| 43 | |||
| 44 | /* -------------------------------------------------------------------------- */ | ||
| 45 | |||
| 46 | #define SLEEP 1 | ||
| 47 | #define WAKE_UP 0 | ||
| 48 | #define RESET 1 | ||
| 49 | #define STANDBY 1 | ||
| 50 | |||
| 51 | /* -------------------------------------------------------------------------- */ | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief Stop the DMP running | ||
| 55 | * | ||
| 56 | * @return INV_SUCCESS or non-zero error code | ||
| 57 | */ | ||
| 58 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 59 | { | ||
| 60 | unsigned char user_ctrl_reg; | ||
| 61 | int result; | ||
| 62 | |||
| 63 | if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) | ||
| 64 | return INV_SUCCESS; | ||
| 65 | |||
| 66 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 67 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 68 | if (result) { | ||
| 69 | LOG_RESULT_LOCATION(result); | ||
| 70 | return result; | ||
| 71 | } | ||
| 72 | user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
| 73 | user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
| 74 | |||
| 75 | result = inv_serial_single_write(gyro_handle, | ||
| 76 | mldl_cfg->mpu_chip_info->addr, | ||
| 77 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 78 | if (result) { | ||
| 79 | LOG_RESULT_LOCATION(result); | ||
| 80 | return result; | ||
| 81 | } | ||
| 82 | mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED; | ||
| 83 | |||
| 84 | return result; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * @brief Starts the DMP running | ||
| 89 | * | ||
| 90 | * @return INV_SUCCESS or non-zero error code | ||
| 91 | */ | ||
| 92 | static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
| 93 | { | ||
| 94 | unsigned char user_ctrl_reg; | ||
| 95 | int result; | ||
| 96 | |||
| 97 | if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
| 98 | mldl_cfg->mpu_gyro_cfg->dmp_enable) | ||
| 99 | || | ||
| 100 | ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
| 101 | !mldl_cfg->mpu_gyro_cfg->dmp_enable)) | ||
| 102 | return INV_SUCCESS; | ||
| 103 | |||
| 104 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 105 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 106 | if (result) { | ||
| 107 | LOG_RESULT_LOCATION(result); | ||
| 108 | return result; | ||
| 109 | } | ||
| 110 | |||
| 111 | result = inv_serial_single_write( | ||
| 112 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 113 | MPUREG_USER_CTRL, | ||
| 114 | ((user_ctrl_reg & (~BIT_FIFO_EN)) | ||
| 115 | | BIT_FIFO_RST)); | ||
| 116 | if (result) { | ||
| 117 | LOG_RESULT_LOCATION(result); | ||
| 118 | return result; | ||
| 119 | } | ||
| 120 | |||
| 121 | result = inv_serial_single_write( | ||
| 122 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 123 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 124 | if (result) { | ||
| 125 | LOG_RESULT_LOCATION(result); | ||
| 126 | return result; | ||
| 127 | } | ||
| 128 | |||
| 129 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 130 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 131 | if (result) { | ||
| 132 | LOG_RESULT_LOCATION(result); | ||
| 133 | return result; | ||
| 134 | } | ||
| 135 | |||
| 136 | user_ctrl_reg |= BIT_DMP_EN; | ||
| 137 | |||
| 138 | if (mldl_cfg->mpu_gyro_cfg->fifo_enable) | ||
| 139 | user_ctrl_reg |= BIT_FIFO_EN; | ||
| 140 | else | ||
| 141 | user_ctrl_reg &= ~BIT_FIFO_EN; | ||
| 142 | |||
| 143 | user_ctrl_reg |= BIT_DMP_RST; | ||
| 144 | |||
| 145 | result = inv_serial_single_write( | ||
| 146 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 147 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 148 | if (result) { | ||
| 149 | LOG_RESULT_LOCATION(result); | ||
| 150 | return result; | ||
| 151 | } | ||
| 152 | mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED; | ||
| 153 | |||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg, | ||
| 160 | void *mlsl_handle, unsigned char enable) | ||
| 161 | { | ||
| 162 | unsigned char b; | ||
| 163 | int result; | ||
| 164 | unsigned char status = mldl_cfg->inv_mpu_state->status; | ||
| 165 | |||
| 166 | if ((status & MPU_GYRO_IS_BYPASSED && enable) || | ||
| 167 | (!(status & MPU_GYRO_IS_BYPASSED) && !enable)) | ||
| 168 | return INV_SUCCESS; | ||
| 169 | |||
| 170 | /*---- get current 'USER_CTRL' into b ----*/ | ||
| 171 | result = inv_serial_read( | ||
| 172 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 173 | MPUREG_USER_CTRL, 1, &b); | ||
| 174 | if (result) { | ||
| 175 | LOG_RESULT_LOCATION(result); | ||
| 176 | return result; | ||
| 177 | } | ||
| 178 | |||
| 179 | b &= ~BIT_AUX_IF_EN; | ||
| 180 | |||
| 181 | if (!enable) { | ||
| 182 | result = inv_serial_single_write( | ||
| 183 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 184 | MPUREG_USER_CTRL, | ||
| 185 | (b | BIT_AUX_IF_EN)); | ||
| 186 | if (result) { | ||
| 187 | LOG_RESULT_LOCATION(result); | ||
| 188 | return result; | ||
| 189 | } | ||
| 190 | } else { | ||
| 191 | /* Coming out of I2C is tricky due to several erratta. Do not | ||
| 192 | * modify this algorithm | ||
| 193 | */ | ||
| 194 | /* | ||
| 195 | * 1) wait for the right time and send the command to change | ||
| 196 | * the aux i2c slave address to an invalid address that will | ||
| 197 | * get nack'ed | ||
| 198 | * | ||
| 199 | * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. | ||
| 200 | */ | ||
| 201 | result = inv_serial_single_write( | ||
| 202 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 203 | MPUREG_AUX_SLV_ADDR, 0x7F); | ||
| 204 | if (result) { | ||
| 205 | LOG_RESULT_LOCATION(result); | ||
| 206 | return result; | ||
| 207 | } | ||
| 208 | /* | ||
| 209 | * 2) wait enough time for a nack to occur, then go into | ||
| 210 | * bypass mode: | ||
| 211 | */ | ||
| 212 | msleep(2); | ||
| 213 | result = inv_serial_single_write( | ||
| 214 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 215 | MPUREG_USER_CTRL, (b)); | ||
| 216 | if (result) { | ||
| 217 | LOG_RESULT_LOCATION(result); | ||
| 218 | return result; | ||
| 219 | } | ||
| 220 | /* | ||
| 221 | * 3) wait for up to one MPU cycle then restore the slave | ||
| 222 | * address | ||
| 223 | */ | ||
| 224 | msleep(inv_mpu_get_sampling_period_us(mldl_cfg->mpu_gyro_cfg) | ||
| 225 | / 1000); | ||
| 226 | result = inv_serial_single_write( | ||
| 227 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 228 | MPUREG_AUX_SLV_ADDR, | ||
| 229 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL] | ||
| 230 | ->address); | ||
| 231 | if (result) { | ||
| 232 | LOG_RESULT_LOCATION(result); | ||
| 233 | return result; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* | ||
| 237 | * 4) reset the ime interface | ||
| 238 | */ | ||
| 239 | |||
| 240 | result = inv_serial_single_write( | ||
| 241 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 242 | MPUREG_USER_CTRL, | ||
| 243 | (b | BIT_AUX_IF_RST)); | ||
| 244 | if (result) { | ||
| 245 | LOG_RESULT_LOCATION(result); | ||
| 246 | return result; | ||
| 247 | } | ||
| 248 | msleep(2); | ||
| 249 | } | ||
| 250 | if (enable) | ||
| 251 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
| 252 | else | ||
| 253 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
| 254 | |||
| 255 | return result; | ||
| 256 | } | ||
| 257 | |||
| 258 | |||
| 259 | /** | ||
| 260 | * @brief enables/disables the I2C bypass to an external device | ||
| 261 | * connected to MPU's secondary I2C bus. | ||
| 262 | * @param enable | ||
| 263 | * Non-zero to enable pass through. | ||
| 264 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
| 265 | */ | ||
| 266 | static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
| 267 | unsigned char enable) | ||
| 268 | { | ||
| 269 | return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable); | ||
| 270 | } | ||
| 271 | |||
| 272 | |||
| 273 | #define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) | ||
| 274 | |||
| 275 | /* NOTE : when not indicated, product revision | ||
| 276 | is considered an 'npp'; non production part */ | ||
| 277 | |||
| 278 | struct prod_rev_map_t { | ||
| 279 | unsigned char silicon_rev; | ||
| 280 | unsigned short gyro_trim; | ||
| 281 | }; | ||
| 282 | |||
| 283 | #define OLDEST_PROD_REV_SUPPORTED 11 | ||
| 284 | static struct prod_rev_map_t prod_rev_map[] = { | ||
| 285 | {0, 0}, | ||
| 286 | {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */ | ||
| 287 | {MPU_SILICON_REV_A4, 131}, /* 2 | */ | ||
| 288 | {MPU_SILICON_REV_A4, 131}, /* 3 | */ | ||
| 289 | {MPU_SILICON_REV_A4, 131}, /* 4 | */ | ||
| 290 | {MPU_SILICON_REV_A4, 131}, /* 5 | */ | ||
| 291 | {MPU_SILICON_REV_A4, 131}, /* 6 | */ | ||
| 292 | {MPU_SILICON_REV_A4, 131}, /* 7 | */ | ||
| 293 | {MPU_SILICON_REV_A4, 131}, /* 8 | */ | ||
| 294 | {MPU_SILICON_REV_A4, 131}, /* 9 | */ | ||
| 295 | {MPU_SILICON_REV_A4, 131}, /* 10 V */ | ||
| 296 | {MPU_SILICON_REV_B1, 131}, /* 11 B1 */ | ||
| 297 | {MPU_SILICON_REV_B1, 131}, /* 12 | */ | ||
| 298 | {MPU_SILICON_REV_B1, 131}, /* 13 | */ | ||
| 299 | {MPU_SILICON_REV_B1, 131}, /* 14 V */ | ||
| 300 | {MPU_SILICON_REV_B4, 131}, /* 15 B4 */ | ||
| 301 | {MPU_SILICON_REV_B4, 131}, /* 16 | */ | ||
| 302 | {MPU_SILICON_REV_B4, 131}, /* 17 | */ | ||
| 303 | {MPU_SILICON_REV_B4, 131}, /* 18 | */ | ||
| 304 | {MPU_SILICON_REV_B4, 115}, /* 19 | */ | ||
| 305 | {MPU_SILICON_REV_B4, 115}, /* 20 V */ | ||
| 306 | {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */ | ||
| 307 | {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */ | ||
| 308 | {MPU_SILICON_REV_B6, 0}, /* 23 B6 */ | ||
| 309 | {MPU_SILICON_REV_B6, 0}, /* 24 | */ | ||
| 310 | {MPU_SILICON_REV_B6, 0}, /* 25 | */ | ||
| 311 | {MPU_SILICON_REV_B6, 131}, /* 26 V (B6/A11) */ | ||
| 312 | }; | ||
| 313 | |||
| 314 | /** | ||
| 315 | * @internal | ||
| 316 | * @brief Get the silicon revision ID from OTP for MPU3050. | ||
| 317 | * The silicon revision number is in read from OTP bank 0, | ||
| 318 | * ADDR6[7:2]. The corresponding ID is retrieved by lookup | ||
| 319 | * in a map. | ||
| 320 | * | ||
| 321 | * @param mldl_cfg | ||
| 322 | * a pointer to the mldl config data structure. | ||
| 323 | * @param mlsl_handle | ||
| 324 | * an file handle to the serial communication device the | ||
| 325 | * device is connected to. | ||
| 326 | * | ||
| 327 | * @return 0 on success, a non-zero error code otherwise. | ||
| 328 | */ | ||
| 329 | static int inv_get_silicon_rev_mpu3050( | ||
| 330 | struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
| 331 | { | ||
| 332 | int result; | ||
| 333 | unsigned char index = 0x00; | ||
| 334 | unsigned char bank = | ||
| 335 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
| 336 | unsigned short mem_addr = ((bank << 8) | 0x06); | ||
| 337 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 338 | |||
| 339 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 340 | MPUREG_PRODUCT_ID, 1, | ||
| 341 | &mpu_chip_info->product_id); | ||
| 342 | if (result) { | ||
| 343 | LOG_RESULT_LOCATION(result); | ||
| 344 | return result; | ||
| 345 | } | ||
| 346 | |||
| 347 | result = inv_serial_read_mem( | ||
| 348 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 349 | mem_addr, 1, &index); | ||
| 350 | if (result) { | ||
| 351 | LOG_RESULT_LOCATION(result); | ||
| 352 | return result; | ||
| 353 | } | ||
| 354 | index >>= 2; | ||
| 355 | |||
| 356 | /* clean the prefetch and cfg user bank bits */ | ||
| 357 | result = inv_serial_single_write( | ||
| 358 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 359 | MPUREG_BANK_SEL, 0); | ||
| 360 | if (result) { | ||
| 361 | LOG_RESULT_LOCATION(result); | ||
| 362 | return result; | ||
| 363 | } | ||
| 364 | |||
| 365 | if (index < OLDEST_PROD_REV_SUPPORTED || index >= NUM_OF_PROD_REVS) { | ||
| 366 | mpu_chip_info->silicon_revision = 0; | ||
| 367 | mpu_chip_info->gyro_sens_trim = 0; | ||
| 368 | MPL_LOGE("Unsupported Product Revision Detected : %d\n", index); | ||
| 369 | return INV_ERROR_INVALID_MODULE; | ||
| 370 | } | ||
| 371 | |||
| 372 | mpu_chip_info->product_revision = index; | ||
| 373 | mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev; | ||
| 374 | mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim; | ||
| 375 | if (mpu_chip_info->gyro_sens_trim == 0) { | ||
| 376 | MPL_LOGE("gyro sensitivity trim is 0" | ||
| 377 | " - unsupported non production part.\n"); | ||
| 378 | return INV_ERROR_INVALID_MODULE; | ||
| 379 | } | ||
| 380 | |||
| 381 | return result; | ||
| 382 | } | ||
| 383 | #define inv_get_silicon_rev inv_get_silicon_rev_mpu3050 | ||
| 384 | |||
| 385 | |||
| 386 | /** | ||
| 387 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
| 388 | * shifters. | ||
| 389 | * When enabled the secondary I2C interface to which the external | ||
| 390 | * device is connected runs at VDD voltage (main supply). | ||
| 391 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
| 392 | * See the device specification for more details. | ||
| 393 | * | ||
| 394 | * @note using this API may produce unpredictable results, depending on how | ||
| 395 | * the MPU and slave device are setup on the target platform. | ||
| 396 | * Use of this API should entirely be restricted to system | ||
| 397 | * integrators. Once the correct value is found, there should be no | ||
| 398 | * need to change the level shifter at runtime. | ||
| 399 | * | ||
| 400 | * @pre Must be called after inv_serial_start(). | ||
| 401 | * @note Typically called before inv_dmp_open(). | ||
| 402 | * | ||
| 403 | * @param[in] enable: | ||
| 404 | * 0 to run at VDDIO (default), | ||
| 405 | * 1 to run at VDD. | ||
| 406 | * | ||
| 407 | * @return INV_SUCCESS if successfull, a non-zero error code otherwise. | ||
| 408 | */ | ||
| 409 | static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg, | ||
| 410 | void *mlsl_handle, unsigned char enable) | ||
| 411 | { | ||
| 412 | int result; | ||
| 413 | unsigned char regval; | ||
| 414 | |||
| 415 | unsigned char reg; | ||
| 416 | unsigned char mask; | ||
| 417 | |||
| 418 | if (0 == mldl_cfg->mpu_chip_info->silicon_revision) | ||
| 419 | return INV_ERROR_INVALID_PARAMETER; | ||
| 420 | |||
| 421 | /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR -- | ||
| 422 | NOTE: this is incompatible with ST accelerometers where the VDDIO | ||
| 423 | bit MUST be set to enable ST's internal logic to autoincrement | ||
| 424 | the register address on burst reads --*/ | ||
| 425 | if ((mldl_cfg->mpu_chip_info->silicon_revision & 0xf) | ||
| 426 | < MPU_SILICON_REV_B6) { | ||
| 427 | reg = MPUREG_ACCEL_BURST_ADDR; | ||
| 428 | mask = 0x80; | ||
| 429 | } else { | ||
| 430 | /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 => | ||
| 431 | the mask is always 0x04 --*/ | ||
| 432 | reg = MPUREG_FIFO_EN2; | ||
| 433 | mask = 0x04; | ||
| 434 | } | ||
| 435 | |||
| 436 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 437 | reg, 1, ®val); | ||
| 438 | if (result) { | ||
| 439 | LOG_RESULT_LOCATION(result); | ||
| 440 | return result; | ||
| 441 | } | ||
| 442 | |||
| 443 | if (enable) | ||
| 444 | regval |= mask; | ||
| 445 | else | ||
| 446 | regval &= ~mask; | ||
| 447 | |||
| 448 | result = inv_serial_single_write( | ||
| 449 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, reg, regval); | ||
| 450 | if (result) { | ||
| 451 | LOG_RESULT_LOCATION(result); | ||
| 452 | return result; | ||
| 453 | } | ||
| 454 | return result; | ||
| 455 | return INV_SUCCESS; | ||
| 456 | } | ||
| 457 | |||
| 458 | |||
| 459 | /** | ||
| 460 | * @internal | ||
| 461 | * @brief This function controls the power management on the MPU device. | ||
| 462 | * The entire chip can be put to low power sleep mode, or individual | ||
| 463 | * gyros can be turned on/off. | ||
| 464 | * | ||
| 465 | * Putting the device into sleep mode depending upon the changing needs | ||
| 466 | * of the associated applications is a recommended method for reducing | ||
| 467 | * power consuption. It is a safe opearation in that sleep/wake up of | ||
| 468 | * gyros while running will not result in any interruption of data. | ||
| 469 | * | ||
| 470 | * Although it is entirely allowed to put the device into full sleep | ||
| 471 | * while running the DMP, it is not recomended because it will disrupt | ||
| 472 | * the ongoing calculations carried on inside the DMP and consequently | ||
| 473 | * the sensor fusion algorithm. Furthermore, while in sleep mode | ||
| 474 | * read & write operation from the app processor on both registers and | ||
| 475 | * memory are disabled and can only regained by restoring the MPU in | ||
| 476 | * normal power mode. | ||
| 477 | * Disabling any of the gyro axis will reduce the associated power | ||
| 478 | * consuption from the PLL but will not stop the DMP from running | ||
| 479 | * state. | ||
| 480 | * | ||
| 481 | * @param reset | ||
| 482 | * Non-zero to reset the device. Note that this setting | ||
| 483 | * is volatile and the corresponding register bit will | ||
| 484 | * clear itself right after being applied. | ||
| 485 | * @param sleep | ||
| 486 | * Non-zero to put device into full sleep. | ||
| 487 | * @param disable_gx | ||
| 488 | * Non-zero to disable gyro X. | ||
| 489 | * @param disable_gy | ||
| 490 | * Non-zero to disable gyro Y. | ||
| 491 | * @param disable_gz | ||
| 492 | * Non-zero to disable gyro Z. | ||
| 493 | * | ||
| 494 | * @return INV_SUCCESS if successfull; a non-zero error code otherwise. | ||
| 495 | */ | ||
| 496 | static int mpu3050_pwr_mgmt(struct mldl_cfg *mldl_cfg, | ||
| 497 | void *mlsl_handle, | ||
| 498 | unsigned char reset, | ||
| 499 | unsigned char sleep, | ||
| 500 | unsigned char disable_gx, | ||
| 501 | unsigned char disable_gy, | ||
| 502 | unsigned char disable_gz) | ||
| 503 | { | ||
| 504 | unsigned char b; | ||
| 505 | int result; | ||
| 506 | |||
| 507 | result = | ||
| 508 | inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 509 | MPUREG_PWR_MGM, 1, &b); | ||
| 510 | if (result) { | ||
| 511 | LOG_RESULT_LOCATION(result); | ||
| 512 | return result; | ||
| 513 | } | ||
| 514 | |||
| 515 | /* If we are awake, we need to put it in bypass before resetting */ | ||
| 516 | if ((!(b & BIT_SLEEP)) && reset) | ||
| 517 | result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1); | ||
| 518 | |||
| 519 | /* Reset if requested */ | ||
| 520 | if (reset) { | ||
| 521 | MPL_LOGV("Reset MPU3050\n"); | ||
| 522 | result = inv_serial_single_write( | ||
| 523 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 524 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
| 525 | if (result) { | ||
| 526 | LOG_RESULT_LOCATION(result); | ||
| 527 | return result; | ||
| 528 | } | ||
| 529 | msleep(5); | ||
| 530 | /* Some chips are awake after reset and some are asleep, | ||
| 531 | * check the status */ | ||
| 532 | result = inv_serial_read( | ||
| 533 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 534 | MPUREG_PWR_MGM, 1, &b); | ||
| 535 | if (result) { | ||
| 536 | LOG_RESULT_LOCATION(result); | ||
| 537 | return result; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | /* Update the suspended state just in case we return early */ | ||
| 542 | if (b & BIT_SLEEP) { | ||
| 543 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED; | ||
| 544 | mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED; | ||
| 545 | } else { | ||
| 546 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED; | ||
| 547 | mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED; | ||
| 548 | } | ||
| 549 | |||
| 550 | /* if power status match requested, nothing else's left to do */ | ||
| 551 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
| 552 | (((sleep != 0) * BIT_SLEEP) | | ||
| 553 | ((disable_gx != 0) * BIT_STBY_XG) | | ||
| 554 | ((disable_gy != 0) * BIT_STBY_YG) | | ||
| 555 | ((disable_gz != 0) * BIT_STBY_ZG))) { | ||
| 556 | return INV_SUCCESS; | ||
| 557 | } | ||
| 558 | |||
| 559 | /* | ||
| 560 | * This specific transition between states needs to be reinterpreted: | ||
| 561 | * (1,1,1,1) -> (0,1,1,1) has to become | ||
| 562 | * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1) | ||
| 563 | * where | ||
| 564 | * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1) | ||
| 565 | */ | ||
| 566 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
| 567 | (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) | ||
| 568 | && ((!sleep) && disable_gx && disable_gy && disable_gz)) { | ||
| 569 | result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0); | ||
| 570 | if (result) | ||
| 571 | return result; | ||
| 572 | b |= BIT_SLEEP; | ||
| 573 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
| 574 | } | ||
| 575 | |||
| 576 | if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) { | ||
| 577 | if (sleep) { | ||
| 578 | result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1); | ||
| 579 | if (result) { | ||
| 580 | LOG_RESULT_LOCATION(result); | ||
| 581 | return result; | ||
| 582 | } | ||
| 583 | b |= BIT_SLEEP; | ||
| 584 | result = | ||
| 585 | inv_serial_single_write( | ||
| 586 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 587 | MPUREG_PWR_MGM, b); | ||
| 588 | if (result) { | ||
| 589 | LOG_RESULT_LOCATION(result); | ||
| 590 | return result; | ||
| 591 | } | ||
| 592 | mldl_cfg->inv_mpu_state->status |= | ||
| 593 | MPU_GYRO_IS_SUSPENDED; | ||
| 594 | mldl_cfg->inv_mpu_state->status |= | ||
| 595 | MPU_DEVICE_IS_SUSPENDED; | ||
| 596 | } else { | ||
| 597 | b &= ~BIT_SLEEP; | ||
| 598 | result = | ||
| 599 | inv_serial_single_write( | ||
| 600 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 601 | MPUREG_PWR_MGM, b); | ||
| 602 | if (result) { | ||
| 603 | LOG_RESULT_LOCATION(result); | ||
| 604 | return result; | ||
| 605 | } | ||
| 606 | mldl_cfg->inv_mpu_state->status &= | ||
| 607 | ~MPU_GYRO_IS_SUSPENDED; | ||
| 608 | mldl_cfg->inv_mpu_state->status &= | ||
| 609 | ~MPU_DEVICE_IS_SUSPENDED; | ||
| 610 | msleep(5); | ||
| 611 | } | ||
| 612 | } | ||
| 613 | /*--- | ||
| 614 | WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE | ||
| 615 | 1) put one axis at a time in stand-by | ||
| 616 | ---*/ | ||
| 617 | if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) { | ||
| 618 | b ^= BIT_STBY_XG; | ||
| 619 | result = inv_serial_single_write( | ||
| 620 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 621 | MPUREG_PWR_MGM, b); | ||
| 622 | if (result) { | ||
| 623 | LOG_RESULT_LOCATION(result); | ||
| 624 | return result; | ||
| 625 | } | ||
| 626 | } | ||
| 627 | if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) { | ||
| 628 | b ^= BIT_STBY_YG; | ||
| 629 | result = inv_serial_single_write( | ||
| 630 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 631 | MPUREG_PWR_MGM, b); | ||
| 632 | if (result) { | ||
| 633 | LOG_RESULT_LOCATION(result); | ||
| 634 | return result; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) { | ||
| 638 | b ^= BIT_STBY_ZG; | ||
| 639 | result = inv_serial_single_write( | ||
| 640 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 641 | MPUREG_PWR_MGM, b); | ||
| 642 | if (result) { | ||
| 643 | LOG_RESULT_LOCATION(result); | ||
| 644 | return result; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | return INV_SUCCESS; | ||
| 649 | } | ||
| 650 | |||
| 651 | |||
| 652 | /** | ||
| 653 | * @brief sets the clock source for the gyros. | ||
| 654 | * @param mldl_cfg | ||
| 655 | * a pointer to the struct mldl_cfg data structure. | ||
| 656 | * @param gyro_handle | ||
| 657 | * an handle to the serial device the gyro is assigned to. | ||
| 658 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 659 | */ | ||
| 660 | static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg) | ||
| 661 | { | ||
| 662 | int result; | ||
| 663 | unsigned char cur_clk_src; | ||
| 664 | unsigned char reg; | ||
| 665 | |||
| 666 | /* clock source selection */ | ||
| 667 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 668 | MPUREG_PWR_MGM, 1, ®); | ||
| 669 | if (result) { | ||
| 670 | LOG_RESULT_LOCATION(result); | ||
| 671 | return result; | ||
| 672 | } | ||
| 673 | cur_clk_src = reg & BITS_CLKSEL; | ||
| 674 | reg &= ~BITS_CLKSEL; | ||
| 675 | |||
| 676 | |||
| 677 | result = inv_serial_single_write( | ||
| 678 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 679 | MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg); | ||
| 680 | if (result) { | ||
| 681 | LOG_RESULT_LOCATION(result); | ||
| 682 | return result; | ||
| 683 | } | ||
| 684 | /* TODO : workarounds to be determined and implemented */ | ||
| 685 | |||
| 686 | return result; | ||
| 687 | } | ||
| 688 | |||
| 689 | /** | ||
| 690 | * Configures the MPU I2C Master | ||
| 691 | * | ||
| 692 | * @mldl_cfg Handle to the configuration data | ||
| 693 | * @gyro_handle handle to the gyro communictation interface | ||
| 694 | * @slave Can be Null if turning off the slave | ||
| 695 | * @slave_pdata Can be null if turning off the slave | ||
| 696 | * @slave_id enum ext_slave_type to determine which index to use | ||
| 697 | * | ||
| 698 | * | ||
| 699 | * This fucntion configures the slaves by: | ||
| 700 | * 1) Setting up the read | ||
| 701 | * a) Read Register | ||
| 702 | * b) Read Length | ||
| 703 | * 2) Set up the data trigger (MPU6050 only) | ||
| 704 | * a) Set trigger write register | ||
| 705 | * b) Set Trigger write value | ||
| 706 | * 3) Set up the divider (MPU6050 only) | ||
| 707 | * 4) Set the slave bypass mode depending on slave | ||
| 708 | * | ||
| 709 | * returns INV_SUCCESS or non-zero error code | ||
| 710 | */ | ||
| 711 | static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg, | ||
| 712 | void *gyro_handle, | ||
| 713 | struct ext_slave_descr *slave, | ||
| 714 | struct ext_slave_platform_data *slave_pdata, | ||
| 715 | int slave_id) | ||
| 716 | { | ||
| 717 | int result; | ||
| 718 | unsigned char reg; | ||
| 719 | unsigned char slave_reg; | ||
| 720 | unsigned char slave_len; | ||
| 721 | unsigned char slave_endian; | ||
| 722 | unsigned char slave_address; | ||
| 723 | |||
| 724 | if (slave_id != EXT_SLAVE_TYPE_ACCEL) | ||
| 725 | return 0; | ||
| 726 | |||
| 727 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
| 728 | |||
| 729 | if (NULL == slave || NULL == slave_pdata) { | ||
| 730 | slave_reg = 0; | ||
| 731 | slave_len = 0; | ||
| 732 | slave_endian = 0; | ||
| 733 | slave_address = 0; | ||
| 734 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
| 735 | } else { | ||
| 736 | slave_reg = slave->read_reg; | ||
| 737 | slave_len = slave->read_len; | ||
| 738 | slave_endian = slave->endian; | ||
| 739 | slave_address = slave_pdata->address; | ||
| 740 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 1; | ||
| 741 | } | ||
| 742 | |||
| 743 | /* Address */ | ||
| 744 | result = inv_serial_single_write(gyro_handle, | ||
| 745 | mldl_cfg->mpu_chip_info->addr, | ||
| 746 | MPUREG_AUX_SLV_ADDR, slave_address); | ||
| 747 | if (result) { | ||
| 748 | LOG_RESULT_LOCATION(result); | ||
| 749 | return result; | ||
| 750 | } | ||
| 751 | /* Register */ | ||
| 752 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 753 | MPUREG_ACCEL_BURST_ADDR, 1, ®); | ||
| 754 | if (result) { | ||
| 755 | LOG_RESULT_LOCATION(result); | ||
| 756 | return result; | ||
| 757 | } | ||
| 758 | reg = ((reg & 0x80) | slave_reg); | ||
| 759 | result = inv_serial_single_write(gyro_handle, | ||
| 760 | mldl_cfg->mpu_chip_info->addr, | ||
| 761 | MPUREG_ACCEL_BURST_ADDR, reg); | ||
| 762 | if (result) { | ||
| 763 | LOG_RESULT_LOCATION(result); | ||
| 764 | return result; | ||
| 765 | } | ||
| 766 | |||
| 767 | /* Length */ | ||
| 768 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 769 | MPUREG_USER_CTRL, 1, ®); | ||
| 770 | if (result) { | ||
| 771 | LOG_RESULT_LOCATION(result); | ||
| 772 | return result; | ||
| 773 | } | ||
| 774 | reg = (reg & ~BIT_AUX_RD_LENG); | ||
| 775 | result = inv_serial_single_write( | ||
| 776 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 777 | MPUREG_USER_CTRL, reg); | ||
| 778 | if (result) { | ||
| 779 | LOG_RESULT_LOCATION(result); | ||
| 780 | return result; | ||
| 781 | } | ||
| 782 | |||
| 783 | return result; | ||
| 784 | } | ||
| 785 | |||
| 786 | |||
| 787 | static int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
| 788 | void *gyro_handle, | ||
| 789 | struct ext_slave_descr *slave, | ||
| 790 | struct ext_slave_platform_data *slave_pdata, | ||
| 791 | int slave_id) | ||
| 792 | { | ||
| 793 | return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave, | ||
| 794 | slave_pdata, slave_id); | ||
| 795 | } | ||
| 796 | /** | ||
| 797 | * Check to see if the gyro was reset by testing a couple of registers known | ||
| 798 | * to change on reset. | ||
| 799 | * | ||
| 800 | * @mldl_cfg mldl configuration structure | ||
| 801 | * @gyro_handle handle used to communicate with the gyro | ||
| 802 | * | ||
| 803 | * @return INV_SUCCESS or non-zero error code | ||
| 804 | */ | ||
| 805 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 806 | { | ||
| 807 | int result = INV_SUCCESS; | ||
| 808 | unsigned char reg; | ||
| 809 | |||
| 810 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 811 | MPUREG_DMP_CFG_2, 1, ®); | ||
| 812 | if (result) { | ||
| 813 | LOG_RESULT_LOCATION(result); | ||
| 814 | return result; | ||
| 815 | } | ||
| 816 | |||
| 817 | if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg) | ||
| 818 | return true; | ||
| 819 | |||
| 820 | if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1) | ||
| 821 | return false; | ||
| 822 | |||
| 823 | /* Inconclusive assume it was reset */ | ||
| 824 | return true; | ||
| 825 | } | ||
| 826 | |||
| 827 | |||
| 828 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
| 829 | const unsigned char *data, int size) | ||
| 830 | { | ||
| 831 | int bank, offset, write_size; | ||
| 832 | int result; | ||
| 833 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
| 834 | |||
| 835 | if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) { | ||
| 836 | #if INV_CACHE_DMP == 1 | ||
| 837 | memcpy(mldl_cfg->mpu_ram->ram, data, size); | ||
| 838 | return INV_SUCCESS; | ||
| 839 | #else | ||
| 840 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
| 841 | return INV_ERROR_MEMORY_SET; | ||
| 842 | #endif | ||
| 843 | } | ||
| 844 | |||
| 845 | if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) { | ||
| 846 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
| 847 | return INV_ERROR_MEMORY_SET; | ||
| 848 | } | ||
| 849 | /* Write and verify memory */ | ||
| 850 | for (bank = 0; size > 0; bank++, | ||
| 851 | size -= write_size, | ||
| 852 | data += write_size) { | ||
| 853 | if (size > MPU_MEM_BANK_SIZE) | ||
| 854 | write_size = MPU_MEM_BANK_SIZE; | ||
| 855 | else | ||
| 856 | write_size = size; | ||
| 857 | |||
| 858 | result = inv_serial_write_mem(mlsl_handle, | ||
| 859 | mldl_cfg->mpu_chip_info->addr, | ||
| 860 | ((bank << 8) | 0x00), | ||
| 861 | write_size, | ||
| 862 | data); | ||
| 863 | if (result) { | ||
| 864 | LOG_RESULT_LOCATION(result); | ||
| 865 | MPL_LOGE("Write mem error in bank %d\n", bank); | ||
| 866 | return result; | ||
| 867 | } | ||
| 868 | result = inv_serial_read_mem(mlsl_handle, | ||
| 869 | mldl_cfg->mpu_chip_info->addr, | ||
| 870 | ((bank << 8) | 0x00), | ||
| 871 | write_size, | ||
| 872 | read); | ||
| 873 | if (result) { | ||
| 874 | LOG_RESULT_LOCATION(result); | ||
| 875 | MPL_LOGE("Read mem error in bank %d\n", bank); | ||
| 876 | return result; | ||
| 877 | } | ||
| 878 | |||
| 879 | #define ML_SKIP_CHECK 20 | ||
| 880 | for (offset = 0; offset < write_size; offset++) { | ||
| 881 | /* skip the register memory locations */ | ||
| 882 | if (bank == 0 && offset < ML_SKIP_CHECK) | ||
| 883 | continue; | ||
| 884 | if (data[offset] != read[offset]) { | ||
| 885 | result = INV_ERROR_SERIAL_WRITE; | ||
| 886 | break; | ||
| 887 | } | ||
| 888 | } | ||
| 889 | if (result != INV_SUCCESS) { | ||
| 890 | LOG_RESULT_LOCATION(result); | ||
| 891 | MPL_LOGE("Read data mismatch at bank %d, offset %d\n", | ||
| 892 | bank, offset); | ||
| 893 | return result; | ||
| 894 | } | ||
| 895 | } | ||
| 896 | return INV_SUCCESS; | ||
| 897 | } | ||
| 898 | |||
| 899 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle, | ||
| 900 | unsigned long sensors) | ||
| 901 | { | ||
| 902 | int result; | ||
| 903 | int ii; | ||
| 904 | unsigned char reg; | ||
| 905 | unsigned char regs[7]; | ||
| 906 | |||
| 907 | /* Wake up the part */ | ||
| 908 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, false, false, | ||
| 909 | !(sensors & INV_X_GYRO), | ||
| 910 | !(sensors & INV_Y_GYRO), | ||
| 911 | !(sensors & INV_Z_GYRO)); | ||
| 912 | |||
| 913 | if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) && | ||
| 914 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
| 915 | return INV_SUCCESS; | ||
| 916 | } | ||
| 917 | |||
| 918 | if (result) { | ||
| 919 | LOG_RESULT_LOCATION(result); | ||
| 920 | return result; | ||
| 921 | } | ||
| 922 | result = inv_serial_single_write( | ||
| 923 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 924 | MPUREG_INT_CFG, | ||
| 925 | (mldl_cfg->mpu_gyro_cfg->int_config | | ||
| 926 | mldl_cfg->pdata->int_config)); | ||
| 927 | if (result) { | ||
| 928 | LOG_RESULT_LOCATION(result); | ||
| 929 | return result; | ||
| 930 | } | ||
| 931 | result = inv_serial_single_write( | ||
| 932 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 933 | MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider); | ||
| 934 | if (result) { | ||
| 935 | LOG_RESULT_LOCATION(result); | ||
| 936 | return result; | ||
| 937 | } | ||
| 938 | result = mpu_set_clock_source(gyro_handle, mldl_cfg); | ||
| 939 | if (result) { | ||
| 940 | LOG_RESULT_LOCATION(result); | ||
| 941 | return result; | ||
| 942 | } | ||
| 943 | |||
| 944 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync, | ||
| 945 | mldl_cfg->mpu_gyro_cfg->full_scale, | ||
| 946 | mldl_cfg->mpu_gyro_cfg->lpf); | ||
| 947 | result = inv_serial_single_write( | ||
| 948 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 949 | MPUREG_DLPF_FS_SYNC, reg); | ||
| 950 | if (result) { | ||
| 951 | LOG_RESULT_LOCATION(result); | ||
| 952 | return result; | ||
| 953 | } | ||
| 954 | result = inv_serial_single_write( | ||
| 955 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 956 | MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1); | ||
| 957 | if (result) { | ||
| 958 | LOG_RESULT_LOCATION(result); | ||
| 959 | return result; | ||
| 960 | } | ||
| 961 | result = inv_serial_single_write( | ||
| 962 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 963 | MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2); | ||
| 964 | if (result) { | ||
| 965 | LOG_RESULT_LOCATION(result); | ||
| 966 | return result; | ||
| 967 | } | ||
| 968 | |||
| 969 | /* Write and verify memory */ | ||
| 970 | #if INV_CACHE_DMP != 0 | ||
| 971 | inv_mpu_set_firmware(mldl_cfg, gyro_handle, | ||
| 972 | mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length); | ||
| 973 | #endif | ||
| 974 | |||
| 975 | result = inv_serial_single_write( | ||
| 976 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 977 | MPUREG_XG_OFFS_TC, mldl_cfg->mpu_offsets->tc[0]); | ||
| 978 | if (result) { | ||
| 979 | LOG_RESULT_LOCATION(result); | ||
| 980 | return result; | ||
| 981 | } | ||
| 982 | result = inv_serial_single_write( | ||
| 983 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 984 | MPUREG_YG_OFFS_TC, mldl_cfg->mpu_offsets->tc[1]); | ||
| 985 | if (result) { | ||
| 986 | LOG_RESULT_LOCATION(result); | ||
| 987 | return result; | ||
| 988 | } | ||
| 989 | result = inv_serial_single_write( | ||
| 990 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 991 | MPUREG_ZG_OFFS_TC, mldl_cfg->mpu_offsets->tc[2]); | ||
| 992 | if (result) { | ||
| 993 | LOG_RESULT_LOCATION(result); | ||
| 994 | return result; | ||
| 995 | } | ||
| 996 | regs[0] = MPUREG_X_OFFS_USRH; | ||
| 997 | for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) { | ||
| 998 | regs[1 + ii * 2] = | ||
| 999 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8) | ||
| 1000 | & 0xff; | ||
| 1001 | regs[1 + ii * 2 + 1] = | ||
| 1002 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff); | ||
| 1003 | } | ||
| 1004 | result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1005 | 7, regs); | ||
| 1006 | if (result) { | ||
| 1007 | LOG_RESULT_LOCATION(result); | ||
| 1008 | return result; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | /* Configure slaves */ | ||
| 1012 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
| 1013 | mldl_cfg->pdata->level_shifter); | ||
| 1014 | if (result) { | ||
| 1015 | LOG_RESULT_LOCATION(result); | ||
| 1016 | return result; | ||
| 1017 | } | ||
| 1018 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG; | ||
| 1019 | |||
| 1020 | return result; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | int gyro_config(void *mlsl_handle, | ||
| 1024 | struct mldl_cfg *mldl_cfg, | ||
| 1025 | struct ext_slave_config *data) | ||
| 1026 | { | ||
| 1027 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 1028 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 1029 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 1030 | int ii; | ||
| 1031 | |||
| 1032 | if (!data->data) | ||
| 1033 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1034 | |||
| 1035 | switch (data->key) { | ||
| 1036 | case MPU_SLAVE_INT_CONFIG: | ||
| 1037 | mpu_gyro_cfg->int_config = *((__u8 *)data->data); | ||
| 1038 | break; | ||
| 1039 | case MPU_SLAVE_EXT_SYNC: | ||
| 1040 | mpu_gyro_cfg->ext_sync = *((__u8 *)data->data); | ||
| 1041 | break; | ||
| 1042 | case MPU_SLAVE_FULL_SCALE: | ||
| 1043 | mpu_gyro_cfg->full_scale = *((__u8 *)data->data); | ||
| 1044 | break; | ||
| 1045 | case MPU_SLAVE_LPF: | ||
| 1046 | mpu_gyro_cfg->lpf = *((__u8 *)data->data); | ||
| 1047 | break; | ||
| 1048 | case MPU_SLAVE_CLK_SRC: | ||
| 1049 | mpu_gyro_cfg->clk_src = *((__u8 *)data->data); | ||
| 1050 | break; | ||
| 1051 | case MPU_SLAVE_DIVIDER: | ||
| 1052 | mpu_gyro_cfg->divider = *((__u8 *)data->data); | ||
| 1053 | break; | ||
| 1054 | case MPU_SLAVE_DMP_ENABLE: | ||
| 1055 | mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data); | ||
| 1056 | break; | ||
| 1057 | case MPU_SLAVE_FIFO_ENABLE: | ||
| 1058 | mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data); | ||
| 1059 | break; | ||
| 1060 | case MPU_SLAVE_DMP_CFG1: | ||
| 1061 | mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data); | ||
| 1062 | break; | ||
| 1063 | case MPU_SLAVE_DMP_CFG2: | ||
| 1064 | mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data); | ||
| 1065 | break; | ||
| 1066 | case MPU_SLAVE_TC: | ||
| 1067 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1068 | mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii]; | ||
| 1069 | break; | ||
| 1070 | case MPU_SLAVE_GYRO: | ||
| 1071 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1072 | mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii]; | ||
| 1073 | break; | ||
| 1074 | case MPU_SLAVE_ADDR: | ||
| 1075 | mpu_chip_info->addr = *((__u8 *)data->data); | ||
| 1076 | break; | ||
| 1077 | case MPU_SLAVE_PRODUCT_REVISION: | ||
| 1078 | mpu_chip_info->product_revision = *((__u8 *)data->data); | ||
| 1079 | break; | ||
| 1080 | case MPU_SLAVE_SILICON_REVISION: | ||
| 1081 | mpu_chip_info->silicon_revision = *((__u8 *)data->data); | ||
| 1082 | break; | ||
| 1083 | case MPU_SLAVE_PRODUCT_ID: | ||
| 1084 | mpu_chip_info->product_id = *((__u8 *)data->data); | ||
| 1085 | break; | ||
| 1086 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
| 1087 | mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data); | ||
| 1088 | break; | ||
| 1089 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
| 1090 | mpu_chip_info->accel_sens_trim = *((__u16 *)data->data); | ||
| 1091 | break; | ||
| 1092 | case MPU_SLAVE_RAM: | ||
| 1093 | if (data->len != mldl_cfg->mpu_ram->length) | ||
| 1094 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1095 | |||
| 1096 | memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len); | ||
| 1097 | break; | ||
| 1098 | default: | ||
| 1099 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 1100 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1101 | }; | ||
| 1102 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG; | ||
| 1103 | return INV_SUCCESS; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | int gyro_get_config(void *mlsl_handle, | ||
| 1107 | struct mldl_cfg *mldl_cfg, | ||
| 1108 | struct ext_slave_config *data) | ||
| 1109 | { | ||
| 1110 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 1111 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 1112 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 1113 | int ii; | ||
| 1114 | |||
| 1115 | if (!data->data) | ||
| 1116 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1117 | |||
| 1118 | switch (data->key) { | ||
| 1119 | case MPU_SLAVE_INT_CONFIG: | ||
| 1120 | *((__u8 *)data->data) = mpu_gyro_cfg->int_config; | ||
| 1121 | break; | ||
| 1122 | case MPU_SLAVE_EXT_SYNC: | ||
| 1123 | *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync; | ||
| 1124 | break; | ||
| 1125 | case MPU_SLAVE_FULL_SCALE: | ||
| 1126 | *((__u8 *)data->data) = mpu_gyro_cfg->full_scale; | ||
| 1127 | break; | ||
| 1128 | case MPU_SLAVE_LPF: | ||
| 1129 | *((__u8 *)data->data) = mpu_gyro_cfg->lpf; | ||
| 1130 | break; | ||
| 1131 | case MPU_SLAVE_CLK_SRC: | ||
| 1132 | *((__u8 *)data->data) = mpu_gyro_cfg->clk_src; | ||
| 1133 | break; | ||
| 1134 | case MPU_SLAVE_DIVIDER: | ||
| 1135 | *((__u8 *)data->data) = mpu_gyro_cfg->divider; | ||
| 1136 | break; | ||
| 1137 | case MPU_SLAVE_DMP_ENABLE: | ||
| 1138 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable; | ||
| 1139 | break; | ||
| 1140 | case MPU_SLAVE_FIFO_ENABLE: | ||
| 1141 | *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable; | ||
| 1142 | break; | ||
| 1143 | case MPU_SLAVE_DMP_CFG1: | ||
| 1144 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1; | ||
| 1145 | break; | ||
| 1146 | case MPU_SLAVE_DMP_CFG2: | ||
| 1147 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2; | ||
| 1148 | break; | ||
| 1149 | case MPU_SLAVE_TC: | ||
| 1150 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1151 | ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii]; | ||
| 1152 | break; | ||
| 1153 | case MPU_SLAVE_GYRO: | ||
| 1154 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1155 | ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii]; | ||
| 1156 | break; | ||
| 1157 | case MPU_SLAVE_ADDR: | ||
| 1158 | *((__u8 *)data->data) = mpu_chip_info->addr; | ||
| 1159 | break; | ||
| 1160 | case MPU_SLAVE_PRODUCT_REVISION: | ||
| 1161 | *((__u8 *)data->data) = mpu_chip_info->product_revision; | ||
| 1162 | break; | ||
| 1163 | case MPU_SLAVE_SILICON_REVISION: | ||
| 1164 | *((__u8 *)data->data) = mpu_chip_info->silicon_revision; | ||
| 1165 | break; | ||
| 1166 | case MPU_SLAVE_PRODUCT_ID: | ||
| 1167 | *((__u8 *)data->data) = mpu_chip_info->product_id; | ||
| 1168 | break; | ||
| 1169 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
| 1170 | *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim; | ||
| 1171 | break; | ||
| 1172 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
| 1173 | *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim; | ||
| 1174 | break; | ||
| 1175 | case MPU_SLAVE_RAM: | ||
| 1176 | if (data->len != mldl_cfg->mpu_ram->length) | ||
| 1177 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1178 | |||
| 1179 | memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len); | ||
| 1180 | break; | ||
| 1181 | default: | ||
| 1182 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 1183 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1184 | }; | ||
| 1185 | |||
| 1186 | return INV_SUCCESS; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | |||
| 1190 | /******************************************************************************* | ||
| 1191 | ******************************************************************************* | ||
| 1192 | * Exported functions | ||
| 1193 | ******************************************************************************* | ||
| 1194 | ******************************************************************************/ | ||
| 1195 | |||
| 1196 | /** | ||
| 1197 | * Initializes the pdata structure to defaults. | ||
| 1198 | * | ||
| 1199 | * Opens the device to read silicon revision, product id and whoami. | ||
| 1200 | * | ||
| 1201 | * @mldl_cfg | ||
| 1202 | * The internal device configuration data structure. | ||
| 1203 | * @mlsl_handle | ||
| 1204 | * The serial communication handle. | ||
| 1205 | * | ||
| 1206 | * @return INV_SUCCESS if silicon revision, product id and woami are supported | ||
| 1207 | * by this software. | ||
| 1208 | */ | ||
| 1209 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
| 1210 | void *gyro_handle, | ||
| 1211 | void *accel_handle, | ||
| 1212 | void *compass_handle, void *pressure_handle) | ||
| 1213 | { | ||
| 1214 | int result; | ||
| 1215 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1216 | int ii; | ||
| 1217 | |||
| 1218 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
| 1219 | ii = 0; | ||
| 1220 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false; | ||
| 1221 | mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN; | ||
| 1222 | mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
| 1223 | mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ; | ||
| 1224 | mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS; | ||
| 1225 | mldl_cfg->mpu_gyro_cfg->divider = 4; | ||
| 1226 | mldl_cfg->mpu_gyro_cfg->dmp_enable = 1; | ||
| 1227 | mldl_cfg->mpu_gyro_cfg->fifo_enable = 1; | ||
| 1228 | mldl_cfg->mpu_gyro_cfg->ext_sync = 0; | ||
| 1229 | mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0; | ||
| 1230 | mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0; | ||
| 1231 | mldl_cfg->inv_mpu_state->status = | ||
| 1232 | MPU_DMP_IS_SUSPENDED | | ||
| 1233 | MPU_GYRO_IS_SUSPENDED | | ||
| 1234 | MPU_ACCEL_IS_SUSPENDED | | ||
| 1235 | MPU_COMPASS_IS_SUSPENDED | | ||
| 1236 | MPU_PRESSURE_IS_SUSPENDED | | ||
| 1237 | MPU_DEVICE_IS_SUSPENDED; | ||
| 1238 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
| 1239 | |||
| 1240 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1241 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1242 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1243 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1244 | |||
| 1245 | if (mldl_cfg->mpu_chip_info->addr == 0) { | ||
| 1246 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 1247 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | /* | ||
| 1251 | * Reset, | ||
| 1252 | * Take the DMP out of sleep, and | ||
| 1253 | * read the product_id, sillicon rev and whoami | ||
| 1254 | */ | ||
| 1255 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
| 1256 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, RESET, 0, 0, 0, 0); | ||
| 1257 | if (result) { | ||
| 1258 | LOG_RESULT_LOCATION(result); | ||
| 1259 | return result; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | result = inv_get_silicon_rev(mldl_cfg, gyro_handle); | ||
| 1263 | if (result) { | ||
| 1264 | LOG_RESULT_LOCATION(result); | ||
| 1265 | return result; | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | /* Get the factory temperature compensation offsets */ | ||
| 1269 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1270 | MPUREG_XG_OFFS_TC, 1, | ||
| 1271 | &mldl_cfg->mpu_offsets->tc[0]); | ||
| 1272 | if (result) { | ||
| 1273 | LOG_RESULT_LOCATION(result); | ||
| 1274 | return result; | ||
| 1275 | } | ||
| 1276 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1277 | MPUREG_YG_OFFS_TC, 1, | ||
| 1278 | &mldl_cfg->mpu_offsets->tc[1]); | ||
| 1279 | if (result) { | ||
| 1280 | LOG_RESULT_LOCATION(result); | ||
| 1281 | return result; | ||
| 1282 | } | ||
| 1283 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1284 | MPUREG_ZG_OFFS_TC, 1, | ||
| 1285 | &mldl_cfg->mpu_offsets->tc[2]); | ||
| 1286 | if (result) { | ||
| 1287 | LOG_RESULT_LOCATION(result); | ||
| 1288 | return result; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | /* Into bypass mode before sleeping and calling the slaves init */ | ||
| 1292 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
| 1293 | if (result) { | ||
| 1294 | LOG_RESULT_LOCATION(result); | ||
| 1295 | return result; | ||
| 1296 | } | ||
| 1297 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
| 1298 | mldl_cfg->pdata->level_shifter); | ||
| 1299 | if (result) { | ||
| 1300 | LOG_RESULT_LOCATION(result); | ||
| 1301 | return result; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | |||
| 1305 | #if INV_CACHE_DMP != 0 | ||
| 1306 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP, 0, 0, 0); | ||
| 1307 | #endif | ||
| 1308 | if (result) { | ||
| 1309 | LOG_RESULT_LOCATION(result); | ||
| 1310 | return result; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | |||
| 1314 | return result; | ||
| 1315 | |||
| 1316 | } | ||
| 1317 | |||
| 1318 | /** | ||
| 1319 | * Close the mpu interface | ||
| 1320 | * | ||
| 1321 | * @mldl_cfg pointer to the configuration structure | ||
| 1322 | * @mlsl_handle pointer to the serial layer handle | ||
| 1323 | * | ||
| 1324 | * @return INV_SUCCESS or non-zero error code | ||
| 1325 | */ | ||
| 1326 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
| 1327 | void *gyro_handle, | ||
| 1328 | void *accel_handle, | ||
| 1329 | void *compass_handle, | ||
| 1330 | void *pressure_handle) | ||
| 1331 | { | ||
| 1332 | return 0; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | /** | ||
| 1336 | * @brief resume the MPU device and all the other sensor | ||
| 1337 | * devices from their low power state. | ||
| 1338 | * | ||
| 1339 | * @mldl_cfg | ||
| 1340 | * pointer to the configuration structure | ||
| 1341 | * @gyro_handle | ||
| 1342 | * the main file handle to the MPU device. | ||
| 1343 | * @accel_handle | ||
| 1344 | * an handle to the accelerometer device, if sitting | ||
| 1345 | * onto a separate bus. Can match mlsl_handle if | ||
| 1346 | * the accelerometer device operates on the same | ||
| 1347 | * primary bus of MPU. | ||
| 1348 | * @compass_handle | ||
| 1349 | * an handle to the compass device, if sitting | ||
| 1350 | * onto a separate bus. Can match mlsl_handle if | ||
| 1351 | * the compass device operates on the same | ||
| 1352 | * primary bus of MPU. | ||
| 1353 | * @pressure_handle | ||
| 1354 | * an handle to the pressure sensor device, if sitting | ||
| 1355 | * onto a separate bus. Can match mlsl_handle if | ||
| 1356 | * the pressure sensor device operates on the same | ||
| 1357 | * primary bus of MPU. | ||
| 1358 | * @resume_gyro | ||
| 1359 | * whether resuming the gyroscope device is | ||
| 1360 | * actually needed (if the device supports low power | ||
| 1361 | * mode of some sort). | ||
| 1362 | * @resume_accel | ||
| 1363 | * whether resuming the accelerometer device is | ||
| 1364 | * actually needed (if the device supports low power | ||
| 1365 | * mode of some sort). | ||
| 1366 | * @resume_compass | ||
| 1367 | * whether resuming the compass device is | ||
| 1368 | * actually needed (if the device supports low power | ||
| 1369 | * mode of some sort). | ||
| 1370 | * @resume_pressure | ||
| 1371 | * whether resuming the pressure sensor device is | ||
| 1372 | * actually needed (if the device supports low power | ||
| 1373 | * mode of some sort). | ||
| 1374 | * @return INV_SUCCESS or a non-zero error code. | ||
| 1375 | */ | ||
| 1376 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
| 1377 | void *gyro_handle, | ||
| 1378 | void *accel_handle, | ||
| 1379 | void *compass_handle, | ||
| 1380 | void *pressure_handle, | ||
| 1381 | unsigned long sensors) | ||
| 1382 | { | ||
| 1383 | int result = INV_SUCCESS; | ||
| 1384 | int ii; | ||
| 1385 | bool resume_slave[EXT_SLAVE_NUM_TYPES]; | ||
| 1386 | bool resume_dmp = sensors & INV_DMP_PROCESSOR; | ||
| 1387 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1388 | resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
| 1389 | (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
| 1390 | resume_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
| 1391 | sensors & INV_THREE_AXIS_ACCEL; | ||
| 1392 | resume_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
| 1393 | sensors & INV_THREE_AXIS_COMPASS; | ||
| 1394 | resume_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
| 1395 | sensors & INV_THREE_AXIS_PRESSURE; | ||
| 1396 | |||
| 1397 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1398 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1399 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1400 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1401 | |||
| 1402 | |||
| 1403 | mldl_print_cfg(mldl_cfg); | ||
| 1404 | |||
| 1405 | /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */ | ||
| 1406 | for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1407 | if (resume_slave[ii] && | ||
| 1408 | ((!mldl_cfg->slave[ii]) || | ||
| 1409 | (!mldl_cfg->slave[ii]->resume))) { | ||
| 1410 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 1411 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1412 | } | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp) | ||
| 1416 | && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) || | ||
| 1417 | (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) { | ||
| 1418 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1419 | if (result) { | ||
| 1420 | LOG_RESULT_LOCATION(result); | ||
| 1421 | return result; | ||
| 1422 | } | ||
| 1423 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
| 1424 | if (result) { | ||
| 1425 | LOG_RESULT_LOCATION(result); | ||
| 1426 | return result; | ||
| 1427 | } | ||
| 1428 | result = gyro_resume(mldl_cfg, gyro_handle, sensors); | ||
| 1429 | if (result) { | ||
| 1430 | LOG_RESULT_LOCATION(result); | ||
| 1431 | return result; | ||
| 1432 | } | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1436 | if (!mldl_cfg->slave[ii] || | ||
| 1437 | !mldl_cfg->pdata_slave[ii] || | ||
| 1438 | !resume_slave[ii] || | ||
| 1439 | !(mldl_cfg->inv_mpu_state->status & (1 << ii))) | ||
| 1440 | continue; | ||
| 1441 | |||
| 1442 | if (EXT_SLAVE_BUS_SECONDARY == | ||
| 1443 | mldl_cfg->pdata_slave[ii]->bus) { | ||
| 1444 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, | ||
| 1445 | true); | ||
| 1446 | if (result) { | ||
| 1447 | LOG_RESULT_LOCATION(result); | ||
| 1448 | return result; | ||
| 1449 | } | ||
| 1450 | } | ||
| 1451 | result = mldl_cfg->slave[ii]->resume(slave_handle[ii], | ||
| 1452 | mldl_cfg->slave[ii], | ||
| 1453 | mldl_cfg->pdata_slave[ii]); | ||
| 1454 | if (result) { | ||
| 1455 | LOG_RESULT_LOCATION(result); | ||
| 1456 | return result; | ||
| 1457 | } | ||
| 1458 | mldl_cfg->inv_mpu_state->status &= ~(1 << ii); | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1462 | if (resume_dmp && | ||
| 1463 | !(mldl_cfg->inv_mpu_state->status & (1 << ii)) && | ||
| 1464 | mldl_cfg->pdata_slave[ii] && | ||
| 1465 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) { | ||
| 1466 | result = mpu_set_slave(mldl_cfg, | ||
| 1467 | gyro_handle, | ||
| 1468 | mldl_cfg->slave[ii], | ||
| 1469 | mldl_cfg->pdata_slave[ii], | ||
| 1470 | mldl_cfg->slave[ii]->type); | ||
| 1471 | if (result) { | ||
| 1472 | LOG_RESULT_LOCATION(result); | ||
| 1473 | return result; | ||
| 1474 | } | ||
| 1475 | } | ||
| 1476 | } | ||
| 1477 | |||
| 1478 | /* Turn on the master i2c iterface if necessary */ | ||
| 1479 | if (resume_dmp) { | ||
| 1480 | result = mpu_set_i2c_bypass( | ||
| 1481 | mldl_cfg, gyro_handle, | ||
| 1482 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
| 1483 | if (result) { | ||
| 1484 | LOG_RESULT_LOCATION(result); | ||
| 1485 | return result; | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | /* Now start */ | ||
| 1489 | result = dmp_start(mldl_cfg, gyro_handle); | ||
| 1490 | if (result) { | ||
| 1491 | LOG_RESULT_LOCATION(result); | ||
| 1492 | return result; | ||
| 1493 | } | ||
| 1494 | } | ||
| 1495 | mldl_cfg->inv_mpu_cfg->requested_sensors = sensors; | ||
| 1496 | |||
| 1497 | return result; | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | /** | ||
| 1501 | * @brief suspend the MPU device and all the other sensor | ||
| 1502 | * devices into their low power state. | ||
| 1503 | * @mldl_cfg | ||
| 1504 | * a pointer to the struct mldl_cfg internal data | ||
| 1505 | * structure. | ||
| 1506 | * @gyro_handle | ||
| 1507 | * the main file handle to the MPU device. | ||
| 1508 | * @accel_handle | ||
| 1509 | * an handle to the accelerometer device, if sitting | ||
| 1510 | * onto a separate bus. Can match gyro_handle if | ||
| 1511 | * the accelerometer device operates on the same | ||
| 1512 | * primary bus of MPU. | ||
| 1513 | * @compass_handle | ||
| 1514 | * an handle to the compass device, if sitting | ||
| 1515 | * onto a separate bus. Can match gyro_handle if | ||
| 1516 | * the compass device operates on the same | ||
| 1517 | * primary bus of MPU. | ||
| 1518 | * @pressure_handle | ||
| 1519 | * an handle to the pressure sensor device, if sitting | ||
| 1520 | * onto a separate bus. Can match gyro_handle if | ||
| 1521 | * the pressure sensor device operates on the same | ||
| 1522 | * primary bus of MPU. | ||
| 1523 | * @accel | ||
| 1524 | * whether suspending the accelerometer device is | ||
| 1525 | * actually needed (if the device supports low power | ||
| 1526 | * mode of some sort). | ||
| 1527 | * @compass | ||
| 1528 | * whether suspending the compass device is | ||
| 1529 | * actually needed (if the device supports low power | ||
| 1530 | * mode of some sort). | ||
| 1531 | * @pressure | ||
| 1532 | * whether suspending the pressure sensor device is | ||
| 1533 | * actually needed (if the device supports low power | ||
| 1534 | * mode of some sort). | ||
| 1535 | * @return INV_SUCCESS or a non-zero error code. | ||
| 1536 | */ | ||
| 1537 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
| 1538 | void *gyro_handle, | ||
| 1539 | void *accel_handle, | ||
| 1540 | void *compass_handle, | ||
| 1541 | void *pressure_handle, | ||
| 1542 | unsigned long sensors) | ||
| 1543 | { | ||
| 1544 | int result = INV_SUCCESS; | ||
| 1545 | int ii; | ||
| 1546 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 1547 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 1548 | bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR); | ||
| 1549 | bool suspend_slave[EXT_SLAVE_NUM_TYPES]; | ||
| 1550 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1551 | |||
| 1552 | suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
| 1553 | ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)) | ||
| 1554 | == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
| 1555 | suspend_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
| 1556 | ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL); | ||
| 1557 | suspend_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
| 1558 | ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS); | ||
| 1559 | suspend_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
| 1560 | ((sensors & INV_THREE_AXIS_PRESSURE) == | ||
| 1561 | INV_THREE_AXIS_PRESSURE); | ||
| 1562 | |||
| 1563 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1564 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1565 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1566 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1567 | |||
| 1568 | if (suspend_dmp) { | ||
| 1569 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1570 | if (result) { | ||
| 1571 | LOG_RESULT_LOCATION(result); | ||
| 1572 | return result; | ||
| 1573 | } | ||
| 1574 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
| 1575 | if (result) { | ||
| 1576 | LOG_RESULT_LOCATION(result); | ||
| 1577 | return result; | ||
| 1578 | } | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /* Gyro */ | ||
| 1582 | if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] && | ||
| 1583 | !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) { | ||
| 1584 | result = mpu3050_pwr_mgmt( | ||
| 1585 | mldl_cfg, gyro_handle, 0, | ||
| 1586 | suspend_dmp && suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 1587 | (unsigned char)(sensors & INV_X_GYRO), | ||
| 1588 | (unsigned char)(sensors & INV_Y_GYRO), | ||
| 1589 | (unsigned char)(sensors & INV_Z_GYRO)); | ||
| 1590 | if (result) { | ||
| 1591 | LOG_RESULT_LOCATION(result); | ||
| 1592 | return result; | ||
| 1593 | } | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1597 | bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii); | ||
| 1598 | if (!slave[ii] || !pdata_slave[ii] || | ||
| 1599 | is_suspended || !suspend_slave[ii]) | ||
| 1600 | continue; | ||
| 1601 | |||
| 1602 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
| 1603 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1604 | if (result) { | ||
| 1605 | LOG_RESULT_LOCATION(result); | ||
| 1606 | return result; | ||
| 1607 | } | ||
| 1608 | } | ||
| 1609 | result = slave[ii]->suspend(slave_handle[ii], | ||
| 1610 | slave[ii], | ||
| 1611 | pdata_slave[ii]); | ||
| 1612 | if (result) { | ||
| 1613 | LOG_RESULT_LOCATION(result); | ||
| 1614 | return result; | ||
| 1615 | } | ||
| 1616 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
| 1617 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
| 1618 | NULL, NULL, | ||
| 1619 | slave[ii]->type); | ||
| 1620 | if (result) { | ||
| 1621 | LOG_RESULT_LOCATION(result); | ||
| 1622 | return result; | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | mldl_cfg->inv_mpu_state->status |= (1 << ii); | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | /* Re-enable the i2c master if there are configured slaves and DMP */ | ||
| 1629 | if (!suspend_dmp) { | ||
| 1630 | result = mpu_set_i2c_bypass( | ||
| 1631 | mldl_cfg, gyro_handle, | ||
| 1632 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
| 1633 | if (result) { | ||
| 1634 | LOG_RESULT_LOCATION(result); | ||
| 1635 | return result; | ||
| 1636 | } | ||
| 1637 | } | ||
| 1638 | mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS; | ||
| 1639 | |||
| 1640 | return result; | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
| 1644 | void *gyro_handle, | ||
| 1645 | void *slave_handle, | ||
| 1646 | struct ext_slave_descr *slave, | ||
| 1647 | struct ext_slave_platform_data *pdata, | ||
| 1648 | unsigned char *data) | ||
| 1649 | { | ||
| 1650 | int result; | ||
| 1651 | int bypass_result; | ||
| 1652 | int remain_bypassed = true; | ||
| 1653 | |||
| 1654 | if (NULL == slave || NULL == slave->read) { | ||
| 1655 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1656 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1660 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1661 | remain_bypassed = false; | ||
| 1662 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1663 | if (result) { | ||
| 1664 | LOG_RESULT_LOCATION(result); | ||
| 1665 | return result; | ||
| 1666 | } | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | result = slave->read(slave_handle, slave, pdata, data); | ||
| 1670 | |||
| 1671 | if (!remain_bypassed) { | ||
| 1672 | bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1673 | if (bypass_result) { | ||
| 1674 | LOG_RESULT_LOCATION(bypass_result); | ||
| 1675 | return bypass_result; | ||
| 1676 | } | ||
| 1677 | } | ||
| 1678 | return result; | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 1682 | void *gyro_handle, | ||
| 1683 | void *slave_handle, | ||
| 1684 | struct ext_slave_config *data, | ||
| 1685 | struct ext_slave_descr *slave, | ||
| 1686 | struct ext_slave_platform_data *pdata) | ||
| 1687 | { | ||
| 1688 | int result; | ||
| 1689 | int remain_bypassed = true; | ||
| 1690 | |||
| 1691 | if (NULL == slave || NULL == slave->config) { | ||
| 1692 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1693 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1697 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1698 | remain_bypassed = false; | ||
| 1699 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1700 | if (result) { | ||
| 1701 | LOG_RESULT_LOCATION(result); | ||
| 1702 | return result; | ||
| 1703 | } | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | result = slave->config(slave_handle, slave, pdata, data); | ||
| 1707 | if (result) { | ||
| 1708 | LOG_RESULT_LOCATION(result); | ||
| 1709 | return result; | ||
| 1710 | } | ||
| 1711 | |||
| 1712 | if (!remain_bypassed) { | ||
| 1713 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1714 | if (result) { | ||
| 1715 | LOG_RESULT_LOCATION(result); | ||
| 1716 | return result; | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | return result; | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 1723 | void *gyro_handle, | ||
| 1724 | void *slave_handle, | ||
| 1725 | struct ext_slave_config *data, | ||
| 1726 | struct ext_slave_descr *slave, | ||
| 1727 | struct ext_slave_platform_data *pdata) | ||
| 1728 | { | ||
| 1729 | int result; | ||
| 1730 | int remain_bypassed = true; | ||
| 1731 | |||
| 1732 | if (NULL == slave || NULL == slave->get_config) { | ||
| 1733 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1734 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1738 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1739 | remain_bypassed = false; | ||
| 1740 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1741 | if (result) { | ||
| 1742 | LOG_RESULT_LOCATION(result); | ||
| 1743 | return result; | ||
| 1744 | } | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | result = slave->get_config(slave_handle, slave, pdata, data); | ||
| 1748 | if (result) { | ||
| 1749 | LOG_RESULT_LOCATION(result); | ||
| 1750 | return result; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | if (!remain_bypassed) { | ||
| 1754 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1755 | if (result) { | ||
| 1756 | LOG_RESULT_LOCATION(result); | ||
| 1757 | return result; | ||
| 1758 | } | ||
| 1759 | } | ||
| 1760 | return result; | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | /** | ||
| 1764 | * @} | ||
| 1765 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c new file mode 100644 index 00000000000..e2b8d30ceba --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_print_cfg.c | ||
| 25 | * @brief The Motion Library Driver Layer. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <stddef.h> | ||
| 29 | #include "mldl_cfg.h" | ||
| 30 | #include "mlsl.h" | ||
| 31 | #include "linux/mpu.h" | ||
| 32 | |||
| 33 | #include "log.h" | ||
| 34 | #undef MPL_LOG_TAG | ||
| 35 | #define MPL_LOG_TAG "mldl_print_cfg:" | ||
| 36 | |||
| 37 | #undef MPL_LOG_NDEBUG | ||
| 38 | #define MPL_LOG_NDEBUG 1 | ||
| 39 | |||
| 40 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg) | ||
| 41 | { | ||
| 42 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 43 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 44 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 45 | struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg; | ||
| 46 | struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state; | ||
| 47 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 48 | struct mpu_platform_data *pdata = mldl_cfg->pdata; | ||
| 49 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 50 | int ii; | ||
| 51 | |||
| 52 | /* mpu_gyro_cfg */ | ||
| 53 | MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config); | ||
| 54 | MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync); | ||
| 55 | MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale); | ||
| 56 | MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf); | ||
| 57 | MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src); | ||
| 58 | MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider); | ||
| 59 | MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable); | ||
| 60 | MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable); | ||
| 61 | MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1); | ||
| 62 | MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2); | ||
| 63 | /* mpu_offsets */ | ||
| 64 | MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]); | ||
| 65 | MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]); | ||
| 66 | MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]); | ||
| 67 | MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]); | ||
| 68 | MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]); | ||
| 69 | MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]); | ||
| 70 | |||
| 71 | /* mpu_chip_info */ | ||
| 72 | MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr); | ||
| 73 | |||
| 74 | MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision); | ||
| 75 | MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision); | ||
| 76 | MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id); | ||
| 77 | MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim); | ||
| 78 | |||
| 79 | MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors); | ||
| 80 | MPL_LOGV("ignore_system_suspend= %04x\n", | ||
| 81 | inv_mpu_cfg->ignore_system_suspend); | ||
| 82 | MPL_LOGV("status = %04x\n", inv_mpu_state->status); | ||
| 83 | MPL_LOGV("i2c_slaves_enabled= %04x\n", | ||
| 84 | inv_mpu_state->i2c_slaves_enabled); | ||
| 85 | |||
| 86 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 87 | if (!slave[ii]) | ||
| 88 | continue; | ||
| 89 | MPL_LOGV("SLAVE %d:\n", ii); | ||
| 90 | MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend); | ||
| 91 | MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume); | ||
| 92 | MPL_LOGV(" read = %02x\n", (int)slave[ii]->read); | ||
| 93 | MPL_LOGV(" type = %02x\n", slave[ii]->type); | ||
| 94 | MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg); | ||
| 95 | MPL_LOGV(" len = %02x\n", slave[ii]->read_len); | ||
| 96 | MPL_LOGV(" endian = %02x\n", slave[ii]->endian); | ||
| 97 | MPL_LOGV(" range.mantissa= %02x\n", | ||
| 98 | slave[ii]->range.mantissa); | ||
| 99 | MPL_LOGV(" range.fraction= %02x\n", | ||
| 100 | slave[ii]->range.fraction); | ||
| 101 | } | ||
| 102 | |||
| 103 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 104 | if (!pdata_slave[ii]) | ||
| 105 | continue; | ||
| 106 | MPL_LOGV("PDATA_SLAVE[%d]\n", ii); | ||
| 107 | MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq); | ||
| 108 | MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num); | ||
| 109 | MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus); | ||
| 110 | MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address); | ||
| 111 | MPL_LOGV(" orientation=\n" | ||
| 112 | " %2d %2d %2d\n" | ||
| 113 | " %2d %2d %2d\n" | ||
| 114 | " %2d %2d %2d\n", | ||
| 115 | pdata_slave[ii]->orientation[0], | ||
| 116 | pdata_slave[ii]->orientation[1], | ||
| 117 | pdata_slave[ii]->orientation[2], | ||
| 118 | pdata_slave[ii]->orientation[3], | ||
| 119 | pdata_slave[ii]->orientation[4], | ||
| 120 | pdata_slave[ii]->orientation[5], | ||
| 121 | pdata_slave[ii]->orientation[6], | ||
| 122 | pdata_slave[ii]->orientation[7], | ||
| 123 | pdata_slave[ii]->orientation[8]); | ||
| 124 | } | ||
| 125 | |||
| 126 | MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config); | ||
| 127 | MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter); | ||
| 128 | MPL_LOGV("pdata->orientation =\n" | ||
| 129 | " %2d %2d %2d\n" | ||
| 130 | " %2d %2d %2d\n" | ||
| 131 | " %2d %2d %2d\n", | ||
| 132 | pdata->orientation[0], pdata->orientation[1], | ||
| 133 | pdata->orientation[2], pdata->orientation[3], | ||
| 134 | pdata->orientation[4], pdata->orientation[5], | ||
| 135 | pdata->orientation[6], pdata->orientation[7], | ||
| 136 | pdata->orientation[8]); | ||
| 137 | } | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c new file mode 100644 index 00000000000..19adf5182c0 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c | |||
| @@ -0,0 +1,420 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "mlsl.h" | ||
| 21 | #include <linux/i2c.h> | ||
| 22 | #include "log.h" | ||
| 23 | #include "mpu3050.h" | ||
| 24 | |||
| 25 | static int inv_i2c_write(struct i2c_adapter *i2c_adap, | ||
| 26 | unsigned char address, | ||
| 27 | unsigned int len, unsigned char const *data) | ||
| 28 | { | ||
| 29 | struct i2c_msg msgs[1]; | ||
| 30 | int res; | ||
| 31 | |||
| 32 | if (!data || !i2c_adap) { | ||
| 33 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 34 | return -EINVAL; | ||
| 35 | } | ||
| 36 | |||
| 37 | msgs[0].addr = address; | ||
| 38 | msgs[0].flags = 0; /* write */ | ||
| 39 | msgs[0].buf = (unsigned char *)data; | ||
| 40 | msgs[0].len = len; | ||
| 41 | |||
| 42 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
| 43 | if (res < 1) { | ||
| 44 | if (res == 0) | ||
| 45 | res = -EIO; | ||
| 46 | LOG_RESULT_LOCATION(res); | ||
| 47 | return res; | ||
| 48 | } else | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int inv_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
| 53 | unsigned char address, | ||
| 54 | unsigned char reg, unsigned char value) | ||
| 55 | { | ||
| 56 | unsigned char data[2]; | ||
| 57 | |||
| 58 | data[0] = reg; | ||
| 59 | data[1] = value; | ||
| 60 | return inv_i2c_write(i2c_adap, address, 2, data); | ||
| 61 | } | ||
| 62 | |||
| 63 | static int inv_i2c_read(struct i2c_adapter *i2c_adap, | ||
| 64 | unsigned char address, unsigned char reg, | ||
| 65 | unsigned int len, unsigned char *data) | ||
| 66 | { | ||
| 67 | struct i2c_msg msgs[2]; | ||
| 68 | int res; | ||
| 69 | |||
| 70 | if (!data || !i2c_adap) { | ||
| 71 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 72 | return -EINVAL; | ||
| 73 | } | ||
| 74 | |||
| 75 | msgs[0].addr = address; | ||
| 76 | msgs[0].flags = 0; /* write */ | ||
| 77 | msgs[0].buf = ® | ||
| 78 | msgs[0].len = 1; | ||
| 79 | |||
| 80 | msgs[1].addr = address; | ||
| 81 | msgs[1].flags = I2C_M_RD; | ||
| 82 | msgs[1].buf = data; | ||
| 83 | msgs[1].len = len; | ||
| 84 | |||
| 85 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
| 86 | if (res < 2) { | ||
| 87 | if (res >= 0) | ||
| 88 | res = -EIO; | ||
| 89 | LOG_RESULT_LOCATION(res); | ||
| 90 | return res; | ||
| 91 | } else | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
| 96 | unsigned char mpu_addr, | ||
| 97 | unsigned short mem_addr, | ||
| 98 | unsigned int len, unsigned char *data) | ||
| 99 | { | ||
| 100 | unsigned char bank[2]; | ||
| 101 | unsigned char addr[2]; | ||
| 102 | unsigned char buf; | ||
| 103 | |||
| 104 | struct i2c_msg msgs[4]; | ||
| 105 | int res; | ||
| 106 | |||
| 107 | if (!data || !i2c_adap) { | ||
| 108 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 109 | return -EINVAL; | ||
| 110 | } | ||
| 111 | |||
| 112 | bank[0] = MPUREG_BANK_SEL; | ||
| 113 | bank[1] = mem_addr >> 8; | ||
| 114 | |||
| 115 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 116 | addr[1] = mem_addr & 0xFF; | ||
| 117 | |||
| 118 | buf = MPUREG_MEM_R_W; | ||
| 119 | |||
| 120 | /* write message */ | ||
| 121 | msgs[0].addr = mpu_addr; | ||
| 122 | msgs[0].flags = 0; | ||
| 123 | msgs[0].buf = bank; | ||
| 124 | msgs[0].len = sizeof(bank); | ||
| 125 | |||
| 126 | msgs[1].addr = mpu_addr; | ||
| 127 | msgs[1].flags = 0; | ||
| 128 | msgs[1].buf = addr; | ||
| 129 | msgs[1].len = sizeof(addr); | ||
| 130 | |||
| 131 | msgs[2].addr = mpu_addr; | ||
| 132 | msgs[2].flags = 0; | ||
| 133 | msgs[2].buf = &buf; | ||
| 134 | msgs[2].len = 1; | ||
| 135 | |||
| 136 | msgs[3].addr = mpu_addr; | ||
| 137 | msgs[3].flags = I2C_M_RD; | ||
| 138 | msgs[3].buf = data; | ||
| 139 | msgs[3].len = len; | ||
| 140 | |||
| 141 | res = i2c_transfer(i2c_adap, msgs, 4); | ||
| 142 | if (res != 4) { | ||
| 143 | if (res >= 0) | ||
| 144 | res = -EIO; | ||
| 145 | LOG_RESULT_LOCATION(res); | ||
| 146 | return res; | ||
| 147 | } else | ||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
| 152 | unsigned char mpu_addr, | ||
| 153 | unsigned short mem_addr, | ||
| 154 | unsigned int len, unsigned char const *data) | ||
| 155 | { | ||
| 156 | unsigned char bank[2]; | ||
| 157 | unsigned char addr[2]; | ||
| 158 | unsigned char buf[513]; | ||
| 159 | |||
| 160 | struct i2c_msg msgs[3]; | ||
| 161 | int res; | ||
| 162 | |||
| 163 | if (!data || !i2c_adap) { | ||
| 164 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 165 | return -EINVAL; | ||
| 166 | } | ||
| 167 | if (len >= (sizeof(buf) - 1)) { | ||
| 168 | LOG_RESULT_LOCATION(-ENOMEM); | ||
| 169 | return -ENOMEM; | ||
| 170 | } | ||
| 171 | |||
| 172 | bank[0] = MPUREG_BANK_SEL; | ||
| 173 | bank[1] = mem_addr >> 8; | ||
| 174 | |||
| 175 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 176 | addr[1] = mem_addr & 0xFF; | ||
| 177 | |||
| 178 | buf[0] = MPUREG_MEM_R_W; | ||
| 179 | memcpy(buf + 1, data, len); | ||
| 180 | |||
| 181 | /* write message */ | ||
| 182 | msgs[0].addr = mpu_addr; | ||
| 183 | msgs[0].flags = 0; | ||
| 184 | msgs[0].buf = bank; | ||
| 185 | msgs[0].len = sizeof(bank); | ||
| 186 | |||
| 187 | msgs[1].addr = mpu_addr; | ||
| 188 | msgs[1].flags = 0; | ||
| 189 | msgs[1].buf = addr; | ||
| 190 | msgs[1].len = sizeof(addr); | ||
| 191 | |||
| 192 | msgs[2].addr = mpu_addr; | ||
| 193 | msgs[2].flags = 0; | ||
| 194 | msgs[2].buf = (unsigned char *)buf; | ||
| 195 | msgs[2].len = len + 1; | ||
| 196 | |||
| 197 | res = i2c_transfer(i2c_adap, msgs, 3); | ||
| 198 | if (res != 3) { | ||
| 199 | if (res >= 0) | ||
| 200 | res = -EIO; | ||
| 201 | LOG_RESULT_LOCATION(res); | ||
| 202 | return res; | ||
| 203 | } else | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | int inv_serial_single_write( | ||
| 208 | void *sl_handle, | ||
| 209 | unsigned char slave_addr, | ||
| 210 | unsigned char register_addr, | ||
| 211 | unsigned char data) | ||
| 212 | { | ||
| 213 | return inv_i2c_write_register((struct i2c_adapter *)sl_handle, | ||
| 214 | slave_addr, register_addr, data); | ||
| 215 | } | ||
| 216 | EXPORT_SYMBOL(inv_serial_single_write); | ||
| 217 | |||
| 218 | int inv_serial_write( | ||
| 219 | void *sl_handle, | ||
| 220 | unsigned char slave_addr, | ||
| 221 | unsigned short length, | ||
| 222 | unsigned char const *data) | ||
| 223 | { | ||
| 224 | int result; | ||
| 225 | const unsigned short data_length = length - 1; | ||
| 226 | const unsigned char start_reg_addr = data[0]; | ||
| 227 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 228 | unsigned short bytes_written = 0; | ||
| 229 | |||
| 230 | while (bytes_written < data_length) { | ||
| 231 | unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE, | ||
| 232 | data_length - bytes_written); | ||
| 233 | if (bytes_written == 0) { | ||
| 234 | result = inv_i2c_write((struct i2c_adapter *) | ||
| 235 | sl_handle, slave_addr, | ||
| 236 | 1 + this_len, data); | ||
| 237 | } else { | ||
| 238 | /* manually increment register addr between chunks */ | ||
| 239 | i2c_write[0] = start_reg_addr + bytes_written; | ||
| 240 | memcpy(&i2c_write[1], &data[1 + bytes_written], | ||
| 241 | this_len); | ||
| 242 | result = inv_i2c_write((struct i2c_adapter *) | ||
| 243 | sl_handle, slave_addr, | ||
| 244 | 1 + this_len, i2c_write); | ||
| 245 | } | ||
| 246 | if (result) { | ||
| 247 | LOG_RESULT_LOCATION(result); | ||
| 248 | return result; | ||
| 249 | } | ||
| 250 | bytes_written += this_len; | ||
| 251 | } | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | EXPORT_SYMBOL(inv_serial_write); | ||
| 255 | |||
| 256 | int inv_serial_read( | ||
| 257 | void *sl_handle, | ||
| 258 | unsigned char slave_addr, | ||
| 259 | unsigned char register_addr, | ||
| 260 | unsigned short length, | ||
| 261 | unsigned char *data) | ||
| 262 | { | ||
| 263 | int result; | ||
| 264 | unsigned short bytes_read = 0; | ||
| 265 | |||
| 266 | if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR | ||
| 267 | && (register_addr == MPUREG_FIFO_R_W || | ||
| 268 | register_addr == MPUREG_MEM_R_W)) { | ||
| 269 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 270 | return INV_ERROR_INVALID_PARAMETER; | ||
| 271 | } | ||
| 272 | |||
| 273 | while (bytes_read < length) { | ||
| 274 | unsigned short this_len = | ||
| 275 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 276 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
| 277 | slave_addr, register_addr + bytes_read, | ||
| 278 | this_len, &data[bytes_read]); | ||
| 279 | if (result) { | ||
| 280 | LOG_RESULT_LOCATION(result); | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | bytes_read += this_len; | ||
| 284 | } | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | EXPORT_SYMBOL(inv_serial_read); | ||
| 288 | |||
| 289 | int inv_serial_write_mem( | ||
| 290 | void *sl_handle, | ||
| 291 | unsigned char slave_addr, | ||
| 292 | unsigned short mem_addr, | ||
| 293 | unsigned short length, | ||
| 294 | unsigned char const *data) | ||
| 295 | { | ||
| 296 | int result; | ||
| 297 | unsigned short bytes_written = 0; | ||
| 298 | |||
| 299 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 300 | pr_err("memory read length (%d B) extends beyond its" | ||
| 301 | " limits (%d) if started at location %d\n", length, | ||
| 302 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
| 303 | return INV_ERROR_INVALID_PARAMETER; | ||
| 304 | } | ||
| 305 | while (bytes_written < length) { | ||
| 306 | unsigned short this_len = | ||
| 307 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
| 308 | result = mpu_memory_write((struct i2c_adapter *)sl_handle, | ||
| 309 | slave_addr, mem_addr + bytes_written, | ||
| 310 | this_len, &data[bytes_written]); | ||
| 311 | if (result) { | ||
| 312 | LOG_RESULT_LOCATION(result); | ||
| 313 | return result; | ||
| 314 | } | ||
| 315 | bytes_written += this_len; | ||
| 316 | } | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | EXPORT_SYMBOL(inv_serial_write_mem); | ||
| 320 | |||
| 321 | int inv_serial_read_mem( | ||
| 322 | void *sl_handle, | ||
| 323 | unsigned char slave_addr, | ||
| 324 | unsigned short mem_addr, | ||
| 325 | unsigned short length, | ||
| 326 | unsigned char *data) | ||
| 327 | { | ||
| 328 | int result; | ||
| 329 | unsigned short bytes_read = 0; | ||
| 330 | |||
| 331 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 332 | printk | ||
| 333 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
| 334 | "if started at location %d\n", length, | ||
| 335 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
| 336 | return INV_ERROR_INVALID_PARAMETER; | ||
| 337 | } | ||
| 338 | while (bytes_read < length) { | ||
| 339 | unsigned short this_len = | ||
| 340 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 341 | result = | ||
| 342 | mpu_memory_read((struct i2c_adapter *)sl_handle, | ||
| 343 | slave_addr, mem_addr + bytes_read, | ||
| 344 | this_len, &data[bytes_read]); | ||
| 345 | if (result) { | ||
| 346 | LOG_RESULT_LOCATION(result); | ||
| 347 | return result; | ||
| 348 | } | ||
| 349 | bytes_read += this_len; | ||
| 350 | } | ||
| 351 | return 0; | ||
| 352 | } | ||
| 353 | EXPORT_SYMBOL(inv_serial_read_mem); | ||
| 354 | |||
| 355 | int inv_serial_write_fifo( | ||
| 356 | void *sl_handle, | ||
| 357 | unsigned char slave_addr, | ||
| 358 | unsigned short length, | ||
| 359 | unsigned char const *data) | ||
| 360 | { | ||
| 361 | int result; | ||
| 362 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 363 | unsigned short bytes_written = 0; | ||
| 364 | |||
| 365 | if (length > FIFO_HW_SIZE) { | ||
| 366 | printk(KERN_ERR | ||
| 367 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
| 368 | return INV_ERROR_INVALID_PARAMETER; | ||
| 369 | } | ||
| 370 | while (bytes_written < length) { | ||
| 371 | unsigned short this_len = | ||
| 372 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
| 373 | i2c_write[0] = MPUREG_FIFO_R_W; | ||
| 374 | memcpy(&i2c_write[1], &data[bytes_written], this_len); | ||
| 375 | result = inv_i2c_write((struct i2c_adapter *)sl_handle, | ||
| 376 | slave_addr, this_len + 1, i2c_write); | ||
| 377 | if (result) { | ||
| 378 | LOG_RESULT_LOCATION(result); | ||
| 379 | return result; | ||
| 380 | } | ||
| 381 | bytes_written += this_len; | ||
| 382 | } | ||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | EXPORT_SYMBOL(inv_serial_write_fifo); | ||
| 386 | |||
| 387 | int inv_serial_read_fifo( | ||
| 388 | void *sl_handle, | ||
| 389 | unsigned char slave_addr, | ||
| 390 | unsigned short length, | ||
| 391 | unsigned char *data) | ||
| 392 | { | ||
| 393 | int result; | ||
| 394 | unsigned short bytes_read = 0; | ||
| 395 | |||
| 396 | if (length > FIFO_HW_SIZE) { | ||
| 397 | printk(KERN_ERR | ||
| 398 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
| 399 | return INV_ERROR_INVALID_PARAMETER; | ||
| 400 | } | ||
| 401 | while (bytes_read < length) { | ||
| 402 | unsigned short this_len = | ||
| 403 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 404 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
| 405 | slave_addr, MPUREG_FIFO_R_W, this_len, | ||
| 406 | &data[bytes_read]); | ||
| 407 | if (result) { | ||
| 408 | LOG_RESULT_LOCATION(result); | ||
| 409 | return result; | ||
| 410 | } | ||
| 411 | bytes_read += this_len; | ||
| 412 | } | ||
| 413 | |||
| 414 | return 0; | ||
| 415 | } | ||
| 416 | EXPORT_SYMBOL(inv_serial_read_fifo); | ||
| 417 | |||
| 418 | /** | ||
| 419 | * @} | ||
| 420 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mpu-dev.c b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c new file mode 100644 index 00000000000..c98ed3b4a80 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c | |||
| @@ -0,0 +1,1250 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/i2c-dev.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/moduleparam.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/stat.h> | ||
| 27 | #include <linux/irq.h> | ||
| 28 | #include <linux/gpio.h> | ||
| 29 | #include <linux/signal.h> | ||
| 30 | #include <linux/miscdevice.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/version.h> | ||
| 33 | #include <linux/pm.h> | ||
| 34 | #include <linux/mutex.h> | ||
| 35 | #include <linux/suspend.h> | ||
| 36 | #include <linux/poll.h> | ||
| 37 | |||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/fs.h> | ||
| 40 | #include <linux/mm.h> | ||
| 41 | #include <linux/sched.h> | ||
| 42 | #include <linux/wait.h> | ||
| 43 | #include <linux/uaccess.h> | ||
| 44 | #include <linux/io.h> | ||
| 45 | |||
| 46 | #include "mpuirq.h" | ||
| 47 | #include "slaveirq.h" | ||
| 48 | #include "mlsl.h" | ||
| 49 | #include "mldl_cfg.h" | ||
| 50 | #include <linux/mpu.h> | ||
| 51 | |||
| 52 | |||
| 53 | /* Platform data for the MPU */ | ||
| 54 | struct mpu_private_data { | ||
| 55 | struct miscdevice dev; | ||
| 56 | struct i2c_client *client; | ||
| 57 | |||
| 58 | /* mldl_cfg data */ | ||
| 59 | struct mldl_cfg mldl_cfg; | ||
| 60 | struct mpu_ram mpu_ram; | ||
| 61 | struct mpu_gyro_cfg mpu_gyro_cfg; | ||
| 62 | struct mpu_offsets mpu_offsets; | ||
| 63 | struct mpu_chip_info mpu_chip_info; | ||
| 64 | struct inv_mpu_cfg inv_mpu_cfg; | ||
| 65 | struct inv_mpu_state inv_mpu_state; | ||
| 66 | |||
| 67 | struct mutex mutex; | ||
| 68 | wait_queue_head_t mpu_event_wait; | ||
| 69 | struct completion completion; | ||
| 70 | struct timer_list timeout; | ||
| 71 | struct notifier_block nb; | ||
| 72 | struct mpuirq_data mpu_pm_event; | ||
| 73 | int response_timeout; /* In seconds */ | ||
| 74 | unsigned long event; | ||
| 75 | int pid; | ||
| 76 | struct module *slave_modules[EXT_SLAVE_NUM_TYPES]; | ||
| 77 | }; | ||
| 78 | |||
| 79 | struct mpu_private_data *mpu_private_data; | ||
| 80 | |||
| 81 | static void mpu_pm_timeout(u_long data) | ||
| 82 | { | ||
| 83 | struct mpu_private_data *mpu = (struct mpu_private_data *)data; | ||
| 84 | struct i2c_client *client = mpu->client; | ||
| 85 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 86 | complete(&mpu->completion); | ||
| 87 | } | ||
| 88 | |||
| 89 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
| 90 | unsigned long event, void *unused) | ||
| 91 | { | ||
| 92 | struct mpu_private_data *mpu = | ||
| 93 | container_of(nb, struct mpu_private_data, nb); | ||
| 94 | struct i2c_client *client = mpu->client; | ||
| 95 | struct timeval event_time; | ||
| 96 | dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event); | ||
| 97 | |||
| 98 | /* Prevent the file handle from being closed before we initialize | ||
| 99 | the completion event */ | ||
| 100 | mutex_lock(&mpu->mutex); | ||
| 101 | if (!(mpu->pid) || | ||
| 102 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
| 103 | mutex_unlock(&mpu->mutex); | ||
| 104 | return NOTIFY_OK; | ||
| 105 | } | ||
| 106 | |||
| 107 | if (event == PM_SUSPEND_PREPARE) | ||
| 108 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
| 109 | if (event == PM_POST_SUSPEND) | ||
| 110 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
| 111 | |||
| 112 | do_gettimeofday(&event_time); | ||
| 113 | mpu->mpu_pm_event.interruptcount++; | ||
| 114 | mpu->mpu_pm_event.irqtime = | ||
| 115 | (((long long)event_time.tv_sec) << 32) + event_time.tv_usec; | ||
| 116 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
| 117 | mpu->mpu_pm_event.data = mpu->event; | ||
| 118 | |||
| 119 | if (mpu->response_timeout > 0) { | ||
| 120 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
| 121 | add_timer(&mpu->timeout); | ||
| 122 | } | ||
| 123 | INIT_COMPLETION(mpu->completion); | ||
| 124 | mutex_unlock(&mpu->mutex); | ||
| 125 | |||
| 126 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
| 127 | wait_for_completion(&mpu->completion); | ||
| 128 | del_timer_sync(&mpu->timeout); | ||
| 129 | dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
| 130 | return NOTIFY_OK; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int mpu_dev_open(struct inode *inode, struct file *file) | ||
| 134 | { | ||
| 135 | struct mpu_private_data *mpu = | ||
| 136 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 137 | struct i2c_client *client = mpu->client; | ||
| 138 | int result; | ||
| 139 | int ii; | ||
| 140 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 141 | dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid); | ||
| 142 | |||
| 143 | result = mutex_lock_interruptible(&mpu->mutex); | ||
| 144 | if (mpu->pid) { | ||
| 145 | mutex_unlock(&mpu->mutex); | ||
| 146 | return -EBUSY; | ||
| 147 | } | ||
| 148 | mpu->pid = current->pid; | ||
| 149 | |||
| 150 | /* Reset the sensors to the default */ | ||
| 151 | if (result) { | ||
| 152 | dev_err(&client->adapter->dev, | ||
| 153 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 154 | __func__, result); | ||
| 155 | return result; | ||
| 156 | } | ||
| 157 | |||
| 158 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
| 159 | __module_get(mpu->slave_modules[ii]); | ||
| 160 | |||
| 161 | mutex_unlock(&mpu->mutex); | ||
| 162 | return 0; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
| 166 | static int mpu_release(struct inode *inode, struct file *file) | ||
| 167 | { | ||
| 168 | struct mpu_private_data *mpu = | ||
| 169 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 170 | struct i2c_client *client = mpu->client; | ||
| 171 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 172 | int result = 0; | ||
| 173 | int ii; | ||
| 174 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 175 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 176 | |||
| 177 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 178 | if (!pdata_slave[ii]) | ||
| 179 | slave_adapter[ii] = NULL; | ||
| 180 | else | ||
| 181 | slave_adapter[ii] = | ||
| 182 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 183 | } | ||
| 184 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 185 | |||
| 186 | mutex_lock(&mpu->mutex); | ||
| 187 | mldl_cfg->inv_mpu_cfg->requested_sensors = 0; | ||
| 188 | result = inv_mpu_suspend(mldl_cfg, | ||
| 189 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 190 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 191 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 192 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 193 | INV_ALL_SENSORS); | ||
| 194 | mpu->pid = 0; | ||
| 195 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
| 196 | module_put(mpu->slave_modules[ii]); | ||
| 197 | |||
| 198 | mutex_unlock(&mpu->mutex); | ||
| 199 | complete(&mpu->completion); | ||
| 200 | dev_dbg(&client->adapter->dev, "mpu_release\n"); | ||
| 201 | |||
| 202 | return result; | ||
| 203 | } | ||
| 204 | |||
| 205 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
| 206 | static ssize_t mpu_read(struct file *file, | ||
| 207 | char __user *buf, size_t count, loff_t *offset) | ||
| 208 | { | ||
| 209 | struct mpu_private_data *mpu = | ||
| 210 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 211 | struct i2c_client *client = mpu->client; | ||
| 212 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
| 213 | int err; | ||
| 214 | |||
| 215 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
| 216 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
| 217 | |||
| 218 | if (!mpu->event || !buf | ||
| 219 | || count < sizeof(mpu->mpu_pm_event)) | ||
| 220 | return 0; | ||
| 221 | |||
| 222 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
| 223 | if (err) { | ||
| 224 | dev_err(&client->adapter->dev, | ||
| 225 | "Copy to user returned %d\n", err); | ||
| 226 | return -EFAULT; | ||
| 227 | } | ||
| 228 | mpu->event = 0; | ||
| 229 | return len; | ||
| 230 | } | ||
| 231 | |||
| 232 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
| 233 | { | ||
| 234 | struct mpu_private_data *mpu = | ||
| 235 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 236 | int mask = 0; | ||
| 237 | |||
| 238 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
| 239 | if (mpu->event) | ||
| 240 | mask |= POLLIN | POLLRDNORM; | ||
| 241 | return mask; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int mpu_dev_ioctl_get_ext_slave_platform_data( | ||
| 245 | struct i2c_client *client, | ||
| 246 | struct ext_slave_platform_data __user *arg) | ||
| 247 | { | ||
| 248 | struct mpu_private_data *mpu = | ||
| 249 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 250 | struct ext_slave_platform_data *pdata_slave; | ||
| 251 | struct ext_slave_platform_data local_pdata_slave; | ||
| 252 | |||
| 253 | if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave))) | ||
| 254 | return -EFAULT; | ||
| 255 | |||
| 256 | if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
| 257 | return -EINVAL; | ||
| 258 | |||
| 259 | pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type]; | ||
| 260 | /* All but private data and irq_data */ | ||
| 261 | if (!pdata_slave) | ||
| 262 | return -ENODEV; | ||
| 263 | if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave))) | ||
| 264 | return -EFAULT; | ||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | |||
| 268 | static int mpu_dev_ioctl_get_mpu_platform_data( | ||
| 269 | struct i2c_client *client, | ||
| 270 | struct mpu_platform_data __user *arg) | ||
| 271 | { | ||
| 272 | struct mpu_private_data *mpu = | ||
| 273 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 274 | struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
| 275 | |||
| 276 | if (copy_to_user(arg, pdata, sizeof(*pdata))) | ||
| 277 | return -EFAULT; | ||
| 278 | return 0; | ||
| 279 | } | ||
| 280 | |||
| 281 | static int mpu_dev_ioctl_get_ext_slave_descr( | ||
| 282 | struct i2c_client *client, | ||
| 283 | struct ext_slave_descr __user *arg) | ||
| 284 | { | ||
| 285 | struct mpu_private_data *mpu = | ||
| 286 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 287 | struct ext_slave_descr *slave; | ||
| 288 | struct ext_slave_descr local_slave; | ||
| 289 | |||
| 290 | if (copy_from_user(&local_slave, arg, sizeof(local_slave))) | ||
| 291 | return -EFAULT; | ||
| 292 | |||
| 293 | if (local_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
| 294 | return -EINVAL; | ||
| 295 | |||
| 296 | slave = mpu->mldl_cfg.slave[local_slave.type]; | ||
| 297 | /* All but private data and irq_data */ | ||
| 298 | if (!slave) | ||
| 299 | return -ENODEV; | ||
| 300 | if (copy_to_user(arg, slave, sizeof(*slave))) | ||
| 301 | return -EFAULT; | ||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | |||
| 306 | /** | ||
| 307 | * slave_config() - Pass a requested slave configuration to the slave sensor | ||
| 308 | * | ||
| 309 | * @adapter the adaptor to use to communicate with the slave | ||
| 310 | * @mldl_cfg the mldl configuration structuer | ||
| 311 | * @slave pointer to the slave descriptor | ||
| 312 | * @usr_config The configuration to pass to the slave sensor | ||
| 313 | * | ||
| 314 | * returns 0 or non-zero error code | ||
| 315 | */ | ||
| 316 | static int inv_mpu_config(struct mldl_cfg *mldl_cfg, | ||
| 317 | void *gyro_adapter, | ||
| 318 | struct ext_slave_config __user *usr_config) | ||
| 319 | { | ||
| 320 | int retval = 0; | ||
| 321 | struct ext_slave_config config; | ||
| 322 | |||
| 323 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 324 | if (retval) | ||
| 325 | return -EFAULT; | ||
| 326 | |||
| 327 | if (config.len && config.data) { | ||
| 328 | void *data; | ||
| 329 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 330 | if (!data) | ||
| 331 | return -ENOMEM; | ||
| 332 | |||
| 333 | retval = copy_from_user(data, | ||
| 334 | (void __user *)config.data, config.len); | ||
| 335 | if (retval) { | ||
| 336 | retval = -EFAULT; | ||
| 337 | kfree(data); | ||
| 338 | return retval; | ||
| 339 | } | ||
| 340 | config.data = data; | ||
| 341 | } | ||
| 342 | retval = gyro_config(gyro_adapter, mldl_cfg, &config); | ||
| 343 | kfree(config.data); | ||
| 344 | return retval; | ||
| 345 | } | ||
| 346 | |||
| 347 | static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg, | ||
| 348 | void *gyro_adapter, | ||
| 349 | struct ext_slave_config __user *usr_config) | ||
| 350 | { | ||
| 351 | int retval = 0; | ||
| 352 | struct ext_slave_config config; | ||
| 353 | void *user_data; | ||
| 354 | |||
| 355 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 356 | if (retval) | ||
| 357 | return -EFAULT; | ||
| 358 | |||
| 359 | user_data = config.data; | ||
| 360 | if (config.len && config.data) { | ||
| 361 | void *data; | ||
| 362 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 363 | if (!data) | ||
| 364 | return -ENOMEM; | ||
| 365 | |||
| 366 | retval = copy_from_user(data, | ||
| 367 | (void __user *)config.data, config.len); | ||
| 368 | if (retval) { | ||
| 369 | retval = -EFAULT; | ||
| 370 | kfree(data); | ||
| 371 | return retval; | ||
| 372 | } | ||
| 373 | config.data = data; | ||
| 374 | } | ||
| 375 | retval = gyro_get_config(gyro_adapter, mldl_cfg, &config); | ||
| 376 | if (!retval) | ||
| 377 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 378 | config.data, config.len); | ||
| 379 | kfree(config.data); | ||
| 380 | return retval; | ||
| 381 | } | ||
| 382 | |||
| 383 | static int slave_config(struct mldl_cfg *mldl_cfg, | ||
| 384 | void *gyro_adapter, | ||
| 385 | void *slave_adapter, | ||
| 386 | struct ext_slave_descr *slave, | ||
| 387 | struct ext_slave_platform_data *pdata, | ||
| 388 | struct ext_slave_config __user *usr_config) | ||
| 389 | { | ||
| 390 | int retval = 0; | ||
| 391 | struct ext_slave_config config; | ||
| 392 | if ((!slave) || (!slave->config)) | ||
| 393 | return -ENODEV; | ||
| 394 | |||
| 395 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 396 | if (retval) | ||
| 397 | return -EFAULT; | ||
| 398 | |||
| 399 | if (config.len && config.data) { | ||
| 400 | void *data; | ||
| 401 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 402 | if (!data) | ||
| 403 | return -ENOMEM; | ||
| 404 | |||
| 405 | retval = copy_from_user(data, | ||
| 406 | (void __user *)config.data, config.len); | ||
| 407 | if (retval) { | ||
| 408 | retval = -EFAULT; | ||
| 409 | kfree(data); | ||
| 410 | return retval; | ||
| 411 | } | ||
| 412 | config.data = data; | ||
| 413 | } | ||
| 414 | retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter, | ||
| 415 | &config, slave, pdata); | ||
| 416 | kfree(config.data); | ||
| 417 | return retval; | ||
| 418 | } | ||
| 419 | |||
| 420 | static int slave_get_config(struct mldl_cfg *mldl_cfg, | ||
| 421 | void *gyro_adapter, | ||
| 422 | void *slave_adapter, | ||
| 423 | struct ext_slave_descr *slave, | ||
| 424 | struct ext_slave_platform_data *pdata, | ||
| 425 | struct ext_slave_config __user *usr_config) | ||
| 426 | { | ||
| 427 | int retval = 0; | ||
| 428 | struct ext_slave_config config; | ||
| 429 | void *user_data; | ||
| 430 | if (!(slave) || !(slave->get_config)) | ||
| 431 | return -ENODEV; | ||
| 432 | |||
| 433 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 434 | if (retval) | ||
| 435 | return -EFAULT; | ||
| 436 | |||
| 437 | user_data = config.data; | ||
| 438 | if (config.len && config.data) { | ||
| 439 | void *data; | ||
| 440 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 441 | if (!data) | ||
| 442 | return -ENOMEM; | ||
| 443 | |||
| 444 | retval = copy_from_user(data, | ||
| 445 | (void __user *)config.data, config.len); | ||
| 446 | if (retval) { | ||
| 447 | retval = -EFAULT; | ||
| 448 | kfree(data); | ||
| 449 | return retval; | ||
| 450 | } | ||
| 451 | config.data = data; | ||
| 452 | } | ||
| 453 | retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter, | ||
| 454 | slave_adapter, &config, slave, pdata); | ||
| 455 | if (retval) { | ||
| 456 | kfree(config.data); | ||
| 457 | return retval; | ||
| 458 | } | ||
| 459 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 460 | config.data, config.len); | ||
| 461 | kfree(config.data); | ||
| 462 | return retval; | ||
| 463 | } | ||
| 464 | |||
| 465 | static int inv_slave_read(struct mldl_cfg *mldl_cfg, | ||
| 466 | void *gyro_adapter, | ||
| 467 | void *slave_adapter, | ||
| 468 | struct ext_slave_descr *slave, | ||
| 469 | struct ext_slave_platform_data *pdata, | ||
| 470 | void __user *usr_data) | ||
| 471 | { | ||
| 472 | int retval; | ||
| 473 | unsigned char *data; | ||
| 474 | data = kzalloc(slave->read_len, GFP_KERNEL); | ||
| 475 | if (!data) | ||
| 476 | return -EFAULT; | ||
| 477 | |||
| 478 | retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter, | ||
| 479 | slave, pdata, data); | ||
| 480 | |||
| 481 | if ((!retval) && | ||
| 482 | (copy_to_user((unsigned char __user *)usr_data, | ||
| 483 | data, slave->read_len))) | ||
| 484 | retval = -EFAULT; | ||
| 485 | |||
| 486 | kfree(data); | ||
| 487 | return retval; | ||
| 488 | } | ||
| 489 | |||
| 490 | static int mpu_handle_mlsl(void *sl_handle, | ||
| 491 | unsigned char addr, | ||
| 492 | unsigned int cmd, | ||
| 493 | struct mpu_read_write __user *usr_msg) | ||
| 494 | { | ||
| 495 | int retval = 0; | ||
| 496 | struct mpu_read_write msg; | ||
| 497 | unsigned char *user_data; | ||
| 498 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
| 499 | if (retval) | ||
| 500 | return -EFAULT; | ||
| 501 | |||
| 502 | user_data = msg.data; | ||
| 503 | if (msg.length && msg.data) { | ||
| 504 | unsigned char *data; | ||
| 505 | data = kmalloc(msg.length, GFP_KERNEL); | ||
| 506 | if (!data) | ||
| 507 | return -ENOMEM; | ||
| 508 | |||
| 509 | retval = copy_from_user(data, | ||
| 510 | (void __user *)msg.data, msg.length); | ||
| 511 | if (retval) { | ||
| 512 | retval = -EFAULT; | ||
| 513 | kfree(data); | ||
| 514 | return retval; | ||
| 515 | } | ||
| 516 | msg.data = data; | ||
| 517 | } else { | ||
| 518 | return -EPERM; | ||
| 519 | } | ||
| 520 | |||
| 521 | switch (cmd) { | ||
| 522 | case MPU_READ: | ||
| 523 | retval = inv_serial_read(sl_handle, addr, | ||
| 524 | msg.address, msg.length, msg.data); | ||
| 525 | break; | ||
| 526 | case MPU_WRITE: | ||
| 527 | retval = inv_serial_write(sl_handle, addr, | ||
| 528 | msg.length, msg.data); | ||
| 529 | break; | ||
| 530 | case MPU_READ_MEM: | ||
| 531 | retval = inv_serial_read_mem(sl_handle, addr, | ||
| 532 | msg.address, msg.length, msg.data); | ||
| 533 | break; | ||
| 534 | case MPU_WRITE_MEM: | ||
| 535 | retval = inv_serial_write_mem(sl_handle, addr, | ||
| 536 | msg.address, msg.length, | ||
| 537 | msg.data); | ||
| 538 | break; | ||
| 539 | case MPU_READ_FIFO: | ||
| 540 | retval = inv_serial_read_fifo(sl_handle, addr, | ||
| 541 | msg.length, msg.data); | ||
| 542 | break; | ||
| 543 | case MPU_WRITE_FIFO: | ||
| 544 | retval = inv_serial_write_fifo(sl_handle, addr, | ||
| 545 | msg.length, msg.data); | ||
| 546 | break; | ||
| 547 | |||
| 548 | }; | ||
| 549 | if (retval) { | ||
| 550 | dev_err(&((struct i2c_adapter *)sl_handle)->dev, | ||
| 551 | "%s: i2c %d error %d\n", | ||
| 552 | __func__, cmd, retval); | ||
| 553 | kfree(msg.data); | ||
| 554 | return retval; | ||
| 555 | } | ||
| 556 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 557 | msg.data, msg.length); | ||
| 558 | kfree(msg.data); | ||
| 559 | return retval; | ||
| 560 | } | ||
| 561 | |||
| 562 | /* ioctl - I/O control */ | ||
| 563 | static long mpu_dev_ioctl(struct file *file, | ||
| 564 | unsigned int cmd, unsigned long arg) | ||
| 565 | { | ||
| 566 | struct mpu_private_data *mpu = | ||
| 567 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 568 | struct i2c_client *client = mpu->client; | ||
| 569 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 570 | int retval = 0; | ||
| 571 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 572 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 573 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 574 | int ii; | ||
| 575 | |||
| 576 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 577 | if (!pdata_slave[ii]) | ||
| 578 | slave_adapter[ii] = NULL; | ||
| 579 | else | ||
| 580 | slave_adapter[ii] = | ||
| 581 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 582 | } | ||
| 583 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 584 | |||
| 585 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
| 586 | if (retval) { | ||
| 587 | dev_err(&client->adapter->dev, | ||
| 588 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 589 | __func__, retval); | ||
| 590 | return retval; | ||
| 591 | } | ||
| 592 | |||
| 593 | switch (cmd) { | ||
| 594 | case MPU_GET_EXT_SLAVE_PLATFORM_DATA: | ||
| 595 | retval = mpu_dev_ioctl_get_ext_slave_platform_data( | ||
| 596 | client, | ||
| 597 | (struct ext_slave_platform_data __user *)arg); | ||
| 598 | break; | ||
| 599 | case MPU_GET_MPU_PLATFORM_DATA: | ||
| 600 | retval = mpu_dev_ioctl_get_mpu_platform_data( | ||
| 601 | client, | ||
| 602 | (struct mpu_platform_data __user *)arg); | ||
| 603 | break; | ||
| 604 | case MPU_GET_EXT_SLAVE_DESCR: | ||
| 605 | retval = mpu_dev_ioctl_get_ext_slave_descr( | ||
| 606 | client, | ||
| 607 | (struct ext_slave_descr __user *)arg); | ||
| 608 | break; | ||
| 609 | case MPU_READ: | ||
| 610 | case MPU_WRITE: | ||
| 611 | case MPU_READ_MEM: | ||
| 612 | case MPU_WRITE_MEM: | ||
| 613 | case MPU_READ_FIFO: | ||
| 614 | case MPU_WRITE_FIFO: | ||
| 615 | retval = mpu_handle_mlsl( | ||
| 616 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 617 | mldl_cfg->mpu_chip_info->addr, cmd, | ||
| 618 | (struct mpu_read_write __user *)arg); | ||
| 619 | break; | ||
| 620 | case MPU_CONFIG_GYRO: | ||
| 621 | retval = inv_mpu_config( | ||
| 622 | mldl_cfg, | ||
| 623 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 624 | (struct ext_slave_config __user *)arg); | ||
| 625 | break; | ||
| 626 | case MPU_CONFIG_ACCEL: | ||
| 627 | retval = slave_config( | ||
| 628 | mldl_cfg, | ||
| 629 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 630 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 631 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 632 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 633 | (struct ext_slave_config __user *)arg); | ||
| 634 | break; | ||
| 635 | case MPU_CONFIG_COMPASS: | ||
| 636 | retval = slave_config( | ||
| 637 | mldl_cfg, | ||
| 638 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 639 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 640 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 641 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 642 | (struct ext_slave_config __user *)arg); | ||
| 643 | break; | ||
| 644 | case MPU_CONFIG_PRESSURE: | ||
| 645 | retval = slave_config( | ||
| 646 | mldl_cfg, | ||
| 647 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 648 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 649 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 650 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 651 | (struct ext_slave_config __user *)arg); | ||
| 652 | break; | ||
| 653 | case MPU_GET_CONFIG_GYRO: | ||
| 654 | retval = inv_mpu_get_config( | ||
| 655 | mldl_cfg, | ||
| 656 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 657 | (struct ext_slave_config __user *)arg); | ||
| 658 | break; | ||
| 659 | case MPU_GET_CONFIG_ACCEL: | ||
| 660 | retval = slave_get_config( | ||
| 661 | mldl_cfg, | ||
| 662 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 663 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 664 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 665 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 666 | (struct ext_slave_config __user *)arg); | ||
| 667 | break; | ||
| 668 | case MPU_GET_CONFIG_COMPASS: | ||
| 669 | retval = slave_get_config( | ||
| 670 | mldl_cfg, | ||
| 671 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 672 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 673 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 674 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 675 | (struct ext_slave_config __user *)arg); | ||
| 676 | break; | ||
| 677 | case MPU_GET_CONFIG_PRESSURE: | ||
| 678 | retval = slave_get_config( | ||
| 679 | mldl_cfg, | ||
| 680 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 681 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 682 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 683 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 684 | (struct ext_slave_config __user *)arg); | ||
| 685 | break; | ||
| 686 | case MPU_SUSPEND: | ||
| 687 | retval = inv_mpu_suspend( | ||
| 688 | mldl_cfg, | ||
| 689 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 690 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 691 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 692 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 693 | arg); | ||
| 694 | break; | ||
| 695 | case MPU_RESUME: | ||
| 696 | retval = inv_mpu_resume( | ||
| 697 | mldl_cfg, | ||
| 698 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 699 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 700 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 701 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 702 | arg); | ||
| 703 | break; | ||
| 704 | case MPU_PM_EVENT_HANDLED: | ||
| 705 | dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd); | ||
| 706 | complete(&mpu->completion); | ||
| 707 | break; | ||
| 708 | case MPU_READ_ACCEL: | ||
| 709 | retval = inv_slave_read( | ||
| 710 | mldl_cfg, | ||
| 711 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 712 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 713 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 714 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 715 | (unsigned char __user *)arg); | ||
| 716 | break; | ||
| 717 | case MPU_READ_COMPASS: | ||
| 718 | retval = inv_slave_read( | ||
| 719 | mldl_cfg, | ||
| 720 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 721 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 722 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 723 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 724 | (unsigned char __user *)arg); | ||
| 725 | break; | ||
| 726 | case MPU_READ_PRESSURE: | ||
| 727 | retval = inv_slave_read( | ||
| 728 | mldl_cfg, | ||
| 729 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 730 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 731 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 732 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 733 | (unsigned char __user *)arg); | ||
| 734 | break; | ||
| 735 | case MPU_GET_REQUESTED_SENSORS: | ||
| 736 | if (copy_to_user( | ||
| 737 | (__u32 __user *)arg, | ||
| 738 | &mldl_cfg->inv_mpu_cfg->requested_sensors, | ||
| 739 | sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors))) | ||
| 740 | retval = -EFAULT; | ||
| 741 | break; | ||
| 742 | case MPU_SET_REQUESTED_SENSORS: | ||
| 743 | mldl_cfg->inv_mpu_cfg->requested_sensors = arg; | ||
| 744 | break; | ||
| 745 | case MPU_GET_IGNORE_SYSTEM_SUSPEND: | ||
| 746 | if (copy_to_user( | ||
| 747 | (unsigned char __user *)arg, | ||
| 748 | &mldl_cfg->inv_mpu_cfg->ignore_system_suspend, | ||
| 749 | sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend))) | ||
| 750 | retval = -EFAULT; | ||
| 751 | break; | ||
| 752 | case MPU_SET_IGNORE_SYSTEM_SUSPEND: | ||
| 753 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg; | ||
| 754 | break; | ||
| 755 | case MPU_GET_MLDL_STATUS: | ||
| 756 | if (copy_to_user( | ||
| 757 | (unsigned char __user *)arg, | ||
| 758 | &mldl_cfg->inv_mpu_state->status, | ||
| 759 | sizeof(mldl_cfg->inv_mpu_state->status))) | ||
| 760 | retval = -EFAULT; | ||
| 761 | break; | ||
| 762 | case MPU_GET_I2C_SLAVES_ENABLED: | ||
| 763 | if (copy_to_user( | ||
| 764 | (unsigned char __user *)arg, | ||
| 765 | &mldl_cfg->inv_mpu_state->i2c_slaves_enabled, | ||
| 766 | sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled))) | ||
| 767 | retval = -EFAULT; | ||
| 768 | break; | ||
| 769 | default: | ||
| 770 | dev_err(&client->adapter->dev, | ||
| 771 | "%s: Unknown cmd %x, arg %lu\n", | ||
| 772 | __func__, cmd, arg); | ||
| 773 | retval = -EINVAL; | ||
| 774 | }; | ||
| 775 | |||
| 776 | mutex_unlock(&mpu->mutex); | ||
| 777 | dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n", | ||
| 778 | __func__, cmd, arg, retval); | ||
| 779 | |||
| 780 | if (retval > 0) | ||
| 781 | retval = -retval; | ||
| 782 | |||
| 783 | return retval; | ||
| 784 | } | ||
| 785 | |||
| 786 | void mpu_shutdown(struct i2c_client *client) | ||
| 787 | { | ||
| 788 | struct mpu_private_data *mpu = | ||
| 789 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 790 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 791 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 792 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 793 | int ii; | ||
| 794 | |||
| 795 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 796 | if (!pdata_slave[ii]) | ||
| 797 | slave_adapter[ii] = NULL; | ||
| 798 | else | ||
| 799 | slave_adapter[ii] = | ||
| 800 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 801 | } | ||
| 802 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 803 | |||
| 804 | mutex_lock(&mpu->mutex); | ||
| 805 | (void)inv_mpu_suspend(mldl_cfg, | ||
| 806 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 807 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 808 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 809 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 810 | INV_ALL_SENSORS); | ||
| 811 | mutex_unlock(&mpu->mutex); | ||
| 812 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 813 | } | ||
| 814 | |||
| 815 | int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg) | ||
| 816 | { | ||
| 817 | struct mpu_private_data *mpu = | ||
| 818 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 819 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 820 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 821 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 822 | int ii; | ||
| 823 | |||
| 824 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 825 | if (!pdata_slave[ii]) | ||
| 826 | slave_adapter[ii] = NULL; | ||
| 827 | else | ||
| 828 | slave_adapter[ii] = | ||
| 829 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 830 | } | ||
| 831 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 832 | |||
| 833 | mutex_lock(&mpu->mutex); | ||
| 834 | if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
| 835 | dev_dbg(&client->adapter->dev, | ||
| 836 | "%s: suspending on event %d\n", __func__, mesg.event); | ||
| 837 | (void)inv_mpu_suspend(mldl_cfg, | ||
| 838 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 839 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 840 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 841 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 842 | INV_ALL_SENSORS); | ||
| 843 | } else { | ||
| 844 | dev_dbg(&client->adapter->dev, | ||
| 845 | "%s: Already suspended %d\n", __func__, mesg.event); | ||
| 846 | } | ||
| 847 | mutex_unlock(&mpu->mutex); | ||
| 848 | return 0; | ||
| 849 | } | ||
| 850 | |||
| 851 | int mpu_dev_resume(struct i2c_client *client) | ||
| 852 | { | ||
| 853 | struct mpu_private_data *mpu = | ||
| 854 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 855 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 856 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 857 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 858 | int ii; | ||
| 859 | |||
| 860 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 861 | if (!pdata_slave[ii]) | ||
| 862 | slave_adapter[ii] = NULL; | ||
| 863 | else | ||
| 864 | slave_adapter[ii] = | ||
| 865 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 866 | } | ||
| 867 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 868 | |||
| 869 | mutex_lock(&mpu->mutex); | ||
| 870 | if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
| 871 | (void)inv_mpu_resume(mldl_cfg, | ||
| 872 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 873 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 874 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 875 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 876 | mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
| 877 | dev_dbg(&client->adapter->dev, | ||
| 878 | "%s for pid %d\n", __func__, mpu->pid); | ||
| 879 | } | ||
| 880 | mutex_unlock(&mpu->mutex); | ||
| 881 | return 0; | ||
| 882 | } | ||
| 883 | |||
| 884 | /* define which file operations are supported */ | ||
| 885 | static const struct file_operations mpu_fops = { | ||
| 886 | .owner = THIS_MODULE, | ||
| 887 | .read = mpu_read, | ||
| 888 | .poll = mpu_poll, | ||
| 889 | .unlocked_ioctl = mpu_dev_ioctl, | ||
| 890 | .open = mpu_dev_open, | ||
| 891 | .release = mpu_release, | ||
| 892 | }; | ||
| 893 | |||
| 894 | int inv_mpu_register_slave(struct module *slave_module, | ||
| 895 | struct i2c_client *slave_client, | ||
| 896 | struct ext_slave_platform_data *slave_pdata, | ||
| 897 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
| 898 | { | ||
| 899 | struct mpu_private_data *mpu = mpu_private_data; | ||
| 900 | struct mldl_cfg *mldl_cfg; | ||
| 901 | struct ext_slave_descr *slave_descr; | ||
| 902 | struct ext_slave_platform_data **pdata_slave; | ||
| 903 | char *irq_name = NULL; | ||
| 904 | int result = 0; | ||
| 905 | |||
| 906 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
| 907 | return -EINVAL; | ||
| 908 | |||
| 909 | if (!mpu) { | ||
| 910 | dev_err(&slave_client->adapter->dev, | ||
| 911 | "%s: Null mpu_private_data\n", __func__); | ||
| 912 | return -EINVAL; | ||
| 913 | } | ||
| 914 | mldl_cfg = &mpu->mldl_cfg; | ||
| 915 | pdata_slave = mldl_cfg->pdata_slave; | ||
| 916 | slave_descr = get_slave_descr(); | ||
| 917 | |||
| 918 | if (!slave_descr) { | ||
| 919 | dev_err(&slave_client->adapter->dev, | ||
| 920 | "%s: Null ext_slave_descr\n", __func__); | ||
| 921 | return -EINVAL; | ||
| 922 | } | ||
| 923 | |||
| 924 | mutex_lock(&mpu->mutex); | ||
| 925 | if (mpu->pid) { | ||
| 926 | mutex_unlock(&mpu->mutex); | ||
| 927 | return -EBUSY; | ||
| 928 | } | ||
| 929 | |||
| 930 | if (pdata_slave[slave_descr->type]) { | ||
| 931 | result = -EBUSY; | ||
| 932 | goto out_unlock_mutex; | ||
| 933 | } | ||
| 934 | |||
| 935 | slave_pdata->address = slave_client->addr; | ||
| 936 | slave_pdata->irq = slave_client->irq; | ||
| 937 | slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter); | ||
| 938 | |||
| 939 | dev_info(&slave_client->adapter->dev, | ||
| 940 | "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n", | ||
| 941 | __func__, | ||
| 942 | slave_descr->name, | ||
| 943 | slave_descr->type, | ||
| 944 | slave_pdata->address, | ||
| 945 | slave_pdata->irq, | ||
| 946 | slave_pdata->adapt_num); | ||
| 947 | |||
| 948 | switch (slave_descr->type) { | ||
| 949 | case EXT_SLAVE_TYPE_ACCEL: | ||
| 950 | irq_name = "accelirq"; | ||
| 951 | break; | ||
| 952 | case EXT_SLAVE_TYPE_COMPASS: | ||
| 953 | irq_name = "compassirq"; | ||
| 954 | break; | ||
| 955 | case EXT_SLAVE_TYPE_PRESSURE: | ||
| 956 | irq_name = "pressureirq"; | ||
| 957 | break; | ||
| 958 | default: | ||
| 959 | irq_name = "none"; | ||
| 960 | }; | ||
| 961 | if (slave_descr->init) { | ||
| 962 | result = slave_descr->init(slave_client->adapter, | ||
| 963 | slave_descr, | ||
| 964 | slave_pdata); | ||
| 965 | if (result) { | ||
| 966 | dev_err(&slave_client->adapter->dev, | ||
| 967 | "%s init failed %d\n", | ||
| 968 | slave_descr->name, result); | ||
| 969 | goto out_unlock_mutex; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | |||
| 973 | pdata_slave[slave_descr->type] = slave_pdata; | ||
| 974 | mpu->slave_modules[slave_descr->type] = slave_module; | ||
| 975 | mldl_cfg->slave[slave_descr->type] = slave_descr; | ||
| 976 | |||
| 977 | goto out_unlock_mutex; | ||
| 978 | |||
| 979 | out_unlock_mutex: | ||
| 980 | mutex_unlock(&mpu->mutex); | ||
| 981 | |||
| 982 | if (!result && irq_name && (slave_pdata->irq > 0)) { | ||
| 983 | int warn_result; | ||
| 984 | dev_info(&slave_client->adapter->dev, | ||
| 985 | "Installing %s irq using %d\n", | ||
| 986 | irq_name, | ||
| 987 | slave_pdata->irq); | ||
| 988 | warn_result = slaveirq_init(slave_client->adapter, | ||
| 989 | slave_pdata, irq_name); | ||
| 990 | if (result) | ||
| 991 | dev_WARN(&slave_client->adapter->dev, | ||
| 992 | "%s irq assigned error: %d\n", | ||
| 993 | slave_descr->name, warn_result); | ||
| 994 | } else { | ||
| 995 | dev_info(&slave_client->adapter->dev, | ||
| 996 | "%s irq not assigned: %d %d %d\n", | ||
| 997 | slave_descr->name, | ||
| 998 | result, (int)irq_name, slave_pdata->irq); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | return result; | ||
| 1002 | } | ||
| 1003 | EXPORT_SYMBOL(inv_mpu_register_slave); | ||
| 1004 | |||
| 1005 | void inv_mpu_unregister_slave(struct i2c_client *slave_client, | ||
| 1006 | struct ext_slave_platform_data *slave_pdata, | ||
| 1007 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
| 1008 | { | ||
| 1009 | struct mpu_private_data *mpu = mpu_private_data; | ||
| 1010 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 1011 | struct ext_slave_descr *slave_descr; | ||
| 1012 | int result; | ||
| 1013 | |||
| 1014 | dev_info(&slave_client->adapter->dev, "%s\n", __func__); | ||
| 1015 | |||
| 1016 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
| 1017 | return; | ||
| 1018 | |||
| 1019 | if (slave_pdata->irq) | ||
| 1020 | slaveirq_exit(slave_pdata); | ||
| 1021 | |||
| 1022 | slave_descr = get_slave_descr(); | ||
| 1023 | if (!slave_descr) | ||
| 1024 | return; | ||
| 1025 | |||
| 1026 | mutex_lock(&mpu->mutex); | ||
| 1027 | |||
| 1028 | if (slave_descr->exit) { | ||
| 1029 | result = slave_descr->exit(slave_client->adapter, | ||
| 1030 | slave_descr, | ||
| 1031 | slave_pdata); | ||
| 1032 | if (result) | ||
| 1033 | dev_err(&slave_client->adapter->dev, | ||
| 1034 | "Accel exit failed %d\n", result); | ||
| 1035 | } | ||
| 1036 | mldl_cfg->slave[slave_descr->type] = NULL; | ||
| 1037 | mldl_cfg->pdata_slave[slave_descr->type] = NULL; | ||
| 1038 | mpu->slave_modules[slave_descr->type] = NULL; | ||
| 1039 | |||
| 1040 | mutex_unlock(&mpu->mutex); | ||
| 1041 | |||
| 1042 | } | ||
| 1043 | EXPORT_SYMBOL(inv_mpu_unregister_slave); | ||
| 1044 | |||
| 1045 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 1046 | |||
| 1047 | static const struct i2c_device_id mpu_id[] = { | ||
| 1048 | {"mpu3050", 0}, | ||
| 1049 | {"mpu6050", 0}, | ||
| 1050 | {"mpu6050_no_accel", 0}, | ||
| 1051 | {} | ||
| 1052 | }; | ||
| 1053 | MODULE_DEVICE_TABLE(i2c, mpu_id); | ||
| 1054 | |||
| 1055 | int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid) | ||
| 1056 | { | ||
| 1057 | struct mpu_platform_data *pdata; | ||
| 1058 | struct mpu_private_data *mpu; | ||
| 1059 | struct mldl_cfg *mldl_cfg; | ||
| 1060 | int res = 0; | ||
| 1061 | int ii = 0; | ||
| 1062 | unsigned long irq_flags; | ||
| 1063 | |||
| 1064 | dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++); | ||
| 1065 | |||
| 1066 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 1067 | res = -ENODEV; | ||
| 1068 | goto out_check_functionality_failed; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
| 1072 | if (!mpu) { | ||
| 1073 | res = -ENOMEM; | ||
| 1074 | goto out_alloc_data_failed; | ||
| 1075 | } | ||
| 1076 | mldl_cfg = &mpu->mldl_cfg; | ||
| 1077 | mldl_cfg->mpu_ram = &mpu->mpu_ram; | ||
| 1078 | mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg; | ||
| 1079 | mldl_cfg->mpu_offsets = &mpu->mpu_offsets; | ||
| 1080 | mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info; | ||
| 1081 | mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg; | ||
| 1082 | mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state; | ||
| 1083 | |||
| 1084 | mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE; | ||
| 1085 | mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL); | ||
| 1086 | if (!mldl_cfg->mpu_ram->ram) { | ||
| 1087 | res = -ENOMEM; | ||
| 1088 | goto out_alloc_ram_failed; | ||
| 1089 | } | ||
| 1090 | mpu_private_data = mpu; | ||
| 1091 | i2c_set_clientdata(client, mpu); | ||
| 1092 | mpu->client = client; | ||
| 1093 | |||
| 1094 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
| 1095 | mutex_init(&mpu->mutex); | ||
| 1096 | init_completion(&mpu->completion); | ||
| 1097 | |||
| 1098 | mpu->response_timeout = 60; /* Seconds */ | ||
| 1099 | mpu->timeout.function = mpu_pm_timeout; | ||
| 1100 | mpu->timeout.data = (u_long) mpu; | ||
| 1101 | init_timer(&mpu->timeout); | ||
| 1102 | |||
| 1103 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
| 1104 | mpu->nb.priority = 0; | ||
| 1105 | res = register_pm_notifier(&mpu->nb); | ||
| 1106 | if (res) { | ||
| 1107 | dev_err(&client->adapter->dev, | ||
| 1108 | "Unable to register pm_notifier %d\n", res); | ||
| 1109 | goto out_register_pm_notifier_failed; | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | pdata = (struct mpu_platform_data *)client->dev.platform_data; | ||
| 1113 | if (!pdata) { | ||
| 1114 | dev_WARN(&client->adapter->dev, | ||
| 1115 | "Missing platform data for mpu\n"); | ||
| 1116 | } | ||
| 1117 | mldl_cfg->pdata = pdata; | ||
| 1118 | |||
| 1119 | mldl_cfg->mpu_chip_info->addr = client->addr; | ||
| 1120 | res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
| 1121 | |||
| 1122 | if (res) { | ||
| 1123 | dev_err(&client->adapter->dev, | ||
| 1124 | "Unable to open %s %d\n", MPU_NAME, res); | ||
| 1125 | res = -ENODEV; | ||
| 1126 | goto out_whoami_failed; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | mpu->dev.minor = MISC_DYNAMIC_MINOR; | ||
| 1130 | mpu->dev.name = "mpu"; | ||
| 1131 | mpu->dev.fops = &mpu_fops; | ||
| 1132 | res = misc_register(&mpu->dev); | ||
| 1133 | if (res < 0) { | ||
| 1134 | dev_err(&client->adapter->dev, | ||
| 1135 | "ERROR: misc_register returned %d\n", res); | ||
| 1136 | goto out_misc_register_failed; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | if (client->irq) { | ||
| 1140 | dev_info(&client->adapter->dev, | ||
| 1141 | "Installing irq using %d\n", client->irq); | ||
| 1142 | if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
| 1143 | irq_flags = IRQF_TRIGGER_FALLING; | ||
| 1144 | else | ||
| 1145 | irq_flags = IRQF_TRIGGER_RISING; | ||
| 1146 | res = mpuirq_init(client, mldl_cfg, irq_flags); | ||
| 1147 | |||
| 1148 | if (res) | ||
| 1149 | goto out_mpuirq_failed; | ||
| 1150 | } else { | ||
| 1151 | dev_WARN(&client->adapter->dev, | ||
| 1152 | "Missing %s IRQ\n", MPU_NAME); | ||
| 1153 | } | ||
| 1154 | return res; | ||
| 1155 | |||
| 1156 | out_mpuirq_failed: | ||
| 1157 | misc_deregister(&mpu->dev); | ||
| 1158 | out_misc_register_failed: | ||
| 1159 | inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
| 1160 | out_whoami_failed: | ||
| 1161 | unregister_pm_notifier(&mpu->nb); | ||
| 1162 | out_register_pm_notifier_failed: | ||
| 1163 | kfree(mldl_cfg->mpu_ram->ram); | ||
| 1164 | mpu_private_data = NULL; | ||
| 1165 | out_alloc_ram_failed: | ||
| 1166 | kfree(mpu); | ||
| 1167 | out_alloc_data_failed: | ||
| 1168 | out_check_functionality_failed: | ||
| 1169 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res); | ||
| 1170 | return res; | ||
| 1171 | |||
| 1172 | } | ||
| 1173 | |||
| 1174 | static int mpu_remove(struct i2c_client *client) | ||
| 1175 | { | ||
| 1176 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
| 1177 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 1178 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 1179 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 1180 | int ii; | ||
| 1181 | |||
| 1182 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1183 | if (!pdata_slave[ii]) | ||
| 1184 | slave_adapter[ii] = NULL; | ||
| 1185 | else | ||
| 1186 | slave_adapter[ii] = | ||
| 1187 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 1191 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 1192 | |||
| 1193 | inv_mpu_close(mldl_cfg, | ||
| 1194 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 1195 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 1196 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 1197 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE]); | ||
| 1198 | |||
| 1199 | |||
| 1200 | if (client->irq) | ||
| 1201 | mpuirq_exit(); | ||
| 1202 | |||
| 1203 | misc_deregister(&mpu->dev); | ||
| 1204 | |||
| 1205 | unregister_pm_notifier(&mpu->nb); | ||
| 1206 | |||
| 1207 | kfree(mpu->mldl_cfg.mpu_ram->ram); | ||
| 1208 | kfree(mpu); | ||
| 1209 | |||
| 1210 | return 0; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | static struct i2c_driver mpu_driver = { | ||
| 1214 | .class = I2C_CLASS_HWMON, | ||
| 1215 | .probe = mpu_probe, | ||
| 1216 | .remove = mpu_remove, | ||
| 1217 | .id_table = mpu_id, | ||
| 1218 | .driver = { | ||
| 1219 | .owner = THIS_MODULE, | ||
| 1220 | .name = MPU_NAME, | ||
| 1221 | }, | ||
| 1222 | .address_list = normal_i2c, | ||
| 1223 | .shutdown = mpu_shutdown, /* optional */ | ||
| 1224 | .suspend = mpu_dev_suspend, /* optional */ | ||
| 1225 | .resume = mpu_dev_resume, /* optional */ | ||
| 1226 | |||
| 1227 | }; | ||
| 1228 | |||
| 1229 | static int __init mpu_init(void) | ||
| 1230 | { | ||
| 1231 | int res = i2c_add_driver(&mpu_driver); | ||
| 1232 | pr_info("%s: Probe name %s\n", __func__, MPU_NAME); | ||
| 1233 | if (res) | ||
| 1234 | pr_err("%s failed\n", __func__); | ||
| 1235 | return res; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | static void __exit mpu_exit(void) | ||
| 1239 | { | ||
| 1240 | pr_info("%s\n", __func__); | ||
| 1241 | i2c_del_driver(&mpu_driver); | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | module_init(mpu_init); | ||
| 1245 | module_exit(mpu_exit); | ||
| 1246 | |||
| 1247 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 1248 | MODULE_DESCRIPTION("User space character device interface for MPU"); | ||
| 1249 | MODULE_LICENSE("GPL"); | ||
| 1250 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/Makefile b/drivers/misc/inv_mpu/mpu6050/Makefile new file mode 100644 index 00000000000..a93aa97a699 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/Makefile | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | |||
| 2 | # Kernel makefile for motions sensors | ||
| 3 | # | ||
| 4 | # | ||
| 5 | |||
| 6 | obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050b1.o | ||
| 7 | |||
| 8 | ccflags-y := -DMPU_CURRENT_BUILD_MPU6050B1 | ||
| 9 | |||
| 10 | mpu6050b1-objs += mldl_cfg.o | ||
| 11 | mpu6050b1-objs += mldl_print_cfg.o | ||
| 12 | mpu6050b1-objs += mlsl-kernel.o | ||
| 13 | mpu6050b1-objs += mpu-dev.o | ||
| 14 | mpu6050b1-objs += ../accel/mpu6050.o | ||
| 15 | |||
| 16 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 17 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
| 18 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c new file mode 100644 index 00000000000..22af0c20098 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c | |||
| @@ -0,0 +1,1916 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_cfg.c | ||
| 25 | * @brief The Motion Library Driver Layer. | ||
| 26 | */ | ||
| 27 | |||
| 28 | /* -------------------------------------------------------------------------- */ | ||
| 29 | #include <linux/delay.h> | ||
| 30 | #include <linux/slab.h> | ||
| 31 | |||
| 32 | #include <stddef.h> | ||
| 33 | |||
| 34 | #include "mldl_cfg.h" | ||
| 35 | #include <linux/mpu.h> | ||
| 36 | #include "mpu6050b1.h" | ||
| 37 | |||
| 38 | #include "mlsl.h" | ||
| 39 | #include "mldl_print_cfg.h" | ||
| 40 | #include "log.h" | ||
| 41 | #undef MPL_LOG_TAG | ||
| 42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
| 43 | |||
| 44 | /* -------------------------------------------------------------------------- */ | ||
| 45 | |||
| 46 | #define SLEEP 0 | ||
| 47 | #define WAKE_UP 7 | ||
| 48 | #define RESET 1 | ||
| 49 | #define STANDBY 1 | ||
| 50 | |||
| 51 | /* -------------------------------------------------------------------------- */ | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief Stop the DMP running | ||
| 55 | * | ||
| 56 | * @return INV_SUCCESS or non-zero error code | ||
| 57 | */ | ||
| 58 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 59 | { | ||
| 60 | unsigned char user_ctrl_reg; | ||
| 61 | int result; | ||
| 62 | |||
| 63 | if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) | ||
| 64 | return INV_SUCCESS; | ||
| 65 | |||
| 66 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 67 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 68 | if (result) { | ||
| 69 | LOG_RESULT_LOCATION(result); | ||
| 70 | return result; | ||
| 71 | } | ||
| 72 | user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
| 73 | user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
| 74 | |||
| 75 | result = inv_serial_single_write(gyro_handle, | ||
| 76 | mldl_cfg->mpu_chip_info->addr, | ||
| 77 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 78 | if (result) { | ||
| 79 | LOG_RESULT_LOCATION(result); | ||
| 80 | return result; | ||
| 81 | } | ||
| 82 | mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED; | ||
| 83 | |||
| 84 | return result; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * @brief Starts the DMP running | ||
| 89 | * | ||
| 90 | * @return INV_SUCCESS or non-zero error code | ||
| 91 | */ | ||
| 92 | static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
| 93 | { | ||
| 94 | unsigned char user_ctrl_reg; | ||
| 95 | int result; | ||
| 96 | |||
| 97 | if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
| 98 | mldl_cfg->mpu_gyro_cfg->dmp_enable) | ||
| 99 | || | ||
| 100 | ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
| 101 | !mldl_cfg->mpu_gyro_cfg->dmp_enable)) | ||
| 102 | return INV_SUCCESS; | ||
| 103 | |||
| 104 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 105 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 106 | if (result) { | ||
| 107 | LOG_RESULT_LOCATION(result); | ||
| 108 | return result; | ||
| 109 | } | ||
| 110 | |||
| 111 | result = inv_serial_single_write( | ||
| 112 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 113 | MPUREG_USER_CTRL, | ||
| 114 | ((user_ctrl_reg & (~BIT_FIFO_EN)) | ||
| 115 | | BIT_FIFO_RST)); | ||
| 116 | if (result) { | ||
| 117 | LOG_RESULT_LOCATION(result); | ||
| 118 | return result; | ||
| 119 | } | ||
| 120 | |||
| 121 | result = inv_serial_single_write( | ||
| 122 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 123 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 124 | if (result) { | ||
| 125 | LOG_RESULT_LOCATION(result); | ||
| 126 | return result; | ||
| 127 | } | ||
| 128 | |||
| 129 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 130 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
| 131 | if (result) { | ||
| 132 | LOG_RESULT_LOCATION(result); | ||
| 133 | return result; | ||
| 134 | } | ||
| 135 | |||
| 136 | user_ctrl_reg |= BIT_DMP_EN; | ||
| 137 | |||
| 138 | if (mldl_cfg->mpu_gyro_cfg->fifo_enable) | ||
| 139 | user_ctrl_reg |= BIT_FIFO_EN; | ||
| 140 | else | ||
| 141 | user_ctrl_reg &= ~BIT_FIFO_EN; | ||
| 142 | |||
| 143 | user_ctrl_reg |= BIT_DMP_RST; | ||
| 144 | |||
| 145 | result = inv_serial_single_write( | ||
| 146 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 147 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
| 148 | if (result) { | ||
| 149 | LOG_RESULT_LOCATION(result); | ||
| 150 | return result; | ||
| 151 | } | ||
| 152 | mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED; | ||
| 153 | |||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * @brief enables/disables the I2C bypass to an external device | ||
| 159 | * connected to MPU's secondary I2C bus. | ||
| 160 | * @param enable | ||
| 161 | * Non-zero to enable pass through. | ||
| 162 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
| 163 | */ | ||
| 164 | static int mpu6050b1_set_i2c_bypass(struct mldl_cfg *mldl_cfg, | ||
| 165 | void *mlsl_handle, unsigned char enable) | ||
| 166 | { | ||
| 167 | unsigned char reg; | ||
| 168 | int result; | ||
| 169 | unsigned char status = mldl_cfg->inv_mpu_state->status; | ||
| 170 | if ((status & MPU_GYRO_IS_BYPASSED && enable) || | ||
| 171 | (!(status & MPU_GYRO_IS_BYPASSED) && !enable)) | ||
| 172 | return INV_SUCCESS; | ||
| 173 | |||
| 174 | /*---- get current 'USER_CTRL' into b ----*/ | ||
| 175 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 176 | MPUREG_USER_CTRL, 1, ®); | ||
| 177 | if (result) { | ||
| 178 | LOG_RESULT_LOCATION(result); | ||
| 179 | return result; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (!enable) { | ||
| 183 | /* setting int_config with the property flag BIT_BYPASS_EN | ||
| 184 | should be done by the setup functions */ | ||
| 185 | result = inv_serial_single_write( | ||
| 186 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 187 | MPUREG_INT_PIN_CFG, | ||
| 188 | (mldl_cfg->pdata->int_config & ~(BIT_BYPASS_EN))); | ||
| 189 | if (!(reg & BIT_I2C_MST_EN)) { | ||
| 190 | result = | ||
| 191 | inv_serial_single_write( | ||
| 192 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 193 | MPUREG_USER_CTRL, | ||
| 194 | (reg | BIT_I2C_MST_EN)); | ||
| 195 | if (result) { | ||
| 196 | LOG_RESULT_LOCATION(result); | ||
| 197 | return result; | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } else if (enable) { | ||
| 201 | if (reg & BIT_AUX_IF_EN) { | ||
| 202 | result = | ||
| 203 | inv_serial_single_write( | ||
| 204 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 205 | MPUREG_USER_CTRL, | ||
| 206 | (reg & (~BIT_I2C_MST_EN))); | ||
| 207 | if (result) { | ||
| 208 | LOG_RESULT_LOCATION(result); | ||
| 209 | return result; | ||
| 210 | } | ||
| 211 | /***************************************** | ||
| 212 | * To avoid hanging the bus we must sleep until all | ||
| 213 | * slave transactions have been completed. | ||
| 214 | * 24 bytes max slave reads | ||
| 215 | * +1 byte possible extra write | ||
| 216 | * +4 max slave address | ||
| 217 | * --- | ||
| 218 | * 33 Maximum bytes | ||
| 219 | * x9 Approximate bits per byte | ||
| 220 | * --- | ||
| 221 | * 297 bits. | ||
| 222 | * 2.97 ms minimum @ 100kbps | ||
| 223 | * 0.75 ms minimum @ 400kbps. | ||
| 224 | *****************************************/ | ||
| 225 | msleep(3); | ||
| 226 | } | ||
| 227 | result = inv_serial_single_write( | ||
| 228 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 229 | MPUREG_INT_PIN_CFG, | ||
| 230 | (mldl_cfg->pdata->int_config | BIT_BYPASS_EN)); | ||
| 231 | if (result) { | ||
| 232 | LOG_RESULT_LOCATION(result); | ||
| 233 | return result; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | if (enable) | ||
| 237 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
| 238 | else | ||
| 239 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
| 240 | |||
| 241 | return result; | ||
| 242 | } | ||
| 243 | |||
| 244 | |||
| 245 | |||
| 246 | |||
| 247 | /** | ||
| 248 | * @brief enables/disables the I2C bypass to an external device | ||
| 249 | * connected to MPU's secondary I2C bus. | ||
| 250 | * @param enable | ||
| 251 | * Non-zero to enable pass through. | ||
| 252 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
| 253 | */ | ||
| 254 | static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
| 255 | unsigned char enable) | ||
| 256 | { | ||
| 257 | return mpu6050b1_set_i2c_bypass(mldl_cfg, mlsl_handle, enable); | ||
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | #define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) | ||
| 262 | |||
| 263 | /* NOTE : when not indicated, product revision | ||
| 264 | is considered an 'npp'; non production part */ | ||
| 265 | |||
| 266 | /* produces an unique identifier for each device based on the | ||
| 267 | combination of product version and product revision */ | ||
| 268 | struct prod_rev_map_t { | ||
| 269 | unsigned short mpl_product_key; | ||
| 270 | unsigned char silicon_rev; | ||
| 271 | unsigned short gyro_trim; | ||
| 272 | unsigned short accel_trim; | ||
| 273 | }; | ||
| 274 | |||
| 275 | /* NOTE: product entries are in chronological order */ | ||
| 276 | static struct prod_rev_map_t prod_rev_map[] = { | ||
| 277 | /* prod_ver = 0 */ | ||
| 278 | {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 279 | {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 280 | {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 281 | {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 282 | {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 283 | {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/C2-1) */ | ||
| 284 | /* prod_ver = 1, forced to 0 for MPU6050 A2 */ | ||
| 285 | {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 286 | {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 287 | {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 288 | {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 289 | {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D2-1) */ | ||
| 290 | {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 291 | {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 292 | {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 293 | {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384}, | ||
| 294 | {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D4) */ | ||
| 295 | /* prod_ver = 1 */ | ||
| 296 | {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-1) */ | ||
| 297 | {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-2) */ | ||
| 298 | {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-3) */ | ||
| 299 | {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-4) */ | ||
| 300 | {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-5) */ | ||
| 301 | {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D4) */ | ||
| 302 | {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-1) */ | ||
| 303 | {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-2) */ | ||
| 304 | {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-3) */ | ||
| 305 | {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-4) */ | ||
| 306 | {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-5) */ | ||
| 307 | {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-6) */ | ||
| 308 | /* prod_ver = 2 */ | ||
| 309 | {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-1) */ | ||
| 310 | {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-2) */ | ||
| 311 | {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-3) */ | ||
| 312 | {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-4) */ | ||
| 313 | {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-5) */ | ||
| 314 | {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-6) */ | ||
| 315 | {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/D4) */ | ||
| 316 | /* prod_ver = 3 */ | ||
| 317 | {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E2) */ | ||
| 318 | /* prod_ver = 4 */ | ||
| 319 | {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192}, /* (B2/F1) */ | ||
| 320 | {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192}, /* (B3/F1) */ | ||
| 321 | {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192}, /* (B4/F1) */ | ||
| 322 | /* prod_ver = 5 */ | ||
| 323 | {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
| 324 | /* prod_ver = 7 */ | ||
| 325 | {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
| 326 | /* prod_ver = 8 */ | ||
| 327 | {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
| 328 | /* prod_ver = 9 */ | ||
| 329 | {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
| 330 | /* prod_ver = 10 */ | ||
| 331 | {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384} /* (B5/E2) */ | ||
| 332 | }; | ||
| 333 | |||
| 334 | /** | ||
| 335 | * @internal | ||
| 336 | * @brief Inverse lookup of the index of an MPL product key . | ||
| 337 | * @param key | ||
| 338 | * the MPL product indentifier also referred to as 'key'. | ||
| 339 | * @return the index position of the key in the array, -1 if not found. | ||
| 340 | */ | ||
| 341 | short index_of_key(unsigned short key) | ||
| 342 | { | ||
| 343 | int i; | ||
| 344 | for (i = 0; i < NUM_OF_PROD_REVS; i++) | ||
| 345 | if (prod_rev_map[i].mpl_product_key == key) | ||
| 346 | return (short)i; | ||
| 347 | return -1; | ||
| 348 | } | ||
| 349 | |||
| 350 | /** | ||
| 351 | * @internal | ||
| 352 | * @brief Get the product revision and version for MPU6050 and | ||
| 353 | * extract all per-part specific information. | ||
| 354 | * The product version number is read from the PRODUCT_ID register in | ||
| 355 | * user space register map. | ||
| 356 | * The product revision number is in read from OTP bank 0, ADDR6[7:2]. | ||
| 357 | * These 2 numbers, combined, provide an unique key to be used to | ||
| 358 | * retrieve some per-device information such as the silicon revision | ||
| 359 | * and the gyro and accel sensitivity trim values. | ||
| 360 | * | ||
| 361 | * @param mldl_cfg | ||
| 362 | * a pointer to the mldl config data structure. | ||
| 363 | * @param mlsl_handle | ||
| 364 | * an file handle to the serial communication device the | ||
| 365 | * device is connected to. | ||
| 366 | * | ||
| 367 | * @return 0 on success, a non-zero error code otherwise. | ||
| 368 | */ | ||
| 369 | static int inv_get_silicon_rev_mpu6050( | ||
| 370 | struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
| 371 | { | ||
| 372 | int result; | ||
| 373 | unsigned char prod_ver = 0x00, prod_rev = 0x00; | ||
| 374 | unsigned char bank = | ||
| 375 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
| 376 | unsigned short memAddr = ((bank << 8) | 0x06); | ||
| 377 | unsigned short key; | ||
| 378 | short index; | ||
| 379 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 380 | |||
| 381 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 382 | MPUREG_PRODUCT_ID, 1, &prod_ver); | ||
| 383 | if (result) { | ||
| 384 | LOG_RESULT_LOCATION(result); | ||
| 385 | return result; | ||
| 386 | } | ||
| 387 | prod_ver &= 0xF; | ||
| 388 | |||
| 389 | result = inv_serial_read_mem(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 390 | memAddr, 1, &prod_rev); | ||
| 391 | if (result) { | ||
| 392 | LOG_RESULT_LOCATION(result); | ||
| 393 | return result; | ||
| 394 | } | ||
| 395 | prod_rev >>= 2; | ||
| 396 | |||
| 397 | /* clean the prefetch and cfg user bank bits */ | ||
| 398 | result = inv_serial_single_write( | ||
| 399 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 400 | MPUREG_BANK_SEL, 0); | ||
| 401 | if (result) { | ||
| 402 | LOG_RESULT_LOCATION(result); | ||
| 403 | return result; | ||
| 404 | } | ||
| 405 | |||
| 406 | key = MPL_PROD_KEY(prod_ver, prod_rev); | ||
| 407 | if (key == 0) { | ||
| 408 | MPL_LOGE("Product id read as 0 " | ||
| 409 | "indicates device is either " | ||
| 410 | "incompatible or an MPU3050\n"); | ||
| 411 | return INV_ERROR_INVALID_MODULE; | ||
| 412 | } | ||
| 413 | index = index_of_key(key); | ||
| 414 | if (index == -1 || index >= NUM_OF_PROD_REVS) { | ||
| 415 | MPL_LOGE("Unsupported product key %d in MPL\n", key); | ||
| 416 | return INV_ERROR_INVALID_MODULE; | ||
| 417 | } | ||
| 418 | /* check MPL is compiled for this device */ | ||
| 419 | if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) { | ||
| 420 | MPL_LOGE("MPL compiled for MPU6050B1 support " | ||
| 421 | "but device is not MPU6050B1 (%d)\n", key); | ||
| 422 | return INV_ERROR_INVALID_MODULE; | ||
| 423 | } | ||
| 424 | |||
| 425 | mpu_chip_info->product_id = prod_ver; | ||
| 426 | mpu_chip_info->product_revision = prod_rev; | ||
| 427 | mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev; | ||
| 428 | mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim; | ||
| 429 | mpu_chip_info->accel_sens_trim = prod_rev_map[index].accel_trim; | ||
| 430 | |||
| 431 | return result; | ||
| 432 | } | ||
| 433 | #define inv_get_silicon_rev inv_get_silicon_rev_mpu6050 | ||
| 434 | |||
| 435 | |||
| 436 | /** | ||
| 437 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
| 438 | * shifters. | ||
| 439 | * When enabled the secondary I2C interface to which the external | ||
| 440 | * device is connected runs at VDD voltage (main supply). | ||
| 441 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
| 442 | * See the device specification for more details. | ||
| 443 | * | ||
| 444 | * @note using this API may produce unpredictable results, depending on how | ||
| 445 | * the MPU and slave device are setup on the target platform. | ||
| 446 | * Use of this API should entirely be restricted to system | ||
| 447 | * integrators. Once the correct value is found, there should be no | ||
| 448 | * need to change the level shifter at runtime. | ||
| 449 | * | ||
| 450 | * @pre Must be called after inv_serial_start(). | ||
| 451 | * @note Typically called before inv_dmp_open(). | ||
| 452 | * | ||
| 453 | * @param[in] enable: | ||
| 454 | * 0 to run at VDDIO (default), | ||
| 455 | * 1 to run at VDD. | ||
| 456 | * | ||
| 457 | * @return INV_SUCCESS if successfull, a non-zero error code otherwise. | ||
| 458 | */ | ||
| 459 | static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg, | ||
| 460 | void *mlsl_handle, unsigned char enable) | ||
| 461 | { | ||
| 462 | int result; | ||
| 463 | unsigned char regval; | ||
| 464 | |||
| 465 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 466 | MPUREG_YG_OFFS_TC, 1, ®val); | ||
| 467 | if (result) { | ||
| 468 | LOG_RESULT_LOCATION(result); | ||
| 469 | return result; | ||
| 470 | } | ||
| 471 | if (enable) | ||
| 472 | regval |= BIT_I2C_MST_VDDIO; | ||
| 473 | |||
| 474 | result = inv_serial_single_write( | ||
| 475 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 476 | MPUREG_YG_OFFS_TC, regval); | ||
| 477 | if (result) { | ||
| 478 | LOG_RESULT_LOCATION(result); | ||
| 479 | return result; | ||
| 480 | } | ||
| 481 | return INV_SUCCESS; | ||
| 482 | } | ||
| 483 | |||
| 484 | |||
| 485 | /** | ||
| 486 | * @internal | ||
| 487 | * @brief MPU6050 B1 power management functions. | ||
| 488 | * @param mldl_cfg | ||
| 489 | * a pointer to the internal mldl_cfg data structure. | ||
| 490 | * @param mlsl_handle | ||
| 491 | * a file handle to the serial device used to communicate | ||
| 492 | * with the MPU6050 B1 device. | ||
| 493 | * @param reset | ||
| 494 | * 1 to reset hardware. | ||
| 495 | * @param sensors | ||
| 496 | * Bitfield of sensors to leave on | ||
| 497 | * | ||
| 498 | * @return 0 on success, a non-zero error code on error. | ||
| 499 | */ | ||
| 500 | static int mpu60xx_pwr_mgmt(struct mldl_cfg *mldl_cfg, | ||
| 501 | void *mlsl_handle, | ||
| 502 | unsigned int reset, unsigned long sensors) | ||
| 503 | { | ||
| 504 | unsigned char pwr_mgmt[2]; | ||
| 505 | unsigned char pwr_mgmt_prev[2]; | ||
| 506 | int result; | ||
| 507 | int sleep = !(sensors & (INV_THREE_AXIS_GYRO | INV_THREE_AXIS_ACCEL | ||
| 508 | | INV_DMP_PROCESSOR)); | ||
| 509 | |||
| 510 | if (reset) { | ||
| 511 | MPL_LOGI("Reset MPU6050 B1\n"); | ||
| 512 | result = inv_serial_single_write( | ||
| 513 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 514 | MPUREG_PWR_MGMT_1, BIT_H_RESET); | ||
| 515 | if (result) { | ||
| 516 | LOG_RESULT_LOCATION(result); | ||
| 517 | return result; | ||
| 518 | } | ||
| 519 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
| 520 | msleep(15); | ||
| 521 | } | ||
| 522 | |||
| 523 | /* NOTE : reading both PWR_MGMT_1 and PWR_MGMT_2 for efficiency because | ||
| 524 | they are accessible even when the device is powered off */ | ||
| 525 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 526 | MPUREG_PWR_MGMT_1, 2, pwr_mgmt_prev); | ||
| 527 | if (result) { | ||
| 528 | LOG_RESULT_LOCATION(result); | ||
| 529 | return result; | ||
| 530 | } | ||
| 531 | |||
| 532 | pwr_mgmt[0] = pwr_mgmt_prev[0]; | ||
| 533 | pwr_mgmt[1] = pwr_mgmt_prev[1]; | ||
| 534 | |||
| 535 | if (sleep) { | ||
| 536 | mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED; | ||
| 537 | pwr_mgmt[0] |= BIT_SLEEP; | ||
| 538 | } else { | ||
| 539 | mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED; | ||
| 540 | pwr_mgmt[0] &= ~BIT_SLEEP; | ||
| 541 | } | ||
| 542 | if (pwr_mgmt[0] != pwr_mgmt_prev[0]) { | ||
| 543 | result = inv_serial_single_write( | ||
| 544 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 545 | MPUREG_PWR_MGMT_1, pwr_mgmt[0]); | ||
| 546 | if (result) { | ||
| 547 | LOG_RESULT_LOCATION(result); | ||
| 548 | return result; | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | pwr_mgmt[1] &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
| 553 | if (!(sensors & INV_X_GYRO)) | ||
| 554 | pwr_mgmt[1] |= BIT_STBY_XG; | ||
| 555 | if (!(sensors & INV_Y_GYRO)) | ||
| 556 | pwr_mgmt[1] |= BIT_STBY_YG; | ||
| 557 | if (!(sensors & INV_Z_GYRO)) | ||
| 558 | pwr_mgmt[1] |= BIT_STBY_ZG; | ||
| 559 | |||
| 560 | if (pwr_mgmt[1] != pwr_mgmt_prev[1]) { | ||
| 561 | result = inv_serial_single_write( | ||
| 562 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 563 | MPUREG_PWR_MGMT_2, pwr_mgmt[1]); | ||
| 564 | if (result) { | ||
| 565 | LOG_RESULT_LOCATION(result); | ||
| 566 | return result; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | if ((pwr_mgmt[1] & (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
| 571 | (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) { | ||
| 572 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED; | ||
| 573 | } else { | ||
| 574 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED; | ||
| 575 | } | ||
| 576 | |||
| 577 | return INV_SUCCESS; | ||
| 578 | } | ||
| 579 | |||
| 580 | |||
| 581 | /** | ||
| 582 | * @brief sets the clock source for the gyros. | ||
| 583 | * @param mldl_cfg | ||
| 584 | * a pointer to the struct mldl_cfg data structure. | ||
| 585 | * @param gyro_handle | ||
| 586 | * an handle to the serial device the gyro is assigned to. | ||
| 587 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
| 588 | */ | ||
| 589 | static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg) | ||
| 590 | { | ||
| 591 | int result; | ||
| 592 | unsigned char cur_clk_src; | ||
| 593 | unsigned char reg; | ||
| 594 | |||
| 595 | /* clock source selection */ | ||
| 596 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 597 | MPUREG_PWR_MGM, 1, ®); | ||
| 598 | if (result) { | ||
| 599 | LOG_RESULT_LOCATION(result); | ||
| 600 | return result; | ||
| 601 | } | ||
| 602 | cur_clk_src = reg & BITS_CLKSEL; | ||
| 603 | reg &= ~BITS_CLKSEL; | ||
| 604 | |||
| 605 | |||
| 606 | result = inv_serial_single_write( | ||
| 607 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 608 | MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg); | ||
| 609 | if (result) { | ||
| 610 | LOG_RESULT_LOCATION(result); | ||
| 611 | return result; | ||
| 612 | } | ||
| 613 | |||
| 614 | /* ERRATA: | ||
| 615 | workaroud to switch from any MPU_CLK_SEL_PLLGYROx to | ||
| 616 | MPU_CLK_SEL_INTERNAL and XGyro is powered up: | ||
| 617 | 1) Select INT_OSC | ||
| 618 | 2) PD XGyro | ||
| 619 | 3) PU XGyro | ||
| 620 | */ | ||
| 621 | if ((cur_clk_src == MPU_CLK_SEL_PLLGYROX | ||
| 622 | || cur_clk_src == MPU_CLK_SEL_PLLGYROY | ||
| 623 | || cur_clk_src == MPU_CLK_SEL_PLLGYROZ) | ||
| 624 | && mldl_cfg->mpu_gyro_cfg->clk_src == MPU_CLK_SEL_INTERNAL | ||
| 625 | && mldl_cfg->inv_mpu_cfg->requested_sensors & INV_X_GYRO) { | ||
| 626 | unsigned char first_result = INV_SUCCESS; | ||
| 627 | mldl_cfg->inv_mpu_cfg->requested_sensors &= ~INV_X_GYRO; | ||
| 628 | result = mpu60xx_pwr_mgmt( | ||
| 629 | mldl_cfg, gyro_handle, | ||
| 630 | false, mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
| 631 | ERROR_CHECK_FIRST(first_result, result); | ||
| 632 | mldl_cfg->inv_mpu_cfg->requested_sensors |= INV_X_GYRO; | ||
| 633 | result = mpu60xx_pwr_mgmt( | ||
| 634 | mldl_cfg, gyro_handle, | ||
| 635 | false, mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
| 636 | ERROR_CHECK_FIRST(first_result, result); | ||
| 637 | result = first_result; | ||
| 638 | } | ||
| 639 | return result; | ||
| 640 | } | ||
| 641 | |||
| 642 | /** | ||
| 643 | * Configures the MPU I2C Master | ||
| 644 | * | ||
| 645 | * @mldl_cfg Handle to the configuration data | ||
| 646 | * @gyro_handle handle to the gyro communictation interface | ||
| 647 | * @slave Can be Null if turning off the slave | ||
| 648 | * @slave_pdata Can be null if turning off the slave | ||
| 649 | * @slave_id enum ext_slave_type to determine which index to use | ||
| 650 | * | ||
| 651 | * | ||
| 652 | * This fucntion configures the slaves by: | ||
| 653 | * 1) Setting up the read | ||
| 654 | * a) Read Register | ||
| 655 | * b) Read Length | ||
| 656 | * 2) Set up the data trigger (MPU6050 only) | ||
| 657 | * a) Set trigger write register | ||
| 658 | * b) Set Trigger write value | ||
| 659 | * 3) Set up the divider (MPU6050 only) | ||
| 660 | * 4) Set the slave bypass mode depending on slave | ||
| 661 | * | ||
| 662 | * returns INV_SUCCESS or non-zero error code | ||
| 663 | */ | ||
| 664 | |||
| 665 | static int mpu_set_slave_mpu60xx(struct mldl_cfg *mldl_cfg, | ||
| 666 | void *gyro_handle, | ||
| 667 | struct ext_slave_descr *slave, | ||
| 668 | struct ext_slave_platform_data *slave_pdata, | ||
| 669 | int slave_id) | ||
| 670 | { | ||
| 671 | int result; | ||
| 672 | unsigned char reg; | ||
| 673 | /* Slave values */ | ||
| 674 | unsigned char slave_reg; | ||
| 675 | unsigned char slave_len; | ||
| 676 | unsigned char slave_endian; | ||
| 677 | unsigned char slave_address; | ||
| 678 | /* Which MPU6050 registers to use */ | ||
| 679 | unsigned char addr_reg; | ||
| 680 | unsigned char reg_reg; | ||
| 681 | unsigned char ctrl_reg; | ||
| 682 | /* Which MPU6050 registers to use for the trigger */ | ||
| 683 | unsigned char addr_trig_reg; | ||
| 684 | unsigned char reg_trig_reg; | ||
| 685 | unsigned char ctrl_trig_reg; | ||
| 686 | |||
| 687 | unsigned char bits_slave_delay = 0; | ||
| 688 | /* Divide down rate for the Slave, from the mpu rate */ | ||
| 689 | unsigned char d0_trig_reg; | ||
| 690 | unsigned char delay_ctrl_orig; | ||
| 691 | unsigned char delay_ctrl; | ||
| 692 | long divider; | ||
| 693 | |||
| 694 | if (NULL == slave || NULL == slave_pdata) { | ||
| 695 | slave_reg = 0; | ||
| 696 | slave_len = 0; | ||
| 697 | slave_endian = 0; | ||
| 698 | slave_address = 0; | ||
| 699 | } else { | ||
| 700 | slave_reg = slave->read_reg; | ||
| 701 | slave_len = slave->read_len; | ||
| 702 | slave_endian = slave->endian; | ||
| 703 | slave_address = slave_pdata->address; | ||
| 704 | slave_address |= BIT_I2C_READ; | ||
| 705 | } | ||
| 706 | |||
| 707 | switch (slave_id) { | ||
| 708 | case EXT_SLAVE_TYPE_ACCEL: | ||
| 709 | addr_reg = MPUREG_I2C_SLV1_ADDR; | ||
| 710 | reg_reg = MPUREG_I2C_SLV1_REG; | ||
| 711 | ctrl_reg = MPUREG_I2C_SLV1_CTRL; | ||
| 712 | addr_trig_reg = 0; | ||
| 713 | reg_trig_reg = 0; | ||
| 714 | ctrl_trig_reg = 0; | ||
| 715 | bits_slave_delay = BIT_SLV1_DLY_EN; | ||
| 716 | break; | ||
| 717 | case EXT_SLAVE_TYPE_COMPASS: | ||
| 718 | addr_reg = MPUREG_I2C_SLV0_ADDR; | ||
| 719 | reg_reg = MPUREG_I2C_SLV0_REG; | ||
| 720 | ctrl_reg = MPUREG_I2C_SLV0_CTRL; | ||
| 721 | addr_trig_reg = MPUREG_I2C_SLV2_ADDR; | ||
| 722 | reg_trig_reg = MPUREG_I2C_SLV2_REG; | ||
| 723 | ctrl_trig_reg = MPUREG_I2C_SLV2_CTRL; | ||
| 724 | d0_trig_reg = MPUREG_I2C_SLV2_DO; | ||
| 725 | bits_slave_delay = BIT_SLV2_DLY_EN | BIT_SLV0_DLY_EN; | ||
| 726 | break; | ||
| 727 | case EXT_SLAVE_TYPE_PRESSURE: | ||
| 728 | addr_reg = MPUREG_I2C_SLV3_ADDR; | ||
| 729 | reg_reg = MPUREG_I2C_SLV3_REG; | ||
| 730 | ctrl_reg = MPUREG_I2C_SLV3_CTRL; | ||
| 731 | addr_trig_reg = MPUREG_I2C_SLV4_ADDR; | ||
| 732 | reg_trig_reg = MPUREG_I2C_SLV4_REG; | ||
| 733 | ctrl_trig_reg = MPUREG_I2C_SLV4_CTRL; | ||
| 734 | bits_slave_delay = BIT_SLV4_DLY_EN | BIT_SLV3_DLY_EN; | ||
| 735 | break; | ||
| 736 | default: | ||
| 737 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 738 | return INV_ERROR_INVALID_PARAMETER; | ||
| 739 | }; | ||
| 740 | |||
| 741 | /* return if this slave has already been set */ | ||
| 742 | if ((slave_address && | ||
| 743 | ((mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) | ||
| 744 | == bits_slave_delay)) || | ||
| 745 | (!slave_address && | ||
| 746 | (mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) == | ||
| 747 | 0)) | ||
| 748 | return 0; | ||
| 749 | |||
| 750 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
| 751 | |||
| 752 | /* Address */ | ||
| 753 | result = inv_serial_single_write(gyro_handle, | ||
| 754 | mldl_cfg->mpu_chip_info->addr, | ||
| 755 | addr_reg, slave_address); | ||
| 756 | if (result) { | ||
| 757 | LOG_RESULT_LOCATION(result); | ||
| 758 | return result; | ||
| 759 | } | ||
| 760 | /* Register */ | ||
| 761 | result = inv_serial_single_write(gyro_handle, | ||
| 762 | mldl_cfg->mpu_chip_info->addr, | ||
| 763 | reg_reg, slave_reg); | ||
| 764 | if (result) { | ||
| 765 | LOG_RESULT_LOCATION(result); | ||
| 766 | return result; | ||
| 767 | } | ||
| 768 | |||
| 769 | /* Length, byte swapping, grouping & enable */ | ||
| 770 | if (slave_len > BITS_SLV_LENG) { | ||
| 771 | MPL_LOGW("Limiting slave burst read length to " | ||
| 772 | "the allowed maximum (15B, req. %d)\n", slave_len); | ||
| 773 | slave_len = BITS_SLV_LENG; | ||
| 774 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 775 | } | ||
| 776 | reg = slave_len; | ||
| 777 | if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) { | ||
| 778 | reg |= BIT_SLV_BYTE_SW; | ||
| 779 | if (slave_reg & 1) | ||
| 780 | reg |= BIT_SLV_GRP; | ||
| 781 | } | ||
| 782 | if (slave_address) | ||
| 783 | reg |= BIT_SLV_ENABLE; | ||
| 784 | |||
| 785 | result = inv_serial_single_write(gyro_handle, | ||
| 786 | mldl_cfg->mpu_chip_info->addr, | ||
| 787 | ctrl_reg, reg); | ||
| 788 | if (result) { | ||
| 789 | LOG_RESULT_LOCATION(result); | ||
| 790 | return result; | ||
| 791 | } | ||
| 792 | |||
| 793 | /* Trigger */ | ||
| 794 | if (addr_trig_reg) { | ||
| 795 | /* If slave address is 0 this clears the trigger */ | ||
| 796 | result = inv_serial_single_write(gyro_handle, | ||
| 797 | mldl_cfg->mpu_chip_info->addr, | ||
| 798 | addr_trig_reg, | ||
| 799 | slave_address & ~BIT_I2C_READ); | ||
| 800 | if (result) { | ||
| 801 | LOG_RESULT_LOCATION(result); | ||
| 802 | return result; | ||
| 803 | } | ||
| 804 | } | ||
| 805 | |||
| 806 | if (slave && slave->trigger && reg_trig_reg) { | ||
| 807 | result = inv_serial_single_write(gyro_handle, | ||
| 808 | mldl_cfg->mpu_chip_info->addr, | ||
| 809 | reg_trig_reg, | ||
| 810 | slave->trigger->reg); | ||
| 811 | if (result) { | ||
| 812 | LOG_RESULT_LOCATION(result); | ||
| 813 | return result; | ||
| 814 | } | ||
| 815 | result = inv_serial_single_write(gyro_handle, | ||
| 816 | mldl_cfg->mpu_chip_info->addr, | ||
| 817 | ctrl_trig_reg, | ||
| 818 | BIT_SLV_ENABLE | 0x01); | ||
| 819 | if (result) { | ||
| 820 | LOG_RESULT_LOCATION(result); | ||
| 821 | return result; | ||
| 822 | } | ||
| 823 | result = inv_serial_single_write(gyro_handle, | ||
| 824 | mldl_cfg->mpu_chip_info->addr, | ||
| 825 | d0_trig_reg, | ||
| 826 | slave->trigger->value); | ||
| 827 | if (result) { | ||
| 828 | LOG_RESULT_LOCATION(result); | ||
| 829 | return result; | ||
| 830 | } | ||
| 831 | } else if (ctrl_trig_reg) { | ||
| 832 | result = inv_serial_single_write(gyro_handle, | ||
| 833 | mldl_cfg->mpu_chip_info->addr, | ||
| 834 | ctrl_trig_reg, 0x00); | ||
| 835 | if (result) { | ||
| 836 | LOG_RESULT_LOCATION(result); | ||
| 837 | return result; | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | /* Data rate */ | ||
| 842 | if (slave) { | ||
| 843 | struct ext_slave_config config; | ||
| 844 | long data; | ||
| 845 | config.key = MPU_SLAVE_CONFIG_ODR_RESUME; | ||
| 846 | config.len = sizeof(long); | ||
| 847 | config.apply = false; | ||
| 848 | config.data = &data; | ||
| 849 | if (!(slave->get_config)) | ||
| 850 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 851 | |||
| 852 | result = slave->get_config(NULL, slave, slave_pdata, &config); | ||
| 853 | if (result) { | ||
| 854 | LOG_RESULT_LOCATION(result); | ||
| 855 | return result; | ||
| 856 | } | ||
| 857 | MPL_LOGI("Slave %d ODR: %ld Hz\n", slave_id, data / 1000); | ||
| 858 | divider = ((1000 * inv_mpu_get_sampling_rate_hz( | ||
| 859 | mldl_cfg->mpu_gyro_cfg)) | ||
| 860 | / data) - 1; | ||
| 861 | } else { | ||
| 862 | divider = 0; | ||
| 863 | } | ||
| 864 | |||
| 865 | result = inv_serial_read(gyro_handle, | ||
| 866 | mldl_cfg->mpu_chip_info->addr, | ||
| 867 | MPUREG_I2C_MST_DELAY_CTRL, | ||
| 868 | 1, &delay_ctrl_orig); | ||
| 869 | delay_ctrl = delay_ctrl_orig; | ||
| 870 | if (result) { | ||
| 871 | LOG_RESULT_LOCATION(result); | ||
| 872 | return result; | ||
| 873 | } | ||
| 874 | |||
| 875 | if (divider > 0 && divider <= MASK_I2C_MST_DLY) { | ||
| 876 | result = inv_serial_read(gyro_handle, | ||
| 877 | mldl_cfg->mpu_chip_info->addr, | ||
| 878 | MPUREG_I2C_SLV4_CTRL, 1, ®); | ||
| 879 | if (result) { | ||
| 880 | LOG_RESULT_LOCATION(result); | ||
| 881 | return result; | ||
| 882 | } | ||
| 883 | if ((reg & MASK_I2C_MST_DLY) && | ||
| 884 | ((long)(reg & MASK_I2C_MST_DLY) != | ||
| 885 | (divider & MASK_I2C_MST_DLY))) { | ||
| 886 | MPL_LOGW("Changing slave divider: %ld to %ld\n", | ||
| 887 | (long)(reg & MASK_I2C_MST_DLY), | ||
| 888 | (divider & MASK_I2C_MST_DLY)); | ||
| 889 | |||
| 890 | } | ||
| 891 | reg |= (unsigned char)(divider & MASK_I2C_MST_DLY); | ||
| 892 | result = inv_serial_single_write(gyro_handle, | ||
| 893 | mldl_cfg->mpu_chip_info->addr, | ||
| 894 | MPUREG_I2C_SLV4_CTRL, | ||
| 895 | reg); | ||
| 896 | if (result) { | ||
| 897 | LOG_RESULT_LOCATION(result); | ||
| 898 | return result; | ||
| 899 | } | ||
| 900 | |||
| 901 | delay_ctrl |= bits_slave_delay; | ||
| 902 | } else { | ||
| 903 | delay_ctrl &= ~(bits_slave_delay); | ||
| 904 | } | ||
| 905 | if (delay_ctrl != delay_ctrl_orig) { | ||
| 906 | result = inv_serial_single_write( | ||
| 907 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 908 | MPUREG_I2C_MST_DELAY_CTRL, | ||
| 909 | delay_ctrl); | ||
| 910 | if (result) { | ||
| 911 | LOG_RESULT_LOCATION(result); | ||
| 912 | return result; | ||
| 913 | } | ||
| 914 | } | ||
| 915 | |||
| 916 | if (slave_address) | ||
| 917 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled |= | ||
| 918 | bits_slave_delay; | ||
| 919 | else | ||
| 920 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled &= | ||
| 921 | ~bits_slave_delay; | ||
| 922 | |||
| 923 | return result; | ||
| 924 | } | ||
| 925 | |||
| 926 | static int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
| 927 | void *gyro_handle, | ||
| 928 | struct ext_slave_descr *slave, | ||
| 929 | struct ext_slave_platform_data *slave_pdata, | ||
| 930 | int slave_id) | ||
| 931 | { | ||
| 932 | return mpu_set_slave_mpu60xx(mldl_cfg, gyro_handle, slave, | ||
| 933 | slave_pdata, slave_id); | ||
| 934 | } | ||
| 935 | /** | ||
| 936 | * Check to see if the gyro was reset by testing a couple of registers known | ||
| 937 | * to change on reset. | ||
| 938 | * | ||
| 939 | * @mldl_cfg mldl configuration structure | ||
| 940 | * @gyro_handle handle used to communicate with the gyro | ||
| 941 | * | ||
| 942 | * @return INV_SUCCESS or non-zero error code | ||
| 943 | */ | ||
| 944 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
| 945 | { | ||
| 946 | int result = INV_SUCCESS; | ||
| 947 | unsigned char reg; | ||
| 948 | |||
| 949 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 950 | MPUREG_DMP_CFG_2, 1, ®); | ||
| 951 | if (result) { | ||
| 952 | LOG_RESULT_LOCATION(result); | ||
| 953 | return result; | ||
| 954 | } | ||
| 955 | |||
| 956 | if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg) | ||
| 957 | return true; | ||
| 958 | |||
| 959 | if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1) | ||
| 960 | return false; | ||
| 961 | |||
| 962 | /* Inconclusive assume it was reset */ | ||
| 963 | return true; | ||
| 964 | } | ||
| 965 | |||
| 966 | |||
| 967 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
| 968 | const unsigned char *data, int size) | ||
| 969 | { | ||
| 970 | int bank, offset, write_size; | ||
| 971 | int result; | ||
| 972 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
| 973 | |||
| 974 | if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) { | ||
| 975 | #if INV_CACHE_DMP == 1 | ||
| 976 | memcpy(mldl_cfg->mpu_ram->ram, data, size); | ||
| 977 | return INV_SUCCESS; | ||
| 978 | #else | ||
| 979 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
| 980 | return INV_ERROR_MEMORY_SET; | ||
| 981 | #endif | ||
| 982 | } | ||
| 983 | |||
| 984 | if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) { | ||
| 985 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
| 986 | return INV_ERROR_MEMORY_SET; | ||
| 987 | } | ||
| 988 | /* Write and verify memory */ | ||
| 989 | for (bank = 0; size > 0; bank++, | ||
| 990 | size -= write_size, | ||
| 991 | data += write_size) { | ||
| 992 | if (size > MPU_MEM_BANK_SIZE) | ||
| 993 | write_size = MPU_MEM_BANK_SIZE; | ||
| 994 | else | ||
| 995 | write_size = size; | ||
| 996 | |||
| 997 | result = inv_serial_write_mem(mlsl_handle, | ||
| 998 | mldl_cfg->mpu_chip_info->addr, | ||
| 999 | ((bank << 8) | 0x00), | ||
| 1000 | write_size, | ||
| 1001 | data); | ||
| 1002 | if (result) { | ||
| 1003 | LOG_RESULT_LOCATION(result); | ||
| 1004 | MPL_LOGE("Write mem error in bank %d\n", bank); | ||
| 1005 | return result; | ||
| 1006 | } | ||
| 1007 | result = inv_serial_read_mem(mlsl_handle, | ||
| 1008 | mldl_cfg->mpu_chip_info->addr, | ||
| 1009 | ((bank << 8) | 0x00), | ||
| 1010 | write_size, | ||
| 1011 | read); | ||
| 1012 | if (result) { | ||
| 1013 | LOG_RESULT_LOCATION(result); | ||
| 1014 | MPL_LOGE("Read mem error in bank %d\n", bank); | ||
| 1015 | return result; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | #define ML_SKIP_CHECK 38 | ||
| 1019 | for (offset = 0; offset < write_size; offset++) { | ||
| 1020 | /* skip the register memory locations */ | ||
| 1021 | if (bank == 0 && offset < ML_SKIP_CHECK) | ||
| 1022 | continue; | ||
| 1023 | if (data[offset] != read[offset]) { | ||
| 1024 | result = INV_ERROR_SERIAL_WRITE; | ||
| 1025 | break; | ||
| 1026 | } | ||
| 1027 | } | ||
| 1028 | if (result != INV_SUCCESS) { | ||
| 1029 | LOG_RESULT_LOCATION(result); | ||
| 1030 | MPL_LOGE("Read data mismatch at bank %d, offset %d\n", | ||
| 1031 | bank, offset); | ||
| 1032 | return result; | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | return INV_SUCCESS; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle, | ||
| 1039 | unsigned long sensors) | ||
| 1040 | { | ||
| 1041 | int result; | ||
| 1042 | int ii; | ||
| 1043 | unsigned char reg; | ||
| 1044 | unsigned char regs[7]; | ||
| 1045 | |||
| 1046 | /* Wake up the part */ | ||
| 1047 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, sensors); | ||
| 1048 | if (result) { | ||
| 1049 | LOG_RESULT_LOCATION(result); | ||
| 1050 | return result; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | /* Always set the INT_ENABLE and DIVIDER as the Accel Only mode for 6050 | ||
| 1054 | can set these too */ | ||
| 1055 | result = inv_serial_single_write( | ||
| 1056 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1057 | MPUREG_INT_ENABLE, (mldl_cfg->mpu_gyro_cfg->int_config)); | ||
| 1058 | if (result) { | ||
| 1059 | LOG_RESULT_LOCATION(result); | ||
| 1060 | return result; | ||
| 1061 | } | ||
| 1062 | result = inv_serial_single_write( | ||
| 1063 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1064 | MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider); | ||
| 1065 | if (result) { | ||
| 1066 | LOG_RESULT_LOCATION(result); | ||
| 1067 | return result; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) && | ||
| 1071 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
| 1072 | return INV_SUCCESS; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | /* Configure the MPU */ | ||
| 1076 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1077 | if (result) { | ||
| 1078 | LOG_RESULT_LOCATION(result); | ||
| 1079 | return result; | ||
| 1080 | } | ||
| 1081 | result = mpu_set_clock_source(gyro_handle, mldl_cfg); | ||
| 1082 | if (result) { | ||
| 1083 | LOG_RESULT_LOCATION(result); | ||
| 1084 | return result; | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | reg = MPUREG_GYRO_CONFIG_VALUE(0, 0, 0, | ||
| 1088 | mldl_cfg->mpu_gyro_cfg->full_scale); | ||
| 1089 | result = inv_serial_single_write( | ||
| 1090 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1091 | MPUREG_GYRO_CONFIG, reg); | ||
| 1092 | reg = MPUREG_CONFIG_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync, | ||
| 1093 | mldl_cfg->mpu_gyro_cfg->lpf); | ||
| 1094 | result = inv_serial_single_write( | ||
| 1095 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1096 | MPUREG_CONFIG, reg); | ||
| 1097 | if (result) { | ||
| 1098 | LOG_RESULT_LOCATION(result); | ||
| 1099 | return result; | ||
| 1100 | } | ||
| 1101 | result = inv_serial_single_write( | ||
| 1102 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1103 | MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1); | ||
| 1104 | if (result) { | ||
| 1105 | LOG_RESULT_LOCATION(result); | ||
| 1106 | return result; | ||
| 1107 | } | ||
| 1108 | result = inv_serial_single_write( | ||
| 1109 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1110 | MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2); | ||
| 1111 | if (result) { | ||
| 1112 | LOG_RESULT_LOCATION(result); | ||
| 1113 | return result; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | /* Write and verify memory */ | ||
| 1117 | #if INV_CACHE_DMP != 0 | ||
| 1118 | inv_mpu_set_firmware(mldl_cfg, gyro_handle, | ||
| 1119 | mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length); | ||
| 1120 | #endif | ||
| 1121 | |||
| 1122 | result = inv_serial_single_write( | ||
| 1123 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1124 | MPUREG_XG_OFFS_TC, | ||
| 1125 | ((mldl_cfg->mpu_offsets->tc[0] << 1) & BITS_XG_OFFS_TC)); | ||
| 1126 | if (result) { | ||
| 1127 | LOG_RESULT_LOCATION(result); | ||
| 1128 | return result; | ||
| 1129 | } | ||
| 1130 | regs[0] = ((mldl_cfg->mpu_offsets->tc[1] << 1) & BITS_YG_OFFS_TC); | ||
| 1131 | result = inv_serial_single_write( | ||
| 1132 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1133 | MPUREG_YG_OFFS_TC, regs[0]); | ||
| 1134 | if (result) { | ||
| 1135 | LOG_RESULT_LOCATION(result); | ||
| 1136 | return result; | ||
| 1137 | } | ||
| 1138 | result = inv_serial_single_write( | ||
| 1139 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1140 | MPUREG_ZG_OFFS_TC, | ||
| 1141 | ((mldl_cfg->mpu_offsets->tc[2] << 1) & BITS_ZG_OFFS_TC)); | ||
| 1142 | if (result) { | ||
| 1143 | LOG_RESULT_LOCATION(result); | ||
| 1144 | return result; | ||
| 1145 | } | ||
| 1146 | regs[0] = MPUREG_X_OFFS_USRH; | ||
| 1147 | for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) { | ||
| 1148 | regs[1 + ii * 2] = | ||
| 1149 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8) | ||
| 1150 | & 0xff; | ||
| 1151 | regs[1 + ii * 2 + 1] = | ||
| 1152 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff); | ||
| 1153 | } | ||
| 1154 | result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1155 | 7, regs); | ||
| 1156 | if (result) { | ||
| 1157 | LOG_RESULT_LOCATION(result); | ||
| 1158 | return result; | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /* Configure slaves */ | ||
| 1162 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
| 1163 | mldl_cfg->pdata->level_shifter); | ||
| 1164 | if (result) { | ||
| 1165 | LOG_RESULT_LOCATION(result); | ||
| 1166 | return result; | ||
| 1167 | } | ||
| 1168 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG; | ||
| 1169 | |||
| 1170 | return result; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | int gyro_config(void *mlsl_handle, | ||
| 1174 | struct mldl_cfg *mldl_cfg, | ||
| 1175 | struct ext_slave_config *data) | ||
| 1176 | { | ||
| 1177 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 1178 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 1179 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 1180 | int ii; | ||
| 1181 | |||
| 1182 | if (!data->data) | ||
| 1183 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1184 | |||
| 1185 | switch (data->key) { | ||
| 1186 | case MPU_SLAVE_INT_CONFIG: | ||
| 1187 | mpu_gyro_cfg->int_config = *((__u8 *)data->data); | ||
| 1188 | break; | ||
| 1189 | case MPU_SLAVE_EXT_SYNC: | ||
| 1190 | mpu_gyro_cfg->ext_sync = *((__u8 *)data->data); | ||
| 1191 | break; | ||
| 1192 | case MPU_SLAVE_FULL_SCALE: | ||
| 1193 | mpu_gyro_cfg->full_scale = *((__u8 *)data->data); | ||
| 1194 | break; | ||
| 1195 | case MPU_SLAVE_LPF: | ||
| 1196 | mpu_gyro_cfg->lpf = *((__u8 *)data->data); | ||
| 1197 | break; | ||
| 1198 | case MPU_SLAVE_CLK_SRC: | ||
| 1199 | mpu_gyro_cfg->clk_src = *((__u8 *)data->data); | ||
| 1200 | break; | ||
| 1201 | case MPU_SLAVE_DIVIDER: | ||
| 1202 | mpu_gyro_cfg->divider = *((__u8 *)data->data); | ||
| 1203 | break; | ||
| 1204 | case MPU_SLAVE_DMP_ENABLE: | ||
| 1205 | mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data); | ||
| 1206 | break; | ||
| 1207 | case MPU_SLAVE_FIFO_ENABLE: | ||
| 1208 | mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data); | ||
| 1209 | break; | ||
| 1210 | case MPU_SLAVE_DMP_CFG1: | ||
| 1211 | mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data); | ||
| 1212 | break; | ||
| 1213 | case MPU_SLAVE_DMP_CFG2: | ||
| 1214 | mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data); | ||
| 1215 | break; | ||
| 1216 | case MPU_SLAVE_TC: | ||
| 1217 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1218 | mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii]; | ||
| 1219 | break; | ||
| 1220 | case MPU_SLAVE_GYRO: | ||
| 1221 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1222 | mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii]; | ||
| 1223 | break; | ||
| 1224 | case MPU_SLAVE_ADDR: | ||
| 1225 | mpu_chip_info->addr = *((__u8 *)data->data); | ||
| 1226 | break; | ||
| 1227 | case MPU_SLAVE_PRODUCT_REVISION: | ||
| 1228 | mpu_chip_info->product_revision = *((__u8 *)data->data); | ||
| 1229 | break; | ||
| 1230 | case MPU_SLAVE_SILICON_REVISION: | ||
| 1231 | mpu_chip_info->silicon_revision = *((__u8 *)data->data); | ||
| 1232 | break; | ||
| 1233 | case MPU_SLAVE_PRODUCT_ID: | ||
| 1234 | mpu_chip_info->product_id = *((__u8 *)data->data); | ||
| 1235 | break; | ||
| 1236 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
| 1237 | mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data); | ||
| 1238 | break; | ||
| 1239 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
| 1240 | mpu_chip_info->accel_sens_trim = *((__u16 *)data->data); | ||
| 1241 | break; | ||
| 1242 | case MPU_SLAVE_RAM: | ||
| 1243 | if (data->len != mldl_cfg->mpu_ram->length) | ||
| 1244 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1245 | |||
| 1246 | memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len); | ||
| 1247 | break; | ||
| 1248 | default: | ||
| 1249 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 1250 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1251 | }; | ||
| 1252 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG; | ||
| 1253 | return INV_SUCCESS; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | int gyro_get_config(void *mlsl_handle, | ||
| 1257 | struct mldl_cfg *mldl_cfg, | ||
| 1258 | struct ext_slave_config *data) | ||
| 1259 | { | ||
| 1260 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 1261 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 1262 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 1263 | int ii; | ||
| 1264 | |||
| 1265 | if (!data->data) | ||
| 1266 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1267 | |||
| 1268 | switch (data->key) { | ||
| 1269 | case MPU_SLAVE_INT_CONFIG: | ||
| 1270 | *((__u8 *)data->data) = mpu_gyro_cfg->int_config; | ||
| 1271 | break; | ||
| 1272 | case MPU_SLAVE_EXT_SYNC: | ||
| 1273 | *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync; | ||
| 1274 | break; | ||
| 1275 | case MPU_SLAVE_FULL_SCALE: | ||
| 1276 | *((__u8 *)data->data) = mpu_gyro_cfg->full_scale; | ||
| 1277 | break; | ||
| 1278 | case MPU_SLAVE_LPF: | ||
| 1279 | *((__u8 *)data->data) = mpu_gyro_cfg->lpf; | ||
| 1280 | break; | ||
| 1281 | case MPU_SLAVE_CLK_SRC: | ||
| 1282 | *((__u8 *)data->data) = mpu_gyro_cfg->clk_src; | ||
| 1283 | break; | ||
| 1284 | case MPU_SLAVE_DIVIDER: | ||
| 1285 | *((__u8 *)data->data) = mpu_gyro_cfg->divider; | ||
| 1286 | break; | ||
| 1287 | case MPU_SLAVE_DMP_ENABLE: | ||
| 1288 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable; | ||
| 1289 | break; | ||
| 1290 | case MPU_SLAVE_FIFO_ENABLE: | ||
| 1291 | *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable; | ||
| 1292 | break; | ||
| 1293 | case MPU_SLAVE_DMP_CFG1: | ||
| 1294 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1; | ||
| 1295 | break; | ||
| 1296 | case MPU_SLAVE_DMP_CFG2: | ||
| 1297 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2; | ||
| 1298 | break; | ||
| 1299 | case MPU_SLAVE_TC: | ||
| 1300 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1301 | ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii]; | ||
| 1302 | break; | ||
| 1303 | case MPU_SLAVE_GYRO: | ||
| 1304 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
| 1305 | ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii]; | ||
| 1306 | break; | ||
| 1307 | case MPU_SLAVE_ADDR: | ||
| 1308 | *((__u8 *)data->data) = mpu_chip_info->addr; | ||
| 1309 | break; | ||
| 1310 | case MPU_SLAVE_PRODUCT_REVISION: | ||
| 1311 | *((__u8 *)data->data) = mpu_chip_info->product_revision; | ||
| 1312 | break; | ||
| 1313 | case MPU_SLAVE_SILICON_REVISION: | ||
| 1314 | *((__u8 *)data->data) = mpu_chip_info->silicon_revision; | ||
| 1315 | break; | ||
| 1316 | case MPU_SLAVE_PRODUCT_ID: | ||
| 1317 | *((__u8 *)data->data) = mpu_chip_info->product_id; | ||
| 1318 | break; | ||
| 1319 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
| 1320 | *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim; | ||
| 1321 | break; | ||
| 1322 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
| 1323 | *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim; | ||
| 1324 | break; | ||
| 1325 | case MPU_SLAVE_RAM: | ||
| 1326 | if (data->len != mldl_cfg->mpu_ram->length) | ||
| 1327 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1328 | |||
| 1329 | memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len); | ||
| 1330 | break; | ||
| 1331 | default: | ||
| 1332 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
| 1333 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
| 1334 | }; | ||
| 1335 | |||
| 1336 | return INV_SUCCESS; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | |||
| 1340 | /******************************************************************************* | ||
| 1341 | ******************************************************************************* | ||
| 1342 | * Exported functions | ||
| 1343 | ******************************************************************************* | ||
| 1344 | ******************************************************************************/ | ||
| 1345 | |||
| 1346 | /** | ||
| 1347 | * Initializes the pdata structure to defaults. | ||
| 1348 | * | ||
| 1349 | * Opens the device to read silicon revision, product id and whoami. | ||
| 1350 | * | ||
| 1351 | * @mldl_cfg | ||
| 1352 | * The internal device configuration data structure. | ||
| 1353 | * @mlsl_handle | ||
| 1354 | * The serial communication handle. | ||
| 1355 | * | ||
| 1356 | * @return INV_SUCCESS if silicon revision, product id and woami are supported | ||
| 1357 | * by this software. | ||
| 1358 | */ | ||
| 1359 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
| 1360 | void *gyro_handle, | ||
| 1361 | void *accel_handle, | ||
| 1362 | void *compass_handle, void *pressure_handle) | ||
| 1363 | { | ||
| 1364 | int result; | ||
| 1365 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1366 | int ii; | ||
| 1367 | |||
| 1368 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
| 1369 | ii = 0; | ||
| 1370 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false; | ||
| 1371 | mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN; | ||
| 1372 | mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
| 1373 | mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ; | ||
| 1374 | mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS; | ||
| 1375 | mldl_cfg->mpu_gyro_cfg->divider = 4; | ||
| 1376 | mldl_cfg->mpu_gyro_cfg->dmp_enable = 1; | ||
| 1377 | mldl_cfg->mpu_gyro_cfg->fifo_enable = 1; | ||
| 1378 | mldl_cfg->mpu_gyro_cfg->ext_sync = 0; | ||
| 1379 | mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0; | ||
| 1380 | mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0; | ||
| 1381 | mldl_cfg->inv_mpu_state->status = | ||
| 1382 | MPU_DMP_IS_SUSPENDED | | ||
| 1383 | MPU_GYRO_IS_SUSPENDED | | ||
| 1384 | MPU_ACCEL_IS_SUSPENDED | | ||
| 1385 | MPU_COMPASS_IS_SUSPENDED | | ||
| 1386 | MPU_PRESSURE_IS_SUSPENDED | | ||
| 1387 | MPU_DEVICE_IS_SUSPENDED; | ||
| 1388 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
| 1389 | |||
| 1390 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1391 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1392 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1393 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1394 | |||
| 1395 | if (mldl_cfg->mpu_chip_info->addr == 0) { | ||
| 1396 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 1397 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | /* | ||
| 1401 | * Reset, | ||
| 1402 | * Take the DMP out of sleep, and | ||
| 1403 | * read the product_id, sillicon rev and whoami | ||
| 1404 | */ | ||
| 1405 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
| 1406 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, true, | ||
| 1407 | INV_THREE_AXIS_GYRO); | ||
| 1408 | if (result) { | ||
| 1409 | LOG_RESULT_LOCATION(result); | ||
| 1410 | return result; | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | result = inv_get_silicon_rev(mldl_cfg, gyro_handle); | ||
| 1414 | if (result) { | ||
| 1415 | LOG_RESULT_LOCATION(result); | ||
| 1416 | return result; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | /* Get the factory temperature compensation offsets */ | ||
| 1420 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1421 | MPUREG_XG_OFFS_TC, 1, | ||
| 1422 | &mldl_cfg->mpu_offsets->tc[0]); | ||
| 1423 | if (result) { | ||
| 1424 | LOG_RESULT_LOCATION(result); | ||
| 1425 | return result; | ||
| 1426 | } | ||
| 1427 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1428 | MPUREG_YG_OFFS_TC, 1, | ||
| 1429 | &mldl_cfg->mpu_offsets->tc[1]); | ||
| 1430 | if (result) { | ||
| 1431 | LOG_RESULT_LOCATION(result); | ||
| 1432 | return result; | ||
| 1433 | } | ||
| 1434 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
| 1435 | MPUREG_ZG_OFFS_TC, 1, | ||
| 1436 | &mldl_cfg->mpu_offsets->tc[2]); | ||
| 1437 | if (result) { | ||
| 1438 | LOG_RESULT_LOCATION(result); | ||
| 1439 | return result; | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | /* Into bypass mode before sleeping and calling the slaves init */ | ||
| 1443 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
| 1444 | if (result) { | ||
| 1445 | LOG_RESULT_LOCATION(result); | ||
| 1446 | return result; | ||
| 1447 | } | ||
| 1448 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
| 1449 | mldl_cfg->pdata->level_shifter); | ||
| 1450 | if (result) { | ||
| 1451 | LOG_RESULT_LOCATION(result); | ||
| 1452 | return result; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) { | ||
| 1456 | mldl_cfg->mpu_offsets->tc[ii] = | ||
| 1457 | (mldl_cfg->mpu_offsets->tc[ii] & BITS_XG_OFFS_TC) >> 1; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | #if INV_CACHE_DMP != 0 | ||
| 1461 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, 0); | ||
| 1462 | #endif | ||
| 1463 | if (result) { | ||
| 1464 | LOG_RESULT_LOCATION(result); | ||
| 1465 | return result; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | |||
| 1469 | return result; | ||
| 1470 | |||
| 1471 | } | ||
| 1472 | |||
| 1473 | /** | ||
| 1474 | * Close the mpu interface | ||
| 1475 | * | ||
| 1476 | * @mldl_cfg pointer to the configuration structure | ||
| 1477 | * @mlsl_handle pointer to the serial layer handle | ||
| 1478 | * | ||
| 1479 | * @return INV_SUCCESS or non-zero error code | ||
| 1480 | */ | ||
| 1481 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
| 1482 | void *gyro_handle, | ||
| 1483 | void *accel_handle, | ||
| 1484 | void *compass_handle, | ||
| 1485 | void *pressure_handle) | ||
| 1486 | { | ||
| 1487 | return 0; | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | /** | ||
| 1491 | * @brief resume the MPU device and all the other sensor | ||
| 1492 | * devices from their low power state. | ||
| 1493 | * | ||
| 1494 | * @mldl_cfg | ||
| 1495 | * pointer to the configuration structure | ||
| 1496 | * @gyro_handle | ||
| 1497 | * the main file handle to the MPU device. | ||
| 1498 | * @accel_handle | ||
| 1499 | * an handle to the accelerometer device, if sitting | ||
| 1500 | * onto a separate bus. Can match mlsl_handle if | ||
| 1501 | * the accelerometer device operates on the same | ||
| 1502 | * primary bus of MPU. | ||
| 1503 | * @compass_handle | ||
| 1504 | * an handle to the compass device, if sitting | ||
| 1505 | * onto a separate bus. Can match mlsl_handle if | ||
| 1506 | * the compass device operates on the same | ||
| 1507 | * primary bus of MPU. | ||
| 1508 | * @pressure_handle | ||
| 1509 | * an handle to the pressure sensor device, if sitting | ||
| 1510 | * onto a separate bus. Can match mlsl_handle if | ||
| 1511 | * the pressure sensor device operates on the same | ||
| 1512 | * primary bus of MPU. | ||
| 1513 | * @resume_gyro | ||
| 1514 | * whether resuming the gyroscope device is | ||
| 1515 | * actually needed (if the device supports low power | ||
| 1516 | * mode of some sort). | ||
| 1517 | * @resume_accel | ||
| 1518 | * whether resuming the accelerometer device is | ||
| 1519 | * actually needed (if the device supports low power | ||
| 1520 | * mode of some sort). | ||
| 1521 | * @resume_compass | ||
| 1522 | * whether resuming the compass device is | ||
| 1523 | * actually needed (if the device supports low power | ||
| 1524 | * mode of some sort). | ||
| 1525 | * @resume_pressure | ||
| 1526 | * whether resuming the pressure sensor device is | ||
| 1527 | * actually needed (if the device supports low power | ||
| 1528 | * mode of some sort). | ||
| 1529 | * @return INV_SUCCESS or a non-zero error code. | ||
| 1530 | */ | ||
| 1531 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
| 1532 | void *gyro_handle, | ||
| 1533 | void *accel_handle, | ||
| 1534 | void *compass_handle, | ||
| 1535 | void *pressure_handle, | ||
| 1536 | unsigned long sensors) | ||
| 1537 | { | ||
| 1538 | int result = INV_SUCCESS; | ||
| 1539 | int ii; | ||
| 1540 | bool resume_slave[EXT_SLAVE_NUM_TYPES]; | ||
| 1541 | bool resume_dmp = sensors & INV_DMP_PROCESSOR; | ||
| 1542 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1543 | resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
| 1544 | (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
| 1545 | resume_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
| 1546 | sensors & INV_THREE_AXIS_ACCEL; | ||
| 1547 | resume_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
| 1548 | sensors & INV_THREE_AXIS_COMPASS; | ||
| 1549 | resume_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
| 1550 | sensors & INV_THREE_AXIS_PRESSURE; | ||
| 1551 | |||
| 1552 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1553 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1554 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1555 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1556 | |||
| 1557 | |||
| 1558 | mldl_print_cfg(mldl_cfg); | ||
| 1559 | |||
| 1560 | /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */ | ||
| 1561 | for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1562 | if (resume_slave[ii] && | ||
| 1563 | ((!mldl_cfg->slave[ii]) || | ||
| 1564 | (!mldl_cfg->slave[ii]->resume))) { | ||
| 1565 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 1566 | return INV_ERROR_INVALID_PARAMETER; | ||
| 1567 | } | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp) | ||
| 1571 | && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) || | ||
| 1572 | (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) { | ||
| 1573 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1574 | if (result) { | ||
| 1575 | LOG_RESULT_LOCATION(result); | ||
| 1576 | return result; | ||
| 1577 | } | ||
| 1578 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
| 1579 | if (result) { | ||
| 1580 | LOG_RESULT_LOCATION(result); | ||
| 1581 | return result; | ||
| 1582 | } | ||
| 1583 | result = gyro_resume(mldl_cfg, gyro_handle, sensors); | ||
| 1584 | if (result) { | ||
| 1585 | LOG_RESULT_LOCATION(result); | ||
| 1586 | return result; | ||
| 1587 | } | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1591 | if (!mldl_cfg->slave[ii] || | ||
| 1592 | !mldl_cfg->pdata_slave[ii] || | ||
| 1593 | !resume_slave[ii] || | ||
| 1594 | !(mldl_cfg->inv_mpu_state->status & (1 << ii))) | ||
| 1595 | continue; | ||
| 1596 | |||
| 1597 | if (EXT_SLAVE_BUS_SECONDARY == | ||
| 1598 | mldl_cfg->pdata_slave[ii]->bus) { | ||
| 1599 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, | ||
| 1600 | true); | ||
| 1601 | if (result) { | ||
| 1602 | LOG_RESULT_LOCATION(result); | ||
| 1603 | return result; | ||
| 1604 | } | ||
| 1605 | } | ||
| 1606 | result = mldl_cfg->slave[ii]->resume(slave_handle[ii], | ||
| 1607 | mldl_cfg->slave[ii], | ||
| 1608 | mldl_cfg->pdata_slave[ii]); | ||
| 1609 | if (result) { | ||
| 1610 | LOG_RESULT_LOCATION(result); | ||
| 1611 | return result; | ||
| 1612 | } | ||
| 1613 | mldl_cfg->inv_mpu_state->status &= ~(1 << ii); | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1617 | if (resume_dmp && | ||
| 1618 | !(mldl_cfg->inv_mpu_state->status & (1 << ii)) && | ||
| 1619 | mldl_cfg->pdata_slave[ii] && | ||
| 1620 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) { | ||
| 1621 | result = mpu_set_slave(mldl_cfg, | ||
| 1622 | gyro_handle, | ||
| 1623 | mldl_cfg->slave[ii], | ||
| 1624 | mldl_cfg->pdata_slave[ii], | ||
| 1625 | mldl_cfg->slave[ii]->type); | ||
| 1626 | if (result) { | ||
| 1627 | LOG_RESULT_LOCATION(result); | ||
| 1628 | return result; | ||
| 1629 | } | ||
| 1630 | } | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | /* Turn on the master i2c iterface if necessary */ | ||
| 1634 | if (resume_dmp) { | ||
| 1635 | result = mpu_set_i2c_bypass( | ||
| 1636 | mldl_cfg, gyro_handle, | ||
| 1637 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
| 1638 | if (result) { | ||
| 1639 | LOG_RESULT_LOCATION(result); | ||
| 1640 | return result; | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | /* Now start */ | ||
| 1644 | result = dmp_start(mldl_cfg, gyro_handle); | ||
| 1645 | if (result) { | ||
| 1646 | LOG_RESULT_LOCATION(result); | ||
| 1647 | return result; | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | mldl_cfg->inv_mpu_cfg->requested_sensors = sensors; | ||
| 1651 | |||
| 1652 | return result; | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | /** | ||
| 1656 | * @brief suspend the MPU device and all the other sensor | ||
| 1657 | * devices into their low power state. | ||
| 1658 | * @mldl_cfg | ||
| 1659 | * a pointer to the struct mldl_cfg internal data | ||
| 1660 | * structure. | ||
| 1661 | * @gyro_handle | ||
| 1662 | * the main file handle to the MPU device. | ||
| 1663 | * @accel_handle | ||
| 1664 | * an handle to the accelerometer device, if sitting | ||
| 1665 | * onto a separate bus. Can match gyro_handle if | ||
| 1666 | * the accelerometer device operates on the same | ||
| 1667 | * primary bus of MPU. | ||
| 1668 | * @compass_handle | ||
| 1669 | * an handle to the compass device, if sitting | ||
| 1670 | * onto a separate bus. Can match gyro_handle if | ||
| 1671 | * the compass device operates on the same | ||
| 1672 | * primary bus of MPU. | ||
| 1673 | * @pressure_handle | ||
| 1674 | * an handle to the pressure sensor device, if sitting | ||
| 1675 | * onto a separate bus. Can match gyro_handle if | ||
| 1676 | * the pressure sensor device operates on the same | ||
| 1677 | * primary bus of MPU. | ||
| 1678 | * @accel | ||
| 1679 | * whether suspending the accelerometer device is | ||
| 1680 | * actually needed (if the device supports low power | ||
| 1681 | * mode of some sort). | ||
| 1682 | * @compass | ||
| 1683 | * whether suspending the compass device is | ||
| 1684 | * actually needed (if the device supports low power | ||
| 1685 | * mode of some sort). | ||
| 1686 | * @pressure | ||
| 1687 | * whether suspending the pressure sensor device is | ||
| 1688 | * actually needed (if the device supports low power | ||
| 1689 | * mode of some sort). | ||
| 1690 | * @return INV_SUCCESS or a non-zero error code. | ||
| 1691 | */ | ||
| 1692 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
| 1693 | void *gyro_handle, | ||
| 1694 | void *accel_handle, | ||
| 1695 | void *compass_handle, | ||
| 1696 | void *pressure_handle, | ||
| 1697 | unsigned long sensors) | ||
| 1698 | { | ||
| 1699 | int result = INV_SUCCESS; | ||
| 1700 | int ii; | ||
| 1701 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 1702 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 1703 | bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR); | ||
| 1704 | bool suspend_slave[EXT_SLAVE_NUM_TYPES]; | ||
| 1705 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
| 1706 | |||
| 1707 | suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
| 1708 | ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)) | ||
| 1709 | == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
| 1710 | suspend_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
| 1711 | ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL); | ||
| 1712 | suspend_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
| 1713 | ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS); | ||
| 1714 | suspend_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
| 1715 | ((sensors & INV_THREE_AXIS_PRESSURE) == | ||
| 1716 | INV_THREE_AXIS_PRESSURE); | ||
| 1717 | |||
| 1718 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
| 1719 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
| 1720 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
| 1721 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
| 1722 | |||
| 1723 | if (suspend_dmp) { | ||
| 1724 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1725 | if (result) { | ||
| 1726 | LOG_RESULT_LOCATION(result); | ||
| 1727 | return result; | ||
| 1728 | } | ||
| 1729 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
| 1730 | if (result) { | ||
| 1731 | LOG_RESULT_LOCATION(result); | ||
| 1732 | return result; | ||
| 1733 | } | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | /* Gyro */ | ||
| 1737 | if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] && | ||
| 1738 | !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) { | ||
| 1739 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, | ||
| 1740 | ((~sensors) & INV_ALL_SENSORS)); | ||
| 1741 | if (result) { | ||
| 1742 | LOG_RESULT_LOCATION(result); | ||
| 1743 | return result; | ||
| 1744 | } | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1748 | bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii); | ||
| 1749 | if (!slave[ii] || !pdata_slave[ii] || | ||
| 1750 | is_suspended || !suspend_slave[ii]) | ||
| 1751 | continue; | ||
| 1752 | |||
| 1753 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
| 1754 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1755 | if (result) { | ||
| 1756 | LOG_RESULT_LOCATION(result); | ||
| 1757 | return result; | ||
| 1758 | } | ||
| 1759 | } | ||
| 1760 | result = slave[ii]->suspend(slave_handle[ii], | ||
| 1761 | slave[ii], | ||
| 1762 | pdata_slave[ii]); | ||
| 1763 | if (result) { | ||
| 1764 | LOG_RESULT_LOCATION(result); | ||
| 1765 | return result; | ||
| 1766 | } | ||
| 1767 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
| 1768 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
| 1769 | NULL, NULL, | ||
| 1770 | slave[ii]->type); | ||
| 1771 | if (result) { | ||
| 1772 | LOG_RESULT_LOCATION(result); | ||
| 1773 | return result; | ||
| 1774 | } | ||
| 1775 | } | ||
| 1776 | mldl_cfg->inv_mpu_state->status |= (1 << ii); | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | /* Re-enable the i2c master if there are configured slaves and DMP */ | ||
| 1780 | if (!suspend_dmp) { | ||
| 1781 | result = mpu_set_i2c_bypass( | ||
| 1782 | mldl_cfg, gyro_handle, | ||
| 1783 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
| 1784 | if (result) { | ||
| 1785 | LOG_RESULT_LOCATION(result); | ||
| 1786 | return result; | ||
| 1787 | } | ||
| 1788 | } | ||
| 1789 | mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS; | ||
| 1790 | |||
| 1791 | return result; | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
| 1795 | void *gyro_handle, | ||
| 1796 | void *slave_handle, | ||
| 1797 | struct ext_slave_descr *slave, | ||
| 1798 | struct ext_slave_platform_data *pdata, | ||
| 1799 | unsigned char *data) | ||
| 1800 | { | ||
| 1801 | int result; | ||
| 1802 | int bypass_result; | ||
| 1803 | int remain_bypassed = true; | ||
| 1804 | |||
| 1805 | if (NULL == slave || NULL == slave->read) { | ||
| 1806 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1807 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1811 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1812 | remain_bypassed = false; | ||
| 1813 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1814 | if (result) { | ||
| 1815 | LOG_RESULT_LOCATION(result); | ||
| 1816 | return result; | ||
| 1817 | } | ||
| 1818 | } | ||
| 1819 | |||
| 1820 | result = slave->read(slave_handle, slave, pdata, data); | ||
| 1821 | |||
| 1822 | if (!remain_bypassed) { | ||
| 1823 | bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1824 | if (bypass_result) { | ||
| 1825 | LOG_RESULT_LOCATION(bypass_result); | ||
| 1826 | return bypass_result; | ||
| 1827 | } | ||
| 1828 | } | ||
| 1829 | return result; | ||
| 1830 | } | ||
| 1831 | |||
| 1832 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 1833 | void *gyro_handle, | ||
| 1834 | void *slave_handle, | ||
| 1835 | struct ext_slave_config *data, | ||
| 1836 | struct ext_slave_descr *slave, | ||
| 1837 | struct ext_slave_platform_data *pdata) | ||
| 1838 | { | ||
| 1839 | int result; | ||
| 1840 | int remain_bypassed = true; | ||
| 1841 | |||
| 1842 | if (NULL == slave || NULL == slave->config) { | ||
| 1843 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1844 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1848 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1849 | remain_bypassed = false; | ||
| 1850 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1851 | if (result) { | ||
| 1852 | LOG_RESULT_LOCATION(result); | ||
| 1853 | return result; | ||
| 1854 | } | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | result = slave->config(slave_handle, slave, pdata, data); | ||
| 1858 | if (result) { | ||
| 1859 | LOG_RESULT_LOCATION(result); | ||
| 1860 | return result; | ||
| 1861 | } | ||
| 1862 | |||
| 1863 | if (!remain_bypassed) { | ||
| 1864 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1865 | if (result) { | ||
| 1866 | LOG_RESULT_LOCATION(result); | ||
| 1867 | return result; | ||
| 1868 | } | ||
| 1869 | } | ||
| 1870 | return result; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
| 1874 | void *gyro_handle, | ||
| 1875 | void *slave_handle, | ||
| 1876 | struct ext_slave_config *data, | ||
| 1877 | struct ext_slave_descr *slave, | ||
| 1878 | struct ext_slave_platform_data *pdata) | ||
| 1879 | { | ||
| 1880 | int result; | ||
| 1881 | int remain_bypassed = true; | ||
| 1882 | |||
| 1883 | if (NULL == slave || NULL == slave->get_config) { | ||
| 1884 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
| 1885 | return INV_ERROR_INVALID_CONFIGURATION; | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
| 1889 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
| 1890 | remain_bypassed = false; | ||
| 1891 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
| 1892 | if (result) { | ||
| 1893 | LOG_RESULT_LOCATION(result); | ||
| 1894 | return result; | ||
| 1895 | } | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | result = slave->get_config(slave_handle, slave, pdata, data); | ||
| 1899 | if (result) { | ||
| 1900 | LOG_RESULT_LOCATION(result); | ||
| 1901 | return result; | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | if (!remain_bypassed) { | ||
| 1905 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
| 1906 | if (result) { | ||
| 1907 | LOG_RESULT_LOCATION(result); | ||
| 1908 | return result; | ||
| 1909 | } | ||
| 1910 | } | ||
| 1911 | return result; | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | /** | ||
| 1915 | * @} | ||
| 1916 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c new file mode 100644 index 00000000000..7379a170761 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @addtogroup MLDL | ||
| 22 | * | ||
| 23 | * @{ | ||
| 24 | * @file mldl_print_cfg.c | ||
| 25 | * @brief The Motion Library Driver Layer. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <stddef.h> | ||
| 29 | #include "mldl_cfg.h" | ||
| 30 | #include "mlsl.h" | ||
| 31 | #include "linux/mpu.h" | ||
| 32 | |||
| 33 | #include "log.h" | ||
| 34 | #undef MPL_LOG_TAG | ||
| 35 | #define MPL_LOG_TAG "mldl_print_cfg:" | ||
| 36 | |||
| 37 | #undef MPL_LOG_NDEBUG | ||
| 38 | #define MPL_LOG_NDEBUG 1 | ||
| 39 | |||
| 40 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg) | ||
| 41 | { | ||
| 42 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
| 43 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
| 44 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
| 45 | struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg; | ||
| 46 | struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state; | ||
| 47 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 48 | struct mpu_platform_data *pdata = mldl_cfg->pdata; | ||
| 49 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 50 | int ii; | ||
| 51 | |||
| 52 | /* mpu_gyro_cfg */ | ||
| 53 | MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config); | ||
| 54 | MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync); | ||
| 55 | MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale); | ||
| 56 | MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf); | ||
| 57 | MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src); | ||
| 58 | MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider); | ||
| 59 | MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable); | ||
| 60 | MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable); | ||
| 61 | MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1); | ||
| 62 | MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2); | ||
| 63 | /* mpu_offsets */ | ||
| 64 | MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]); | ||
| 65 | MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]); | ||
| 66 | MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]); | ||
| 67 | MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]); | ||
| 68 | MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]); | ||
| 69 | MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]); | ||
| 70 | |||
| 71 | /* mpu_chip_info */ | ||
| 72 | MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr); | ||
| 73 | |||
| 74 | MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision); | ||
| 75 | MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision); | ||
| 76 | MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id); | ||
| 77 | MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim); | ||
| 78 | MPL_LOGV("accel_sens_trim = %02x\n", mpu_chip_info->accel_sens_trim); | ||
| 79 | |||
| 80 | MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors); | ||
| 81 | MPL_LOGV("ignore_system_suspend= %04x\n", | ||
| 82 | inv_mpu_cfg->ignore_system_suspend); | ||
| 83 | MPL_LOGV("status = %04x\n", inv_mpu_state->status); | ||
| 84 | MPL_LOGV("i2c_slaves_enabled= %04x\n", | ||
| 85 | inv_mpu_state->i2c_slaves_enabled); | ||
| 86 | |||
| 87 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 88 | if (!slave[ii]) | ||
| 89 | continue; | ||
| 90 | MPL_LOGV("SLAVE %d:\n", ii); | ||
| 91 | MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend); | ||
| 92 | MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume); | ||
| 93 | MPL_LOGV(" read = %02x\n", (int)slave[ii]->read); | ||
| 94 | MPL_LOGV(" type = %02x\n", slave[ii]->type); | ||
| 95 | MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg); | ||
| 96 | MPL_LOGV(" len = %02x\n", slave[ii]->read_len); | ||
| 97 | MPL_LOGV(" endian = %02x\n", slave[ii]->endian); | ||
| 98 | MPL_LOGV(" range.mantissa= %02x\n", | ||
| 99 | slave[ii]->range.mantissa); | ||
| 100 | MPL_LOGV(" range.fraction= %02x\n", | ||
| 101 | slave[ii]->range.fraction); | ||
| 102 | } | ||
| 103 | |||
| 104 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 105 | if (!pdata_slave[ii]) | ||
| 106 | continue; | ||
| 107 | MPL_LOGV("PDATA_SLAVE[%d]\n", ii); | ||
| 108 | MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq); | ||
| 109 | MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num); | ||
| 110 | MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus); | ||
| 111 | MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address); | ||
| 112 | MPL_LOGV(" orientation=\n" | ||
| 113 | " %2d %2d %2d\n" | ||
| 114 | " %2d %2d %2d\n" | ||
| 115 | " %2d %2d %2d\n", | ||
| 116 | pdata_slave[ii]->orientation[0], | ||
| 117 | pdata_slave[ii]->orientation[1], | ||
| 118 | pdata_slave[ii]->orientation[2], | ||
| 119 | pdata_slave[ii]->orientation[3], | ||
| 120 | pdata_slave[ii]->orientation[4], | ||
| 121 | pdata_slave[ii]->orientation[5], | ||
| 122 | pdata_slave[ii]->orientation[6], | ||
| 123 | pdata_slave[ii]->orientation[7], | ||
| 124 | pdata_slave[ii]->orientation[8]); | ||
| 125 | } | ||
| 126 | |||
| 127 | MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config); | ||
| 128 | MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter); | ||
| 129 | MPL_LOGV("pdata->orientation =\n" | ||
| 130 | " %2d %2d %2d\n" | ||
| 131 | " %2d %2d %2d\n" | ||
| 132 | " %2d %2d %2d\n", | ||
| 133 | pdata->orientation[0], pdata->orientation[1], | ||
| 134 | pdata->orientation[2], pdata->orientation[3], | ||
| 135 | pdata->orientation[4], pdata->orientation[5], | ||
| 136 | pdata->orientation[6], pdata->orientation[7], | ||
| 137 | pdata->orientation[8]); | ||
| 138 | } | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c new file mode 100644 index 00000000000..f1c228f48fe --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c | |||
| @@ -0,0 +1,420 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "mlsl.h" | ||
| 21 | #include <linux/i2c.h> | ||
| 22 | #include "log.h" | ||
| 23 | #include "mpu6050b1.h" | ||
| 24 | |||
| 25 | static int inv_i2c_write(struct i2c_adapter *i2c_adap, | ||
| 26 | unsigned char address, | ||
| 27 | unsigned int len, unsigned char const *data) | ||
| 28 | { | ||
| 29 | struct i2c_msg msgs[1]; | ||
| 30 | int res; | ||
| 31 | |||
| 32 | if (!data || !i2c_adap) { | ||
| 33 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 34 | return -EINVAL; | ||
| 35 | } | ||
| 36 | |||
| 37 | msgs[0].addr = address; | ||
| 38 | msgs[0].flags = 0; /* write */ | ||
| 39 | msgs[0].buf = (unsigned char *)data; | ||
| 40 | msgs[0].len = len; | ||
| 41 | |||
| 42 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
| 43 | if (res < 1) { | ||
| 44 | if (res == 0) | ||
| 45 | res = -EIO; | ||
| 46 | LOG_RESULT_LOCATION(res); | ||
| 47 | return res; | ||
| 48 | } else | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int inv_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
| 53 | unsigned char address, | ||
| 54 | unsigned char reg, unsigned char value) | ||
| 55 | { | ||
| 56 | unsigned char data[2]; | ||
| 57 | |||
| 58 | data[0] = reg; | ||
| 59 | data[1] = value; | ||
| 60 | return inv_i2c_write(i2c_adap, address, 2, data); | ||
| 61 | } | ||
| 62 | |||
| 63 | static int inv_i2c_read(struct i2c_adapter *i2c_adap, | ||
| 64 | unsigned char address, unsigned char reg, | ||
| 65 | unsigned int len, unsigned char *data) | ||
| 66 | { | ||
| 67 | struct i2c_msg msgs[2]; | ||
| 68 | int res; | ||
| 69 | |||
| 70 | if (!data || !i2c_adap) { | ||
| 71 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 72 | return -EINVAL; | ||
| 73 | } | ||
| 74 | |||
| 75 | msgs[0].addr = address; | ||
| 76 | msgs[0].flags = 0; /* write */ | ||
| 77 | msgs[0].buf = ® | ||
| 78 | msgs[0].len = 1; | ||
| 79 | |||
| 80 | msgs[1].addr = address; | ||
| 81 | msgs[1].flags = I2C_M_RD; | ||
| 82 | msgs[1].buf = data; | ||
| 83 | msgs[1].len = len; | ||
| 84 | |||
| 85 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
| 86 | if (res < 2) { | ||
| 87 | if (res >= 0) | ||
| 88 | res = -EIO; | ||
| 89 | LOG_RESULT_LOCATION(res); | ||
| 90 | return res; | ||
| 91 | } else | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
| 96 | unsigned char mpu_addr, | ||
| 97 | unsigned short mem_addr, | ||
| 98 | unsigned int len, unsigned char *data) | ||
| 99 | { | ||
| 100 | unsigned char bank[2]; | ||
| 101 | unsigned char addr[2]; | ||
| 102 | unsigned char buf; | ||
| 103 | |||
| 104 | struct i2c_msg msgs[4]; | ||
| 105 | int res; | ||
| 106 | |||
| 107 | if (!data || !i2c_adap) { | ||
| 108 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 109 | return -EINVAL; | ||
| 110 | } | ||
| 111 | |||
| 112 | bank[0] = MPUREG_BANK_SEL; | ||
| 113 | bank[1] = mem_addr >> 8; | ||
| 114 | |||
| 115 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 116 | addr[1] = mem_addr & 0xFF; | ||
| 117 | |||
| 118 | buf = MPUREG_MEM_R_W; | ||
| 119 | |||
| 120 | /* write message */ | ||
| 121 | msgs[0].addr = mpu_addr; | ||
| 122 | msgs[0].flags = 0; | ||
| 123 | msgs[0].buf = bank; | ||
| 124 | msgs[0].len = sizeof(bank); | ||
| 125 | |||
| 126 | msgs[1].addr = mpu_addr; | ||
| 127 | msgs[1].flags = 0; | ||
| 128 | msgs[1].buf = addr; | ||
| 129 | msgs[1].len = sizeof(addr); | ||
| 130 | |||
| 131 | msgs[2].addr = mpu_addr; | ||
| 132 | msgs[2].flags = 0; | ||
| 133 | msgs[2].buf = &buf; | ||
| 134 | msgs[2].len = 1; | ||
| 135 | |||
| 136 | msgs[3].addr = mpu_addr; | ||
| 137 | msgs[3].flags = I2C_M_RD; | ||
| 138 | msgs[3].buf = data; | ||
| 139 | msgs[3].len = len; | ||
| 140 | |||
| 141 | res = i2c_transfer(i2c_adap, msgs, 4); | ||
| 142 | if (res != 4) { | ||
| 143 | if (res >= 0) | ||
| 144 | res = -EIO; | ||
| 145 | LOG_RESULT_LOCATION(res); | ||
| 146 | return res; | ||
| 147 | } else | ||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
| 152 | unsigned char mpu_addr, | ||
| 153 | unsigned short mem_addr, | ||
| 154 | unsigned int len, unsigned char const *data) | ||
| 155 | { | ||
| 156 | unsigned char bank[2]; | ||
| 157 | unsigned char addr[2]; | ||
| 158 | unsigned char buf[513]; | ||
| 159 | |||
| 160 | struct i2c_msg msgs[3]; | ||
| 161 | int res; | ||
| 162 | |||
| 163 | if (!data || !i2c_adap) { | ||
| 164 | LOG_RESULT_LOCATION(-EINVAL); | ||
| 165 | return -EINVAL; | ||
| 166 | } | ||
| 167 | if (len >= (sizeof(buf) - 1)) { | ||
| 168 | LOG_RESULT_LOCATION(-ENOMEM); | ||
| 169 | return -ENOMEM; | ||
| 170 | } | ||
| 171 | |||
| 172 | bank[0] = MPUREG_BANK_SEL; | ||
| 173 | bank[1] = mem_addr >> 8; | ||
| 174 | |||
| 175 | addr[0] = MPUREG_MEM_START_ADDR; | ||
| 176 | addr[1] = mem_addr & 0xFF; | ||
| 177 | |||
| 178 | buf[0] = MPUREG_MEM_R_W; | ||
| 179 | memcpy(buf + 1, data, len); | ||
| 180 | |||
| 181 | /* write message */ | ||
| 182 | msgs[0].addr = mpu_addr; | ||
| 183 | msgs[0].flags = 0; | ||
| 184 | msgs[0].buf = bank; | ||
| 185 | msgs[0].len = sizeof(bank); | ||
| 186 | |||
| 187 | msgs[1].addr = mpu_addr; | ||
| 188 | msgs[1].flags = 0; | ||
| 189 | msgs[1].buf = addr; | ||
| 190 | msgs[1].len = sizeof(addr); | ||
| 191 | |||
| 192 | msgs[2].addr = mpu_addr; | ||
| 193 | msgs[2].flags = 0; | ||
| 194 | msgs[2].buf = (unsigned char *)buf; | ||
| 195 | msgs[2].len = len + 1; | ||
| 196 | |||
| 197 | res = i2c_transfer(i2c_adap, msgs, 3); | ||
| 198 | if (res != 3) { | ||
| 199 | if (res >= 0) | ||
| 200 | res = -EIO; | ||
| 201 | LOG_RESULT_LOCATION(res); | ||
| 202 | return res; | ||
| 203 | } else | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | int inv_serial_single_write( | ||
| 208 | void *sl_handle, | ||
| 209 | unsigned char slave_addr, | ||
| 210 | unsigned char register_addr, | ||
| 211 | unsigned char data) | ||
| 212 | { | ||
| 213 | return inv_i2c_write_register((struct i2c_adapter *)sl_handle, | ||
| 214 | slave_addr, register_addr, data); | ||
| 215 | } | ||
| 216 | EXPORT_SYMBOL(inv_serial_single_write); | ||
| 217 | |||
| 218 | int inv_serial_write( | ||
| 219 | void *sl_handle, | ||
| 220 | unsigned char slave_addr, | ||
| 221 | unsigned short length, | ||
| 222 | unsigned char const *data) | ||
| 223 | { | ||
| 224 | int result; | ||
| 225 | const unsigned short data_length = length - 1; | ||
| 226 | const unsigned char start_reg_addr = data[0]; | ||
| 227 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 228 | unsigned short bytes_written = 0; | ||
| 229 | |||
| 230 | while (bytes_written < data_length) { | ||
| 231 | unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE, | ||
| 232 | data_length - bytes_written); | ||
| 233 | if (bytes_written == 0) { | ||
| 234 | result = inv_i2c_write((struct i2c_adapter *) | ||
| 235 | sl_handle, slave_addr, | ||
| 236 | 1 + this_len, data); | ||
| 237 | } else { | ||
| 238 | /* manually increment register addr between chunks */ | ||
| 239 | i2c_write[0] = start_reg_addr + bytes_written; | ||
| 240 | memcpy(&i2c_write[1], &data[1 + bytes_written], | ||
| 241 | this_len); | ||
| 242 | result = inv_i2c_write((struct i2c_adapter *) | ||
| 243 | sl_handle, slave_addr, | ||
| 244 | 1 + this_len, i2c_write); | ||
| 245 | } | ||
| 246 | if (result) { | ||
| 247 | LOG_RESULT_LOCATION(result); | ||
| 248 | return result; | ||
| 249 | } | ||
| 250 | bytes_written += this_len; | ||
| 251 | } | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | EXPORT_SYMBOL(inv_serial_write); | ||
| 255 | |||
| 256 | int inv_serial_read( | ||
| 257 | void *sl_handle, | ||
| 258 | unsigned char slave_addr, | ||
| 259 | unsigned char register_addr, | ||
| 260 | unsigned short length, | ||
| 261 | unsigned char *data) | ||
| 262 | { | ||
| 263 | int result; | ||
| 264 | unsigned short bytes_read = 0; | ||
| 265 | |||
| 266 | if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR | ||
| 267 | && (register_addr == MPUREG_FIFO_R_W || | ||
| 268 | register_addr == MPUREG_MEM_R_W)) { | ||
| 269 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
| 270 | return INV_ERROR_INVALID_PARAMETER; | ||
| 271 | } | ||
| 272 | |||
| 273 | while (bytes_read < length) { | ||
| 274 | unsigned short this_len = | ||
| 275 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 276 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
| 277 | slave_addr, register_addr + bytes_read, | ||
| 278 | this_len, &data[bytes_read]); | ||
| 279 | if (result) { | ||
| 280 | LOG_RESULT_LOCATION(result); | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | bytes_read += this_len; | ||
| 284 | } | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | EXPORT_SYMBOL(inv_serial_read); | ||
| 288 | |||
| 289 | int inv_serial_write_mem( | ||
| 290 | void *sl_handle, | ||
| 291 | unsigned char slave_addr, | ||
| 292 | unsigned short mem_addr, | ||
| 293 | unsigned short length, | ||
| 294 | unsigned char const *data) | ||
| 295 | { | ||
| 296 | int result; | ||
| 297 | unsigned short bytes_written = 0; | ||
| 298 | |||
| 299 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 300 | pr_err("memory read length (%d B) extends beyond its" | ||
| 301 | " limits (%d) if started at location %d\n", length, | ||
| 302 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
| 303 | return INV_ERROR_INVALID_PARAMETER; | ||
| 304 | } | ||
| 305 | while (bytes_written < length) { | ||
| 306 | unsigned short this_len = | ||
| 307 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
| 308 | result = mpu_memory_write((struct i2c_adapter *)sl_handle, | ||
| 309 | slave_addr, mem_addr + bytes_written, | ||
| 310 | this_len, &data[bytes_written]); | ||
| 311 | if (result) { | ||
| 312 | LOG_RESULT_LOCATION(result); | ||
| 313 | return result; | ||
| 314 | } | ||
| 315 | bytes_written += this_len; | ||
| 316 | } | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | EXPORT_SYMBOL(inv_serial_write_mem); | ||
| 320 | |||
| 321 | int inv_serial_read_mem( | ||
| 322 | void *sl_handle, | ||
| 323 | unsigned char slave_addr, | ||
| 324 | unsigned short mem_addr, | ||
| 325 | unsigned short length, | ||
| 326 | unsigned char *data) | ||
| 327 | { | ||
| 328 | int result; | ||
| 329 | unsigned short bytes_read = 0; | ||
| 330 | |||
| 331 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
| 332 | printk | ||
| 333 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
| 334 | "if started at location %d\n", length, | ||
| 335 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
| 336 | return INV_ERROR_INVALID_PARAMETER; | ||
| 337 | } | ||
| 338 | while (bytes_read < length) { | ||
| 339 | unsigned short this_len = | ||
| 340 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 341 | result = | ||
| 342 | mpu_memory_read((struct i2c_adapter *)sl_handle, | ||
| 343 | slave_addr, mem_addr + bytes_read, | ||
| 344 | this_len, &data[bytes_read]); | ||
| 345 | if (result) { | ||
| 346 | LOG_RESULT_LOCATION(result); | ||
| 347 | return result; | ||
| 348 | } | ||
| 349 | bytes_read += this_len; | ||
| 350 | } | ||
| 351 | return 0; | ||
| 352 | } | ||
| 353 | EXPORT_SYMBOL(inv_serial_read_mem); | ||
| 354 | |||
| 355 | int inv_serial_write_fifo( | ||
| 356 | void *sl_handle, | ||
| 357 | unsigned char slave_addr, | ||
| 358 | unsigned short length, | ||
| 359 | unsigned char const *data) | ||
| 360 | { | ||
| 361 | int result; | ||
| 362 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
| 363 | unsigned short bytes_written = 0; | ||
| 364 | |||
| 365 | if (length > FIFO_HW_SIZE) { | ||
| 366 | printk(KERN_ERR | ||
| 367 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
| 368 | return INV_ERROR_INVALID_PARAMETER; | ||
| 369 | } | ||
| 370 | while (bytes_written < length) { | ||
| 371 | unsigned short this_len = | ||
| 372 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
| 373 | i2c_write[0] = MPUREG_FIFO_R_W; | ||
| 374 | memcpy(&i2c_write[1], &data[bytes_written], this_len); | ||
| 375 | result = inv_i2c_write((struct i2c_adapter *)sl_handle, | ||
| 376 | slave_addr, this_len + 1, i2c_write); | ||
| 377 | if (result) { | ||
| 378 | LOG_RESULT_LOCATION(result); | ||
| 379 | return result; | ||
| 380 | } | ||
| 381 | bytes_written += this_len; | ||
| 382 | } | ||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | EXPORT_SYMBOL(inv_serial_write_fifo); | ||
| 386 | |||
| 387 | int inv_serial_read_fifo( | ||
| 388 | void *sl_handle, | ||
| 389 | unsigned char slave_addr, | ||
| 390 | unsigned short length, | ||
| 391 | unsigned char *data) | ||
| 392 | { | ||
| 393 | int result; | ||
| 394 | unsigned short bytes_read = 0; | ||
| 395 | |||
| 396 | if (length > FIFO_HW_SIZE) { | ||
| 397 | printk(KERN_ERR | ||
| 398 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
| 399 | return INV_ERROR_INVALID_PARAMETER; | ||
| 400 | } | ||
| 401 | while (bytes_read < length) { | ||
| 402 | unsigned short this_len = | ||
| 403 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
| 404 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
| 405 | slave_addr, MPUREG_FIFO_R_W, this_len, | ||
| 406 | &data[bytes_read]); | ||
| 407 | if (result) { | ||
| 408 | LOG_RESULT_LOCATION(result); | ||
| 409 | return result; | ||
| 410 | } | ||
| 411 | bytes_read += this_len; | ||
| 412 | } | ||
| 413 | |||
| 414 | return 0; | ||
| 415 | } | ||
| 416 | EXPORT_SYMBOL(inv_serial_read_fifo); | ||
| 417 | |||
| 418 | /** | ||
| 419 | * @} | ||
| 420 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mpu-dev.c b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c new file mode 100644 index 00000000000..e171dc2a7ef --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c | |||
| @@ -0,0 +1,1309 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/i2c-dev.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/moduleparam.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/stat.h> | ||
| 27 | #include <linux/irq.h> | ||
| 28 | #include <linux/gpio.h> | ||
| 29 | #include <linux/signal.h> | ||
| 30 | #include <linux/miscdevice.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/version.h> | ||
| 33 | #include <linux/pm.h> | ||
| 34 | #include <linux/mutex.h> | ||
| 35 | #include <linux/suspend.h> | ||
| 36 | #include <linux/poll.h> | ||
| 37 | |||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/fs.h> | ||
| 40 | #include <linux/mm.h> | ||
| 41 | #include <linux/sched.h> | ||
| 42 | #include <linux/wait.h> | ||
| 43 | #include <linux/uaccess.h> | ||
| 44 | #include <linux/io.h> | ||
| 45 | |||
| 46 | #include "mpuirq.h" | ||
| 47 | #include "slaveirq.h" | ||
| 48 | #include "mlsl.h" | ||
| 49 | #include "mldl_cfg.h" | ||
| 50 | #include <linux/mpu.h> | ||
| 51 | |||
| 52 | #include "accel/mpu6050.h" | ||
| 53 | |||
| 54 | /* Platform data for the MPU */ | ||
| 55 | struct mpu_private_data { | ||
| 56 | struct miscdevice dev; | ||
| 57 | struct i2c_client *client; | ||
| 58 | |||
| 59 | /* mldl_cfg data */ | ||
| 60 | struct mldl_cfg mldl_cfg; | ||
| 61 | struct mpu_ram mpu_ram; | ||
| 62 | struct mpu_gyro_cfg mpu_gyro_cfg; | ||
| 63 | struct mpu_offsets mpu_offsets; | ||
| 64 | struct mpu_chip_info mpu_chip_info; | ||
| 65 | struct inv_mpu_cfg inv_mpu_cfg; | ||
| 66 | struct inv_mpu_state inv_mpu_state; | ||
| 67 | |||
| 68 | struct mutex mutex; | ||
| 69 | wait_queue_head_t mpu_event_wait; | ||
| 70 | struct completion completion; | ||
| 71 | struct timer_list timeout; | ||
| 72 | struct notifier_block nb; | ||
| 73 | struct mpuirq_data mpu_pm_event; | ||
| 74 | int response_timeout; /* In seconds */ | ||
| 75 | unsigned long event; | ||
| 76 | int pid; | ||
| 77 | struct module *slave_modules[EXT_SLAVE_NUM_TYPES]; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct mpu_private_data *mpu_private_data; | ||
| 81 | |||
| 82 | static void mpu_pm_timeout(u_long data) | ||
| 83 | { | ||
| 84 | struct mpu_private_data *mpu = (struct mpu_private_data *)data; | ||
| 85 | struct i2c_client *client = mpu->client; | ||
| 86 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 87 | complete(&mpu->completion); | ||
| 88 | } | ||
| 89 | |||
| 90 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
| 91 | unsigned long event, void *unused) | ||
| 92 | { | ||
| 93 | struct mpu_private_data *mpu = | ||
| 94 | container_of(nb, struct mpu_private_data, nb); | ||
| 95 | struct i2c_client *client = mpu->client; | ||
| 96 | struct timeval event_time; | ||
| 97 | dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event); | ||
| 98 | |||
| 99 | /* Prevent the file handle from being closed before we initialize | ||
| 100 | the completion event */ | ||
| 101 | mutex_lock(&mpu->mutex); | ||
| 102 | if (!(mpu->pid) || | ||
| 103 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
| 104 | mutex_unlock(&mpu->mutex); | ||
| 105 | return NOTIFY_OK; | ||
| 106 | } | ||
| 107 | |||
| 108 | if (event == PM_SUSPEND_PREPARE) | ||
| 109 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
| 110 | if (event == PM_POST_SUSPEND) | ||
| 111 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
| 112 | |||
| 113 | do_gettimeofday(&event_time); | ||
| 114 | mpu->mpu_pm_event.interruptcount++; | ||
| 115 | mpu->mpu_pm_event.irqtime = | ||
| 116 | (((long long)event_time.tv_sec) << 32) + event_time.tv_usec; | ||
| 117 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
| 118 | mpu->mpu_pm_event.data = mpu->event; | ||
| 119 | |||
| 120 | if (mpu->response_timeout > 0) { | ||
| 121 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
| 122 | add_timer(&mpu->timeout); | ||
| 123 | } | ||
| 124 | INIT_COMPLETION(mpu->completion); | ||
| 125 | mutex_unlock(&mpu->mutex); | ||
| 126 | |||
| 127 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
| 128 | wait_for_completion(&mpu->completion); | ||
| 129 | del_timer_sync(&mpu->timeout); | ||
| 130 | dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
| 131 | return NOTIFY_OK; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int mpu_dev_open(struct inode *inode, struct file *file) | ||
| 135 | { | ||
| 136 | struct mpu_private_data *mpu = | ||
| 137 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 138 | struct i2c_client *client = mpu->client; | ||
| 139 | int result; | ||
| 140 | int ii; | ||
| 141 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 142 | dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid); | ||
| 143 | |||
| 144 | result = mutex_lock_interruptible(&mpu->mutex); | ||
| 145 | if (mpu->pid) { | ||
| 146 | mutex_unlock(&mpu->mutex); | ||
| 147 | return -EBUSY; | ||
| 148 | } | ||
| 149 | mpu->pid = current->pid; | ||
| 150 | |||
| 151 | /* Reset the sensors to the default */ | ||
| 152 | if (result) { | ||
| 153 | dev_err(&client->adapter->dev, | ||
| 154 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 155 | __func__, result); | ||
| 156 | return result; | ||
| 157 | } | ||
| 158 | |||
| 159 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
| 160 | __module_get(mpu->slave_modules[ii]); | ||
| 161 | |||
| 162 | mutex_unlock(&mpu->mutex); | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
| 167 | static int mpu_release(struct inode *inode, struct file *file) | ||
| 168 | { | ||
| 169 | struct mpu_private_data *mpu = | ||
| 170 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 171 | struct i2c_client *client = mpu->client; | ||
| 172 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 173 | int result = 0; | ||
| 174 | int ii; | ||
| 175 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 176 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 177 | |||
| 178 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 179 | if (!pdata_slave[ii]) | ||
| 180 | slave_adapter[ii] = NULL; | ||
| 181 | else | ||
| 182 | slave_adapter[ii] = | ||
| 183 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 184 | } | ||
| 185 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 186 | |||
| 187 | mutex_lock(&mpu->mutex); | ||
| 188 | mldl_cfg->inv_mpu_cfg->requested_sensors = 0; | ||
| 189 | result = inv_mpu_suspend(mldl_cfg, | ||
| 190 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 191 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 192 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 193 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 194 | INV_ALL_SENSORS); | ||
| 195 | mpu->pid = 0; | ||
| 196 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
| 197 | module_put(mpu->slave_modules[ii]); | ||
| 198 | |||
| 199 | mutex_unlock(&mpu->mutex); | ||
| 200 | complete(&mpu->completion); | ||
| 201 | dev_dbg(&client->adapter->dev, "mpu_release\n"); | ||
| 202 | |||
| 203 | return result; | ||
| 204 | } | ||
| 205 | |||
| 206 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
| 207 | static ssize_t mpu_read(struct file *file, | ||
| 208 | char __user *buf, size_t count, loff_t *offset) | ||
| 209 | { | ||
| 210 | struct mpu_private_data *mpu = | ||
| 211 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 212 | struct i2c_client *client = mpu->client; | ||
| 213 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
| 214 | int err; | ||
| 215 | |||
| 216 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
| 217 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
| 218 | |||
| 219 | if (!mpu->event || !buf | ||
| 220 | || count < sizeof(mpu->mpu_pm_event)) | ||
| 221 | return 0; | ||
| 222 | |||
| 223 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
| 224 | if (err) { | ||
| 225 | dev_err(&client->adapter->dev, | ||
| 226 | "Copy to user returned %d\n", err); | ||
| 227 | return -EFAULT; | ||
| 228 | } | ||
| 229 | mpu->event = 0; | ||
| 230 | return len; | ||
| 231 | } | ||
| 232 | |||
| 233 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
| 234 | { | ||
| 235 | struct mpu_private_data *mpu = | ||
| 236 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 237 | int mask = 0; | ||
| 238 | |||
| 239 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
| 240 | if (mpu->event) | ||
| 241 | mask |= POLLIN | POLLRDNORM; | ||
| 242 | return mask; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int mpu_dev_ioctl_get_ext_slave_platform_data( | ||
| 246 | struct i2c_client *client, | ||
| 247 | struct ext_slave_platform_data __user *arg) | ||
| 248 | { | ||
| 249 | struct mpu_private_data *mpu = | ||
| 250 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 251 | struct ext_slave_platform_data *pdata_slave; | ||
| 252 | struct ext_slave_platform_data local_pdata_slave; | ||
| 253 | |||
| 254 | if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave))) | ||
| 255 | return -EFAULT; | ||
| 256 | |||
| 257 | if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
| 258 | return -EINVAL; | ||
| 259 | |||
| 260 | pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type]; | ||
| 261 | /* All but private data and irq_data */ | ||
| 262 | if (!pdata_slave) | ||
| 263 | return -ENODEV; | ||
| 264 | if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave))) | ||
| 265 | return -EFAULT; | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | static int mpu_dev_ioctl_get_mpu_platform_data( | ||
| 270 | struct i2c_client *client, | ||
| 271 | struct mpu_platform_data __user *arg) | ||
| 272 | { | ||
| 273 | struct mpu_private_data *mpu = | ||
| 274 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 275 | struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
| 276 | |||
| 277 | if (copy_to_user(arg, pdata, sizeof(*pdata))) | ||
| 278 | return -EFAULT; | ||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int mpu_dev_ioctl_get_ext_slave_descr( | ||
| 283 | struct i2c_client *client, | ||
| 284 | struct ext_slave_descr __user *arg) | ||
| 285 | { | ||
| 286 | struct mpu_private_data *mpu = | ||
| 287 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 288 | struct ext_slave_descr *slave; | ||
| 289 | struct ext_slave_descr local_slave; | ||
| 290 | |||
| 291 | if (copy_from_user(&local_slave, arg, sizeof(local_slave))) | ||
| 292 | return -EFAULT; | ||
| 293 | |||
| 294 | if (local_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
| 295 | return -EINVAL; | ||
| 296 | |||
| 297 | slave = mpu->mldl_cfg.slave[local_slave.type]; | ||
| 298 | /* All but private data and irq_data */ | ||
| 299 | if (!slave) | ||
| 300 | return -ENODEV; | ||
| 301 | if (copy_to_user(arg, slave, sizeof(*slave))) | ||
| 302 | return -EFAULT; | ||
| 303 | return 0; | ||
| 304 | } | ||
| 305 | |||
| 306 | |||
| 307 | /** | ||
| 308 | * slave_config() - Pass a requested slave configuration to the slave sensor | ||
| 309 | * | ||
| 310 | * @adapter the adaptor to use to communicate with the slave | ||
| 311 | * @mldl_cfg the mldl configuration structuer | ||
| 312 | * @slave pointer to the slave descriptor | ||
| 313 | * @usr_config The configuration to pass to the slave sensor | ||
| 314 | * | ||
| 315 | * returns 0 or non-zero error code | ||
| 316 | */ | ||
| 317 | static int inv_mpu_config(struct mldl_cfg *mldl_cfg, | ||
| 318 | void *gyro_adapter, | ||
| 319 | struct ext_slave_config __user *usr_config) | ||
| 320 | { | ||
| 321 | int retval = 0; | ||
| 322 | struct ext_slave_config config; | ||
| 323 | |||
| 324 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 325 | if (retval) | ||
| 326 | return -EFAULT; | ||
| 327 | |||
| 328 | if (config.len && config.data) { | ||
| 329 | void *data; | ||
| 330 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 331 | if (!data) | ||
| 332 | return -ENOMEM; | ||
| 333 | |||
| 334 | retval = copy_from_user(data, | ||
| 335 | (void __user *)config.data, config.len); | ||
| 336 | if (retval) { | ||
| 337 | retval = -EFAULT; | ||
| 338 | kfree(data); | ||
| 339 | return retval; | ||
| 340 | } | ||
| 341 | config.data = data; | ||
| 342 | } | ||
| 343 | retval = gyro_config(gyro_adapter, mldl_cfg, &config); | ||
| 344 | kfree(config.data); | ||
| 345 | return retval; | ||
| 346 | } | ||
| 347 | |||
| 348 | static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg, | ||
| 349 | void *gyro_adapter, | ||
| 350 | struct ext_slave_config __user *usr_config) | ||
| 351 | { | ||
| 352 | int retval = 0; | ||
| 353 | struct ext_slave_config config; | ||
| 354 | void *user_data; | ||
| 355 | |||
| 356 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 357 | if (retval) | ||
| 358 | return -EFAULT; | ||
| 359 | |||
| 360 | user_data = config.data; | ||
| 361 | if (config.len && config.data) { | ||
| 362 | void *data; | ||
| 363 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 364 | if (!data) | ||
| 365 | return -ENOMEM; | ||
| 366 | |||
| 367 | retval = copy_from_user(data, | ||
| 368 | (void __user *)config.data, config.len); | ||
| 369 | if (retval) { | ||
| 370 | retval = -EFAULT; | ||
| 371 | kfree(data); | ||
| 372 | return retval; | ||
| 373 | } | ||
| 374 | config.data = data; | ||
| 375 | } | ||
| 376 | retval = gyro_get_config(gyro_adapter, mldl_cfg, &config); | ||
| 377 | if (!retval) | ||
| 378 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 379 | config.data, config.len); | ||
| 380 | kfree(config.data); | ||
| 381 | return retval; | ||
| 382 | } | ||
| 383 | |||
| 384 | static int slave_config(struct mldl_cfg *mldl_cfg, | ||
| 385 | void *gyro_adapter, | ||
| 386 | void *slave_adapter, | ||
| 387 | struct ext_slave_descr *slave, | ||
| 388 | struct ext_slave_platform_data *pdata, | ||
| 389 | struct ext_slave_config __user *usr_config) | ||
| 390 | { | ||
| 391 | int retval = 0; | ||
| 392 | struct ext_slave_config config; | ||
| 393 | if ((!slave) || (!slave->config)) | ||
| 394 | return -ENODEV; | ||
| 395 | |||
| 396 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 397 | if (retval) | ||
| 398 | return -EFAULT; | ||
| 399 | |||
| 400 | if (config.len && config.data) { | ||
| 401 | void *data; | ||
| 402 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 403 | if (!data) | ||
| 404 | return -ENOMEM; | ||
| 405 | |||
| 406 | retval = copy_from_user(data, | ||
| 407 | (void __user *)config.data, config.len); | ||
| 408 | if (retval) { | ||
| 409 | retval = -EFAULT; | ||
| 410 | kfree(data); | ||
| 411 | return retval; | ||
| 412 | } | ||
| 413 | config.data = data; | ||
| 414 | } | ||
| 415 | retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter, | ||
| 416 | &config, slave, pdata); | ||
| 417 | kfree(config.data); | ||
| 418 | return retval; | ||
| 419 | } | ||
| 420 | |||
| 421 | static int slave_get_config(struct mldl_cfg *mldl_cfg, | ||
| 422 | void *gyro_adapter, | ||
| 423 | void *slave_adapter, | ||
| 424 | struct ext_slave_descr *slave, | ||
| 425 | struct ext_slave_platform_data *pdata, | ||
| 426 | struct ext_slave_config __user *usr_config) | ||
| 427 | { | ||
| 428 | int retval = 0; | ||
| 429 | struct ext_slave_config config; | ||
| 430 | void *user_data; | ||
| 431 | if (!(slave) || !(slave->get_config)) | ||
| 432 | return -ENODEV; | ||
| 433 | |||
| 434 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
| 435 | if (retval) | ||
| 436 | return -EFAULT; | ||
| 437 | |||
| 438 | user_data = config.data; | ||
| 439 | if (config.len && config.data) { | ||
| 440 | void *data; | ||
| 441 | data = kmalloc(config.len, GFP_KERNEL); | ||
| 442 | if (!data) | ||
| 443 | return -ENOMEM; | ||
| 444 | |||
| 445 | retval = copy_from_user(data, | ||
| 446 | (void __user *)config.data, config.len); | ||
| 447 | if (retval) { | ||
| 448 | retval = -EFAULT; | ||
| 449 | kfree(data); | ||
| 450 | return retval; | ||
| 451 | } | ||
| 452 | config.data = data; | ||
| 453 | } | ||
| 454 | retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter, | ||
| 455 | slave_adapter, &config, slave, pdata); | ||
| 456 | if (retval) { | ||
| 457 | kfree(config.data); | ||
| 458 | return retval; | ||
| 459 | } | ||
| 460 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 461 | config.data, config.len); | ||
| 462 | kfree(config.data); | ||
| 463 | return retval; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int inv_slave_read(struct mldl_cfg *mldl_cfg, | ||
| 467 | void *gyro_adapter, | ||
| 468 | void *slave_adapter, | ||
| 469 | struct ext_slave_descr *slave, | ||
| 470 | struct ext_slave_platform_data *pdata, | ||
| 471 | void __user *usr_data) | ||
| 472 | { | ||
| 473 | int retval; | ||
| 474 | unsigned char *data; | ||
| 475 | data = kzalloc(slave->read_len, GFP_KERNEL); | ||
| 476 | if (!data) | ||
| 477 | return -EFAULT; | ||
| 478 | |||
| 479 | retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter, | ||
| 480 | slave, pdata, data); | ||
| 481 | |||
| 482 | if ((!retval) && | ||
| 483 | (copy_to_user((unsigned char __user *)usr_data, | ||
| 484 | data, slave->read_len))) | ||
| 485 | retval = -EFAULT; | ||
| 486 | |||
| 487 | kfree(data); | ||
| 488 | return retval; | ||
| 489 | } | ||
| 490 | |||
| 491 | static int mpu_handle_mlsl(void *sl_handle, | ||
| 492 | unsigned char addr, | ||
| 493 | unsigned int cmd, | ||
| 494 | struct mpu_read_write __user *usr_msg) | ||
| 495 | { | ||
| 496 | int retval = 0; | ||
| 497 | struct mpu_read_write msg; | ||
| 498 | unsigned char *user_data; | ||
| 499 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
| 500 | if (retval) | ||
| 501 | return -EFAULT; | ||
| 502 | |||
| 503 | user_data = msg.data; | ||
| 504 | if (msg.length && msg.data) { | ||
| 505 | unsigned char *data; | ||
| 506 | data = kmalloc(msg.length, GFP_KERNEL); | ||
| 507 | if (!data) | ||
| 508 | return -ENOMEM; | ||
| 509 | |||
| 510 | retval = copy_from_user(data, | ||
| 511 | (void __user *)msg.data, msg.length); | ||
| 512 | if (retval) { | ||
| 513 | retval = -EFAULT; | ||
| 514 | kfree(data); | ||
| 515 | return retval; | ||
| 516 | } | ||
| 517 | msg.data = data; | ||
| 518 | } else { | ||
| 519 | return -EPERM; | ||
| 520 | } | ||
| 521 | |||
| 522 | switch (cmd) { | ||
| 523 | case MPU_READ: | ||
| 524 | retval = inv_serial_read(sl_handle, addr, | ||
| 525 | msg.address, msg.length, msg.data); | ||
| 526 | break; | ||
| 527 | case MPU_WRITE: | ||
| 528 | retval = inv_serial_write(sl_handle, addr, | ||
| 529 | msg.length, msg.data); | ||
| 530 | break; | ||
| 531 | case MPU_READ_MEM: | ||
| 532 | retval = inv_serial_read_mem(sl_handle, addr, | ||
| 533 | msg.address, msg.length, msg.data); | ||
| 534 | break; | ||
| 535 | case MPU_WRITE_MEM: | ||
| 536 | retval = inv_serial_write_mem(sl_handle, addr, | ||
| 537 | msg.address, msg.length, | ||
| 538 | msg.data); | ||
| 539 | break; | ||
| 540 | case MPU_READ_FIFO: | ||
| 541 | retval = inv_serial_read_fifo(sl_handle, addr, | ||
| 542 | msg.length, msg.data); | ||
| 543 | break; | ||
| 544 | case MPU_WRITE_FIFO: | ||
| 545 | retval = inv_serial_write_fifo(sl_handle, addr, | ||
| 546 | msg.length, msg.data); | ||
| 547 | break; | ||
| 548 | |||
| 549 | }; | ||
| 550 | if (retval) { | ||
| 551 | dev_err(&((struct i2c_adapter *)sl_handle)->dev, | ||
| 552 | "%s: i2c %d error %d\n", | ||
| 553 | __func__, cmd, retval); | ||
| 554 | kfree(msg.data); | ||
| 555 | return retval; | ||
| 556 | } | ||
| 557 | retval = copy_to_user((unsigned char __user *)user_data, | ||
| 558 | msg.data, msg.length); | ||
| 559 | kfree(msg.data); | ||
| 560 | return retval; | ||
| 561 | } | ||
| 562 | |||
| 563 | /* ioctl - I/O control */ | ||
| 564 | static long mpu_dev_ioctl(struct file *file, | ||
| 565 | unsigned int cmd, unsigned long arg) | ||
| 566 | { | ||
| 567 | struct mpu_private_data *mpu = | ||
| 568 | container_of(file->private_data, struct mpu_private_data, dev); | ||
| 569 | struct i2c_client *client = mpu->client; | ||
| 570 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 571 | int retval = 0; | ||
| 572 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 573 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
| 574 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 575 | int ii; | ||
| 576 | |||
| 577 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 578 | if (!pdata_slave[ii]) | ||
| 579 | slave_adapter[ii] = NULL; | ||
| 580 | else | ||
| 581 | slave_adapter[ii] = | ||
| 582 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 583 | } | ||
| 584 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 585 | |||
| 586 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
| 587 | if (retval) { | ||
| 588 | dev_err(&client->adapter->dev, | ||
| 589 | "%s: mutex_lock_interruptible returned %d\n", | ||
| 590 | __func__, retval); | ||
| 591 | return retval; | ||
| 592 | } | ||
| 593 | |||
| 594 | switch (cmd) { | ||
| 595 | case MPU_GET_EXT_SLAVE_PLATFORM_DATA: | ||
| 596 | retval = mpu_dev_ioctl_get_ext_slave_platform_data( | ||
| 597 | client, | ||
| 598 | (struct ext_slave_platform_data __user *)arg); | ||
| 599 | break; | ||
| 600 | case MPU_GET_MPU_PLATFORM_DATA: | ||
| 601 | retval = mpu_dev_ioctl_get_mpu_platform_data( | ||
| 602 | client, | ||
| 603 | (struct mpu_platform_data __user *)arg); | ||
| 604 | break; | ||
| 605 | case MPU_GET_EXT_SLAVE_DESCR: | ||
| 606 | retval = mpu_dev_ioctl_get_ext_slave_descr( | ||
| 607 | client, | ||
| 608 | (struct ext_slave_descr __user *)arg); | ||
| 609 | break; | ||
| 610 | case MPU_READ: | ||
| 611 | case MPU_WRITE: | ||
| 612 | case MPU_READ_MEM: | ||
| 613 | case MPU_WRITE_MEM: | ||
| 614 | case MPU_READ_FIFO: | ||
| 615 | case MPU_WRITE_FIFO: | ||
| 616 | retval = mpu_handle_mlsl( | ||
| 617 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 618 | mldl_cfg->mpu_chip_info->addr, cmd, | ||
| 619 | (struct mpu_read_write __user *)arg); | ||
| 620 | break; | ||
| 621 | case MPU_CONFIG_GYRO: | ||
| 622 | retval = inv_mpu_config( | ||
| 623 | mldl_cfg, | ||
| 624 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 625 | (struct ext_slave_config __user *)arg); | ||
| 626 | break; | ||
| 627 | case MPU_CONFIG_ACCEL: | ||
| 628 | retval = slave_config( | ||
| 629 | mldl_cfg, | ||
| 630 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 631 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 632 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 633 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 634 | (struct ext_slave_config __user *)arg); | ||
| 635 | break; | ||
| 636 | case MPU_CONFIG_COMPASS: | ||
| 637 | retval = slave_config( | ||
| 638 | mldl_cfg, | ||
| 639 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 640 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 641 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 642 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 643 | (struct ext_slave_config __user *)arg); | ||
| 644 | break; | ||
| 645 | case MPU_CONFIG_PRESSURE: | ||
| 646 | retval = slave_config( | ||
| 647 | mldl_cfg, | ||
| 648 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 649 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 650 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 651 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 652 | (struct ext_slave_config __user *)arg); | ||
| 653 | break; | ||
| 654 | case MPU_GET_CONFIG_GYRO: | ||
| 655 | retval = inv_mpu_get_config( | ||
| 656 | mldl_cfg, | ||
| 657 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 658 | (struct ext_slave_config __user *)arg); | ||
| 659 | break; | ||
| 660 | case MPU_GET_CONFIG_ACCEL: | ||
| 661 | retval = slave_get_config( | ||
| 662 | mldl_cfg, | ||
| 663 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 664 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 665 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 666 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 667 | (struct ext_slave_config __user *)arg); | ||
| 668 | break; | ||
| 669 | case MPU_GET_CONFIG_COMPASS: | ||
| 670 | retval = slave_get_config( | ||
| 671 | mldl_cfg, | ||
| 672 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 673 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 674 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 675 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 676 | (struct ext_slave_config __user *)arg); | ||
| 677 | break; | ||
| 678 | case MPU_GET_CONFIG_PRESSURE: | ||
| 679 | retval = slave_get_config( | ||
| 680 | mldl_cfg, | ||
| 681 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 682 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 683 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 684 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 685 | (struct ext_slave_config __user *)arg); | ||
| 686 | break; | ||
| 687 | case MPU_SUSPEND: | ||
| 688 | retval = inv_mpu_suspend( | ||
| 689 | mldl_cfg, | ||
| 690 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 691 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 692 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 693 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 694 | arg); | ||
| 695 | break; | ||
| 696 | case MPU_RESUME: | ||
| 697 | retval = inv_mpu_resume( | ||
| 698 | mldl_cfg, | ||
| 699 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 700 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 701 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 702 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 703 | arg); | ||
| 704 | break; | ||
| 705 | case MPU_PM_EVENT_HANDLED: | ||
| 706 | dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd); | ||
| 707 | complete(&mpu->completion); | ||
| 708 | break; | ||
| 709 | case MPU_READ_ACCEL: | ||
| 710 | retval = inv_slave_read( | ||
| 711 | mldl_cfg, | ||
| 712 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 713 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 714 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 715 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 716 | (unsigned char __user *)arg); | ||
| 717 | break; | ||
| 718 | case MPU_READ_COMPASS: | ||
| 719 | retval = inv_slave_read( | ||
| 720 | mldl_cfg, | ||
| 721 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 722 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 723 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 724 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
| 725 | (unsigned char __user *)arg); | ||
| 726 | break; | ||
| 727 | case MPU_READ_PRESSURE: | ||
| 728 | retval = inv_slave_read( | ||
| 729 | mldl_cfg, | ||
| 730 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 731 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 732 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 733 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
| 734 | (unsigned char __user *)arg); | ||
| 735 | break; | ||
| 736 | case MPU_GET_REQUESTED_SENSORS: | ||
| 737 | if (copy_to_user( | ||
| 738 | (__u32 __user *)arg, | ||
| 739 | &mldl_cfg->inv_mpu_cfg->requested_sensors, | ||
| 740 | sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors))) | ||
| 741 | retval = -EFAULT; | ||
| 742 | break; | ||
| 743 | case MPU_SET_REQUESTED_SENSORS: | ||
| 744 | mldl_cfg->inv_mpu_cfg->requested_sensors = arg; | ||
| 745 | break; | ||
| 746 | case MPU_GET_IGNORE_SYSTEM_SUSPEND: | ||
| 747 | if (copy_to_user( | ||
| 748 | (unsigned char __user *)arg, | ||
| 749 | &mldl_cfg->inv_mpu_cfg->ignore_system_suspend, | ||
| 750 | sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend))) | ||
| 751 | retval = -EFAULT; | ||
| 752 | break; | ||
| 753 | case MPU_SET_IGNORE_SYSTEM_SUSPEND: | ||
| 754 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg; | ||
| 755 | break; | ||
| 756 | case MPU_GET_MLDL_STATUS: | ||
| 757 | if (copy_to_user( | ||
| 758 | (unsigned char __user *)arg, | ||
| 759 | &mldl_cfg->inv_mpu_state->status, | ||
| 760 | sizeof(mldl_cfg->inv_mpu_state->status))) | ||
| 761 | retval = -EFAULT; | ||
| 762 | break; | ||
| 763 | case MPU_GET_I2C_SLAVES_ENABLED: | ||
| 764 | if (copy_to_user( | ||
| 765 | (unsigned char __user *)arg, | ||
| 766 | &mldl_cfg->inv_mpu_state->i2c_slaves_enabled, | ||
| 767 | sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled))) | ||
| 768 | retval = -EFAULT; | ||
| 769 | break; | ||
| 770 | default: | ||
| 771 | dev_err(&client->adapter->dev, | ||
| 772 | "%s: Unknown cmd %x, arg %lu\n", | ||
| 773 | __func__, cmd, arg); | ||
| 774 | retval = -EINVAL; | ||
| 775 | }; | ||
| 776 | |||
| 777 | mutex_unlock(&mpu->mutex); | ||
| 778 | dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n", | ||
| 779 | __func__, cmd, arg, retval); | ||
| 780 | |||
| 781 | if (retval > 0) | ||
| 782 | retval = -retval; | ||
| 783 | |||
| 784 | return retval; | ||
| 785 | } | ||
| 786 | |||
| 787 | void mpu_shutdown(struct i2c_client *client) | ||
| 788 | { | ||
| 789 | struct mpu_private_data *mpu = | ||
| 790 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 791 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 792 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 793 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 794 | int ii; | ||
| 795 | |||
| 796 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 797 | if (!pdata_slave[ii]) | ||
| 798 | slave_adapter[ii] = NULL; | ||
| 799 | else | ||
| 800 | slave_adapter[ii] = | ||
| 801 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 802 | } | ||
| 803 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 804 | |||
| 805 | mutex_lock(&mpu->mutex); | ||
| 806 | (void)inv_mpu_suspend(mldl_cfg, | ||
| 807 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 808 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 809 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 810 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 811 | INV_ALL_SENSORS); | ||
| 812 | mutex_unlock(&mpu->mutex); | ||
| 813 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 814 | } | ||
| 815 | |||
| 816 | int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg) | ||
| 817 | { | ||
| 818 | struct mpu_private_data *mpu = | ||
| 819 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 820 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 821 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 822 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 823 | int ii; | ||
| 824 | |||
| 825 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 826 | if (!pdata_slave[ii]) | ||
| 827 | slave_adapter[ii] = NULL; | ||
| 828 | else | ||
| 829 | slave_adapter[ii] = | ||
| 830 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 831 | } | ||
| 832 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 833 | |||
| 834 | mutex_lock(&mpu->mutex); | ||
| 835 | if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
| 836 | dev_dbg(&client->adapter->dev, | ||
| 837 | "%s: suspending on event %d\n", __func__, mesg.event); | ||
| 838 | (void)inv_mpu_suspend(mldl_cfg, | ||
| 839 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 840 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 841 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 842 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 843 | INV_ALL_SENSORS); | ||
| 844 | } else { | ||
| 845 | dev_dbg(&client->adapter->dev, | ||
| 846 | "%s: Already suspended %d\n", __func__, mesg.event); | ||
| 847 | } | ||
| 848 | mutex_unlock(&mpu->mutex); | ||
| 849 | return 0; | ||
| 850 | } | ||
| 851 | |||
| 852 | int mpu_dev_resume(struct i2c_client *client) | ||
| 853 | { | ||
| 854 | struct mpu_private_data *mpu = | ||
| 855 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
| 856 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 857 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 858 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 859 | int ii; | ||
| 860 | |||
| 861 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 862 | if (!pdata_slave[ii]) | ||
| 863 | slave_adapter[ii] = NULL; | ||
| 864 | else | ||
| 865 | slave_adapter[ii] = | ||
| 866 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 867 | } | ||
| 868 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 869 | |||
| 870 | mutex_lock(&mpu->mutex); | ||
| 871 | if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
| 872 | (void)inv_mpu_resume(mldl_cfg, | ||
| 873 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 874 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 875 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 876 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
| 877 | mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
| 878 | dev_dbg(&client->adapter->dev, | ||
| 879 | "%s for pid %d\n", __func__, mpu->pid); | ||
| 880 | } | ||
| 881 | mutex_unlock(&mpu->mutex); | ||
| 882 | return 0; | ||
| 883 | } | ||
| 884 | |||
| 885 | /* define which file operations are supported */ | ||
| 886 | static const struct file_operations mpu_fops = { | ||
| 887 | .owner = THIS_MODULE, | ||
| 888 | .read = mpu_read, | ||
| 889 | .poll = mpu_poll, | ||
| 890 | .unlocked_ioctl = mpu_dev_ioctl, | ||
| 891 | .open = mpu_dev_open, | ||
| 892 | .release = mpu_release, | ||
| 893 | }; | ||
| 894 | |||
| 895 | int inv_mpu_register_slave(struct module *slave_module, | ||
| 896 | struct i2c_client *slave_client, | ||
| 897 | struct ext_slave_platform_data *slave_pdata, | ||
| 898 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
| 899 | { | ||
| 900 | struct mpu_private_data *mpu = mpu_private_data; | ||
| 901 | struct mldl_cfg *mldl_cfg; | ||
| 902 | struct ext_slave_descr *slave_descr; | ||
| 903 | struct ext_slave_platform_data **pdata_slave; | ||
| 904 | char *irq_name = NULL; | ||
| 905 | int result = 0; | ||
| 906 | |||
| 907 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
| 908 | return -EINVAL; | ||
| 909 | |||
| 910 | if (!mpu) { | ||
| 911 | dev_err(&slave_client->adapter->dev, | ||
| 912 | "%s: Null mpu_private_data\n", __func__); | ||
| 913 | return -EINVAL; | ||
| 914 | } | ||
| 915 | mldl_cfg = &mpu->mldl_cfg; | ||
| 916 | pdata_slave = mldl_cfg->pdata_slave; | ||
| 917 | slave_descr = get_slave_descr(); | ||
| 918 | |||
| 919 | if (!slave_descr) { | ||
| 920 | dev_err(&slave_client->adapter->dev, | ||
| 921 | "%s: Null ext_slave_descr\n", __func__); | ||
| 922 | return -EINVAL; | ||
| 923 | } | ||
| 924 | |||
| 925 | mutex_lock(&mpu->mutex); | ||
| 926 | if (mpu->pid) { | ||
| 927 | mutex_unlock(&mpu->mutex); | ||
| 928 | return -EBUSY; | ||
| 929 | } | ||
| 930 | |||
| 931 | if (pdata_slave[slave_descr->type]) { | ||
| 932 | result = -EBUSY; | ||
| 933 | goto out_unlock_mutex; | ||
| 934 | } | ||
| 935 | |||
| 936 | slave_pdata->address = slave_client->addr; | ||
| 937 | slave_pdata->irq = slave_client->irq; | ||
| 938 | slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter); | ||
| 939 | |||
| 940 | dev_info(&slave_client->adapter->dev, | ||
| 941 | "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n", | ||
| 942 | __func__, | ||
| 943 | slave_descr->name, | ||
| 944 | slave_descr->type, | ||
| 945 | slave_pdata->address, | ||
| 946 | slave_pdata->irq, | ||
| 947 | slave_pdata->adapt_num); | ||
| 948 | |||
| 949 | switch (slave_descr->type) { | ||
| 950 | case EXT_SLAVE_TYPE_ACCEL: | ||
| 951 | irq_name = "accelirq"; | ||
| 952 | break; | ||
| 953 | case EXT_SLAVE_TYPE_COMPASS: | ||
| 954 | irq_name = "compassirq"; | ||
| 955 | break; | ||
| 956 | case EXT_SLAVE_TYPE_PRESSURE: | ||
| 957 | irq_name = "pressureirq"; | ||
| 958 | break; | ||
| 959 | default: | ||
| 960 | irq_name = "none"; | ||
| 961 | }; | ||
| 962 | if (slave_descr->init) { | ||
| 963 | result = slave_descr->init(slave_client->adapter, | ||
| 964 | slave_descr, | ||
| 965 | slave_pdata); | ||
| 966 | if (result) { | ||
| 967 | dev_err(&slave_client->adapter->dev, | ||
| 968 | "%s init failed %d\n", | ||
| 969 | slave_descr->name, result); | ||
| 970 | goto out_unlock_mutex; | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | if (slave_descr->type == EXT_SLAVE_TYPE_ACCEL && | ||
| 975 | slave_descr->id == ACCEL_ID_MPU6050 && | ||
| 976 | slave_descr->config) { | ||
| 977 | /* pass a reference to the mldl_cfg data | ||
| 978 | structure to the mpu6050 accel "class" */ | ||
| 979 | struct ext_slave_config config; | ||
| 980 | config.key = MPU_SLAVE_CONFIG_INTERNAL_REFERENCE; | ||
| 981 | config.len = sizeof(struct mldl_cfg *); | ||
| 982 | config.apply = true; | ||
| 983 | config.data = mldl_cfg; | ||
| 984 | result = slave_descr->config( | ||
| 985 | slave_client->adapter, slave_descr, | ||
| 986 | slave_pdata, &config); | ||
| 987 | if (result) { | ||
| 988 | LOG_RESULT_LOCATION(result); | ||
| 989 | goto out_slavedescr_exit; | ||
| 990 | } | ||
| 991 | } | ||
| 992 | pdata_slave[slave_descr->type] = slave_pdata; | ||
| 993 | mpu->slave_modules[slave_descr->type] = slave_module; | ||
| 994 | mldl_cfg->slave[slave_descr->type] = slave_descr; | ||
| 995 | |||
| 996 | goto out_unlock_mutex; | ||
| 997 | |||
| 998 | out_slavedescr_exit: | ||
| 999 | if (slave_descr->exit) | ||
| 1000 | slave_descr->exit(slave_client->adapter, | ||
| 1001 | slave_descr, slave_pdata); | ||
| 1002 | out_unlock_mutex: | ||
| 1003 | mutex_unlock(&mpu->mutex); | ||
| 1004 | |||
| 1005 | if (!result && irq_name && (slave_pdata->irq > 0)) { | ||
| 1006 | int warn_result; | ||
| 1007 | dev_info(&slave_client->adapter->dev, | ||
| 1008 | "Installing %s irq using %d\n", | ||
| 1009 | irq_name, | ||
| 1010 | slave_pdata->irq); | ||
| 1011 | warn_result = slaveirq_init(slave_client->adapter, | ||
| 1012 | slave_pdata, irq_name); | ||
| 1013 | if (result) | ||
| 1014 | dev_WARN(&slave_client->adapter->dev, | ||
| 1015 | "%s irq assigned error: %d\n", | ||
| 1016 | slave_descr->name, warn_result); | ||
| 1017 | } else { | ||
| 1018 | dev_WARN(&slave_client->adapter->dev, | ||
| 1019 | "%s irq not assigned: %d %d %d\n", | ||
| 1020 | slave_descr->name, | ||
| 1021 | result, (int)irq_name, slave_pdata->irq); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | return result; | ||
| 1025 | } | ||
| 1026 | EXPORT_SYMBOL(inv_mpu_register_slave); | ||
| 1027 | |||
| 1028 | void inv_mpu_unregister_slave(struct i2c_client *slave_client, | ||
| 1029 | struct ext_slave_platform_data *slave_pdata, | ||
| 1030 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
| 1031 | { | ||
| 1032 | struct mpu_private_data *mpu = mpu_private_data; | ||
| 1033 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 1034 | struct ext_slave_descr *slave_descr; | ||
| 1035 | int result; | ||
| 1036 | |||
| 1037 | dev_info(&slave_client->adapter->dev, "%s\n", __func__); | ||
| 1038 | |||
| 1039 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
| 1040 | return; | ||
| 1041 | |||
| 1042 | if (slave_pdata->irq) | ||
| 1043 | slaveirq_exit(slave_pdata); | ||
| 1044 | |||
| 1045 | slave_descr = get_slave_descr(); | ||
| 1046 | if (!slave_descr) | ||
| 1047 | return; | ||
| 1048 | |||
| 1049 | mutex_lock(&mpu->mutex); | ||
| 1050 | |||
| 1051 | if (slave_descr->exit) { | ||
| 1052 | result = slave_descr->exit(slave_client->adapter, | ||
| 1053 | slave_descr, | ||
| 1054 | slave_pdata); | ||
| 1055 | if (result) | ||
| 1056 | dev_err(&slave_client->adapter->dev, | ||
| 1057 | "Accel exit failed %d\n", result); | ||
| 1058 | } | ||
| 1059 | mldl_cfg->slave[slave_descr->type] = NULL; | ||
| 1060 | mldl_cfg->pdata_slave[slave_descr->type] = NULL; | ||
| 1061 | mpu->slave_modules[slave_descr->type] = NULL; | ||
| 1062 | |||
| 1063 | mutex_unlock(&mpu->mutex); | ||
| 1064 | |||
| 1065 | } | ||
| 1066 | EXPORT_SYMBOL(inv_mpu_unregister_slave); | ||
| 1067 | |||
| 1068 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 1069 | |||
| 1070 | static const struct i2c_device_id mpu_id[] = { | ||
| 1071 | {"mpu3050", 0}, | ||
| 1072 | {"mpu6050", 0}, | ||
| 1073 | {"mpu6050_no_accel", 0}, | ||
| 1074 | {} | ||
| 1075 | }; | ||
| 1076 | MODULE_DEVICE_TABLE(i2c, mpu_id); | ||
| 1077 | |||
| 1078 | int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid) | ||
| 1079 | { | ||
| 1080 | struct mpu_platform_data *pdata; | ||
| 1081 | struct mpu_private_data *mpu; | ||
| 1082 | struct mldl_cfg *mldl_cfg; | ||
| 1083 | int res = 0; | ||
| 1084 | int ii = 0; | ||
| 1085 | unsigned long irq_flags; | ||
| 1086 | |||
| 1087 | dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++); | ||
| 1088 | |||
| 1089 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 1090 | res = -ENODEV; | ||
| 1091 | goto out_check_functionality_failed; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
| 1095 | if (!mpu) { | ||
| 1096 | res = -ENOMEM; | ||
| 1097 | goto out_alloc_data_failed; | ||
| 1098 | } | ||
| 1099 | mldl_cfg = &mpu->mldl_cfg; | ||
| 1100 | mldl_cfg->mpu_ram = &mpu->mpu_ram; | ||
| 1101 | mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg; | ||
| 1102 | mldl_cfg->mpu_offsets = &mpu->mpu_offsets; | ||
| 1103 | mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info; | ||
| 1104 | mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg; | ||
| 1105 | mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state; | ||
| 1106 | |||
| 1107 | mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE; | ||
| 1108 | mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL); | ||
| 1109 | if (!mldl_cfg->mpu_ram->ram) { | ||
| 1110 | res = -ENOMEM; | ||
| 1111 | goto out_alloc_ram_failed; | ||
| 1112 | } | ||
| 1113 | mpu_private_data = mpu; | ||
| 1114 | i2c_set_clientdata(client, mpu); | ||
| 1115 | mpu->client = client; | ||
| 1116 | |||
| 1117 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
| 1118 | mutex_init(&mpu->mutex); | ||
| 1119 | init_completion(&mpu->completion); | ||
| 1120 | |||
| 1121 | mpu->response_timeout = 60; /* Seconds */ | ||
| 1122 | mpu->timeout.function = mpu_pm_timeout; | ||
| 1123 | mpu->timeout.data = (u_long) mpu; | ||
| 1124 | init_timer(&mpu->timeout); | ||
| 1125 | |||
| 1126 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
| 1127 | mpu->nb.priority = 0; | ||
| 1128 | res = register_pm_notifier(&mpu->nb); | ||
| 1129 | if (res) { | ||
| 1130 | dev_err(&client->adapter->dev, | ||
| 1131 | "Unable to register pm_notifier %d\n", res); | ||
| 1132 | goto out_register_pm_notifier_failed; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | pdata = (struct mpu_platform_data *)client->dev.platform_data; | ||
| 1136 | if (!pdata) { | ||
| 1137 | dev_WARN(&client->adapter->dev, | ||
| 1138 | "Missing platform data for mpu\n"); | ||
| 1139 | } | ||
| 1140 | mldl_cfg->pdata = pdata; | ||
| 1141 | |||
| 1142 | mldl_cfg->mpu_chip_info->addr = client->addr; | ||
| 1143 | res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
| 1144 | |||
| 1145 | if (res) { | ||
| 1146 | dev_err(&client->adapter->dev, | ||
| 1147 | "Unable to open %s %d\n", MPU_NAME, res); | ||
| 1148 | res = -ENODEV; | ||
| 1149 | goto out_whoami_failed; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | mpu->dev.minor = MISC_DYNAMIC_MINOR; | ||
| 1153 | mpu->dev.name = "mpu"; | ||
| 1154 | mpu->dev.fops = &mpu_fops; | ||
| 1155 | res = misc_register(&mpu->dev); | ||
| 1156 | if (res < 0) { | ||
| 1157 | dev_err(&client->adapter->dev, | ||
| 1158 | "ERROR: misc_register returned %d\n", res); | ||
| 1159 | goto out_misc_register_failed; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | if (client->irq) { | ||
| 1163 | dev_info(&client->adapter->dev, | ||
| 1164 | "Installing irq using %d\n", client->irq); | ||
| 1165 | if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
| 1166 | irq_flags = IRQF_TRIGGER_FALLING; | ||
| 1167 | else | ||
| 1168 | irq_flags = IRQF_TRIGGER_RISING; | ||
| 1169 | res = mpuirq_init(client, mldl_cfg, irq_flags); | ||
| 1170 | |||
| 1171 | if (res) | ||
| 1172 | goto out_mpuirq_failed; | ||
| 1173 | } else { | ||
| 1174 | dev_WARN(&client->adapter->dev, | ||
| 1175 | "Missing %s IRQ\n", MPU_NAME); | ||
| 1176 | } | ||
| 1177 | if (!strcmp(mpu_id[1].name, devid->name)) { | ||
| 1178 | /* Special case to re-use the inv_mpu_register_slave */ | ||
| 1179 | struct ext_slave_platform_data *slave_pdata; | ||
| 1180 | slave_pdata = kzalloc(sizeof(*slave_pdata), GFP_KERNEL); | ||
| 1181 | if (!slave_pdata) { | ||
| 1182 | res = -ENOMEM; | ||
| 1183 | goto out_slave_pdata_kzalloc_failed; | ||
| 1184 | } | ||
| 1185 | slave_pdata->bus = EXT_SLAVE_BUS_PRIMARY; | ||
| 1186 | for (ii = 0; ii < 9; ii++) | ||
| 1187 | slave_pdata->orientation[ii] = pdata->orientation[ii]; | ||
| 1188 | res = inv_mpu_register_slave( | ||
| 1189 | NULL, client, | ||
| 1190 | slave_pdata, | ||
| 1191 | mpu6050_get_slave_descr); | ||
| 1192 | if (res) { | ||
| 1193 | /* if inv_mpu_register_slave fails there are no pointer | ||
| 1194 | references to the memory allocated to slave_pdata */ | ||
| 1195 | kfree(slave_pdata); | ||
| 1196 | goto out_slave_pdata_kzalloc_failed; | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | return res; | ||
| 1200 | |||
| 1201 | out_slave_pdata_kzalloc_failed: | ||
| 1202 | if (client->irq) | ||
| 1203 | mpuirq_exit(); | ||
| 1204 | out_mpuirq_failed: | ||
| 1205 | misc_deregister(&mpu->dev); | ||
| 1206 | out_misc_register_failed: | ||
| 1207 | inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
| 1208 | out_whoami_failed: | ||
| 1209 | unregister_pm_notifier(&mpu->nb); | ||
| 1210 | out_register_pm_notifier_failed: | ||
| 1211 | kfree(mldl_cfg->mpu_ram->ram); | ||
| 1212 | mpu_private_data = NULL; | ||
| 1213 | out_alloc_ram_failed: | ||
| 1214 | kfree(mpu); | ||
| 1215 | out_alloc_data_failed: | ||
| 1216 | out_check_functionality_failed: | ||
| 1217 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res); | ||
| 1218 | return res; | ||
| 1219 | |||
| 1220 | } | ||
| 1221 | |||
| 1222 | static int mpu_remove(struct i2c_client *client) | ||
| 1223 | { | ||
| 1224 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
| 1225 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
| 1226 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
| 1227 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
| 1228 | int ii; | ||
| 1229 | |||
| 1230 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
| 1231 | if (!pdata_slave[ii]) | ||
| 1232 | slave_adapter[ii] = NULL; | ||
| 1233 | else | ||
| 1234 | slave_adapter[ii] = | ||
| 1235 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
| 1239 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 1240 | |||
| 1241 | inv_mpu_close(mldl_cfg, | ||
| 1242 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
| 1243 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
| 1244 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
| 1245 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE]); | ||
| 1246 | |||
| 1247 | if (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL] && | ||
| 1248 | (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL]->id == | ||
| 1249 | ACCEL_ID_MPU6050)) { | ||
| 1250 | struct ext_slave_platform_data *slave_pdata = | ||
| 1251 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]; | ||
| 1252 | inv_mpu_unregister_slave( | ||
| 1253 | client, | ||
| 1254 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
| 1255 | mpu6050_get_slave_descr); | ||
| 1256 | kfree(slave_pdata); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | if (client->irq) | ||
| 1260 | mpuirq_exit(); | ||
| 1261 | |||
| 1262 | misc_deregister(&mpu->dev); | ||
| 1263 | |||
| 1264 | unregister_pm_notifier(&mpu->nb); | ||
| 1265 | |||
| 1266 | kfree(mpu->mldl_cfg.mpu_ram->ram); | ||
| 1267 | kfree(mpu); | ||
| 1268 | |||
| 1269 | return 0; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | static struct i2c_driver mpu_driver = { | ||
| 1273 | .class = I2C_CLASS_HWMON, | ||
| 1274 | .probe = mpu_probe, | ||
| 1275 | .remove = mpu_remove, | ||
| 1276 | .id_table = mpu_id, | ||
| 1277 | .driver = { | ||
| 1278 | .owner = THIS_MODULE, | ||
| 1279 | .name = MPU_NAME, | ||
| 1280 | }, | ||
| 1281 | .address_list = normal_i2c, | ||
| 1282 | .shutdown = mpu_shutdown, /* optional */ | ||
| 1283 | .suspend = mpu_dev_suspend, /* optional */ | ||
| 1284 | .resume = mpu_dev_resume, /* optional */ | ||
| 1285 | |||
| 1286 | }; | ||
| 1287 | |||
| 1288 | static int __init mpu_init(void) | ||
| 1289 | { | ||
| 1290 | int res = i2c_add_driver(&mpu_driver); | ||
| 1291 | pr_info("%s: Probe name %s\n", __func__, MPU_NAME); | ||
| 1292 | if (res) | ||
| 1293 | pr_err("%s failed\n", __func__); | ||
| 1294 | return res; | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | static void __exit mpu_exit(void) | ||
| 1298 | { | ||
| 1299 | pr_info("%s\n", __func__); | ||
| 1300 | i2c_del_driver(&mpu_driver); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | module_init(mpu_init); | ||
| 1304 | module_exit(mpu_exit); | ||
| 1305 | |||
| 1306 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 1307 | MODULE_DESCRIPTION("User space character device interface for MPU"); | ||
| 1308 | MODULE_LICENSE("GPL"); | ||
| 1309 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h new file mode 100644 index 00000000000..686f6e5d86a --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050b1.h | |||
| @@ -0,0 +1,435 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup | ||
| 22 | * @brief | ||
| 23 | * | ||
| 24 | * @{ | ||
| 25 | * @file mpu6050.h | ||
| 26 | * @brief | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef __MPU_H_ | ||
| 30 | #error Do not include this file directly. Include mpu.h instead. | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #ifndef __MPU6050B1_H_ | ||
| 34 | #define __MPU6050B1_H_ | ||
| 35 | |||
| 36 | |||
| 37 | #define MPU_NAME "mpu6050B1" | ||
| 38 | #define DEFAULT_MPU_SLAVEADDR 0x68 | ||
| 39 | |||
| 40 | /*==== MPU6050B1 REGISTER SET ====*/ | ||
| 41 | enum { | ||
| 42 | MPUREG_XG_OFFS_TC = 0, /* 0x00, 0 */ | ||
| 43 | MPUREG_YG_OFFS_TC, /* 0x01, 1 */ | ||
| 44 | MPUREG_ZG_OFFS_TC, /* 0x02, 2 */ | ||
| 45 | MPUREG_X_FINE_GAIN, /* 0x03, 3 */ | ||
| 46 | MPUREG_Y_FINE_GAIN, /* 0x04, 4 */ | ||
| 47 | MPUREG_Z_FINE_GAIN, /* 0x05, 5 */ | ||
| 48 | MPUREG_XA_OFFS_H, /* 0x06, 6 */ | ||
| 49 | MPUREG_XA_OFFS_L, /* 0x07, 7 */ | ||
| 50 | MPUREG_YA_OFFS_H, /* 0x08, 8 */ | ||
| 51 | MPUREG_YA_OFFS_L, /* 0x09, 9 */ | ||
| 52 | MPUREG_ZA_OFFS_H, /* 0x0a, 10 */ | ||
| 53 | MPUREG_ZA_OFFS_L, /* 0x0B, 11 */ | ||
| 54 | MPUREG_PRODUCT_ID, /* 0x0c, 12 */ | ||
| 55 | MPUREG_0D_RSVD, /* 0x0d, 13 */ | ||
| 56 | MPUREG_0E_RSVD, /* 0x0e, 14 */ | ||
| 57 | MPUREG_0F_RSVD, /* 0x0f, 15 */ | ||
| 58 | MPUREG_10_RSVD, /* 0x00, 16 */ | ||
| 59 | MPUREG_11_RSVD, /* 0x11, 17 */ | ||
| 60 | MPUREG_12_RSVD, /* 0x12, 18 */ | ||
| 61 | MPUREG_XG_OFFS_USRH, /* 0x13, 19 */ | ||
| 62 | MPUREG_XG_OFFS_USRL, /* 0x14, 20 */ | ||
| 63 | MPUREG_YG_OFFS_USRH, /* 0x15, 21 */ | ||
| 64 | MPUREG_YG_OFFS_USRL, /* 0x16, 22 */ | ||
| 65 | MPUREG_ZG_OFFS_USRH, /* 0x17, 23 */ | ||
| 66 | MPUREG_ZG_OFFS_USRL, /* 0x18, 24 */ | ||
| 67 | MPUREG_SMPLRT_DIV, /* 0x19, 25 */ | ||
| 68 | MPUREG_CONFIG, /* 0x1A, 26 */ | ||
| 69 | MPUREG_GYRO_CONFIG, /* 0x1b, 27 */ | ||
| 70 | MPUREG_ACCEL_CONFIG, /* 0x1c, 28 */ | ||
| 71 | MPUREG_ACCEL_FF_THR, /* 0x1d, 29 */ | ||
| 72 | MPUREG_ACCEL_FF_DUR, /* 0x1e, 30 */ | ||
| 73 | MPUREG_ACCEL_MOT_THR, /* 0x1f, 31 */ | ||
| 74 | MPUREG_ACCEL_MOT_DUR, /* 0x20, 32 */ | ||
| 75 | MPUREG_ACCEL_ZRMOT_THR, /* 0x21, 33 */ | ||
| 76 | MPUREG_ACCEL_ZRMOT_DUR, /* 0x22, 34 */ | ||
| 77 | MPUREG_FIFO_EN, /* 0x23, 35 */ | ||
| 78 | MPUREG_I2C_MST_CTRL, /* 0x24, 36 */ | ||
| 79 | MPUREG_I2C_SLV0_ADDR, /* 0x25, 37 */ | ||
| 80 | MPUREG_I2C_SLV0_REG, /* 0x26, 38 */ | ||
| 81 | MPUREG_I2C_SLV0_CTRL, /* 0x27, 39 */ | ||
| 82 | MPUREG_I2C_SLV1_ADDR, /* 0x28, 40 */ | ||
| 83 | MPUREG_I2C_SLV1_REG, /* 0x29, 41 */ | ||
| 84 | MPUREG_I2C_SLV1_CTRL, /* 0x2a, 42 */ | ||
| 85 | MPUREG_I2C_SLV2_ADDR, /* 0x2B, 43 */ | ||
| 86 | MPUREG_I2C_SLV2_REG, /* 0x2c, 44 */ | ||
| 87 | MPUREG_I2C_SLV2_CTRL, /* 0x2d, 45 */ | ||
| 88 | MPUREG_I2C_SLV3_ADDR, /* 0x2E, 46 */ | ||
| 89 | MPUREG_I2C_SLV3_REG, /* 0x2f, 47 */ | ||
| 90 | MPUREG_I2C_SLV3_CTRL, /* 0x30, 48 */ | ||
| 91 | MPUREG_I2C_SLV4_ADDR, /* 0x31, 49 */ | ||
| 92 | MPUREG_I2C_SLV4_REG, /* 0x32, 50 */ | ||
| 93 | MPUREG_I2C_SLV4_DO, /* 0x33, 51 */ | ||
| 94 | MPUREG_I2C_SLV4_CTRL, /* 0x34, 52 */ | ||
| 95 | MPUREG_I2C_SLV4_DI, /* 0x35, 53 */ | ||
| 96 | MPUREG_I2C_MST_STATUS, /* 0x36, 54 */ | ||
| 97 | MPUREG_INT_PIN_CFG, /* 0x37, 55 */ | ||
| 98 | MPUREG_INT_ENABLE, /* 0x38, 56 */ | ||
| 99 | MPUREG_DMP_INT_STATUS, /* 0x39, 57 */ | ||
| 100 | MPUREG_INT_STATUS, /* 0x3A, 58 */ | ||
| 101 | MPUREG_ACCEL_XOUT_H, /* 0x3B, 59 */ | ||
| 102 | MPUREG_ACCEL_XOUT_L, /* 0x3c, 60 */ | ||
| 103 | MPUREG_ACCEL_YOUT_H, /* 0x3d, 61 */ | ||
| 104 | MPUREG_ACCEL_YOUT_L, /* 0x3e, 62 */ | ||
| 105 | MPUREG_ACCEL_ZOUT_H, /* 0x3f, 63 */ | ||
| 106 | MPUREG_ACCEL_ZOUT_L, /* 0x40, 64 */ | ||
| 107 | MPUREG_TEMP_OUT_H, /* 0x41, 65 */ | ||
| 108 | MPUREG_TEMP_OUT_L, /* 0x42, 66 */ | ||
| 109 | MPUREG_GYRO_XOUT_H, /* 0x43, 67 */ | ||
| 110 | MPUREG_GYRO_XOUT_L, /* 0x44, 68 */ | ||
| 111 | MPUREG_GYRO_YOUT_H, /* 0x45, 69 */ | ||
| 112 | MPUREG_GYRO_YOUT_L, /* 0x46, 70 */ | ||
| 113 | MPUREG_GYRO_ZOUT_H, /* 0x47, 71 */ | ||
| 114 | MPUREG_GYRO_ZOUT_L, /* 0x48, 72 */ | ||
| 115 | MPUREG_EXT_SLV_SENS_DATA_00, /* 0x49, 73 */ | ||
| 116 | MPUREG_EXT_SLV_SENS_DATA_01, /* 0x4a, 74 */ | ||
| 117 | MPUREG_EXT_SLV_SENS_DATA_02, /* 0x4b, 75 */ | ||
| 118 | MPUREG_EXT_SLV_SENS_DATA_03, /* 0x4c, 76 */ | ||
| 119 | MPUREG_EXT_SLV_SENS_DATA_04, /* 0x4d, 77 */ | ||
| 120 | MPUREG_EXT_SLV_SENS_DATA_05, /* 0x4e, 78 */ | ||
| 121 | MPUREG_EXT_SLV_SENS_DATA_06, /* 0x4F, 79 */ | ||
| 122 | MPUREG_EXT_SLV_SENS_DATA_07, /* 0x50, 80 */ | ||
| 123 | MPUREG_EXT_SLV_SENS_DATA_08, /* 0x51, 81 */ | ||
| 124 | MPUREG_EXT_SLV_SENS_DATA_09, /* 0x52, 82 */ | ||
| 125 | MPUREG_EXT_SLV_SENS_DATA_10, /* 0x53, 83 */ | ||
| 126 | MPUREG_EXT_SLV_SENS_DATA_11, /* 0x54, 84 */ | ||
| 127 | MPUREG_EXT_SLV_SENS_DATA_12, /* 0x55, 85 */ | ||
| 128 | MPUREG_EXT_SLV_SENS_DATA_13, /* 0x56, 86 */ | ||
| 129 | MPUREG_EXT_SLV_SENS_DATA_14, /* 0x57, 87 */ | ||
| 130 | MPUREG_EXT_SLV_SENS_DATA_15, /* 0x58, 88 */ | ||
| 131 | MPUREG_EXT_SLV_SENS_DATA_16, /* 0x59, 89 */ | ||
| 132 | MPUREG_EXT_SLV_SENS_DATA_17, /* 0x5a, 90 */ | ||
| 133 | MPUREG_EXT_SLV_SENS_DATA_18, /* 0x5B, 91 */ | ||
| 134 | MPUREG_EXT_SLV_SENS_DATA_19, /* 0x5c, 92 */ | ||
| 135 | MPUREG_EXT_SLV_SENS_DATA_20, /* 0x5d, 93 */ | ||
| 136 | MPUREG_EXT_SLV_SENS_DATA_21, /* 0x5e, 94 */ | ||
| 137 | MPUREG_EXT_SLV_SENS_DATA_22, /* 0x5f, 95 */ | ||
| 138 | MPUREG_EXT_SLV_SENS_DATA_23, /* 0x60, 96 */ | ||
| 139 | MPUREG_ACCEL_INTEL_STATUS, /* 0x61, 97 */ | ||
| 140 | MPUREG_62_RSVD, /* 0x62, 98 */ | ||
| 141 | MPUREG_I2C_SLV0_DO, /* 0x63, 99 */ | ||
| 142 | MPUREG_I2C_SLV1_DO, /* 0x64, 100 */ | ||
| 143 | MPUREG_I2C_SLV2_DO, /* 0x65, 101 */ | ||
| 144 | MPUREG_I2C_SLV3_DO, /* 0x66, 102 */ | ||
| 145 | MPUREG_I2C_MST_DELAY_CTRL, /* 0x67, 103 */ | ||
| 146 | MPUREG_SIGNAL_PATH_RESET, /* 0x68, 104 */ | ||
| 147 | MPUREG_ACCEL_INTEL_CTRL, /* 0x69, 105 */ | ||
| 148 | MPUREG_USER_CTRL, /* 0x6A, 106 */ | ||
| 149 | MPUREG_PWR_MGMT_1, /* 0x6B, 107 */ | ||
| 150 | MPUREG_PWR_MGMT_2, /* 0x6C, 108 */ | ||
| 151 | MPUREG_BANK_SEL, /* 0x6D, 109 */ | ||
| 152 | MPUREG_MEM_START_ADDR, /* 0x6E, 100 */ | ||
| 153 | MPUREG_MEM_R_W, /* 0x6F, 111 */ | ||
| 154 | MPUREG_DMP_CFG_1, /* 0x70, 112 */ | ||
| 155 | MPUREG_DMP_CFG_2, /* 0x71, 113 */ | ||
| 156 | MPUREG_FIFO_COUNTH, /* 0x72, 114 */ | ||
| 157 | MPUREG_FIFO_COUNTL, /* 0x73, 115 */ | ||
| 158 | MPUREG_FIFO_R_W, /* 0x74, 116 */ | ||
| 159 | MPUREG_WHOAMI, /* 0x75, 117 */ | ||
| 160 | |||
| 161 | NUM_OF_MPU_REGISTERS /* = 0x76, 118 */ | ||
| 162 | }; | ||
| 163 | |||
| 164 | /*==== MPU6050B1 MEMORY ====*/ | ||
| 165 | enum MPU_MEMORY_BANKS { | ||
| 166 | MEM_RAM_BANK_0 = 0, | ||
| 167 | MEM_RAM_BANK_1, | ||
| 168 | MEM_RAM_BANK_2, | ||
| 169 | MEM_RAM_BANK_3, | ||
| 170 | MEM_RAM_BANK_4, | ||
| 171 | MEM_RAM_BANK_5, | ||
| 172 | MEM_RAM_BANK_6, | ||
| 173 | MEM_RAM_BANK_7, | ||
| 174 | MEM_RAM_BANK_8, | ||
| 175 | MEM_RAM_BANK_9, | ||
| 176 | MEM_RAM_BANK_10, | ||
| 177 | MEM_RAM_BANK_11, | ||
| 178 | MPU_MEM_NUM_RAM_BANKS, | ||
| 179 | MPU_MEM_OTP_BANK_0 = 16 | ||
| 180 | }; | ||
| 181 | |||
| 182 | |||
| 183 | /*==== MPU6050B1 parameters ====*/ | ||
| 184 | |||
| 185 | #define NUM_REGS (NUM_OF_MPU_REGISTERS) | ||
| 186 | #define START_SENS_REGS (0x3B) | ||
| 187 | #define NUM_SENS_REGS (0x60 - START_SENS_REGS + 1) | ||
| 188 | |||
| 189 | /*---- MPU Memory ----*/ | ||
| 190 | #define NUM_BANKS (MPU_MEM_NUM_RAM_BANKS) | ||
| 191 | #define BANK_SIZE (256) | ||
| 192 | #define MEM_SIZE (NUM_BANKS * BANK_SIZE) | ||
| 193 | #define MPU_MEM_BANK_SIZE (BANK_SIZE) /*alternative name */ | ||
| 194 | |||
| 195 | #define FIFO_HW_SIZE (1024) | ||
| 196 | |||
| 197 | #define NUM_EXT_SLAVES (4) | ||
| 198 | |||
| 199 | |||
| 200 | /*==== BITS FOR MPU6050B1 ====*/ | ||
| 201 | /*---- MPU6050B1 'XG_OFFS_TC' register (0, 1, 2) ----*/ | ||
| 202 | #define BIT_PU_SLEEP_MODE 0x80 | ||
| 203 | #define BITS_XG_OFFS_TC 0x7E | ||
| 204 | #define BIT_OTP_BNK_VLD 0x01 | ||
| 205 | |||
| 206 | #define BIT_I2C_MST_VDDIO 0x80 | ||
| 207 | #define BITS_YG_OFFS_TC 0x7E | ||
| 208 | #define BITS_ZG_OFFS_TC 0x7E | ||
| 209 | /*---- MPU6050B1 'FIFO_EN' register (23) ----*/ | ||
| 210 | #define BIT_TEMP_OUT 0x80 | ||
| 211 | #define BIT_GYRO_XOUT 0x40 | ||
| 212 | #define BIT_GYRO_YOUT 0x20 | ||
| 213 | #define BIT_GYRO_ZOUT 0x10 | ||
| 214 | #define BIT_ACCEL 0x08 | ||
| 215 | #define BIT_SLV_2 0x04 | ||
| 216 | #define BIT_SLV_1 0x02 | ||
| 217 | #define BIT_SLV_0 0x01 | ||
| 218 | /*---- MPU6050B1 'CONFIG' register (1A) ----*/ | ||
| 219 | /*NONE 0xC0 */ | ||
| 220 | #define BITS_EXT_SYNC_SET 0x38 | ||
| 221 | #define BITS_DLPF_CFG 0x07 | ||
| 222 | /*---- MPU6050B1 'GYRO_CONFIG' register (1B) ----*/ | ||
| 223 | /* voluntarily modified label from BITS_FS_SEL to | ||
| 224 | * BITS_GYRO_FS_SEL to avoid confusion with MPU | ||
| 225 | */ | ||
| 226 | #define BITS_GYRO_FS_SEL 0x18 | ||
| 227 | /*NONE 0x07 */ | ||
| 228 | /*---- MPU6050B1 'ACCEL_CONFIG' register (1C) ----*/ | ||
| 229 | #define BITS_ACCEL_FS_SEL 0x18 | ||
| 230 | #define BITS_ACCEL_HPF 0x07 | ||
| 231 | /*---- MPU6050B1 'I2C_MST_CTRL' register (24) ----*/ | ||
| 232 | #define BIT_MULT_MST_EN 0x80 | ||
| 233 | #define BIT_WAIT_FOR_ES 0x40 | ||
| 234 | #define BIT_SLV_3_FIFO_EN 0x20 | ||
| 235 | #define BIT_I2C_MST_PSR 0x10 | ||
| 236 | #define BITS_I2C_MST_CLK 0x0F | ||
| 237 | /*---- MPU6050B1 'I2C_SLV?_ADDR' register (27,2A,2D,30) ----*/ | ||
| 238 | #define BIT_I2C_READ 0x80 | ||
| 239 | #define BIT_I2C_WRITE 0x00 | ||
| 240 | #define BITS_I2C_ADDR 0x7F | ||
| 241 | /*---- MPU6050B1 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/ | ||
| 242 | #define BIT_SLV_ENABLE 0x80 | ||
| 243 | #define BIT_SLV_BYTE_SW 0x40 | ||
| 244 | #define BIT_SLV_REG_DIS 0x20 | ||
| 245 | #define BIT_SLV_GRP 0x10 | ||
| 246 | #define BITS_SLV_LENG 0x0F | ||
| 247 | /*---- MPU6050B1 'I2C_SLV4_ADDR' register (31) ----*/ | ||
| 248 | #define BIT_I2C_SLV4_RNW 0x80 | ||
| 249 | /*---- MPU6050B1 'I2C_SLV4_CTRL' register (34) ----*/ | ||
| 250 | #define BIT_I2C_SLV4_EN 0x80 | ||
| 251 | #define BIT_SLV4_DONE_INT_EN 0x40 | ||
| 252 | #define BIT_SLV4_REG_DIS 0x20 | ||
| 253 | #define MASK_I2C_MST_DLY 0x1F | ||
| 254 | /*---- MPU6050B1 'I2C_MST_STATUS' register (36) ----*/ | ||
| 255 | #define BIT_PASS_THROUGH 0x80 | ||
| 256 | #define BIT_I2C_SLV4_DONE 0x40 | ||
| 257 | #define BIT_I2C_LOST_ARB 0x20 | ||
| 258 | #define BIT_I2C_SLV4_NACK 0x10 | ||
| 259 | #define BIT_I2C_SLV3_NACK 0x08 | ||
| 260 | #define BIT_I2C_SLV2_NACK 0x04 | ||
| 261 | #define BIT_I2C_SLV1_NACK 0x02 | ||
| 262 | #define BIT_I2C_SLV0_NACK 0x01 | ||
| 263 | /*---- MPU6050B1 'INT_PIN_CFG' register (37) ----*/ | ||
| 264 | #define BIT_ACTL 0x80 | ||
| 265 | #define BIT_ACTL_LOW 0x80 | ||
| 266 | #define BIT_ACTL_HIGH 0x00 | ||
| 267 | #define BIT_OPEN 0x40 | ||
| 268 | #define BIT_LATCH_INT_EN 0x20 | ||
| 269 | #define BIT_INT_ANYRD_2CLEAR 0x10 | ||
| 270 | #define BIT_ACTL_FSYNC 0x08 | ||
| 271 | #define BIT_FSYNC_INT_EN 0x04 | ||
| 272 | #define BIT_BYPASS_EN 0x02 | ||
| 273 | #define BIT_CLKOUT_EN 0x01 | ||
| 274 | /*---- MPU6050B1 'INT_ENABLE' register (38) ----*/ | ||
| 275 | #define BIT_FF_EN 0x80 | ||
| 276 | #define BIT_MOT_EN 0x40 | ||
| 277 | #define BIT_ZMOT_EN 0x20 | ||
| 278 | #define BIT_FIFO_OVERFLOW_EN 0x10 | ||
| 279 | #define BIT_I2C_MST_INT_EN 0x08 | ||
| 280 | #define BIT_PLL_RDY_EN 0x04 | ||
| 281 | #define BIT_DMP_INT_EN 0x02 | ||
| 282 | #define BIT_RAW_RDY_EN 0x01 | ||
| 283 | /*---- MPU6050B1 'DMP_INT_STATUS' register (39) ----*/ | ||
| 284 | /*NONE 0x80 */ | ||
| 285 | /*NONE 0x40 */ | ||
| 286 | #define BIT_DMP_INT_5 0x20 | ||
| 287 | #define BIT_DMP_INT_4 0x10 | ||
| 288 | #define BIT_DMP_INT_3 0x08 | ||
| 289 | #define BIT_DMP_INT_2 0x04 | ||
| 290 | #define BIT_DMP_INT_1 0x02 | ||
| 291 | #define BIT_DMP_INT_0 0x01 | ||
| 292 | /*---- MPU6050B1 'INT_STATUS' register (3A) ----*/ | ||
| 293 | #define BIT_FF_INT 0x80 | ||
| 294 | #define BIT_MOT_INT 0x40 | ||
| 295 | #define BIT_ZMOT_INT 0x20 | ||
| 296 | #define BIT_FIFO_OVERFLOW_INT 0x10 | ||
| 297 | #define BIT_I2C_MST_INT 0x08 | ||
| 298 | #define BIT_PLL_RDY_INT 0x04 | ||
| 299 | #define BIT_DMP_INT 0x02 | ||
| 300 | #define BIT_RAW_DATA_RDY_INT 0x01 | ||
| 301 | /*---- MPU6050B1 'MPUREG_I2C_MST_DELAY_CTRL' register (0x67) ----*/ | ||
| 302 | #define BIT_DELAY_ES_SHADOW 0x80 | ||
| 303 | #define BIT_SLV4_DLY_EN 0x10 | ||
| 304 | #define BIT_SLV3_DLY_EN 0x08 | ||
| 305 | #define BIT_SLV2_DLY_EN 0x04 | ||
| 306 | #define BIT_SLV1_DLY_EN 0x02 | ||
| 307 | #define BIT_SLV0_DLY_EN 0x01 | ||
| 308 | /*---- MPU6050B1 'BANK_SEL' register (6D) ----*/ | ||
| 309 | #define BIT_PRFTCH_EN 0x40 | ||
| 310 | #define BIT_CFG_USER_BANK 0x20 | ||
| 311 | #define BITS_MEM_SEL 0x1f | ||
| 312 | /*---- MPU6050B1 'USER_CTRL' register (6A) ----*/ | ||
| 313 | #define BIT_DMP_EN 0x80 | ||
| 314 | #define BIT_FIFO_EN 0x40 | ||
| 315 | #define BIT_I2C_MST_EN 0x20 | ||
| 316 | #define BIT_I2C_IF_DIS 0x10 | ||
| 317 | #define BIT_DMP_RST 0x08 | ||
| 318 | #define BIT_FIFO_RST 0x04 | ||
| 319 | #define BIT_I2C_MST_RST 0x02 | ||
| 320 | #define BIT_SIG_COND_RST 0x01 | ||
| 321 | /*---- MPU6050B1 'PWR_MGMT_1' register (6B) ----*/ | ||
| 322 | #define BIT_H_RESET 0x80 | ||
| 323 | #define BIT_SLEEP 0x40 | ||
| 324 | #define BIT_CYCLE 0x20 | ||
| 325 | #define BIT_PD_PTAT 0x08 | ||
| 326 | #define BITS_CLKSEL 0x07 | ||
| 327 | /*---- MPU6050B1 'PWR_MGMT_2' register (6C) ----*/ | ||
| 328 | #define BITS_LPA_WAKE_CTRL 0xC0 | ||
| 329 | #define BITS_LPA_WAKE_1HZ 0x00 | ||
| 330 | #define BITS_LPA_WAKE_2HZ 0x40 | ||
| 331 | #define BITS_LPA_WAKE_10HZ 0x80 | ||
| 332 | #define BITS_LPA_WAKE_40HZ 0xC0 | ||
| 333 | #define BIT_STBY_XA 0x20 | ||
| 334 | #define BIT_STBY_YA 0x10 | ||
| 335 | #define BIT_STBY_ZA 0x08 | ||
| 336 | #define BIT_STBY_XG 0x04 | ||
| 337 | #define BIT_STBY_YG 0x02 | ||
| 338 | #define BIT_STBY_ZG 0x01 | ||
| 339 | |||
| 340 | #define ACCEL_MOT_THR_LSB (32) /* mg */ | ||
| 341 | #define ACCEL_MOT_DUR_LSB (1) | ||
| 342 | #define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255) | ||
| 343 | #define ACCEL_ZRMOT_DUR_LSB (64) | ||
| 344 | |||
| 345 | /*----------------------------------------------------------------------------*/ | ||
| 346 | /*---- Alternative names to take care of conflicts with current mpu3050.h ----*/ | ||
| 347 | /*----------------------------------------------------------------------------*/ | ||
| 348 | |||
| 349 | /*-- registers --*/ | ||
| 350 | #define MPUREG_DLPF_FS_SYNC MPUREG_CONFIG /* 0x1A */ | ||
| 351 | |||
| 352 | #define MPUREG_PWR_MGM MPUREG_PWR_MGMT_1 /* 0x6B */ | ||
| 353 | #define MPUREG_FIFO_EN1 MPUREG_FIFO_EN /* 0x23 */ | ||
| 354 | #define MPUREG_INT_CFG MPUREG_INT_ENABLE /* 0x38 */ | ||
| 355 | #define MPUREG_X_OFFS_USRH MPUREG_XG_OFFS_USRH /* 0x13 */ | ||
| 356 | #define MPUREG_WHO_AM_I MPUREG_WHOAMI /* 0x75 */ | ||
| 357 | #define MPUREG_23_RSVD MPUREG_EXT_SLV_SENS_DATA_00 /* 0x49 */ | ||
| 358 | |||
| 359 | /*-- bits --*/ | ||
| 360 | /* 'USER_CTRL' register */ | ||
| 361 | #define BIT_AUX_IF_EN BIT_I2C_MST_EN | ||
| 362 | #define BIT_AUX_RD_LENG BIT_I2C_MST_EN | ||
| 363 | #define BIT_IME_IF_RST BIT_I2C_MST_RST | ||
| 364 | #define BIT_GYRO_RST BIT_SIG_COND_RST | ||
| 365 | /* 'INT_ENABLE' register */ | ||
| 366 | #define BIT_RAW_RDY BIT_RAW_DATA_RDY_INT | ||
| 367 | #define BIT_MPU_RDY_EN BIT_PLL_RDY_EN | ||
| 368 | /* 'INT_STATUS' register */ | ||
| 369 | #define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT | ||
| 370 | |||
| 371 | /*---- MPU6050 Silicon Revisions ----*/ | ||
| 372 | #define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */ | ||
| 373 | #define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */ | ||
| 374 | |||
| 375 | /*---- MPU6050 notable product revisions ----*/ | ||
| 376 | #define MPU_PRODUCT_KEY_B1_E1_5 105 | ||
| 377 | #define MPU_PRODUCT_KEY_B2_F1 431 | ||
| 378 | |||
| 379 | /*---- structure containing control variables used by MLDL ----*/ | ||
| 380 | /*---- MPU clock source settings ----*/ | ||
| 381 | /*---- MPU filter selections ----*/ | ||
| 382 | enum mpu_filter { | ||
| 383 | MPU_FILTER_256HZ_NOLPF2 = 0, | ||
| 384 | MPU_FILTER_188HZ, | ||
| 385 | MPU_FILTER_98HZ, | ||
| 386 | MPU_FILTER_42HZ, | ||
| 387 | MPU_FILTER_20HZ, | ||
| 388 | MPU_FILTER_10HZ, | ||
| 389 | MPU_FILTER_5HZ, | ||
| 390 | MPU_FILTER_2100HZ_NOLPF, | ||
| 391 | NUM_MPU_FILTER | ||
| 392 | }; | ||
| 393 | |||
| 394 | enum mpu_fullscale { | ||
| 395 | MPU_FS_250DPS = 0, | ||
| 396 | MPU_FS_500DPS, | ||
| 397 | MPU_FS_1000DPS, | ||
| 398 | MPU_FS_2000DPS, | ||
| 399 | NUM_MPU_FS | ||
| 400 | }; | ||
| 401 | |||
| 402 | enum mpu_clock_sel { | ||
| 403 | MPU_CLK_SEL_INTERNAL = 0, | ||
| 404 | MPU_CLK_SEL_PLLGYROX, | ||
| 405 | MPU_CLK_SEL_PLLGYROY, | ||
| 406 | MPU_CLK_SEL_PLLGYROZ, | ||
| 407 | MPU_CLK_SEL_PLLEXT32K, | ||
| 408 | MPU_CLK_SEL_PLLEXT19M, | ||
| 409 | MPU_CLK_SEL_RESERVED, | ||
| 410 | MPU_CLK_SEL_STOP, | ||
| 411 | NUM_CLK_SEL | ||
| 412 | }; | ||
| 413 | |||
| 414 | enum mpu_ext_sync { | ||
| 415 | MPU_EXT_SYNC_NONE = 0, | ||
| 416 | MPU_EXT_SYNC_TEMP, | ||
| 417 | MPU_EXT_SYNC_GYROX, | ||
| 418 | MPU_EXT_SYNC_GYROY, | ||
| 419 | MPU_EXT_SYNC_GYROZ, | ||
| 420 | MPU_EXT_SYNC_ACCELX, | ||
| 421 | MPU_EXT_SYNC_ACCELY, | ||
| 422 | MPU_EXT_SYNC_ACCELZ, | ||
| 423 | NUM_MPU_EXT_SYNC | ||
| 424 | }; | ||
| 425 | |||
| 426 | #define MPUREG_CONFIG_VALUE(ext_sync, lpf) \ | ||
| 427 | ((ext_sync << 3) | lpf) | ||
| 428 | |||
| 429 | #define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \ | ||
| 430 | ((x_st ? 0x80 : 0) | \ | ||
| 431 | (y_st ? 0x70 : 0) | \ | ||
| 432 | (z_st ? 0x60 : 0) | \ | ||
| 433 | (full_scale << 3)) | ||
| 434 | |||
| 435 | #endif /* __MPU6050_H_ */ | ||
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c new file mode 100644 index 00000000000..d8b721e4346 --- /dev/null +++ b/drivers/misc/inv_mpu/mpuirq.c | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/irq.h> | ||
| 26 | #include <linux/signal.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #include <linux/i2c.h> | ||
| 29 | #include <linux/i2c-dev.h> | ||
| 30 | #include <linux/poll.h> | ||
| 31 | |||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/mm.h> | ||
| 35 | #include <linux/sched.h> | ||
| 36 | #include <linux/wait.h> | ||
| 37 | #include <linux/uaccess.h> | ||
| 38 | #include <linux/io.h> | ||
| 39 | |||
| 40 | #include <linux/mpu.h> | ||
| 41 | #include "mpuirq.h" | ||
| 42 | #include "mldl_cfg.h" | ||
| 43 | |||
| 44 | #define MPUIRQ_NAME "mpuirq" | ||
| 45 | |||
| 46 | /* function which gets accel data and sends it to MPU */ | ||
| 47 | |||
| 48 | DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait); | ||
| 49 | |||
| 50 | struct mpuirq_dev_data { | ||
| 51 | struct i2c_client *mpu_client; | ||
| 52 | struct miscdevice *dev; | ||
| 53 | int irq; | ||
| 54 | int pid; | ||
| 55 | int accel_divider; | ||
| 56 | int data_ready; | ||
| 57 | int timeout; | ||
| 58 | }; | ||
| 59 | |||
| 60 | static struct mpuirq_dev_data mpuirq_dev_data; | ||
| 61 | static struct mpuirq_data mpuirq_data; | ||
| 62 | static char *interface = MPUIRQ_NAME; | ||
| 63 | |||
| 64 | static int mpuirq_open(struct inode *inode, struct file *file) | ||
| 65 | { | ||
| 66 | dev_dbg(mpuirq_dev_data.dev->this_device, | ||
| 67 | "%s current->pid %d\n", __func__, current->pid); | ||
| 68 | mpuirq_dev_data.pid = current->pid; | ||
| 69 | file->private_data = &mpuirq_dev_data; | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* close function - called when the "file" /dev/mpuirq is closed in userspace */ | ||
| 74 | static int mpuirq_release(struct inode *inode, struct file *file) | ||
| 75 | { | ||
| 76 | dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n"); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | /* read function called when from /dev/mpuirq is read */ | ||
| 81 | static ssize_t mpuirq_read(struct file *file, | ||
| 82 | char *buf, size_t count, loff_t *ppos) | ||
| 83 | { | ||
| 84 | int len, err; | ||
| 85 | struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data; | ||
| 86 | |||
| 87 | if (!mpuirq_dev_data.data_ready && | ||
| 88 | mpuirq_dev_data.timeout && (!(file->f_flags & O_NONBLOCK))) { | ||
| 89 | wait_event_interruptible_timeout(mpuirq_wait, | ||
| 90 | mpuirq_dev_data.data_ready, | ||
| 91 | mpuirq_dev_data.timeout); | ||
| 92 | } | ||
| 93 | |||
| 94 | if (mpuirq_dev_data.data_ready && NULL != buf | ||
| 95 | && count >= sizeof(mpuirq_data)) { | ||
| 96 | err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data)); | ||
| 97 | mpuirq_data.data_type = 0; | ||
| 98 | } else { | ||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | if (err != 0) { | ||
| 102 | dev_err(p_mpuirq_dev_data->dev->this_device, | ||
| 103 | "Copy to user returned %d\n", err); | ||
| 104 | return -EFAULT; | ||
| 105 | } | ||
| 106 | mpuirq_dev_data.data_ready = 0; | ||
| 107 | len = sizeof(mpuirq_data); | ||
| 108 | return len; | ||
| 109 | } | ||
| 110 | |||
| 111 | unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll) | ||
| 112 | { | ||
| 113 | int mask = 0; | ||
| 114 | |||
| 115 | poll_wait(file, &mpuirq_wait, poll); | ||
| 116 | if (mpuirq_dev_data.data_ready) | ||
| 117 | mask |= POLLIN | POLLRDNORM; | ||
| 118 | return mask; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* ioctl - I/O control */ | ||
| 122 | static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 123 | { | ||
| 124 | int retval = 0; | ||
| 125 | int data; | ||
| 126 | |||
| 127 | switch (cmd) { | ||
| 128 | case MPUIRQ_SET_TIMEOUT: | ||
| 129 | mpuirq_dev_data.timeout = arg; | ||
| 130 | break; | ||
| 131 | |||
| 132 | case MPUIRQ_GET_INTERRUPT_CNT: | ||
| 133 | data = mpuirq_data.interruptcount - 1; | ||
| 134 | if (mpuirq_data.interruptcount > 1) | ||
| 135 | mpuirq_data.interruptcount = 1; | ||
| 136 | |||
| 137 | if (copy_to_user((int *)arg, &data, sizeof(int))) | ||
| 138 | return -EFAULT; | ||
| 139 | break; | ||
| 140 | case MPUIRQ_GET_IRQ_TIME: | ||
| 141 | if (copy_to_user((int *)arg, &mpuirq_data.irqtime, | ||
| 142 | sizeof(mpuirq_data.irqtime))) | ||
| 143 | return -EFAULT; | ||
| 144 | mpuirq_data.irqtime = 0; | ||
| 145 | break; | ||
| 146 | case MPUIRQ_SET_FREQUENCY_DIVIDER: | ||
| 147 | mpuirq_dev_data.accel_divider = arg; | ||
| 148 | break; | ||
| 149 | default: | ||
| 150 | retval = -EINVAL; | ||
| 151 | } | ||
| 152 | return retval; | ||
| 153 | } | ||
| 154 | |||
| 155 | static irqreturn_t mpuirq_handler(int irq, void *dev_id) | ||
| 156 | { | ||
| 157 | static int mycount; | ||
| 158 | struct timeval irqtime; | ||
| 159 | mycount++; | ||
| 160 | |||
| 161 | mpuirq_data.interruptcount++; | ||
| 162 | |||
| 163 | /* wake up (unblock) for reading data from userspace */ | ||
| 164 | /* and ignore first interrupt generated in module init */ | ||
| 165 | mpuirq_dev_data.data_ready = 1; | ||
| 166 | |||
| 167 | do_gettimeofday(&irqtime); | ||
| 168 | mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
| 169 | mpuirq_data.irqtime += irqtime.tv_usec; | ||
| 170 | mpuirq_data.data_type = MPUIRQ_DATA_TYPE_MPU_IRQ; | ||
| 171 | mpuirq_data.data = 0; | ||
| 172 | |||
| 173 | wake_up_interruptible(&mpuirq_wait); | ||
| 174 | |||
| 175 | return IRQ_HANDLED; | ||
| 176 | |||
| 177 | } | ||
| 178 | |||
| 179 | /* define which file operations are supported */ | ||
| 180 | const struct file_operations mpuirq_fops = { | ||
| 181 | .owner = THIS_MODULE, | ||
| 182 | .read = mpuirq_read, | ||
| 183 | .poll = mpuirq_poll, | ||
| 184 | |||
| 185 | .unlocked_ioctl = mpuirq_ioctl, | ||
| 186 | .open = mpuirq_open, | ||
| 187 | .release = mpuirq_release, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static struct miscdevice mpuirq_device = { | ||
| 191 | .minor = MISC_DYNAMIC_MINOR, | ||
| 192 | .name = MPUIRQ_NAME, | ||
| 193 | .fops = &mpuirq_fops, | ||
| 194 | }; | ||
| 195 | |||
| 196 | int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg, | ||
| 197 | unsigned long irq_flags) | ||
| 198 | { | ||
| 199 | |||
| 200 | int res; | ||
| 201 | |||
| 202 | mpuirq_dev_data.mpu_client = mpu_client; | ||
| 203 | |||
| 204 | dev_info(&mpu_client->adapter->dev, | ||
| 205 | "Module Param interface = %s\n", interface); | ||
| 206 | |||
| 207 | mpuirq_dev_data.irq = mpu_client->irq; | ||
| 208 | mpuirq_dev_data.pid = 0; | ||
| 209 | mpuirq_dev_data.accel_divider = -1; | ||
| 210 | mpuirq_dev_data.data_ready = 0; | ||
| 211 | mpuirq_dev_data.timeout = 0; | ||
| 212 | mpuirq_dev_data.dev = &mpuirq_device; | ||
| 213 | |||
| 214 | if (mpuirq_dev_data.irq) { | ||
| 215 | irq_flags |= IRQF_SHARED; | ||
| 216 | res = | ||
| 217 | request_irq(mpuirq_dev_data.irq, mpuirq_handler, irq_flags, | ||
| 218 | interface, &mpuirq_dev_data.irq); | ||
| 219 | if (res) { | ||
| 220 | dev_err(&mpu_client->adapter->dev, | ||
| 221 | "myirqtest: cannot register IRQ %d\n", | ||
| 222 | mpuirq_dev_data.irq); | ||
| 223 | } else { | ||
| 224 | res = misc_register(&mpuirq_device); | ||
| 225 | if (res < 0) { | ||
| 226 | dev_err(&mpu_client->adapter->dev, | ||
| 227 | "misc_register returned %d\n", res); | ||
| 228 | free_irq(mpuirq_dev_data.irq, | ||
| 229 | &mpuirq_dev_data.irq); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | } else { | ||
| 234 | res = 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | return res; | ||
| 238 | } | ||
| 239 | EXPORT_SYMBOL(mpuirq_init); | ||
| 240 | |||
| 241 | void mpuirq_exit(void) | ||
| 242 | { | ||
| 243 | if (mpuirq_dev_data.irq > 0) | ||
| 244 | free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq); | ||
| 245 | |||
| 246 | dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME); | ||
| 247 | misc_deregister(&mpuirq_device); | ||
| 248 | |||
| 249 | return; | ||
| 250 | } | ||
| 251 | EXPORT_SYMBOL(mpuirq_exit); | ||
| 252 | |||
| 253 | module_param(interface, charp, S_IRUGO | S_IWUSR); | ||
| 254 | MODULE_PARM_DESC(interface, "The Interface name"); | ||
diff --git a/drivers/misc/inv_mpu/mpuirq.h b/drivers/misc/inv_mpu/mpuirq.h new file mode 100644 index 00000000000..073867d830c --- /dev/null +++ b/drivers/misc/inv_mpu/mpuirq.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MPUIRQ__ | ||
| 21 | #define __MPUIRQ__ | ||
| 22 | |||
| 23 | #include <linux/i2c-dev.h> | ||
| 24 | #include <linux/time.h> | ||
| 25 | #include <linux/ioctl.h> | ||
| 26 | #include "mldl_cfg.h" | ||
| 27 | |||
| 28 | #define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long) | ||
| 29 | #define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long) | ||
| 30 | #define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval) | ||
| 31 | #define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long) | ||
| 32 | |||
| 33 | void mpuirq_exit(void); | ||
| 34 | int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg, | ||
| 35 | unsigned long irq_flags); | ||
| 36 | |||
| 37 | #endif | ||
diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig new file mode 100644 index 00000000000..f1c021e8f12 --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Kconfig | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | menuconfig: INV_SENSORS_PRESSURE | ||
| 2 | bool "Pressure Sensor Slaves" | ||
| 3 | depends on INV_SENSORS | ||
| 4 | default y | ||
| 5 | help | ||
| 6 | Select y to see a list of supported pressure sensors that can be | ||
| 7 | integrated with the MPUxxxx set of motion processors. | ||
| 8 | |||
| 9 | if INV_SENSORS_PRESSURE | ||
| 10 | |||
| 11 | config MPU_SENSORS_BMA085 | ||
| 12 | tristate "Bosch BMA085" | ||
| 13 | help | ||
| 14 | This enables support for the Bosch bma085 pressure sensor | ||
| 15 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
| 16 | device driver. Only one accelerometer can be registered at a time. | ||
| 17 | Specifying more that one accelerometer in the board file will result | ||
| 18 | in runtime errors. | ||
| 19 | |||
| 20 | endif | ||
diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile new file mode 100644 index 00000000000..595923d809d --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # | ||
| 2 | # Pressure Slaves to MPUxxxx | ||
| 3 | # | ||
| 4 | obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o | ||
| 5 | inv_mpu_bma085-objs += bma085.o | ||
| 6 | |||
| 7 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
| 8 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c new file mode 100644 index 00000000000..696d2b6e183 --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/bma085.c | |||
| @@ -0,0 +1,367 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /** | ||
| 21 | * @defgroup ACCELDL (Motion Library - Pressure Driver Layer) | ||
| 22 | * @brief Provides the interface to setup and handle a pressure | ||
| 23 | * connected to the secondary I2C interface of the gyroscope. | ||
| 24 | * | ||
| 25 | * @{ | ||
| 26 | * @file bma085.c | ||
| 27 | * @brief Pressure setup and handling methods. | ||
| 28 | */ | ||
| 29 | |||
| 30 | /* ------------------ */ | ||
| 31 | /* - Include Files. - */ | ||
| 32 | /* ------------------ */ | ||
| 33 | |||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/moduleparam.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/delay.h> | ||
| 41 | #include "mpu-dev.h" | ||
| 42 | |||
| 43 | #include <linux/mpu.h> | ||
| 44 | #include "mlsl.h" | ||
| 45 | #include "log.h" | ||
| 46 | |||
| 47 | /* | ||
| 48 | * this structure holds all device specific calibration parameters | ||
| 49 | */ | ||
| 50 | struct bmp085_calibration_param_t { | ||
| 51 | short ac1; | ||
| 52 | short ac2; | ||
| 53 | short ac3; | ||
| 54 | unsigned short ac4; | ||
| 55 | unsigned short ac5; | ||
| 56 | unsigned short ac6; | ||
| 57 | short b1; | ||
| 58 | short b2; | ||
| 59 | short mb; | ||
| 60 | short mc; | ||
| 61 | short md; | ||
| 62 | long param_b5; | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct bmp085_calibration_param_t cal_param; | ||
| 66 | |||
| 67 | #define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */ | ||
| 68 | #define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */ | ||
| 69 | #define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */ | ||
| 70 | |||
| 71 | /********************************************* | ||
| 72 | * Pressure Initialization Functions | ||
| 73 | *********************************************/ | ||
| 74 | |||
| 75 | static int bma085_suspend(void *mlsl_handle, | ||
| 76 | struct ext_slave_descr *slave, | ||
| 77 | struct ext_slave_platform_data *pdata) | ||
| 78 | { | ||
| 79 | int result = INV_SUCCESS; | ||
| 80 | return result; | ||
| 81 | } | ||
| 82 | |||
| 83 | #define PRESSURE_BMA085_PROM_START_ADDR (0xAA) | ||
| 84 | #define PRESSURE_BMA085_PROM_DATA_LEN (22) | ||
| 85 | #define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4) | ||
| 86 | /* temperature measurent */ | ||
| 87 | #define PRESSURE_BMP085_T_MEAS (0x2E) | ||
| 88 | /* pressure measurement; oversampling_setting */ | ||
| 89 | #define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34) | ||
| 90 | #define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74) | ||
| 91 | #define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4) | ||
| 92 | #define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4) | ||
| 93 | #define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6) | ||
| 94 | #define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7) | ||
| 95 | |||
| 96 | static int bma085_resume(void *mlsl_handle, | ||
| 97 | struct ext_slave_descr *slave, | ||
| 98 | struct ext_slave_platform_data *pdata) | ||
| 99 | { | ||
| 100 | int result; | ||
| 101 | unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN]; | ||
| 102 | |||
| 103 | result = | ||
| 104 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 105 | PRESSURE_BMA085_PROM_START_ADDR, | ||
| 106 | PRESSURE_BMA085_PROM_DATA_LEN, data); | ||
| 107 | if (result) { | ||
| 108 | LOG_RESULT_LOCATION(result); | ||
| 109 | return result; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* parameters AC1-AC6 */ | ||
| 113 | cal_param.ac1 = (data[0] << 8) | data[1]; | ||
| 114 | cal_param.ac2 = (data[2] << 8) | data[3]; | ||
| 115 | cal_param.ac3 = (data[4] << 8) | data[5]; | ||
| 116 | cal_param.ac4 = (data[6] << 8) | data[7]; | ||
| 117 | cal_param.ac5 = (data[8] << 8) | data[9]; | ||
| 118 | cal_param.ac6 = (data[10] << 8) | data[11]; | ||
| 119 | |||
| 120 | /* parameters B1,B2 */ | ||
| 121 | cal_param.b1 = (data[12] << 8) | data[13]; | ||
| 122 | cal_param.b2 = (data[14] << 8) | data[15]; | ||
| 123 | |||
| 124 | /* parameters MB,MC,MD */ | ||
| 125 | cal_param.mb = (data[16] << 8) | data[17]; | ||
| 126 | cal_param.mc = (data[18] << 8) | data[19]; | ||
| 127 | cal_param.md = (data[20] << 8) | data[21]; | ||
| 128 | |||
| 129 | return result; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int bma085_read(void *mlsl_handle, | ||
| 133 | struct ext_slave_descr *slave, | ||
| 134 | struct ext_slave_platform_data *pdata, | ||
| 135 | unsigned char *data) | ||
| 136 | { | ||
| 137 | int result; | ||
| 138 | long pressure, x1, x2, x3, b3, b6; | ||
| 139 | unsigned long b4, b7; | ||
| 140 | unsigned long up; | ||
| 141 | unsigned short ut; | ||
| 142 | short oversampling_setting = 0; | ||
| 143 | short temperature; | ||
| 144 | long divisor; | ||
| 145 | |||
| 146 | /* get temprature */ | ||
| 147 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 148 | PRESSURE_BMP085_CTRL_MEAS_REG, | ||
| 149 | PRESSURE_BMP085_T_MEAS); | ||
| 150 | msleep(5); | ||
| 151 | result = | ||
| 152 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 153 | PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, | ||
| 154 | (unsigned char *)data); | ||
| 155 | if (result) { | ||
| 156 | LOG_RESULT_LOCATION(result); | ||
| 157 | return result; | ||
| 158 | } | ||
| 159 | ut = (data[0] << 8) | data[1]; | ||
| 160 | |||
| 161 | x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15; | ||
| 162 | divisor = x1 + cal_param.md; | ||
| 163 | if (!divisor) | ||
| 164 | return INV_ERROR_DIVIDE_BY_ZERO; | ||
| 165 | |||
| 166 | x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md); | ||
| 167 | cal_param.param_b5 = x1 + x2; | ||
| 168 | /* temperature in 0.1 degree C */ | ||
| 169 | temperature = (short)((cal_param.param_b5 + 8) >> 4); | ||
| 170 | |||
| 171 | /* get pressure */ | ||
| 172 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
| 173 | PRESSURE_BMP085_CTRL_MEAS_REG, | ||
| 174 | PRESSURE_BMP085_P_MEAS_OSS_0); | ||
| 175 | msleep(5); | ||
| 176 | result = | ||
| 177 | inv_serial_read(mlsl_handle, pdata->address, | ||
| 178 | PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, | ||
| 179 | (unsigned char *)data); | ||
| 180 | if (result) { | ||
| 181 | LOG_RESULT_LOCATION(result); | ||
| 182 | return result; | ||
| 183 | } | ||
| 184 | up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1])); | ||
| 185 | |||
| 186 | b6 = cal_param.param_b5 - 4000; | ||
| 187 | /* calculate B3 */ | ||
| 188 | x1 = (b6*b6) >> 12; | ||
| 189 | x1 *= cal_param.b2; | ||
| 190 | x1 >>= 11; | ||
| 191 | |||
| 192 | x2 = (cal_param.ac2*b6); | ||
| 193 | x2 >>= 11; | ||
| 194 | |||
| 195 | x3 = x1 + x2; | ||
| 196 | |||
| 197 | b3 = (((((long)cal_param.ac1) * 4 + x3) | ||
| 198 | << oversampling_setting) + 2) >> 2; | ||
| 199 | |||
| 200 | /* calculate B4 */ | ||
| 201 | x1 = (cal_param.ac3 * b6) >> 13; | ||
| 202 | x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16; | ||
| 203 | x3 = ((x1 + x2) + 2) >> 2; | ||
| 204 | b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15; | ||
| 205 | if (!b4) | ||
| 206 | return INV_ERROR; | ||
| 207 | |||
| 208 | b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting)); | ||
| 209 | if (b7 < 0x80000000) | ||
| 210 | pressure = (b7 << 1) / b4; | ||
| 211 | else | ||
| 212 | pressure = (b7 / b4) << 1; | ||
| 213 | |||
| 214 | x1 = pressure >> 8; | ||
| 215 | x1 *= x1; | ||
| 216 | x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16; | ||
| 217 | x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16; | ||
| 218 | /* pressure in Pa */ | ||
| 219 | pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4; | ||
| 220 | |||
| 221 | data[0] = (unsigned char)(pressure >> 16); | ||
| 222 | data[1] = (unsigned char)(pressure >> 8); | ||
| 223 | data[2] = (unsigned char)(pressure & 0xFF); | ||
| 224 | |||
| 225 | return result; | ||
| 226 | } | ||
| 227 | |||
| 228 | static struct ext_slave_descr bma085_descr = { | ||
| 229 | .init = NULL, | ||
| 230 | .exit = NULL, | ||
| 231 | .suspend = bma085_suspend, | ||
| 232 | .resume = bma085_resume, | ||
| 233 | .read = bma085_read, | ||
| 234 | .config = NULL, | ||
| 235 | .get_config = NULL, | ||
| 236 | .name = "bma085", | ||
| 237 | .type = EXT_SLAVE_TYPE_PRESSURE, | ||
| 238 | .id = PRESSURE_ID_BMA085, | ||
| 239 | .read_reg = 0xF6, | ||
| 240 | .read_len = 3, | ||
| 241 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
| 242 | .range = {0, 0}, | ||
| 243 | }; | ||
| 244 | |||
| 245 | static | ||
| 246 | struct ext_slave_descr *bma085_get_slave_descr(void) | ||
| 247 | { | ||
| 248 | return &bma085_descr; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* Platform data for the MPU */ | ||
| 252 | struct bma085_mod_private_data { | ||
| 253 | struct i2c_client *client; | ||
| 254 | struct ext_slave_platform_data *pdata; | ||
| 255 | }; | ||
| 256 | |||
| 257 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 258 | |||
| 259 | static int bma085_mod_probe(struct i2c_client *client, | ||
| 260 | const struct i2c_device_id *devid) | ||
| 261 | { | ||
| 262 | struct ext_slave_platform_data *pdata; | ||
| 263 | struct bma085_mod_private_data *private_data; | ||
| 264 | int result = 0; | ||
| 265 | |||
| 266 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
| 267 | |||
| 268 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 269 | result = -ENODEV; | ||
| 270 | goto out_no_free; | ||
| 271 | } | ||
| 272 | |||
| 273 | pdata = client->dev.platform_data; | ||
| 274 | if (!pdata) { | ||
| 275 | dev_err(&client->adapter->dev, | ||
| 276 | "Missing platform data for slave %s\n", devid->name); | ||
| 277 | result = -EFAULT; | ||
| 278 | goto out_no_free; | ||
| 279 | } | ||
| 280 | |||
| 281 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
| 282 | if (!private_data) { | ||
| 283 | result = -ENOMEM; | ||
| 284 | goto out_no_free; | ||
| 285 | } | ||
| 286 | |||
| 287 | i2c_set_clientdata(client, private_data); | ||
| 288 | private_data->client = client; | ||
| 289 | private_data->pdata = pdata; | ||
| 290 | |||
| 291 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
| 292 | bma085_get_slave_descr); | ||
| 293 | if (result) { | ||
| 294 | dev_err(&client->adapter->dev, | ||
| 295 | "Slave registration failed: %s, %d\n", | ||
| 296 | devid->name, result); | ||
| 297 | goto out_free_memory; | ||
| 298 | } | ||
| 299 | |||
| 300 | return result; | ||
| 301 | |||
| 302 | out_free_memory: | ||
| 303 | kfree(private_data); | ||
| 304 | out_no_free: | ||
| 305 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
| 306 | return result; | ||
| 307 | |||
| 308 | } | ||
| 309 | |||
| 310 | static int bma085_mod_remove(struct i2c_client *client) | ||
| 311 | { | ||
| 312 | struct bma085_mod_private_data *private_data = | ||
| 313 | i2c_get_clientdata(client); | ||
| 314 | |||
| 315 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
| 316 | |||
| 317 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
| 318 | bma085_get_slave_descr); | ||
| 319 | |||
| 320 | kfree(private_data); | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | static const struct i2c_device_id bma085_mod_id[] = { | ||
| 325 | { "bma085", PRESSURE_ID_BMA085 }, | ||
| 326 | {} | ||
| 327 | }; | ||
| 328 | |||
| 329 | MODULE_DEVICE_TABLE(i2c, bma085_mod_id); | ||
| 330 | |||
| 331 | static struct i2c_driver bma085_mod_driver = { | ||
| 332 | .class = I2C_CLASS_HWMON, | ||
| 333 | .probe = bma085_mod_probe, | ||
| 334 | .remove = bma085_mod_remove, | ||
| 335 | .id_table = bma085_mod_id, | ||
| 336 | .driver = { | ||
| 337 | .owner = THIS_MODULE, | ||
| 338 | .name = "bma085_mod", | ||
| 339 | }, | ||
| 340 | .address_list = normal_i2c, | ||
| 341 | }; | ||
| 342 | |||
| 343 | static int __init bma085_mod_init(void) | ||
| 344 | { | ||
| 345 | int res = i2c_add_driver(&bma085_mod_driver); | ||
| 346 | pr_info("%s: Probe name %s\n", __func__, "bma085_mod"); | ||
| 347 | if (res) | ||
| 348 | pr_err("%s failed\n", __func__); | ||
| 349 | return res; | ||
| 350 | } | ||
| 351 | |||
| 352 | static void __exit bma085_mod_exit(void) | ||
| 353 | { | ||
| 354 | pr_info("%s\n", __func__); | ||
| 355 | i2c_del_driver(&bma085_mod_driver); | ||
| 356 | } | ||
| 357 | |||
| 358 | module_init(bma085_mod_init); | ||
| 359 | module_exit(bma085_mod_exit); | ||
| 360 | |||
| 361 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 362 | MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU"); | ||
| 363 | MODULE_LICENSE("GPL"); | ||
| 364 | MODULE_ALIAS("bma085_mod"); | ||
| 365 | /** | ||
| 366 | * @} | ||
| 367 | **/ | ||
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c new file mode 100644 index 00000000000..22cfa4e458e --- /dev/null +++ b/drivers/misc/inv_mpu/slaveirq.c | |||
| @@ -0,0 +1,268 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/irq.h> | ||
| 26 | #include <linux/signal.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #include <linux/i2c.h> | ||
| 29 | #include <linux/i2c-dev.h> | ||
| 30 | #include <linux/poll.h> | ||
| 31 | |||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/mm.h> | ||
| 35 | #include <linux/sched.h> | ||
| 36 | #include <linux/wait.h> | ||
| 37 | #include <linux/uaccess.h> | ||
| 38 | #include <linux/io.h> | ||
| 39 | #include <linux/wait.h> | ||
| 40 | #include <linux/slab.h> | ||
| 41 | |||
| 42 | #include <linux/mpu.h> | ||
| 43 | #include "slaveirq.h" | ||
| 44 | #include "mldl_cfg.h" | ||
| 45 | |||
| 46 | /* function which gets slave data and sends it to SLAVE */ | ||
| 47 | |||
| 48 | struct slaveirq_dev_data { | ||
| 49 | struct miscdevice dev; | ||
| 50 | struct i2c_client *slave_client; | ||
| 51 | struct mpuirq_data data; | ||
| 52 | wait_queue_head_t slaveirq_wait; | ||
| 53 | int irq; | ||
| 54 | int pid; | ||
| 55 | int data_ready; | ||
| 56 | int timeout; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
| 60 | * drivers: misc: pass miscdevice pointer via file private data | ||
| 61 | */ | ||
| 62 | static int slaveirq_open(struct inode *inode, struct file *file) | ||
| 63 | { | ||
| 64 | /* Device node is availabe in the file->private_data, this is | ||
| 65 | * exactly what we want so we leave it there */ | ||
| 66 | struct slaveirq_dev_data *data = | ||
| 67 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 68 | |||
| 69 | dev_dbg(data->dev.this_device, | ||
| 70 | "%s current->pid %d\n", __func__, current->pid); | ||
| 71 | data->pid = current->pid; | ||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int slaveirq_release(struct inode *inode, struct file *file) | ||
| 76 | { | ||
| 77 | struct slaveirq_dev_data *data = | ||
| 78 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 79 | dev_dbg(data->dev.this_device, "slaveirq_release\n"); | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* read function called when from /dev/slaveirq is read */ | ||
| 84 | static ssize_t slaveirq_read(struct file *file, | ||
| 85 | char *buf, size_t count, loff_t *ppos) | ||
| 86 | { | ||
| 87 | int len, err; | ||
| 88 | struct slaveirq_dev_data *data = | ||
| 89 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 90 | |||
| 91 | if (!data->data_ready && data->timeout && | ||
| 92 | !(file->f_flags & O_NONBLOCK)) { | ||
| 93 | wait_event_interruptible_timeout(data->slaveirq_wait, | ||
| 94 | data->data_ready, | ||
| 95 | data->timeout); | ||
| 96 | } | ||
| 97 | |||
| 98 | if (data->data_ready && NULL != buf && count >= sizeof(data->data)) { | ||
| 99 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
| 100 | data->data.data_type = 0; | ||
| 101 | } else { | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | if (err != 0) { | ||
| 105 | dev_err(data->dev.this_device, | ||
| 106 | "Copy to user returned %d\n", err); | ||
| 107 | return -EFAULT; | ||
| 108 | } | ||
| 109 | data->data_ready = 0; | ||
| 110 | len = sizeof(data->data); | ||
| 111 | return len; | ||
| 112 | } | ||
| 113 | |||
| 114 | static unsigned int slaveirq_poll(struct file *file, | ||
| 115 | struct poll_table_struct *poll) | ||
| 116 | { | ||
| 117 | int mask = 0; | ||
| 118 | struct slaveirq_dev_data *data = | ||
| 119 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 120 | |||
| 121 | poll_wait(file, &data->slaveirq_wait, poll); | ||
| 122 | if (data->data_ready) | ||
| 123 | mask |= POLLIN | POLLRDNORM; | ||
| 124 | return mask; | ||
| 125 | } | ||
| 126 | |||
| 127 | /* ioctl - I/O control */ | ||
| 128 | static long slaveirq_ioctl(struct file *file, | ||
| 129 | unsigned int cmd, unsigned long arg) | ||
| 130 | { | ||
| 131 | int retval = 0; | ||
| 132 | int tmp; | ||
| 133 | struct slaveirq_dev_data *data = | ||
| 134 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
| 135 | |||
| 136 | switch (cmd) { | ||
| 137 | case SLAVEIRQ_SET_TIMEOUT: | ||
| 138 | data->timeout = arg; | ||
| 139 | break; | ||
| 140 | |||
| 141 | case SLAVEIRQ_GET_INTERRUPT_CNT: | ||
| 142 | tmp = data->data.interruptcount - 1; | ||
| 143 | if (data->data.interruptcount > 1) | ||
| 144 | data->data.interruptcount = 1; | ||
| 145 | |||
| 146 | if (copy_to_user((int *)arg, &tmp, sizeof(int))) | ||
| 147 | return -EFAULT; | ||
| 148 | break; | ||
| 149 | case SLAVEIRQ_GET_IRQ_TIME: | ||
| 150 | if (copy_to_user((int *)arg, &data->data.irqtime, | ||
| 151 | sizeof(data->data.irqtime))) | ||
| 152 | return -EFAULT; | ||
| 153 | data->data.irqtime = 0; | ||
| 154 | break; | ||
| 155 | default: | ||
| 156 | retval = -EINVAL; | ||
| 157 | } | ||
| 158 | return retval; | ||
| 159 | } | ||
| 160 | |||
| 161 | static irqreturn_t slaveirq_handler(int irq, void *dev_id) | ||
| 162 | { | ||
| 163 | struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; | ||
| 164 | static int mycount; | ||
| 165 | struct timeval irqtime; | ||
| 166 | mycount++; | ||
| 167 | |||
| 168 | data->data.interruptcount++; | ||
| 169 | |||
| 170 | /* wake up (unblock) for reading data from userspace */ | ||
| 171 | data->data_ready = 1; | ||
| 172 | |||
| 173 | do_gettimeofday(&irqtime); | ||
| 174 | data->data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
| 175 | data->data.irqtime += irqtime.tv_usec; | ||
| 176 | data->data.data_type |= 1; | ||
| 177 | |||
| 178 | wake_up_interruptible(&data->slaveirq_wait); | ||
| 179 | |||
| 180 | return IRQ_HANDLED; | ||
| 181 | |||
| 182 | } | ||
| 183 | |||
| 184 | /* define which file operations are supported */ | ||
| 185 | static const struct file_operations slaveirq_fops = { | ||
| 186 | .owner = THIS_MODULE, | ||
| 187 | .read = slaveirq_read, | ||
| 188 | .poll = slaveirq_poll, | ||
| 189 | |||
| 190 | #if HAVE_COMPAT_IOCTL | ||
| 191 | .compat_ioctl = slaveirq_ioctl, | ||
| 192 | #endif | ||
| 193 | #if HAVE_UNLOCKED_IOCTL | ||
| 194 | .unlocked_ioctl = slaveirq_ioctl, | ||
| 195 | #endif | ||
| 196 | .open = slaveirq_open, | ||
| 197 | .release = slaveirq_release, | ||
| 198 | }; | ||
| 199 | |||
| 200 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
| 201 | struct ext_slave_platform_data *pdata, char *name) | ||
| 202 | { | ||
| 203 | |||
| 204 | int res; | ||
| 205 | struct slaveirq_dev_data *data; | ||
| 206 | |||
| 207 | if (!pdata->irq) | ||
| 208 | return -EINVAL; | ||
| 209 | |||
| 210 | pdata->irq_data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 211 | data = (struct slaveirq_dev_data *)pdata->irq_data; | ||
| 212 | if (!data) | ||
| 213 | return -ENOMEM; | ||
| 214 | |||
| 215 | data->dev.minor = MISC_DYNAMIC_MINOR; | ||
| 216 | data->dev.name = name; | ||
| 217 | data->dev.fops = &slaveirq_fops; | ||
| 218 | data->irq = pdata->irq; | ||
| 219 | data->pid = 0; | ||
| 220 | data->data_ready = 0; | ||
| 221 | data->timeout = 0; | ||
| 222 | |||
| 223 | init_waitqueue_head(&data->slaveirq_wait); | ||
| 224 | |||
| 225 | res = request_irq(data->irq, slaveirq_handler, | ||
| 226 | IRQF_TRIGGER_RISING | IRQF_SHARED, | ||
| 227 | data->dev.name, data); | ||
| 228 | |||
| 229 | if (res) { | ||
| 230 | dev_err(&slave_adapter->dev, | ||
| 231 | "myirqtest: cannot register IRQ %d\n", data->irq); | ||
| 232 | goto out_request_irq; | ||
| 233 | } | ||
| 234 | |||
| 235 | res = misc_register(&data->dev); | ||
| 236 | if (res < 0) { | ||
| 237 | dev_err(&slave_adapter->dev, | ||
| 238 | "misc_register returned %d\n", res); | ||
| 239 | goto out_misc_register; | ||
| 240 | } | ||
| 241 | |||
| 242 | return res; | ||
| 243 | |||
| 244 | out_misc_register: | ||
| 245 | free_irq(data->irq, data); | ||
| 246 | out_request_irq: | ||
| 247 | kfree(pdata->irq_data); | ||
| 248 | pdata->irq_data = NULL; | ||
| 249 | |||
| 250 | return res; | ||
| 251 | } | ||
| 252 | EXPORT_SYMBOL(slaveirq_init); | ||
| 253 | |||
| 254 | void slaveirq_exit(struct ext_slave_platform_data *pdata) | ||
| 255 | { | ||
| 256 | struct slaveirq_dev_data *data = pdata->irq_data; | ||
| 257 | |||
| 258 | if (!pdata->irq_data || data->irq <= 0) | ||
| 259 | return; | ||
| 260 | |||
| 261 | dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name); | ||
| 262 | |||
| 263 | free_irq(data->irq, data); | ||
| 264 | misc_deregister(&data->dev); | ||
| 265 | kfree(pdata->irq_data); | ||
| 266 | pdata->irq_data = NULL; | ||
| 267 | } | ||
| 268 | EXPORT_SYMBOL(slaveirq_exit); | ||
diff --git a/drivers/misc/inv_mpu/slaveirq.h b/drivers/misc/inv_mpu/slaveirq.h new file mode 100644 index 00000000000..6926634ff94 --- /dev/null +++ b/drivers/misc/inv_mpu/slaveirq.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SLAVEIRQ__ | ||
| 21 | #define __SLAVEIRQ__ | ||
| 22 | |||
| 23 | #include <linux/i2c-dev.h> | ||
| 24 | |||
| 25 | #include <linux/mpu.h> | ||
| 26 | #include "mpuirq.h" | ||
| 27 | |||
| 28 | #define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long) | ||
| 29 | #define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long) | ||
| 30 | #define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long) | ||
| 31 | |||
| 32 | void slaveirq_exit(struct ext_slave_platform_data *pdata); | ||
| 33 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
| 34 | struct ext_slave_platform_data *pdata, char *name); | ||
| 35 | |||
| 36 | #endif | ||
diff --git a/drivers/misc/inv_mpu/timerirq.c b/drivers/misc/inv_mpu/timerirq.c new file mode 100644 index 00000000000..601858f9c4d --- /dev/null +++ b/drivers/misc/inv_mpu/timerirq.c | |||
| @@ -0,0 +1,296 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/stat.h> | ||
| 25 | #include <linux/signal.h> | ||
| 26 | #include <linux/miscdevice.h> | ||
| 27 | #include <linux/i2c.h> | ||
| 28 | #include <linux/i2c-dev.h> | ||
| 29 | #include <linux/poll.h> | ||
| 30 | |||
| 31 | #include <linux/errno.h> | ||
| 32 | #include <linux/fs.h> | ||
| 33 | #include <linux/mm.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #include <linux/wait.h> | ||
| 36 | #include <linux/uaccess.h> | ||
| 37 | #include <linux/io.h> | ||
| 38 | #include <linux/timer.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | |||
| 41 | #include <linux/mpu.h> | ||
| 42 | #include "mltypes.h" | ||
| 43 | #include "timerirq.h" | ||
| 44 | |||
| 45 | /* function which gets timer data and sends it to TIMER */ | ||
| 46 | struct timerirq_data { | ||
| 47 | int pid; | ||
| 48 | int data_ready; | ||
| 49 | int run; | ||
| 50 | int timeout; | ||
| 51 | unsigned long period; | ||
| 52 | struct mpuirq_data data; | ||
| 53 | struct completion timer_done; | ||
| 54 | wait_queue_head_t timerirq_wait; | ||
| 55 | struct timer_list timer; | ||
| 56 | struct miscdevice *dev; | ||
| 57 | }; | ||
| 58 | |||
| 59 | static struct miscdevice *timerirq_dev_data; | ||
| 60 | |||
| 61 | static void timerirq_handler(unsigned long arg) | ||
| 62 | { | ||
| 63 | struct timerirq_data *data = (struct timerirq_data *)arg; | ||
| 64 | struct timeval irqtime; | ||
| 65 | |||
| 66 | data->data.interruptcount++; | ||
| 67 | |||
| 68 | data->data_ready = 1; | ||
| 69 | |||
| 70 | do_gettimeofday(&irqtime); | ||
| 71 | data->data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
| 72 | data->data.irqtime += irqtime.tv_usec; | ||
| 73 | data->data.data_type |= 1; | ||
| 74 | |||
| 75 | dev_dbg(data->dev->this_device, | ||
| 76 | "%s, %lld, %ld\n", __func__, data->data.irqtime, | ||
| 77 | (unsigned long)data); | ||
| 78 | |||
| 79 | wake_up_interruptible(&data->timerirq_wait); | ||
| 80 | |||
| 81 | if (data->run) | ||
| 82 | mod_timer(&data->timer, | ||
| 83 | jiffies + msecs_to_jiffies(data->period)); | ||
| 84 | else | ||
| 85 | complete(&data->timer_done); | ||
| 86 | } | ||
| 87 | |||
| 88 | static int start_timerirq(struct timerirq_data *data) | ||
| 89 | { | ||
| 90 | dev_dbg(data->dev->this_device, | ||
| 91 | "%s current->pid %d\n", __func__, current->pid); | ||
| 92 | |||
| 93 | /* Timer already running... success */ | ||
| 94 | if (data->run) | ||
| 95 | return 0; | ||
| 96 | |||
| 97 | /* Don't allow a period of 0 since this would fire constantly */ | ||
| 98 | if (!data->period) | ||
| 99 | return -EINVAL; | ||
| 100 | |||
| 101 | data->run = true; | ||
| 102 | data->data_ready = false; | ||
| 103 | |||
| 104 | init_completion(&data->timer_done); | ||
| 105 | setup_timer(&data->timer, timerirq_handler, (unsigned long)data); | ||
| 106 | |||
| 107 | return mod_timer(&data->timer, | ||
| 108 | jiffies + msecs_to_jiffies(data->period)); | ||
| 109 | } | ||
| 110 | |||
| 111 | static int stop_timerirq(struct timerirq_data *data) | ||
| 112 | { | ||
| 113 | dev_dbg(data->dev->this_device, | ||
| 114 | "%s current->pid %lx\n", __func__, (unsigned long)data); | ||
| 115 | |||
| 116 | if (data->run) { | ||
| 117 | data->run = false; | ||
| 118 | mod_timer(&data->timer, jiffies + 1); | ||
| 119 | wait_for_completion(&data->timer_done); | ||
| 120 | } | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
| 125 | * drivers: misc: pass miscdevice pointer via file private data | ||
| 126 | */ | ||
| 127 | static int timerirq_open(struct inode *inode, struct file *file) | ||
| 128 | { | ||
| 129 | /* Device node is availabe in the file->private_data, this is | ||
| 130 | * exactly what we want so we leave it there */ | ||
| 131 | struct miscdevice *dev_data = file->private_data; | ||
| 132 | struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 133 | if (!data) | ||
| 134 | return -ENOMEM; | ||
| 135 | |||
| 136 | data->dev = dev_data; | ||
| 137 | file->private_data = data; | ||
| 138 | data->pid = current->pid; | ||
| 139 | init_waitqueue_head(&data->timerirq_wait); | ||
| 140 | |||
| 141 | dev_dbg(data->dev->this_device, | ||
| 142 | "%s current->pid %d\n", __func__, current->pid); | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static int timerirq_release(struct inode *inode, struct file *file) | ||
| 147 | { | ||
| 148 | struct timerirq_data *data = file->private_data; | ||
| 149 | dev_dbg(data->dev->this_device, "timerirq_release\n"); | ||
| 150 | if (data->run) | ||
| 151 | stop_timerirq(data); | ||
| 152 | kfree(data); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* read function called when from /dev/timerirq is read */ | ||
| 157 | static ssize_t timerirq_read(struct file *file, | ||
| 158 | char *buf, size_t count, loff_t *ppos) | ||
| 159 | { | ||
| 160 | int len, err; | ||
| 161 | struct timerirq_data *data = file->private_data; | ||
| 162 | |||
| 163 | if (!data->data_ready && data->timeout && | ||
| 164 | !(file->f_flags & O_NONBLOCK)) { | ||
| 165 | wait_event_interruptible_timeout(data->timerirq_wait, | ||
| 166 | data->data_ready, | ||
| 167 | data->timeout); | ||
| 168 | } | ||
| 169 | |||
| 170 | if (data->data_ready && NULL != buf && count >= sizeof(data->data)) { | ||
| 171 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
| 172 | data->data.data_type = 0; | ||
| 173 | } else { | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | if (err != 0) { | ||
| 177 | dev_err(data->dev->this_device, | ||
| 178 | "Copy to user returned %d\n", err); | ||
| 179 | return -EFAULT; | ||
| 180 | } | ||
| 181 | data->data_ready = 0; | ||
| 182 | len = sizeof(data->data); | ||
| 183 | return len; | ||
| 184 | } | ||
| 185 | |||
| 186 | static unsigned int timerirq_poll(struct file *file, | ||
| 187 | struct poll_table_struct *poll) | ||
| 188 | { | ||
| 189 | int mask = 0; | ||
| 190 | struct timerirq_data *data = file->private_data; | ||
| 191 | |||
| 192 | poll_wait(file, &data->timerirq_wait, poll); | ||
| 193 | if (data->data_ready) | ||
| 194 | mask |= POLLIN | POLLRDNORM; | ||
| 195 | return mask; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* ioctl - I/O control */ | ||
| 199 | static long timerirq_ioctl(struct file *file, | ||
| 200 | unsigned int cmd, unsigned long arg) | ||
| 201 | { | ||
| 202 | int retval = 0; | ||
| 203 | int tmp; | ||
| 204 | struct timerirq_data *data = file->private_data; | ||
| 205 | |||
| 206 | dev_dbg(data->dev->this_device, | ||
| 207 | "%s current->pid %d, %d, %ld\n", | ||
| 208 | __func__, current->pid, cmd, arg); | ||
| 209 | |||
| 210 | if (!data) | ||
| 211 | return -EFAULT; | ||
| 212 | |||
| 213 | switch (cmd) { | ||
| 214 | case TIMERIRQ_SET_TIMEOUT: | ||
| 215 | data->timeout = arg; | ||
| 216 | break; | ||
| 217 | case TIMERIRQ_GET_INTERRUPT_CNT: | ||
| 218 | tmp = data->data.interruptcount - 1; | ||
| 219 | if (data->data.interruptcount > 1) | ||
| 220 | data->data.interruptcount = 1; | ||
| 221 | |||
| 222 | if (copy_to_user((int *)arg, &tmp, sizeof(int))) | ||
| 223 | return -EFAULT; | ||
| 224 | break; | ||
| 225 | case TIMERIRQ_START: | ||
| 226 | data->period = arg; | ||
| 227 | retval = start_timerirq(data); | ||
| 228 | break; | ||
| 229 | case TIMERIRQ_STOP: | ||
| 230 | retval = stop_timerirq(data); | ||
| 231 | break; | ||
| 232 | default: | ||
| 233 | retval = -EINVAL; | ||
| 234 | } | ||
| 235 | return retval; | ||
| 236 | } | ||
| 237 | |||
| 238 | /* define which file operations are supported */ | ||
| 239 | static const struct file_operations timerirq_fops = { | ||
| 240 | .owner = THIS_MODULE, | ||
| 241 | .read = timerirq_read, | ||
| 242 | .poll = timerirq_poll, | ||
| 243 | |||
| 244 | #if HAVE_COMPAT_IOCTL | ||
| 245 | .compat_ioctl = timerirq_ioctl, | ||
| 246 | #endif | ||
| 247 | #if HAVE_UNLOCKED_IOCTL | ||
| 248 | .unlocked_ioctl = timerirq_ioctl, | ||
| 249 | #endif | ||
| 250 | .open = timerirq_open, | ||
| 251 | .release = timerirq_release, | ||
| 252 | }; | ||
| 253 | |||
| 254 | static int __init timerirq_init(void) | ||
| 255 | { | ||
| 256 | |||
| 257 | int res; | ||
| 258 | static struct miscdevice *data; | ||
| 259 | |||
| 260 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 261 | if (!data) | ||
| 262 | return -ENOMEM; | ||
| 263 | timerirq_dev_data = data; | ||
| 264 | data->minor = MISC_DYNAMIC_MINOR; | ||
| 265 | data->name = "timerirq"; | ||
| 266 | data->fops = &timerirq_fops; | ||
| 267 | |||
| 268 | res = misc_register(data); | ||
| 269 | if (res < 0) { | ||
| 270 | dev_err(data->this_device, "misc_register returned %d\n", res); | ||
| 271 | return res; | ||
| 272 | } | ||
| 273 | |||
| 274 | return res; | ||
| 275 | } | ||
| 276 | |||
| 277 | module_init(timerirq_init); | ||
| 278 | |||
| 279 | static void __exit timerirq_exit(void) | ||
| 280 | { | ||
| 281 | struct miscdevice *data = timerirq_dev_data; | ||
| 282 | |||
| 283 | dev_info(data->this_device, "Unregistering %s\n", data->name); | ||
| 284 | |||
| 285 | misc_deregister(data); | ||
| 286 | kfree(data); | ||
| 287 | |||
| 288 | timerirq_dev_data = NULL; | ||
| 289 | } | ||
| 290 | |||
| 291 | module_exit(timerirq_exit); | ||
| 292 | |||
| 293 | MODULE_AUTHOR("Invensense Corporation"); | ||
| 294 | MODULE_DESCRIPTION("Timer IRQ device driver."); | ||
| 295 | MODULE_LICENSE("GPL"); | ||
| 296 | MODULE_ALIAS("timerirq"); | ||
diff --git a/drivers/misc/inv_mpu/timerirq.h b/drivers/misc/inv_mpu/timerirq.h new file mode 100644 index 00000000000..f69f07a45a3 --- /dev/null +++ b/drivers/misc/inv_mpu/timerirq.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | $License: | ||
| 3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
| 4 | |||
| 5 | This program is free software; you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 2 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | $ | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __TIMERIRQ__ | ||
| 21 | #define __TIMERIRQ__ | ||
| 22 | |||
| 23 | #include <linux/mpu.h> | ||
| 24 | |||
| 25 | #define TIMERIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x60, unsigned long) | ||
| 26 | #define TIMERIRQ_GET_INTERRUPT_CNT _IOW(MPU_IOCTL, 0x61, unsigned long) | ||
| 27 | #define TIMERIRQ_START _IOW(MPU_IOCTL, 0x62, unsigned long) | ||
| 28 | #define TIMERIRQ_STOP _IO(MPU_IOCTL, 0x63) | ||
| 29 | |||
| 30 | #endif | ||
