diff options
Diffstat (limited to 'drivers/misc/inv_mpu/accel')
| -rw-r--r-- | drivers/misc/inv_mpu/accel/Kconfig | 133 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/Makefile | 38 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/adxl34x.c | 728 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/bma150.c | 777 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/bma222.c | 654 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/bma250.c | 787 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/cma3000.c | 222 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/kxsd9.c | 264 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/kxtf9.c | 841 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/lis331.c | 745 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/lis3dh.c | 728 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/lsm303dlx_a.c | 881 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/mma8450.c | 804 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/mma845x.c | 713 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/mpu6050.c | 695 | ||||
| -rw-r--r-- | drivers/misc/inv_mpu/accel/mpu6050.h | 28 |
16 files changed, 9038 insertions, 0 deletions
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 | ||
