diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/inv_mpu | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/inv_mpu')
61 files changed, 25836 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig new file mode 100644 index 00000000000..4c231b576f6 --- /dev/null +++ b/drivers/misc/inv_mpu/Kconfig | |||
@@ -0,0 +1,70 @@ | |||
1 | config MPU_SENSORS_TIMERIRQ | ||
2 | tristate "MPU Timer IRQ" | ||
3 | help | ||
4 | If you say yes here you get access to the timerirq device handle which | ||
5 | can be used to select on. This can be used instead of IRQ's, sleeping, | ||
6 | or timer threads. Reading from this device returns the same type of | ||
7 | information as reading from the MPU and slave IRQ's. | ||
8 | |||
9 | menuconfig INV_SENSORS | ||
10 | tristate "Motion Processing Unit" | ||
11 | depends on I2C | ||
12 | default y | ||
13 | |||
14 | if INV_SENSORS | ||
15 | |||
16 | choice | ||
17 | tristate "MPU Master" | ||
18 | depends on I2C && INV_SENSORS | ||
19 | default MPU_SENSORS_MPU3050 | ||
20 | |||
21 | config MPU_SENSORS_MPU3050 | ||
22 | tristate "MPU3050" | ||
23 | depends on I2C | ||
24 | select MPU_SENSORS_MPU3050_GYRO | ||
25 | help | ||
26 | If you say yes here you get support for the MPU3050 Gyroscope driver | ||
27 | This driver can also be built as a module. If so, the module | ||
28 | will be called mpu3050. | ||
29 | |||
30 | config MPU_SENSORS_MPU6050A2 | ||
31 | tristate "MPU6050A2" | ||
32 | depends on I2C | ||
33 | select MPU_SENSORS_MPU6050_GYRO | ||
34 | help | ||
35 | If you say yes here you get support for the MPU6050A2 Gyroscope driver | ||
36 | This driver can also be built as a module. If so, the module | ||
37 | will be called mpu6050a2. | ||
38 | |||
39 | config MPU_SENSORS_MPU6050B1 | ||
40 | tristate "MPU6050B1" | ||
41 | depends on I2C | ||
42 | select MPU_SENSORS_MPU6050_GYRO | ||
43 | help | ||
44 | If you say yes here you get support for the MPU6050 Gyroscope driver | ||
45 | This driver can also be built as a module. If so, the module | ||
46 | will be called mpu6050b1. | ||
47 | |||
48 | endchoice | ||
49 | |||
50 | config MPU_SENSORS_MPU3050_GYRO | ||
51 | bool "MPU3050 built in gyroscope" | ||
52 | depends on MPU_SENSORS_MPU3050 | ||
53 | |||
54 | config MPU_SENSORS_MPU6050_GYRO | ||
55 | bool "MPU6050 built in gyroscope" | ||
56 | depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
57 | |||
58 | source "drivers/misc/inv_mpu/accel/Kconfig" | ||
59 | source "drivers/misc/inv_mpu/compass/Kconfig" | ||
60 | source "drivers/misc/inv_mpu/pressure/Kconfig" | ||
61 | |||
62 | config MPU_USERSPACE_DEBUG | ||
63 | bool "MPU Userspace debugging ioctls" | ||
64 | help | ||
65 | Allows the ioctls MPU_SET_MPU_PLATFORM_DATA and | ||
66 | MPU_SET_EXT_SLAVE_PLATFORM_DATA, which allows userspace applications | ||
67 | to override the slave address and orientation. This is dangerous | ||
68 | and could cause the devices not to work. | ||
69 | |||
70 | endif #INV_SENSORS | ||
diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile new file mode 100644 index 00000000000..f10c6844a08 --- /dev/null +++ b/drivers/misc/inv_mpu/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | |||
2 | # Kernel makefile for motions sensors | ||
3 | # | ||
4 | # | ||
5 | |||
6 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
7 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
8 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
9 | |||
10 | obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o | ||
11 | obj-$(CONFIG_INV_SENSORS) += mpuirq.o slaveirq.o | ||
12 | |||
13 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050/ | ||
14 | obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050/ | ||
15 | |||
16 | obj-y += accel/ | ||
17 | obj-y += compass/ | ||
18 | obj-y += pressure/ | ||
diff --git a/drivers/misc/inv_mpu/accel/Kconfig b/drivers/misc/inv_mpu/accel/Kconfig new file mode 100644 index 00000000000..4e280bd876b --- /dev/null +++ b/drivers/misc/inv_mpu/accel/Kconfig | |||
@@ -0,0 +1,133 @@ | |||
1 | menuconfig INV_SENSORS_ACCELEROMETERS | ||
2 | bool "Accelerometer Slave Sensors" | ||
3 | default y | ||
4 | ---help--- | ||
5 | Say Y here to get to see options for device drivers for various | ||
6 | accelerometrs for integration with the MPU3050 or MPU6050 driver. | ||
7 | This option alone does not add any kernel code. | ||
8 | |||
9 | If you say N, all options in this submenu will be skipped and disabled. | ||
10 | |||
11 | if INV_SENSORS_ACCELEROMETERS | ||
12 | |||
13 | config MPU_SENSORS_ADXL34X | ||
14 | tristate "ADI adxl34x" | ||
15 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
16 | help | ||
17 | This enables support for the ADI adxl345 or adxl346 accelerometers. | ||
18 | This support is for integration with the MPU3050 gyroscope device | ||
19 | driver. Only one accelerometer can be registered at a time. | ||
20 | Specifying more that one accelerometer in the board file will result | ||
21 | in runtime errors. | ||
22 | |||
23 | config MPU_SENSORS_BMA222 | ||
24 | tristate "Bosch BMA222" | ||
25 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
26 | help | ||
27 | This enables support for the Bosch BMA222 accelerometer | ||
28 | This support is for integration with the MPU3050 gyroscope device | ||
29 | driver. Only one accelerometer can be registered at a time. | ||
30 | Specifying more that one accelerometer in the board file will result | ||
31 | in runtime errors. | ||
32 | |||
33 | config MPU_SENSORS_BMA150 | ||
34 | tristate "Bosch BMA150" | ||
35 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
36 | help | ||
37 | This enables support for the Bosch BMA150 accelerometer | ||
38 | This support is for integration with the MPU3050 gyroscope device | ||
39 | driver. Only one accelerometer can be registered at a time. | ||
40 | Specifying more that one accelerometer in the board file will result | ||
41 | in runtime errors. | ||
42 | |||
43 | config MPU_SENSORS_BMA250 | ||
44 | tristate "Bosch BMA250" | ||
45 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
46 | help | ||
47 | This enables support for the Bosch BMA250 accelerometer | ||
48 | This support is for integration with the MPU3050 gyroscope device | ||
49 | driver. Only one accelerometer can be registered at a time. | ||
50 | Specifying more that one accelerometer in the board file will result | ||
51 | in runtime errors. | ||
52 | |||
53 | config MPU_SENSORS_KXSD9 | ||
54 | tristate "Kionix KXSD9" | ||
55 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
56 | help | ||
57 | This enables support for the Kionix KXSD9 accelerometer | ||
58 | This support is for integration with the MPU3050 gyroscope device | ||
59 | driver. Only one accelerometer can be registered at a time. | ||
60 | Specifying more that one accelerometer in the board file will result | ||
61 | in runtime errors. | ||
62 | |||
63 | config MPU_SENSORS_KXTF9 | ||
64 | tristate "Kionix KXTF9" | ||
65 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
66 | help | ||
67 | This enables support for the Kionix KXFT9 accelerometer | ||
68 | This support is for integration with the MPU3050 gyroscope device | ||
69 | driver. Only one accelerometer can be registered at a time. | ||
70 | Specifying more that one accelerometer in the board file will result | ||
71 | in runtime errors. | ||
72 | |||
73 | config MPU_SENSORS_LIS331DLH | ||
74 | tristate "ST lis331dlh" | ||
75 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
76 | help | ||
77 | This enables support for the ST lis331dlh accelerometer | ||
78 | This support is for integration with the MPU3050 gyroscope device | ||
79 | driver. Only one accelerometer can be registered at a time. | ||
80 | Specifying more that one accelerometer in the board file will result | ||
81 | in runtime errors. | ||
82 | |||
83 | config MPU_SENSORS_LIS3DH | ||
84 | tristate "ST lis3dh" | ||
85 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
86 | help | ||
87 | This enables support for the ST lis3dh accelerometer | ||
88 | This support is for integration with the MPU3050 gyroscope device | ||
89 | driver. Only one accelerometer can be registered at a time. | ||
90 | Specifying more that one accelerometer in the board file will result | ||
91 | in runtime errors. | ||
92 | |||
93 | config MPU_SENSORS_LSM303DLX_A | ||
94 | tristate "ST lsm303dlx" | ||
95 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
96 | help | ||
97 | This enables support for the ST lsm303dlh and lsm303dlm accelerometers | ||
98 | This support is for integration with the MPU3050 gyroscope device | ||
99 | driver. Only one accelerometer can be registered at a time. | ||
100 | Specifying more that one accelerometer in the board file will result | ||
101 | in runtime errors. | ||
102 | |||
103 | config MPU_SENSORS_MMA8450 | ||
104 | tristate "Freescale mma8450" | ||
105 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
106 | help | ||
107 | This enables support for the Freescale mma8450 accelerometer | ||
108 | This support is for integration with the MPU3050 gyroscope device | ||
109 | driver. Only one accelerometer can be registered at a time. | ||
110 | Specifying more that one accelerometer in the board file will result | ||
111 | in runtime errors. | ||
112 | |||
113 | config MPU_SENSORS_MMA845X | ||
114 | tristate "Freescale mma8451/8452/8453" | ||
115 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
116 | help | ||
117 | This enables support for the Freescale mma8451/8452/8453 accelerometer | ||
118 | This support is for integration with the MPU3050 gyroscope device | ||
119 | driver. Only one accelerometer can be registered at a time. | ||
120 | Specifying more that one accelerometer in the board file will result | ||
121 | in runtime errors. | ||
122 | |||
123 | config MPU_SENSORS_MPU6050_ACCEL | ||
124 | tristate "MPU6050 built in accelerometer" | ||
125 | depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2 | ||
126 | help | ||
127 | This enables support for the MPU6050 built in accelerometer. | ||
128 | This the built in support for integration with the MPU6050 gyroscope | ||
129 | device driver. This is the only accelerometer supported with the | ||
130 | MPU6050. Specifying another accelerometer in the board file will | ||
131 | result in runtime errors. | ||
132 | |||
133 | endif | ||
diff --git a/drivers/misc/inv_mpu/accel/Makefile b/drivers/misc/inv_mpu/accel/Makefile new file mode 100644 index 00000000000..1f0f5bec677 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/Makefile | |||
@@ -0,0 +1,38 @@ | |||
1 | # | ||
2 | # Accel Slaves to MPUxxxx | ||
3 | # | ||
4 | obj-$(CONFIG_MPU_SENSORS_ADXL34X) += inv_mpu_adxl34x.o | ||
5 | inv_mpu_adxl34x-objs += adxl34x.o | ||
6 | |||
7 | obj-$(CONFIG_MPU_SENSORS_BMA150) += inv_mpu_bma150.o | ||
8 | inv_mpu_bma150-objs += bma150.o | ||
9 | |||
10 | obj-$(CONFIG_MPU_SENSORS_KXTF9) += inv_mpu_kxtf9.o | ||
11 | inv_mpu_kxtf9-objs += kxtf9.o | ||
12 | |||
13 | obj-$(CONFIG_MPU_SENSORS_BMA222) += inv_mpu_bma222.o | ||
14 | inv_mpu_bma222-objs += bma222.o | ||
15 | |||
16 | obj-$(CONFIG_MPU_SENSORS_BMA250) += inv_mpu_bma250.o | ||
17 | inv_mpu_bma250-objs += bma250.o | ||
18 | |||
19 | obj-$(CONFIG_MPU_SENSORS_KXSD9) += inv_mpu_kxsd9.o | ||
20 | inv_mpu_kxsd9-objs += kxsd9.o | ||
21 | |||
22 | obj-$(CONFIG_MPU_SENSORS_LIS331DLH) += inv_mpu_lis331.o | ||
23 | inv_mpu_lis331-objs += lis331.o | ||
24 | |||
25 | obj-$(CONFIG_MPU_SENSORS_LIS3DH) += inv_mpu_lis3dh.o | ||
26 | inv_mpu_lis3dh-objs += lis3dh.o | ||
27 | |||
28 | obj-$(CONFIG_MPU_SENSORS_LSM303DLX_A) += inv_mpu_lsm303dlx_a.o | ||
29 | inv_mpu_lsm303dlx_a-objs += lsm303dlx_a.o | ||
30 | |||
31 | obj-$(CONFIG_MPU_SENSORS_MMA8450) += inv_mpu_mma8450.o | ||
32 | inv_mpu_mma8450-objs += mma8450.o | ||
33 | |||
34 | obj-$(CONFIG_MPU_SENSORS_MMA845X) += inv_mpu_mma845x.o | ||
35 | inv_mpu_mma845x-objs += mma845x.o | ||
36 | |||
37 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
38 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/accel/adxl34x.c b/drivers/misc/inv_mpu/accel/adxl34x.c new file mode 100644 index 00000000000..f2bff8a7592 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/adxl34x.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file adxl34x.c | ||
26 | * @brief Accelerometer setup and handling methods for AD adxl345 and | ||
27 | * adxl346. | ||
28 | */ | ||
29 | |||
30 | /* -------------------------------------------------------------------------- */ | ||
31 | |||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/moduleparam.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include "mpu-dev.h" | ||
40 | |||
41 | #include <log.h> | ||
42 | |||
43 | #include <linux/mpu.h> | ||
44 | #include "mlsl.h" | ||
45 | #include "mldl_cfg.h" | ||
46 | |||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-acc" | ||
49 | |||
50 | /* -------------------------------------------------------------------------- */ | ||
51 | |||
52 | /* registers */ | ||
53 | #define ADXL34X_ODR_REG (0x2C) | ||
54 | #define ADXL34X_PWR_REG (0x2D) | ||
55 | #define ADXL34X_DATAFORMAT_REG (0x31) | ||
56 | |||
57 | /* masks */ | ||
58 | #define ADXL34X_ODR_MASK (0x0F) | ||
59 | #define ADXL34X_PWR_SLEEP_MASK (0x04) | ||
60 | #define ADXL34X_PWR_MEAS_MASK (0x08) | ||
61 | #define ADXL34X_DATAFORMAT_JUSTIFY_MASK (0x04) | ||
62 | #define ADXL34X_DATAFORMAT_FSR_MASK (0x03) | ||
63 | |||
64 | /* -------------------------------------------------------------------------- */ | ||
65 | |||
66 | struct adxl34x_config { | ||
67 | unsigned int odr; /** < output data rate in mHz */ | ||
68 | unsigned int fsr; /** < full scale range mg */ | ||
69 | unsigned int fsr_reg_mask; /** < register setting for fsr */ | ||
70 | }; | ||
71 | |||
72 | struct adxl34x_private_data { | ||
73 | struct adxl34x_config suspend; /** < suspend configuration */ | ||
74 | struct adxl34x_config resume; /** < resume configuration */ | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * @brief Set the output data rate for the particular configuration. | ||
79 | * | ||
80 | * @param mlsl_handle | ||
81 | * the handle to the serial channel the device is connected to. | ||
82 | * @param pdata | ||
83 | * a pointer to the slave platform data. | ||
84 | * @param config | ||
85 | * Config to modify with new ODR. | ||
86 | * @param apply | ||
87 | * whether to apply immediately or save the settings to be applied | ||
88 | * at the next resume. | ||
89 | * @param odr | ||
90 | * Output data rate in units of 1/1000Hz (mHz). | ||
91 | * | ||
92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
93 | */ | ||
94 | static int adxl34x_set_odr(void *mlsl_handle, | ||
95 | struct ext_slave_platform_data *pdata, | ||
96 | struct adxl34x_config *config, | ||
97 | int apply, | ||
98 | long odr) | ||
99 | { | ||
100 | int result = INV_SUCCESS; | ||
101 | unsigned char new_odr_mask; | ||
102 | |||
103 | /* ADXL346 (Rev. A) pages 13, 24 */ | ||
104 | if (odr >= 3200000) { | ||
105 | new_odr_mask = 0x0F; | ||
106 | config->odr = 3200000; | ||
107 | } else if (odr >= 1600000) { | ||
108 | new_odr_mask = 0x0E; | ||
109 | config->odr = 1600000; | ||
110 | } else if (odr >= 800000) { | ||
111 | new_odr_mask = 0x0D; | ||
112 | config->odr = 800000; | ||
113 | } else if (odr >= 400000) { | ||
114 | new_odr_mask = 0x0C; | ||
115 | config->odr = 400000; | ||
116 | } else if (odr >= 200000) { | ||
117 | new_odr_mask = 0x0B; | ||
118 | config->odr = 200000; | ||
119 | } else if (odr >= 100000) { | ||
120 | new_odr_mask = 0x0A; | ||
121 | config->odr = 100000; | ||
122 | } else if (odr >= 50000) { | ||
123 | new_odr_mask = 0x09; | ||
124 | config->odr = 50000; | ||
125 | } else if (odr >= 25000) { | ||
126 | new_odr_mask = 0x08; | ||
127 | config->odr = 25000; | ||
128 | } else if (odr >= 12500) { | ||
129 | new_odr_mask = 0x07; | ||
130 | config->odr = 12500; | ||
131 | } else if (odr >= 6250) { | ||
132 | new_odr_mask = 0x06; | ||
133 | config->odr = 6250; | ||
134 | } else if (odr >= 3130) { | ||
135 | new_odr_mask = 0x05; | ||
136 | config->odr = 3130; | ||
137 | } else if (odr >= 1560) { | ||
138 | new_odr_mask = 0x04; | ||
139 | config->odr = 1560; | ||
140 | } else if (odr >= 780) { | ||
141 | new_odr_mask = 0x03; | ||
142 | config->odr = 780; | ||
143 | } else if (odr >= 390) { | ||
144 | new_odr_mask = 0x02; | ||
145 | config->odr = 390; | ||
146 | } else if (odr >= 200) { | ||
147 | new_odr_mask = 0x01; | ||
148 | config->odr = 200; | ||
149 | } else { | ||
150 | new_odr_mask = 0x00; | ||
151 | config->odr = 100; | ||
152 | } | ||
153 | |||
154 | if (apply) { | ||
155 | unsigned char reg_odr; | ||
156 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
157 | ADXL34X_ODR_REG, 1, ®_odr); | ||
158 | if (result) { | ||
159 | LOG_RESULT_LOCATION(result); | ||
160 | return result; | ||
161 | } | ||
162 | reg_odr &= ~ADXL34X_ODR_MASK; | ||
163 | reg_odr |= new_odr_mask; | ||
164 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
165 | ADXL34X_ODR_REG, reg_odr); | ||
166 | if (result) { | ||
167 | LOG_RESULT_LOCATION(result); | ||
168 | return result; | ||
169 | } | ||
170 | MPL_LOGV("ODR: %d mHz\n", config->odr); | ||
171 | } | ||
172 | return result; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * @brief Set the full scale range of the accels | ||
177 | * | ||
178 | * @param mlsl_handle | ||
179 | * the handle to the serial channel the device is connected to. | ||
180 | * @param pdata | ||
181 | * a pointer to the slave platform data. | ||
182 | * @param config | ||
183 | * pointer to configuration. | ||
184 | * @param apply | ||
185 | * whether to apply immediately or save the settings to be applied | ||
186 | * at the next resume. | ||
187 | * @param fsr | ||
188 | * requested full scale range in milli gees (mg). | ||
189 | * | ||
190 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
191 | */ | ||
192 | static int adxl34x_set_fsr(void *mlsl_handle, | ||
193 | struct ext_slave_platform_data *pdata, | ||
194 | struct adxl34x_config *config, | ||
195 | int apply, | ||
196 | long fsr) | ||
197 | { | ||
198 | int result = INV_SUCCESS; | ||
199 | |||
200 | if (fsr <= 2000) { | ||
201 | config->fsr_reg_mask = 0x00; | ||
202 | config->fsr = 2000; | ||
203 | } else if (fsr <= 4000) { | ||
204 | config->fsr_reg_mask = 0x01; | ||
205 | config->fsr = 4000; | ||
206 | } else if (fsr <= 8000) { | ||
207 | config->fsr_reg_mask = 0x02; | ||
208 | config->fsr = 8000; | ||
209 | } else { /* 8001 -> oo */ | ||
210 | config->fsr_reg_mask = 0x03; | ||
211 | config->fsr = 16000; | ||
212 | } | ||
213 | |||
214 | if (apply) { | ||
215 | unsigned char reg_df; | ||
216 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
217 | ADXL34X_DATAFORMAT_REG, 1, ®_df); | ||
218 | reg_df &= ~ADXL34X_DATAFORMAT_FSR_MASK; | ||
219 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
220 | ADXL34X_DATAFORMAT_REG, | ||
221 | reg_df | config->fsr_reg_mask); | ||
222 | if (result) { | ||
223 | LOG_RESULT_LOCATION(result); | ||
224 | return result; | ||
225 | } | ||
226 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
227 | } | ||
228 | return result; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * @brief facility to retrieve the device configuration. | ||
233 | * | ||
234 | * @param mlsl_handle | ||
235 | * the handle to the serial channel the device is connected to. | ||
236 | * @param slave | ||
237 | * a pointer to the slave descriptor data structure. | ||
238 | * @param pdata | ||
239 | * a pointer to the slave platform data. | ||
240 | * @param data | ||
241 | * a pointer to store the returned configuration data structure. | ||
242 | * | ||
243 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
244 | */ | ||
245 | static int adxl34x_get_config(void *mlsl_handle, | ||
246 | struct ext_slave_descr *slave, | ||
247 | struct ext_slave_platform_data *pdata, | ||
248 | struct ext_slave_config *data) | ||
249 | { | ||
250 | struct adxl34x_private_data *private_data = | ||
251 | (struct adxl34x_private_data *)(pdata->private_data); | ||
252 | |||
253 | if (!data->data) | ||
254 | return INV_ERROR_INVALID_PARAMETER; | ||
255 | |||
256 | switch (data->key) { | ||
257 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
258 | (*(unsigned long *)data->data) = | ||
259 | (unsigned long) private_data->suspend.odr; | ||
260 | break; | ||
261 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
262 | (*(unsigned long *)data->data) = | ||
263 | (unsigned long) private_data->resume.odr; | ||
264 | break; | ||
265 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
266 | (*(unsigned long *)data->data) = | ||
267 | (unsigned long) private_data->suspend.fsr; | ||
268 | break; | ||
269 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
270 | (*(unsigned long *)data->data) = | ||
271 | (unsigned long) private_data->resume.fsr; | ||
272 | break; | ||
273 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
274 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
275 | default: | ||
276 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
277 | }; | ||
278 | |||
279 | return INV_SUCCESS; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * @brief device configuration facility. | ||
284 | * | ||
285 | * @param mlsl_handle | ||
286 | * the handle to the serial channel the device is connected to. | ||
287 | * @param slave | ||
288 | * a pointer to the slave descriptor data structure. | ||
289 | * @param pdata | ||
290 | * a pointer to the slave platform data. | ||
291 | * @param data | ||
292 | * a pointer to the configuration data structure. | ||
293 | * | ||
294 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
295 | */ | ||
296 | static int adxl34x_config(void *mlsl_handle, | ||
297 | struct ext_slave_descr *slave, | ||
298 | struct ext_slave_platform_data *pdata, | ||
299 | struct ext_slave_config *data) | ||
300 | { | ||
301 | struct adxl34x_private_data *private_data = | ||
302 | (struct adxl34x_private_data *)(pdata->private_data); | ||
303 | |||
304 | if (!data->data) | ||
305 | return INV_ERROR_INVALID_PARAMETER; | ||
306 | |||
307 | switch (data->key) { | ||
308 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
309 | return adxl34x_set_odr(mlsl_handle, pdata, | ||
310 | &private_data->suspend, | ||
311 | data->apply, | ||
312 | *((long *)data->data)); | ||
313 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
314 | return adxl34x_set_odr(mlsl_handle, pdata, | ||
315 | &private_data->resume, | ||
316 | data->apply, | ||
317 | *((long *)data->data)); | ||
318 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
319 | return adxl34x_set_fsr(mlsl_handle, pdata, | ||
320 | &private_data->suspend, | ||
321 | data->apply, | ||
322 | *((long *)data->data)); | ||
323 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
324 | return adxl34x_set_fsr(mlsl_handle, pdata, | ||
325 | &private_data->resume, | ||
326 | data->apply, | ||
327 | *((long *)data->data)); | ||
328 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
329 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
330 | default: | ||
331 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
332 | }; | ||
333 | return INV_SUCCESS; | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * @brief suspends the device to put it in its lowest power mode. | ||
338 | * | ||
339 | * @param mlsl_handle | ||
340 | * the handle to the serial channel the device is connected to. | ||
341 | * @param slave | ||
342 | * a pointer to the slave descriptor data structure. | ||
343 | * @param pdata | ||
344 | * a pointer to the slave platform data. | ||
345 | * | ||
346 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
347 | */ | ||
348 | static int adxl34x_suspend(void *mlsl_handle, | ||
349 | struct ext_slave_descr *slave, | ||
350 | struct ext_slave_platform_data *pdata) | ||
351 | { | ||
352 | int result; | ||
353 | |||
354 | /* | ||
355 | struct adxl34x_config *suspend_config = | ||
356 | &((struct adxl34x_private_data *)pdata->private_data)->suspend; | ||
357 | |||
358 | result = adxl34x_set_odr(mlsl_handle, pdata, suspend_config, | ||
359 | true, suspend_config->odr); | ||
360 | if (result) { | ||
361 | LOG_RESULT_LOCATION(result); | ||
362 | return result; | ||
363 | } | ||
364 | result = adxl34x_set_fsr(mlsl_handle, pdata, suspend_config, | ||
365 | true, suspend_config->fsr); | ||
366 | if (result) { | ||
367 | LOG_RESULT_LOCATION(result); | ||
368 | return result; | ||
369 | } | ||
370 | */ | ||
371 | |||
372 | /* | ||
373 | Page 25 | ||
374 | When clearing the sleep bit, it is recommended that the part | ||
375 | be placed into standby mode and then set back to measurement mode | ||
376 | with a subsequent write. | ||
377 | This is done to ensure that the device is properly biased if sleep | ||
378 | mode is manually disabled; otherwise, the first few samples of data | ||
379 | after the sleep bit is cleared may have additional noise, | ||
380 | especially if the device was asleep when the bit was cleared. */ | ||
381 | |||
382 | /* go in standy-by mode (suspends measurements) */ | ||
383 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
384 | ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK); | ||
385 | if (result) { | ||
386 | LOG_RESULT_LOCATION(result); | ||
387 | return result; | ||
388 | } | ||
389 | /* and then in sleep */ | ||
390 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
391 | ADXL34X_PWR_REG, | ||
392 | ADXL34X_PWR_MEAS_MASK | ADXL34X_PWR_SLEEP_MASK); | ||
393 | if (result) { | ||
394 | LOG_RESULT_LOCATION(result); | ||
395 | return result; | ||
396 | } | ||
397 | return result; | ||
398 | } | ||
399 | |||
400 | /** | ||
401 | * @brief resume the device in the proper power state given the configuration | ||
402 | * chosen. | ||
403 | * | ||
404 | * @param mlsl_handle | ||
405 | * the handle to the serial channel the device is connected to. | ||
406 | * @param slave | ||
407 | * a pointer to the slave descriptor data structure. | ||
408 | * @param pdata | ||
409 | * a pointer to the slave platform data. | ||
410 | * | ||
411 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
412 | */ | ||
413 | static int adxl34x_resume(void *mlsl_handle, | ||
414 | struct ext_slave_descr *slave, | ||
415 | struct ext_slave_platform_data *pdata) | ||
416 | { | ||
417 | int result = INV_SUCCESS; | ||
418 | struct adxl34x_config *resume_config = | ||
419 | &((struct adxl34x_private_data *)pdata->private_data)->resume; | ||
420 | unsigned char reg; | ||
421 | |||
422 | /* | ||
423 | Page 25 | ||
424 | When clearing the sleep bit, it is recommended that the part | ||
425 | be placed into standby mode and then set back to measurement mode | ||
426 | with a subsequent write. | ||
427 | This is done to ensure that the device is properly biased if sleep | ||
428 | mode is manually disabled; otherwise, the first few samples of data | ||
429 | after the sleep bit is cleared may have additional noise, | ||
430 | especially if the device was asleep when the bit was cleared. */ | ||
431 | |||
432 | /* remove sleep, but leave in stand-by */ | ||
433 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
434 | ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK); | ||
435 | if (result) { | ||
436 | LOG_RESULT_LOCATION(result); | ||
437 | return result; | ||
438 | } | ||
439 | |||
440 | result = adxl34x_set_odr(mlsl_handle, pdata, resume_config, | ||
441 | true, resume_config->odr); | ||
442 | if (result) { | ||
443 | LOG_RESULT_LOCATION(result); | ||
444 | return result; | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | -> FSR | ||
449 | -> Justiy bit for Big endianess | ||
450 | -> resulution to 10 bits | ||
451 | */ | ||
452 | reg = ADXL34X_DATAFORMAT_JUSTIFY_MASK; | ||
453 | reg |= resume_config->fsr_reg_mask; | ||
454 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
455 | ADXL34X_DATAFORMAT_REG, reg); | ||
456 | if (result) { | ||
457 | LOG_RESULT_LOCATION(result); | ||
458 | return result; | ||
459 | } | ||
460 | |||
461 | /* go in measurement mode */ | ||
462 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
463 | ADXL34X_PWR_REG, 0x00); | ||
464 | if (result) { | ||
465 | LOG_RESULT_LOCATION(result); | ||
466 | return result; | ||
467 | } | ||
468 | |||
469 | /* DATA_FORMAT: full resolution of +/-2g; data is left justified */ | ||
470 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
471 | 0x31, reg); | ||
472 | |||
473 | return result; | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * @brief one-time device driver initialization function. | ||
478 | * If the driver is built as a kernel module, this function will be | ||
479 | * called when the module is loaded in the kernel. | ||
480 | * If the driver is built-in in the kernel, this function will be | ||
481 | * called at boot time. | ||
482 | * | ||
483 | * @param mlsl_handle | ||
484 | * the handle to the serial channel the device is connected to. | ||
485 | * @param slave | ||
486 | * a pointer to the slave descriptor data structure. | ||
487 | * @param pdata | ||
488 | * a pointer to the slave platform data. | ||
489 | * | ||
490 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
491 | */ | ||
492 | static int adxl34x_init(void *mlsl_handle, | ||
493 | struct ext_slave_descr *slave, | ||
494 | struct ext_slave_platform_data *pdata) | ||
495 | { | ||
496 | int result; | ||
497 | long range; | ||
498 | |||
499 | struct adxl34x_private_data *private_data; | ||
500 | private_data = (struct adxl34x_private_data *) | ||
501 | kzalloc(sizeof(struct adxl34x_private_data), GFP_KERNEL); | ||
502 | |||
503 | if (!private_data) | ||
504 | return INV_ERROR_MEMORY_EXAUSTED; | ||
505 | |||
506 | pdata->private_data = private_data; | ||
507 | |||
508 | result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
509 | false, 0); | ||
510 | if (result) { | ||
511 | LOG_RESULT_LOCATION(result); | ||
512 | return result; | ||
513 | } | ||
514 | result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
515 | false, 200000); | ||
516 | if (result) { | ||
517 | LOG_RESULT_LOCATION(result); | ||
518 | return result; | ||
519 | } | ||
520 | |||
521 | range = range_fixedpoint_to_long_mg(slave->range); | ||
522 | result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
523 | false, range); | ||
524 | result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
525 | false, range); | ||
526 | if (result) { | ||
527 | LOG_RESULT_LOCATION(result); | ||
528 | return result; | ||
529 | } | ||
530 | |||
531 | result = adxl34x_suspend(mlsl_handle, slave, pdata); | ||
532 | if (result) { | ||
533 | LOG_RESULT_LOCATION(result); | ||
534 | return result; | ||
535 | } | ||
536 | |||
537 | return result; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * @brief one-time device driver exit function. | ||
542 | * If the driver is built as a kernel module, this function will be | ||
543 | * called when the module is removed from the kernel. | ||
544 | * | ||
545 | * @param mlsl_handle | ||
546 | * the handle to the serial channel the device is connected to. | ||
547 | * @param slave | ||
548 | * a pointer to the slave descriptor data structure. | ||
549 | * @param pdata | ||
550 | * a pointer to the slave platform data. | ||
551 | * | ||
552 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
553 | */ | ||
554 | static int adxl34x_exit(void *mlsl_handle, | ||
555 | struct ext_slave_descr *slave, | ||
556 | struct ext_slave_platform_data *pdata) | ||
557 | { | ||
558 | kfree(pdata->private_data); | ||
559 | return INV_SUCCESS; | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * @brief read the sensor data from the device. | ||
564 | * | ||
565 | * @param mlsl_handle | ||
566 | * the handle to the serial channel the device is connected to. | ||
567 | * @param slave | ||
568 | * a pointer to the slave descriptor data structure. | ||
569 | * @param pdata | ||
570 | * a pointer to the slave platform data. | ||
571 | * @param data | ||
572 | * a buffer to store the data read. | ||
573 | * | ||
574 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
575 | */ | ||
576 | static int adxl34x_read(void *mlsl_handle, | ||
577 | struct ext_slave_descr *slave, | ||
578 | struct ext_slave_platform_data *pdata, | ||
579 | unsigned char *data) | ||
580 | { | ||
581 | int result; | ||
582 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
583 | slave->read_reg, slave->read_len, data); | ||
584 | return result; | ||
585 | } | ||
586 | |||
587 | static struct ext_slave_descr adxl34x_descr = { | ||
588 | .init = adxl34x_init, | ||
589 | .exit = adxl34x_exit, | ||
590 | .suspend = adxl34x_suspend, | ||
591 | .resume = adxl34x_resume, | ||
592 | .read = adxl34x_read, | ||
593 | .config = adxl34x_config, | ||
594 | .get_config = adxl34x_get_config, | ||
595 | .name = "adxl34x", /* 5 or 6 */ | ||
596 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
597 | .id = ACCEL_ID_ADXL34X, | ||
598 | .read_reg = 0x32, | ||
599 | .read_len = 6, | ||
600 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
601 | .range = {2, 0}, | ||
602 | .trigger = NULL, | ||
603 | }; | ||
604 | |||
605 | static | ||
606 | struct ext_slave_descr *adxl34x_get_slave_descr(void) | ||
607 | { | ||
608 | return &adxl34x_descr; | ||
609 | } | ||
610 | |||
611 | /* -------------------------------------------------------------------------- */ | ||
612 | struct adxl34x_mod_private_data { | ||
613 | struct i2c_client *client; | ||
614 | struct ext_slave_platform_data *pdata; | ||
615 | }; | ||
616 | |||
617 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
618 | |||
619 | static int adxl34x_mod_probe(struct i2c_client *client, | ||
620 | const struct i2c_device_id *devid) | ||
621 | { | ||
622 | struct ext_slave_platform_data *pdata; | ||
623 | struct adxl34x_mod_private_data *private_data; | ||
624 | int result = 0; | ||
625 | |||
626 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
627 | |||
628 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
629 | result = -ENODEV; | ||
630 | goto out_no_free; | ||
631 | } | ||
632 | |||
633 | pdata = client->dev.platform_data; | ||
634 | if (!pdata) { | ||
635 | dev_err(&client->adapter->dev, | ||
636 | "Missing platform data for slave %s\n", devid->name); | ||
637 | result = -EFAULT; | ||
638 | goto out_no_free; | ||
639 | } | ||
640 | |||
641 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
642 | if (!private_data) { | ||
643 | result = -ENOMEM; | ||
644 | goto out_no_free; | ||
645 | } | ||
646 | |||
647 | i2c_set_clientdata(client, private_data); | ||
648 | private_data->client = client; | ||
649 | private_data->pdata = pdata; | ||
650 | |||
651 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
652 | adxl34x_get_slave_descr); | ||
653 | if (result) { | ||
654 | dev_err(&client->adapter->dev, | ||
655 | "Slave registration failed: %s, %d\n", | ||
656 | devid->name, result); | ||
657 | goto out_free_memory; | ||
658 | } | ||
659 | |||
660 | return result; | ||
661 | |||
662 | out_free_memory: | ||
663 | kfree(private_data); | ||
664 | out_no_free: | ||
665 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
666 | return result; | ||
667 | |||
668 | } | ||
669 | |||
670 | static int adxl34x_mod_remove(struct i2c_client *client) | ||
671 | { | ||
672 | struct adxl34x_mod_private_data *private_data = | ||
673 | i2c_get_clientdata(client); | ||
674 | |||
675 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
676 | |||
677 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
678 | adxl34x_get_slave_descr); | ||
679 | |||
680 | kfree(private_data); | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static const struct i2c_device_id adxl34x_mod_id[] = { | ||
685 | { "adxl34x", ACCEL_ID_ADXL34X }, | ||
686 | {} | ||
687 | }; | ||
688 | |||
689 | MODULE_DEVICE_TABLE(i2c, adxl34x_mod_id); | ||
690 | |||
691 | static struct i2c_driver adxl34x_mod_driver = { | ||
692 | .class = I2C_CLASS_HWMON, | ||
693 | .probe = adxl34x_mod_probe, | ||
694 | .remove = adxl34x_mod_remove, | ||
695 | .id_table = adxl34x_mod_id, | ||
696 | .driver = { | ||
697 | .owner = THIS_MODULE, | ||
698 | .name = "adxl34x_mod", | ||
699 | }, | ||
700 | .address_list = normal_i2c, | ||
701 | }; | ||
702 | |||
703 | static int __init adxl34x_mod_init(void) | ||
704 | { | ||
705 | int res = i2c_add_driver(&adxl34x_mod_driver); | ||
706 | pr_info("%s: Probe name %s\n", __func__, "adxl34x_mod"); | ||
707 | if (res) | ||
708 | pr_err("%s failed\n", __func__); | ||
709 | return res; | ||
710 | } | ||
711 | |||
712 | static void __exit adxl34x_mod_exit(void) | ||
713 | { | ||
714 | pr_info("%s\n", __func__); | ||
715 | i2c_del_driver(&adxl34x_mod_driver); | ||
716 | } | ||
717 | |||
718 | module_init(adxl34x_mod_init); | ||
719 | module_exit(adxl34x_mod_exit); | ||
720 | |||
721 | MODULE_AUTHOR("Invensense Corporation"); | ||
722 | MODULE_DESCRIPTION("Driver to integrate ADXL34X sensor with the MPU"); | ||
723 | MODULE_LICENSE("GPL"); | ||
724 | MODULE_ALIAS("adxl34x_mod"); | ||
725 | |||
726 | /** | ||
727 | * @} | ||
728 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/bma150.c b/drivers/misc/inv_mpu/accel/bma150.c new file mode 100644 index 00000000000..745d90a6744 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma150.c | |||
@@ -0,0 +1,777 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file bma150.c | ||
26 | * @brief Accelerometer setup and handling methods for Bosch BMA150. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/errno.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include "mpu-dev.h" | ||
38 | |||
39 | #include <linux/mpu.h> | ||
40 | #include "mlsl.h" | ||
41 | #include "mldl_cfg.h" | ||
42 | |||
43 | /* -------------------------------------------------------------------------- */ | ||
44 | /* registers */ | ||
45 | #define BMA150_CTRL_REG (0x14) | ||
46 | #define BMA150_INT_REG (0x15) | ||
47 | #define BMA150_PWR_REG (0x0A) | ||
48 | |||
49 | /* masks */ | ||
50 | #define BMA150_CTRL_MASK (0x18) | ||
51 | #define BMA150_CTRL_MASK_ODR (0xF8) | ||
52 | #define BMA150_CTRL_MASK_FSR (0xE7) | ||
53 | #define BMA150_INT_MASK_WUP (0xF8) | ||
54 | #define BMA150_INT_MASK_IRQ (0xDF) | ||
55 | #define BMA150_PWR_MASK_SLEEP (0x01) | ||
56 | #define BMA150_PWR_MASK_SOFT_RESET (0x02) | ||
57 | |||
58 | /* -------------------------------------------------------------------------- */ | ||
59 | struct bma150_config { | ||
60 | unsigned int odr; /** < output data rate mHz */ | ||
61 | unsigned int fsr; /** < full scale range mgees */ | ||
62 | unsigned int irq_type; /** < type of IRQ, see bma150_set_irq */ | ||
63 | unsigned char ctrl_reg; /** < control register value */ | ||
64 | unsigned char int_reg; /** < interrupt control register value */ | ||
65 | }; | ||
66 | |||
67 | struct bma150_private_data { | ||
68 | struct bma150_config suspend; /** < suspend configuration */ | ||
69 | struct bma150_config resume; /** < resume configuration */ | ||
70 | }; | ||
71 | |||
72 | /** | ||
73 | * @brief Simply disables the IRQ since it is not usable on BMA150 devices. | ||
74 | * | ||
75 | * @param mlsl_handle | ||
76 | * the handle to the serial channel the device is connected to. | ||
77 | * @param pdata | ||
78 | * a pointer to the slave platform data. | ||
79 | * @param config | ||
80 | * configuration to apply to, suspend or resume | ||
81 | * @param apply | ||
82 | * whether to apply immediately or save the settings to be applied | ||
83 | * at the next resume. | ||
84 | * @param irq_type | ||
85 | * the type of IRQ. Valid values are | ||
86 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
87 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
88 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
89 | * The only supported IRQ type is MPU_SLAVE_IRQ_TYPE_NONE which | ||
90 | * corresponds to disabling the IRQ completely. | ||
91 | * | ||
92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
93 | */ | ||
94 | static int bma150_set_irq(void *mlsl_handle, | ||
95 | struct ext_slave_platform_data *pdata, | ||
96 | struct bma150_config *config, | ||
97 | int apply, | ||
98 | long irq_type) | ||
99 | { | ||
100 | int result = INV_SUCCESS; | ||
101 | |||
102 | if (irq_type != MPU_SLAVE_IRQ_TYPE_NONE) | ||
103 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
104 | |||
105 | config->irq_type = MPU_SLAVE_IRQ_TYPE_NONE; | ||
106 | config->int_reg = 0x00; | ||
107 | |||
108 | if (apply) { | ||
109 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
110 | BMA150_CTRL_REG, config->ctrl_reg); | ||
111 | if (result) { | ||
112 | LOG_RESULT_LOCATION(result); | ||
113 | return result; | ||
114 | } | ||
115 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
116 | BMA150_INT_REG, config->int_reg); | ||
117 | if (result) { | ||
118 | LOG_RESULT_LOCATION(result); | ||
119 | return result; | ||
120 | } | ||
121 | } | ||
122 | return result; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * @brief Set the output data rate for the particular configuration. | ||
127 | * | ||
128 | * @param mlsl_handle | ||
129 | * the handle to the serial channel the device is connected to. | ||
130 | * @param pdata | ||
131 | * a pointer to the slave platform data. | ||
132 | * @param config | ||
133 | * Config to modify with new ODR. | ||
134 | * @param apply | ||
135 | * whether to apply immediately or save the settings to be applied | ||
136 | * at the next resume. | ||
137 | * @param odr | ||
138 | * Output data rate in units of 1/1000Hz (mHz). | ||
139 | * | ||
140 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
141 | */ | ||
142 | static int bma150_set_odr(void *mlsl_handle, | ||
143 | struct ext_slave_platform_data *pdata, | ||
144 | struct bma150_config *config, | ||
145 | int apply, | ||
146 | long odr) | ||
147 | { | ||
148 | unsigned char odr_bits = 0; | ||
149 | unsigned char wup_bits = 0; | ||
150 | int result = INV_SUCCESS; | ||
151 | |||
152 | if (odr > 100000) { | ||
153 | config->odr = 190000; | ||
154 | odr_bits = 0x03; | ||
155 | } else if (odr > 50000) { | ||
156 | config->odr = 100000; | ||
157 | odr_bits = 0x02; | ||
158 | } else if (odr > 25000) { | ||
159 | config->odr = 50000; | ||
160 | odr_bits = 0x01; | ||
161 | } else if (odr > 0) { | ||
162 | config->odr = 25000; | ||
163 | odr_bits = 0x00; | ||
164 | } else { | ||
165 | config->odr = 0; | ||
166 | wup_bits = 0x00; | ||
167 | } | ||
168 | |||
169 | config->int_reg &= BMA150_INT_MASK_WUP; | ||
170 | config->ctrl_reg &= BMA150_CTRL_MASK_ODR; | ||
171 | config->ctrl_reg |= odr_bits; | ||
172 | |||
173 | MPL_LOGV("ODR: %d\n", config->odr); | ||
174 | if (apply) { | ||
175 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
176 | BMA150_CTRL_REG, config->ctrl_reg); | ||
177 | if (result) { | ||
178 | LOG_RESULT_LOCATION(result); | ||
179 | return result; | ||
180 | } | ||
181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
182 | BMA150_INT_REG, config->int_reg); | ||
183 | if (result) { | ||
184 | LOG_RESULT_LOCATION(result); | ||
185 | return result; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | return result; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * @brief Set the full scale range of the accels | ||
194 | * | ||
195 | * @param mlsl_handle | ||
196 | * the handle to the serial channel the device is connected to. | ||
197 | * @param pdata | ||
198 | * a pointer to the slave platform data. | ||
199 | * @param config | ||
200 | * pointer to configuration. | ||
201 | * @param apply | ||
202 | * whether to apply immediately or save the settings to be applied | ||
203 | * at the next resume. | ||
204 | * @param fsr | ||
205 | * requested full scale range. | ||
206 | * | ||
207 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
208 | */ | ||
209 | static int bma150_set_fsr(void *mlsl_handle, | ||
210 | struct ext_slave_platform_data *pdata, | ||
211 | struct bma150_config *config, | ||
212 | int apply, | ||
213 | long fsr) | ||
214 | { | ||
215 | unsigned char fsr_bits; | ||
216 | int result = INV_SUCCESS; | ||
217 | |||
218 | if (fsr <= 2048) { | ||
219 | fsr_bits = 0x00; | ||
220 | config->fsr = 2048; | ||
221 | } else if (fsr <= 4096) { | ||
222 | fsr_bits = 0x08; | ||
223 | config->fsr = 4096; | ||
224 | } else { | ||
225 | fsr_bits = 0x10; | ||
226 | config->fsr = 8192; | ||
227 | } | ||
228 | |||
229 | config->ctrl_reg &= BMA150_CTRL_MASK_FSR; | ||
230 | config->ctrl_reg |= fsr_bits; | ||
231 | |||
232 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
233 | if (apply) { | ||
234 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
235 | BMA150_CTRL_REG, config->ctrl_reg); | ||
236 | if (result) { | ||
237 | LOG_RESULT_LOCATION(result); | ||
238 | return result; | ||
239 | } | ||
240 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
241 | BMA150_CTRL_REG, config->ctrl_reg); | ||
242 | if (result) { | ||
243 | LOG_RESULT_LOCATION(result); | ||
244 | return result; | ||
245 | } | ||
246 | } | ||
247 | return result; | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * @brief one-time device driver initialization function. | ||
252 | * If the driver is built as a kernel module, this function will be | ||
253 | * called when the module is loaded in the kernel. | ||
254 | * If the driver is built-in in the kernel, this function will be | ||
255 | * called at boot time. | ||
256 | * | ||
257 | * @param mlsl_handle | ||
258 | * the handle to the serial channel the device is connected to. | ||
259 | * @param slave | ||
260 | * a pointer to the slave descriptor data structure. | ||
261 | * @param pdata | ||
262 | * a pointer to the slave platform data. | ||
263 | * | ||
264 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
265 | */ | ||
266 | static int bma150_init(void *mlsl_handle, | ||
267 | struct ext_slave_descr *slave, | ||
268 | struct ext_slave_platform_data *pdata) | ||
269 | { | ||
270 | int result; | ||
271 | unsigned char reg; | ||
272 | long range; | ||
273 | |||
274 | struct bma150_private_data *private_data; | ||
275 | private_data = (struct bma150_private_data *) | ||
276 | kzalloc(sizeof(struct bma150_private_data), GFP_KERNEL); | ||
277 | |||
278 | if (!private_data) | ||
279 | return INV_ERROR_MEMORY_EXAUSTED; | ||
280 | |||
281 | pdata->private_data = private_data; | ||
282 | |||
283 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
284 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
285 | if (result) { | ||
286 | LOG_RESULT_LOCATION(result); | ||
287 | return result; | ||
288 | } | ||
289 | msleep(1); | ||
290 | |||
291 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
292 | BMA150_CTRL_REG, 1, ®); | ||
293 | if (result) { | ||
294 | LOG_RESULT_LOCATION(result); | ||
295 | return result; | ||
296 | } | ||
297 | private_data->resume.ctrl_reg = reg; | ||
298 | private_data->suspend.ctrl_reg = reg; | ||
299 | |||
300 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
301 | BMA150_INT_REG, 1, ®); | ||
302 | if (result) { | ||
303 | LOG_RESULT_LOCATION(result); | ||
304 | return result; | ||
305 | } | ||
306 | private_data->resume.int_reg = reg; | ||
307 | private_data->suspend.int_reg = reg; | ||
308 | |||
309 | result = bma150_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
310 | false, 0); | ||
311 | if (result) { | ||
312 | LOG_RESULT_LOCATION(result); | ||
313 | return result; | ||
314 | } | ||
315 | result = bma150_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
316 | false, 200000); | ||
317 | if (result) { | ||
318 | LOG_RESULT_LOCATION(result); | ||
319 | return result; | ||
320 | } | ||
321 | |||
322 | range = range_fixedpoint_to_long_mg(slave->range); | ||
323 | result = bma150_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
324 | false, range); | ||
325 | result = bma150_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
326 | false, range); | ||
327 | if (result) { | ||
328 | LOG_RESULT_LOCATION(result); | ||
329 | return result; | ||
330 | } | ||
331 | |||
332 | result = bma150_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
333 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
334 | if (result) { | ||
335 | LOG_RESULT_LOCATION(result); | ||
336 | return result; | ||
337 | } | ||
338 | result = bma150_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
339 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
340 | if (result) { | ||
341 | LOG_RESULT_LOCATION(result); | ||
342 | return result; | ||
343 | } | ||
344 | |||
345 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
346 | BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP); | ||
347 | if (result) { | ||
348 | LOG_RESULT_LOCATION(result); | ||
349 | return result; | ||
350 | } | ||
351 | |||
352 | return result; | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * @brief one-time device driver exit function. | ||
357 | * If the driver is built as a kernel module, this function will be | ||
358 | * called when the module is removed from the kernel. | ||
359 | * | ||
360 | * @param mlsl_handle | ||
361 | * the handle to the serial channel the device is connected to. | ||
362 | * @param slave | ||
363 | * a pointer to the slave descriptor data structure. | ||
364 | * @param pdata | ||
365 | * a pointer to the slave platform data. | ||
366 | * | ||
367 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
368 | */ | ||
369 | static int bma150_exit(void *mlsl_handle, | ||
370 | struct ext_slave_descr *slave, | ||
371 | struct ext_slave_platform_data *pdata) | ||
372 | { | ||
373 | kfree(pdata->private_data); | ||
374 | return INV_SUCCESS; | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * @brief device configuration facility. | ||
379 | * | ||
380 | * @param mlsl_handle | ||
381 | * the handle to the serial channel the device is connected to. | ||
382 | * @param slave | ||
383 | * a pointer to the slave descriptor data structure. | ||
384 | * @param pdata | ||
385 | * a pointer to the slave platform data. | ||
386 | * @param data | ||
387 | * a pointer to the configuration data structure. | ||
388 | * | ||
389 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
390 | */ | ||
391 | static int bma150_config(void *mlsl_handle, | ||
392 | struct ext_slave_descr *slave, | ||
393 | struct ext_slave_platform_data *pdata, | ||
394 | struct ext_slave_config *data) | ||
395 | { | ||
396 | struct bma150_private_data *private_data = | ||
397 | (struct bma150_private_data *)(pdata->private_data); | ||
398 | |||
399 | if (!data->data) | ||
400 | return INV_ERROR_INVALID_PARAMETER; | ||
401 | |||
402 | switch (data->key) { | ||
403 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
404 | return bma150_set_odr(mlsl_handle, pdata, | ||
405 | &private_data->suspend, | ||
406 | data->apply, | ||
407 | *((long *)data->data)); | ||
408 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
409 | return bma150_set_odr(mlsl_handle, pdata, | ||
410 | &private_data->resume, | ||
411 | data->apply, | ||
412 | *((long *)data->data)); | ||
413 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
414 | return bma150_set_fsr(mlsl_handle, pdata, | ||
415 | &private_data->suspend, | ||
416 | data->apply, | ||
417 | *((long *)data->data)); | ||
418 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
419 | return bma150_set_fsr(mlsl_handle, pdata, | ||
420 | &private_data->resume, | ||
421 | data->apply, | ||
422 | *((long *)data->data)); | ||
423 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
424 | return bma150_set_irq(mlsl_handle, pdata, | ||
425 | &private_data->suspend, | ||
426 | data->apply, | ||
427 | *((long *)data->data)); | ||
428 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
429 | return bma150_set_irq(mlsl_handle, pdata, | ||
430 | &private_data->resume, | ||
431 | data->apply, | ||
432 | *((long *)data->data)); | ||
433 | default: | ||
434 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
435 | }; | ||
436 | return INV_SUCCESS; | ||
437 | } | ||
438 | |||
439 | /** | ||
440 | * @brief facility to retrieve the device configuration. | ||
441 | * | ||
442 | * @param mlsl_handle | ||
443 | * the handle to the serial channel the device is connected to. | ||
444 | * @param slave | ||
445 | * a pointer to the slave descriptor data structure. | ||
446 | * @param pdata | ||
447 | * a pointer to the slave platform data. | ||
448 | * @param data | ||
449 | * a pointer to store the returned configuration data structure. | ||
450 | * | ||
451 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
452 | */ | ||
453 | static int bma150_get_config(void *mlsl_handle, | ||
454 | struct ext_slave_descr *slave, | ||
455 | struct ext_slave_platform_data *pdata, | ||
456 | struct ext_slave_config *data) | ||
457 | { | ||
458 | struct bma150_private_data *private_data = | ||
459 | (struct bma150_private_data *)(pdata->private_data); | ||
460 | |||
461 | if (!data->data) | ||
462 | return INV_ERROR_INVALID_PARAMETER; | ||
463 | |||
464 | switch (data->key) { | ||
465 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
466 | (*(unsigned long *)data->data) = | ||
467 | (unsigned long) private_data->suspend.odr; | ||
468 | break; | ||
469 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
470 | (*(unsigned long *)data->data) = | ||
471 | (unsigned long) private_data->resume.odr; | ||
472 | break; | ||
473 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
474 | (*(unsigned long *)data->data) = | ||
475 | (unsigned long) private_data->suspend.fsr; | ||
476 | break; | ||
477 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
478 | (*(unsigned long *)data->data) = | ||
479 | (unsigned long) private_data->resume.fsr; | ||
480 | break; | ||
481 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
482 | (*(unsigned long *)data->data) = | ||
483 | (unsigned long) private_data->suspend.irq_type; | ||
484 | break; | ||
485 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
486 | (*(unsigned long *)data->data) = | ||
487 | (unsigned long) private_data->resume.irq_type; | ||
488 | break; | ||
489 | default: | ||
490 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
491 | }; | ||
492 | |||
493 | return INV_SUCCESS; | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * @brief suspends the device to put it in its lowest power mode. | ||
498 | * | ||
499 | * @param mlsl_handle | ||
500 | * the handle to the serial channel the device is connected to. | ||
501 | * @param slave | ||
502 | * a pointer to the slave descriptor data structure. | ||
503 | * @param pdata | ||
504 | * a pointer to the slave platform data. | ||
505 | * | ||
506 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
507 | */ | ||
508 | static int bma150_suspend(void *mlsl_handle, | ||
509 | struct ext_slave_descr *slave, | ||
510 | struct ext_slave_platform_data *pdata) | ||
511 | { | ||
512 | int result; | ||
513 | unsigned char ctrl_reg; | ||
514 | unsigned char int_reg; | ||
515 | |||
516 | struct bma150_private_data *private_data = | ||
517 | (struct bma150_private_data *)(pdata->private_data); | ||
518 | |||
519 | ctrl_reg = private_data->suspend.ctrl_reg; | ||
520 | int_reg = private_data->suspend.int_reg; | ||
521 | |||
522 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
523 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
524 | if (result) { | ||
525 | LOG_RESULT_LOCATION(result); | ||
526 | return result; | ||
527 | } | ||
528 | msleep(1); | ||
529 | |||
530 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
531 | BMA150_CTRL_REG, ctrl_reg); | ||
532 | if (result) { | ||
533 | LOG_RESULT_LOCATION(result); | ||
534 | return result; | ||
535 | } | ||
536 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
537 | BMA150_INT_REG, int_reg); | ||
538 | if (result) { | ||
539 | LOG_RESULT_LOCATION(result); | ||
540 | return result; | ||
541 | } | ||
542 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
543 | BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP); | ||
544 | if (result) { | ||
545 | LOG_RESULT_LOCATION(result); | ||
546 | return result; | ||
547 | } | ||
548 | |||
549 | return result; | ||
550 | } | ||
551 | |||
552 | /** | ||
553 | * @brief resume the device in the proper power state given the configuration | ||
554 | * chosen. | ||
555 | * | ||
556 | * @param mlsl_handle | ||
557 | * the handle to the serial channel the device is connected to. | ||
558 | * @param slave | ||
559 | * a pointer to the slave descriptor data structure. | ||
560 | * @param pdata | ||
561 | * a pointer to the slave platform data. | ||
562 | * | ||
563 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
564 | */ | ||
565 | static int bma150_resume(void *mlsl_handle, | ||
566 | struct ext_slave_descr *slave, | ||
567 | struct ext_slave_platform_data *pdata) | ||
568 | { | ||
569 | int result; | ||
570 | unsigned char ctrl_reg; | ||
571 | unsigned char int_reg; | ||
572 | |||
573 | struct bma150_private_data *private_data = | ||
574 | (struct bma150_private_data *)(pdata->private_data); | ||
575 | |||
576 | ctrl_reg = private_data->resume.ctrl_reg; | ||
577 | int_reg = private_data->resume.int_reg; | ||
578 | |||
579 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
580 | BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET); | ||
581 | if (result) { | ||
582 | LOG_RESULT_LOCATION(result); | ||
583 | return result; | ||
584 | } | ||
585 | msleep(1); | ||
586 | |||
587 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
588 | BMA150_CTRL_REG, ctrl_reg); | ||
589 | if (result) { | ||
590 | LOG_RESULT_LOCATION(result); | ||
591 | return result; | ||
592 | } | ||
593 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
594 | BMA150_INT_REG, int_reg); | ||
595 | if (result) { | ||
596 | LOG_RESULT_LOCATION(result); | ||
597 | return result; | ||
598 | } | ||
599 | |||
600 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
601 | BMA150_PWR_REG, 0x00); | ||
602 | if (result) { | ||
603 | LOG_RESULT_LOCATION(result); | ||
604 | return result; | ||
605 | } | ||
606 | |||
607 | return result; | ||
608 | } | ||
609 | |||
610 | /** | ||
611 | * @brief read the sensor data from the device. | ||
612 | * | ||
613 | * @param mlsl_handle | ||
614 | * the handle to the serial channel the device is connected to. | ||
615 | * @param slave | ||
616 | * a pointer to the slave descriptor data structure. | ||
617 | * @param pdata | ||
618 | * a pointer to the slave platform data. | ||
619 | * @param data | ||
620 | * a buffer to store the data read. | ||
621 | * | ||
622 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
623 | */ | ||
624 | static int bma150_read(void *mlsl_handle, | ||
625 | struct ext_slave_descr *slave, | ||
626 | struct ext_slave_platform_data *pdata, | ||
627 | unsigned char *data) | ||
628 | { | ||
629 | return inv_serial_read(mlsl_handle, pdata->address, | ||
630 | slave->read_reg, slave->read_len, data); | ||
631 | } | ||
632 | |||
633 | static struct ext_slave_descr bma150_descr = { | ||
634 | .init = bma150_init, | ||
635 | .exit = bma150_exit, | ||
636 | .suspend = bma150_suspend, | ||
637 | .resume = bma150_resume, | ||
638 | .read = bma150_read, | ||
639 | .config = bma150_config, | ||
640 | .get_config = bma150_get_config, | ||
641 | .name = "bma150", | ||
642 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
643 | .id = ACCEL_ID_BMA150, | ||
644 | .read_reg = 0x02, | ||
645 | .read_len = 6, | ||
646 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
647 | .range = {2, 0}, | ||
648 | .trigger = NULL, | ||
649 | }; | ||
650 | |||
651 | static | ||
652 | struct ext_slave_descr *bma150_get_slave_descr(void) | ||
653 | { | ||
654 | return &bma150_descr; | ||
655 | } | ||
656 | |||
657 | /* -------------------------------------------------------------------------- */ | ||
658 | |||
659 | /* Platform data for the MPU */ | ||
660 | struct bma150_mod_private_data { | ||
661 | struct i2c_client *client; | ||
662 | struct ext_slave_platform_data *pdata; | ||
663 | }; | ||
664 | |||
665 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
666 | |||
667 | static int bma150_mod_probe(struct i2c_client *client, | ||
668 | const struct i2c_device_id *devid) | ||
669 | { | ||
670 | struct ext_slave_platform_data *pdata; | ||
671 | struct bma150_mod_private_data *private_data; | ||
672 | int result = 0; | ||
673 | |||
674 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
675 | |||
676 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
677 | result = -ENODEV; | ||
678 | goto out_no_free; | ||
679 | } | ||
680 | |||
681 | pdata = client->dev.platform_data; | ||
682 | if (!pdata) { | ||
683 | dev_err(&client->adapter->dev, | ||
684 | "Missing platform data for slave %s\n", devid->name); | ||
685 | result = -EFAULT; | ||
686 | goto out_no_free; | ||
687 | } | ||
688 | |||
689 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
690 | if (!private_data) { | ||
691 | result = -ENOMEM; | ||
692 | goto out_no_free; | ||
693 | } | ||
694 | |||
695 | i2c_set_clientdata(client, private_data); | ||
696 | private_data->client = client; | ||
697 | private_data->pdata = pdata; | ||
698 | |||
699 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
700 | bma150_get_slave_descr); | ||
701 | if (result) { | ||
702 | dev_err(&client->adapter->dev, | ||
703 | "Slave registration failed: %s, %d\n", | ||
704 | devid->name, result); | ||
705 | goto out_free_memory; | ||
706 | } | ||
707 | |||
708 | return result; | ||
709 | |||
710 | out_free_memory: | ||
711 | kfree(private_data); | ||
712 | out_no_free: | ||
713 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
714 | return result; | ||
715 | |||
716 | } | ||
717 | |||
718 | static int bma150_mod_remove(struct i2c_client *client) | ||
719 | { | ||
720 | struct bma150_mod_private_data *private_data = | ||
721 | i2c_get_clientdata(client); | ||
722 | |||
723 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
724 | |||
725 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
726 | bma150_get_slave_descr); | ||
727 | |||
728 | kfree(private_data); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static const struct i2c_device_id bma150_mod_id[] = { | ||
733 | { "bma150", ACCEL_ID_BMA150 }, | ||
734 | {} | ||
735 | }; | ||
736 | |||
737 | MODULE_DEVICE_TABLE(i2c, bma150_mod_id); | ||
738 | |||
739 | static struct i2c_driver bma150_mod_driver = { | ||
740 | .class = I2C_CLASS_HWMON, | ||
741 | .probe = bma150_mod_probe, | ||
742 | .remove = bma150_mod_remove, | ||
743 | .id_table = bma150_mod_id, | ||
744 | .driver = { | ||
745 | .owner = THIS_MODULE, | ||
746 | .name = "bma150_mod", | ||
747 | }, | ||
748 | .address_list = normal_i2c, | ||
749 | }; | ||
750 | |||
751 | static int __init bma150_mod_init(void) | ||
752 | { | ||
753 | int res = i2c_add_driver(&bma150_mod_driver); | ||
754 | pr_info("%s: Probe name %s\n", __func__, "bma150_mod"); | ||
755 | if (res) | ||
756 | pr_err("%s failed\n", __func__); | ||
757 | return res; | ||
758 | } | ||
759 | |||
760 | static void __exit bma150_mod_exit(void) | ||
761 | { | ||
762 | pr_info("%s\n", __func__); | ||
763 | i2c_del_driver(&bma150_mod_driver); | ||
764 | } | ||
765 | |||
766 | module_init(bma150_mod_init); | ||
767 | module_exit(bma150_mod_exit); | ||
768 | |||
769 | MODULE_AUTHOR("Invensense Corporation"); | ||
770 | MODULE_DESCRIPTION("Driver to integrate BMA150 sensor with the MPU"); | ||
771 | MODULE_LICENSE("GPL"); | ||
772 | MODULE_ALIAS("bma150_mod"); | ||
773 | |||
774 | /** | ||
775 | * @} | ||
776 | */ | ||
777 | |||
diff --git a/drivers/misc/inv_mpu/accel/bma222.c b/drivers/misc/inv_mpu/accel/bma222.c new file mode 100644 index 00000000000..e9fc99b1a62 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma222.c | |||
@@ -0,0 +1,654 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file bma222.c | ||
26 | * @brief Accelerometer setup and handling methods for Bosch BMA222. | ||
27 | */ | ||
28 | |||
29 | /* ------------------ */ | ||
30 | /* - Include Files. - */ | ||
31 | /* ------------------ */ | ||
32 | |||
33 | #include <linux/i2c.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/errno.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include "mpu-dev.h" | ||
41 | |||
42 | #include <linux/mpu.h> | ||
43 | #include "mlsl.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | |||
46 | /* -------------------------------------------------------------------------- */ | ||
47 | |||
48 | #define BMA222_STATUS_REG (0x0A) | ||
49 | #define BMA222_FSR_REG (0x0F) | ||
50 | #define ADXL34X_ODR_REG (0x10) | ||
51 | #define BMA222_PWR_REG (0x11) | ||
52 | #define BMA222_SOFTRESET_REG (0x14) | ||
53 | |||
54 | #define BMA222_STATUS_RDY_MASK (0x80) | ||
55 | #define BMA222_FSR_MASK (0x0F) | ||
56 | #define BMA222_ODR_MASK (0x1F) | ||
57 | #define BMA222_PWR_SLEEP_MASK (0x80) | ||
58 | #define BMA222_PWR_AWAKE_MASK (0x00) | ||
59 | #define BMA222_SOFTRESET_MASK (0xB6) | ||
60 | #define BMA222_SOFTRESET_MASK (0xB6) | ||
61 | |||
62 | /* -------------------------------------------------------------------------- */ | ||
63 | |||
64 | struct bma222_config { | ||
65 | unsigned int odr; /** < output data rate in mHz */ | ||
66 | unsigned int fsr; /** < full scale range mg */ | ||
67 | }; | ||
68 | |||
69 | struct bma222_private_data { | ||
70 | struct bma222_config suspend; /** < suspend configuration */ | ||
71 | struct bma222_config resume; /** < resume configuration */ | ||
72 | }; | ||
73 | |||
74 | |||
75 | /* -------------------------------------------------------------------------- */ | ||
76 | |||
77 | /** | ||
78 | * @brief Set the output data rate for the particular configuration. | ||
79 | * | ||
80 | * @param mlsl_handle | ||
81 | * the handle to the serial channel the device is connected to. | ||
82 | * @param pdata | ||
83 | * a pointer to the slave platform data. | ||
84 | * @param config | ||
85 | * Config to modify with new ODR. | ||
86 | * @param apply | ||
87 | * whether to apply immediately or save the settings to be applied | ||
88 | * at the next resume. | ||
89 | * @param odr | ||
90 | * Output data rate in units of 1/1000Hz (mHz). | ||
91 | * | ||
92 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
93 | */ | ||
94 | static int bma222_set_odr(void *mlsl_handle, | ||
95 | struct ext_slave_platform_data *pdata, | ||
96 | struct bma222_config *config, | ||
97 | int apply, | ||
98 | long odr) | ||
99 | { | ||
100 | int result = INV_SUCCESS; | ||
101 | unsigned char reg_odr; | ||
102 | |||
103 | if (odr >= 1000000) { | ||
104 | reg_odr = 0x0F; | ||
105 | config->odr = 1000000; | ||
106 | } else if (odr >= 500000) { | ||
107 | reg_odr = 0x0E; | ||
108 | config->odr = 500000; | ||
109 | } else if (odr >= 250000) { | ||
110 | reg_odr = 0x0D; | ||
111 | config->odr = 250000; | ||
112 | } else if (odr >= 125000) { | ||
113 | reg_odr = 0x0C; | ||
114 | config->odr = 125000; | ||
115 | } else if (odr >= 62500) { | ||
116 | reg_odr = 0x0B; | ||
117 | config->odr = 62500; | ||
118 | } else if (odr >= 32000) { | ||
119 | reg_odr = 0x0A; | ||
120 | config->odr = 32000; | ||
121 | } else if (odr >= 16000) { | ||
122 | reg_odr = 0x09; | ||
123 | config->odr = 16000; | ||
124 | } else { | ||
125 | reg_odr = 0x08; | ||
126 | config->odr = 8000; | ||
127 | } | ||
128 | |||
129 | if (apply) { | ||
130 | MPL_LOGV("ODR: %d\n", config->odr); | ||
131 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
132 | ADXL34X_ODR_REG, reg_odr); | ||
133 | if (result) { | ||
134 | LOG_RESULT_LOCATION(result); | ||
135 | return result; | ||
136 | } | ||
137 | } | ||
138 | return result; | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * @brief Set the full scale range of the accels | ||
143 | * | ||
144 | * @param mlsl_handle | ||
145 | * the handle to the serial channel the device is connected to. | ||
146 | * @param pdata | ||
147 | * a pointer to the slave platform data. | ||
148 | * @param config | ||
149 | * pointer to configuration. | ||
150 | * @param apply | ||
151 | * whether to apply immediately or save the settings to be applied | ||
152 | * at the next resume. | ||
153 | * @param fsr | ||
154 | * requested full scale range. | ||
155 | * | ||
156 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
157 | */ | ||
158 | static int bma222_set_fsr(void *mlsl_handle, | ||
159 | struct ext_slave_platform_data *pdata, | ||
160 | struct bma222_config *config, | ||
161 | int apply, | ||
162 | long fsr) | ||
163 | { | ||
164 | int result = INV_SUCCESS; | ||
165 | unsigned char reg_fsr_mask; | ||
166 | |||
167 | if (fsr <= 2000) { | ||
168 | reg_fsr_mask = 0x03; | ||
169 | config->fsr = 2000; | ||
170 | } else if (fsr <= 4000) { | ||
171 | reg_fsr_mask = 0x05; | ||
172 | config->fsr = 4000; | ||
173 | } else if (fsr <= 8000) { | ||
174 | reg_fsr_mask = 0x08; | ||
175 | config->fsr = 8000; | ||
176 | } else { /* 8001 -> oo */ | ||
177 | reg_fsr_mask = 0x0C; | ||
178 | config->fsr = 16000; | ||
179 | } | ||
180 | |||
181 | if (apply) { | ||
182 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
183 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
184 | BMA222_FSR_REG, reg_fsr_mask); | ||
185 | if (result) { | ||
186 | LOG_RESULT_LOCATION(result); | ||
187 | return result; | ||
188 | } | ||
189 | } | ||
190 | return result; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * @brief one-time device driver initialization function. | ||
195 | * If the driver is built as a kernel module, this function will be | ||
196 | * called when the module is loaded in the kernel. | ||
197 | * If the driver is built-in in the kernel, this function will be | ||
198 | * called at boot time. | ||
199 | * | ||
200 | * @param mlsl_handle | ||
201 | * the handle to the serial channel the device is connected to. | ||
202 | * @param slave | ||
203 | * a pointer to the slave descriptor data structure. | ||
204 | * @param pdata | ||
205 | * a pointer to the slave platform data. | ||
206 | * | ||
207 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
208 | */ | ||
209 | static int bma222_init(void *mlsl_handle, | ||
210 | struct ext_slave_descr *slave, | ||
211 | struct ext_slave_platform_data *pdata) | ||
212 | { | ||
213 | int result; | ||
214 | |||
215 | struct bma222_private_data *private_data; | ||
216 | private_data = (struct bma222_private_data *) | ||
217 | kzalloc(sizeof(struct bma222_private_data), GFP_KERNEL); | ||
218 | |||
219 | if (!private_data) | ||
220 | return INV_ERROR_MEMORY_EXAUSTED; | ||
221 | |||
222 | pdata->private_data = private_data; | ||
223 | |||
224 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
225 | BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK); | ||
226 | if (result) { | ||
227 | LOG_RESULT_LOCATION(result); | ||
228 | return result; | ||
229 | } | ||
230 | msleep(1); | ||
231 | |||
232 | result = bma222_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
233 | false, 0); | ||
234 | if (result) { | ||
235 | LOG_RESULT_LOCATION(result); | ||
236 | return result; | ||
237 | } | ||
238 | result = bma222_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
239 | false, 200000); | ||
240 | if (result) { | ||
241 | LOG_RESULT_LOCATION(result); | ||
242 | return result; | ||
243 | } | ||
244 | |||
245 | result = bma222_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
246 | false, 2000); | ||
247 | result = bma222_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
248 | false, 2000); | ||
249 | if (result) { | ||
250 | LOG_RESULT_LOCATION(result); | ||
251 | return result; | ||
252 | } | ||
253 | |||
254 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
255 | BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK); | ||
256 | if (result) { | ||
257 | LOG_RESULT_LOCATION(result); | ||
258 | return result; | ||
259 | } | ||
260 | |||
261 | return result; | ||
262 | } | ||
263 | |||
264 | /** | ||
265 | * @brief one-time device driver exit function. | ||
266 | * If the driver is built as a kernel module, this function will be | ||
267 | * called when the module is removed from the kernel. | ||
268 | * | ||
269 | * @param mlsl_handle | ||
270 | * the handle to the serial channel the device is connected to. | ||
271 | * @param slave | ||
272 | * a pointer to the slave descriptor data structure. | ||
273 | * @param pdata | ||
274 | * a pointer to the slave platform data. | ||
275 | * | ||
276 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
277 | */ | ||
278 | static int bma222_exit(void *mlsl_handle, | ||
279 | struct ext_slave_descr *slave, | ||
280 | struct ext_slave_platform_data *pdata) | ||
281 | { | ||
282 | kfree(pdata->private_data); | ||
283 | return INV_SUCCESS; | ||
284 | } | ||
285 | |||
286 | |||
287 | /** | ||
288 | * @brief facility to retrieve the device configuration. | ||
289 | * | ||
290 | * @param mlsl_handle | ||
291 | * the handle to the serial channel the device is connected to. | ||
292 | * @param slave | ||
293 | * a pointer to the slave descriptor data structure. | ||
294 | * @param pdata | ||
295 | * a pointer to the slave platform data. | ||
296 | * @param data | ||
297 | * a pointer to store the returned configuration data structure. | ||
298 | * | ||
299 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
300 | */ | ||
301 | static int bma222_get_config(void *mlsl_handle, | ||
302 | struct ext_slave_descr *slave, | ||
303 | struct ext_slave_platform_data *pdata, | ||
304 | struct ext_slave_config *data) | ||
305 | { | ||
306 | struct bma222_private_data *private_data = | ||
307 | (struct bma222_private_data *)(pdata->private_data); | ||
308 | |||
309 | if (!data->data) | ||
310 | return INV_ERROR_INVALID_PARAMETER; | ||
311 | |||
312 | switch (data->key) { | ||
313 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
314 | (*(unsigned long *)data->data) = | ||
315 | (unsigned long) private_data->suspend.odr; | ||
316 | break; | ||
317 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
318 | (*(unsigned long *)data->data) = | ||
319 | (unsigned long) private_data->resume.odr; | ||
320 | break; | ||
321 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
322 | (*(unsigned long *)data->data) = | ||
323 | (unsigned long) private_data->suspend.fsr; | ||
324 | break; | ||
325 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
326 | (*(unsigned long *)data->data) = | ||
327 | (unsigned long) private_data->resume.fsr; | ||
328 | break; | ||
329 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
330 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
331 | default: | ||
332 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
333 | }; | ||
334 | |||
335 | return INV_SUCCESS; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * @brief device configuration facility. | ||
340 | * | ||
341 | * @param mlsl_handle | ||
342 | * the handle to the serial channel the device is connected to. | ||
343 | * @param slave | ||
344 | * a pointer to the slave descriptor data structure. | ||
345 | * @param pdata | ||
346 | * a pointer to the slave platform data. | ||
347 | * @param data | ||
348 | * a pointer to the configuration data structure. | ||
349 | * | ||
350 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
351 | */ | ||
352 | static int bma222_config(void *mlsl_handle, | ||
353 | struct ext_slave_descr *slave, | ||
354 | struct ext_slave_platform_data *pdata, | ||
355 | struct ext_slave_config *data) | ||
356 | { | ||
357 | struct bma222_private_data *private_data = | ||
358 | (struct bma222_private_data *)(pdata->private_data); | ||
359 | |||
360 | if (!data->data) | ||
361 | return INV_ERROR_INVALID_PARAMETER; | ||
362 | |||
363 | switch (data->key) { | ||
364 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
365 | return bma222_set_odr(mlsl_handle, pdata, | ||
366 | &private_data->suspend, | ||
367 | data->apply, | ||
368 | *((long *)data->data)); | ||
369 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
370 | return bma222_set_odr(mlsl_handle, pdata, | ||
371 | &private_data->resume, | ||
372 | data->apply, | ||
373 | *((long *)data->data)); | ||
374 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
375 | return bma222_set_fsr(mlsl_handle, pdata, | ||
376 | &private_data->suspend, | ||
377 | data->apply, | ||
378 | *((long *)data->data)); | ||
379 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
380 | return bma222_set_fsr(mlsl_handle, pdata, | ||
381 | &private_data->resume, | ||
382 | data->apply, | ||
383 | *((long *)data->data)); | ||
384 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
385 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
386 | default: | ||
387 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
388 | }; | ||
389 | return INV_SUCCESS; | ||
390 | } | ||
391 | |||
392 | /** | ||
393 | * @brief suspends the device to put it in its lowest power mode. | ||
394 | * | ||
395 | * @param mlsl_handle | ||
396 | * the handle to the serial channel the device is connected to. | ||
397 | * @param slave | ||
398 | * a pointer to the slave descriptor data structure. | ||
399 | * @param pdata | ||
400 | * a pointer to the slave platform data. | ||
401 | * | ||
402 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
403 | */ | ||
404 | static int bma222_suspend(void *mlsl_handle, | ||
405 | struct ext_slave_descr *slave, | ||
406 | struct ext_slave_platform_data *pdata) | ||
407 | { | ||
408 | int result; | ||
409 | struct bma222_config *suspend_config = | ||
410 | &((struct bma222_private_data *)pdata->private_data)->suspend; | ||
411 | |||
412 | result = bma222_set_odr(mlsl_handle, pdata, suspend_config, | ||
413 | true, suspend_config->odr); | ||
414 | if (result) { | ||
415 | LOG_RESULT_LOCATION(result); | ||
416 | return result; | ||
417 | } | ||
418 | result = bma222_set_fsr(mlsl_handle, pdata, suspend_config, | ||
419 | true, suspend_config->fsr); | ||
420 | if (result) { | ||
421 | LOG_RESULT_LOCATION(result); | ||
422 | return result; | ||
423 | } | ||
424 | |||
425 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
426 | BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK); | ||
427 | if (result) { | ||
428 | LOG_RESULT_LOCATION(result); | ||
429 | return result; | ||
430 | } | ||
431 | |||
432 | msleep(3); /* 3 ms powerup time maximum */ | ||
433 | return result; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * @brief resume the device in the proper power state given the configuration | ||
438 | * chosen. | ||
439 | * | ||
440 | * @param mlsl_handle | ||
441 | * the handle to the serial channel the device is connected to. | ||
442 | * @param slave | ||
443 | * a pointer to the slave descriptor data structure. | ||
444 | * @param pdata | ||
445 | * a pointer to the slave platform data. | ||
446 | * | ||
447 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
448 | */ | ||
449 | static int bma222_resume(void *mlsl_handle, | ||
450 | struct ext_slave_descr *slave, | ||
451 | struct ext_slave_platform_data *pdata) | ||
452 | { | ||
453 | int result; | ||
454 | struct bma222_config *resume_config = | ||
455 | &((struct bma222_private_data *)pdata->private_data)->resume; | ||
456 | |||
457 | /* Soft reset */ | ||
458 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
459 | BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK); | ||
460 | if (result) { | ||
461 | LOG_RESULT_LOCATION(result); | ||
462 | return result; | ||
463 | } | ||
464 | msleep(10); | ||
465 | |||
466 | result = bma222_set_odr(mlsl_handle, pdata, resume_config, | ||
467 | true, resume_config->odr); | ||
468 | if (result) { | ||
469 | LOG_RESULT_LOCATION(result); | ||
470 | return result; | ||
471 | } | ||
472 | result = bma222_set_fsr(mlsl_handle, pdata, resume_config, | ||
473 | true, resume_config->fsr); | ||
474 | if (result) { | ||
475 | LOG_RESULT_LOCATION(result); | ||
476 | return result; | ||
477 | } | ||
478 | |||
479 | return result; | ||
480 | } | ||
481 | |||
482 | /** | ||
483 | * @brief read the sensor data from the device. | ||
484 | * | ||
485 | * @param mlsl_handle | ||
486 | * the handle to the serial channel the device is connected to. | ||
487 | * @param slave | ||
488 | * a pointer to the slave descriptor data structure. | ||
489 | * @param pdata | ||
490 | * a pointer to the slave platform data. | ||
491 | * @param data | ||
492 | * a buffer to store the data read. | ||
493 | * | ||
494 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
495 | */ | ||
496 | static int bma222_read(void *mlsl_handle, | ||
497 | struct ext_slave_descr *slave, | ||
498 | struct ext_slave_platform_data *pdata, | ||
499 | unsigned char *data) | ||
500 | { | ||
501 | int result = INV_SUCCESS; | ||
502 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
503 | BMA222_STATUS_REG, 1, data); | ||
504 | if (data[0] & BMA222_STATUS_RDY_MASK) { | ||
505 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
506 | slave->read_reg, slave->read_len, data); | ||
507 | return result; | ||
508 | } else | ||
509 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
510 | } | ||
511 | |||
512 | static struct ext_slave_descr bma222_descr = { | ||
513 | .init = bma222_init, | ||
514 | .exit = bma222_exit, | ||
515 | .suspend = bma222_suspend, | ||
516 | .resume = bma222_resume, | ||
517 | .read = bma222_read, | ||
518 | .config = bma222_config, | ||
519 | .get_config = bma222_get_config, | ||
520 | .name = "bma222", | ||
521 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
522 | .id = ACCEL_ID_BMA222, | ||
523 | .read_reg = 0x02, | ||
524 | .read_len = 6, | ||
525 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
526 | .range = {2, 0}, | ||
527 | .trigger = NULL, | ||
528 | }; | ||
529 | |||
530 | static | ||
531 | struct ext_slave_descr *bma222_get_slave_descr(void) | ||
532 | { | ||
533 | return &bma222_descr; | ||
534 | } | ||
535 | |||
536 | /* -------------------------------------------------------------------------- */ | ||
537 | |||
538 | struct bma222_mod_private_data { | ||
539 | struct i2c_client *client; | ||
540 | struct ext_slave_platform_data *pdata; | ||
541 | }; | ||
542 | |||
543 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
544 | |||
545 | static int bma222_mod_probe(struct i2c_client *client, | ||
546 | const struct i2c_device_id *devid) | ||
547 | { | ||
548 | struct ext_slave_platform_data *pdata; | ||
549 | struct bma222_mod_private_data *private_data; | ||
550 | int result = 0; | ||
551 | |||
552 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
553 | |||
554 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
555 | result = -ENODEV; | ||
556 | goto out_no_free; | ||
557 | } | ||
558 | |||
559 | pdata = client->dev.platform_data; | ||
560 | if (!pdata) { | ||
561 | dev_err(&client->adapter->dev, | ||
562 | "Missing platform data for slave %s\n", devid->name); | ||
563 | result = -EFAULT; | ||
564 | goto out_no_free; | ||
565 | } | ||
566 | |||
567 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
568 | if (!private_data) { | ||
569 | result = -ENOMEM; | ||
570 | goto out_no_free; | ||
571 | } | ||
572 | |||
573 | i2c_set_clientdata(client, private_data); | ||
574 | private_data->client = client; | ||
575 | private_data->pdata = pdata; | ||
576 | |||
577 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
578 | bma222_get_slave_descr); | ||
579 | if (result) { | ||
580 | dev_err(&client->adapter->dev, | ||
581 | "Slave registration failed: %s, %d\n", | ||
582 | devid->name, result); | ||
583 | goto out_free_memory; | ||
584 | } | ||
585 | |||
586 | return result; | ||
587 | |||
588 | out_free_memory: | ||
589 | kfree(private_data); | ||
590 | out_no_free: | ||
591 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
592 | return result; | ||
593 | |||
594 | } | ||
595 | |||
596 | static int bma222_mod_remove(struct i2c_client *client) | ||
597 | { | ||
598 | struct bma222_mod_private_data *private_data = | ||
599 | i2c_get_clientdata(client); | ||
600 | |||
601 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
602 | |||
603 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
604 | bma222_get_slave_descr); | ||
605 | |||
606 | kfree(private_data); | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static const struct i2c_device_id bma222_mod_id[] = { | ||
611 | { "bma222", ACCEL_ID_BMA222 }, | ||
612 | {} | ||
613 | }; | ||
614 | |||
615 | MODULE_DEVICE_TABLE(i2c, bma222_mod_id); | ||
616 | |||
617 | static struct i2c_driver bma222_mod_driver = { | ||
618 | .class = I2C_CLASS_HWMON, | ||
619 | .probe = bma222_mod_probe, | ||
620 | .remove = bma222_mod_remove, | ||
621 | .id_table = bma222_mod_id, | ||
622 | .driver = { | ||
623 | .owner = THIS_MODULE, | ||
624 | .name = "bma222_mod", | ||
625 | }, | ||
626 | .address_list = normal_i2c, | ||
627 | }; | ||
628 | |||
629 | static int __init bma222_mod_init(void) | ||
630 | { | ||
631 | int res = i2c_add_driver(&bma222_mod_driver); | ||
632 | pr_info("%s: Probe name %s\n", __func__, "bma222_mod"); | ||
633 | if (res) | ||
634 | pr_err("%s failed\n", __func__); | ||
635 | return res; | ||
636 | } | ||
637 | |||
638 | static void __exit bma222_mod_exit(void) | ||
639 | { | ||
640 | pr_info("%s\n", __func__); | ||
641 | i2c_del_driver(&bma222_mod_driver); | ||
642 | } | ||
643 | |||
644 | module_init(bma222_mod_init); | ||
645 | module_exit(bma222_mod_exit); | ||
646 | |||
647 | MODULE_AUTHOR("Invensense Corporation"); | ||
648 | MODULE_DESCRIPTION("Driver to integrate BMA222 sensor with the MPU"); | ||
649 | MODULE_LICENSE("GPL"); | ||
650 | MODULE_ALIAS("bma222_mod"); | ||
651 | |||
652 | /** | ||
653 | * @} | ||
654 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/bma250.c b/drivers/misc/inv_mpu/accel/bma250.c new file mode 100644 index 00000000000..6a245f4566a --- /dev/null +++ b/drivers/misc/inv_mpu/accel/bma250.c | |||
@@ -0,0 +1,787 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file bma250.c | ||
26 | * @brief Accelerometer setup and handling methods for Bosch BMA250. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <linux/mpu.h> | ||
41 | #include "mlsl.h" | ||
42 | #include "mldl_cfg.h" | ||
43 | |||
44 | /* -------------------------------------------------------------------------- */ | ||
45 | |||
46 | /* registers */ | ||
47 | #define BMA250_STATUS_REG (0x0A) | ||
48 | #define BMA250_FSR_REG (0x0F) | ||
49 | #define BMA250_ODR_REG (0x10) | ||
50 | #define BMA250_PWR_REG (0x11) | ||
51 | #define BMA250_SOFTRESET_REG (0x14) | ||
52 | #define BMA250_INT_TYPE_REG (0x17) | ||
53 | #define BMA250_INT_DST_REG (0x1A) | ||
54 | #define BMA250_INT_SRC_REG (0x1E) | ||
55 | |||
56 | /* masks */ | ||
57 | #define BMA250_STATUS_RDY_MASK (0x80) | ||
58 | #define BMA250_FSR_MASK (0x0F) | ||
59 | #define BMA250_ODR_MASK (0x1F) | ||
60 | #define BMA250_PWR_SLEEP_MASK (0x80) | ||
61 | #define BMA250_PWR_AWAKE_MASK (0x00) | ||
62 | #define BMA250_SOFTRESET_MASK (0xB6) | ||
63 | #define BMA250_INT_TYPE_MASK (0x10) | ||
64 | #define BMA250_INT_DST_1_MASK (0x01) | ||
65 | #define BMA250_INT_DST_2_MASK (0x80) | ||
66 | #define BMA250_INT_SRC_MASK (0x00) | ||
67 | |||
68 | /* -------------------------------------------------------------------------- */ | ||
69 | |||
70 | struct bma250_config { | ||
71 | unsigned int odr; /** < output data rate in mHz */ | ||
72 | unsigned int fsr; /** < full scale range mg */ | ||
73 | unsigned char irq_type; | ||
74 | }; | ||
75 | |||
76 | struct bma250_private_data { | ||
77 | struct bma250_config suspend; /** < suspend configuration */ | ||
78 | struct bma250_config resume; /** < resume configuration */ | ||
79 | }; | ||
80 | |||
81 | /* -------------------------------------------------------------------------- */ | ||
82 | /** | ||
83 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
84 | * Threshold and duration will not be used unless the type is MOT or | ||
85 | * NMOT. | ||
86 | * | ||
87 | * @param mlsl_handle | ||
88 | * the handle to the serial channel the device is connected to. | ||
89 | * @param pdata | ||
90 | * a pointer to the slave platform data. | ||
91 | * @param config | ||
92 | * configuration to apply to, suspend or resume | ||
93 | * @param apply | ||
94 | * whether to apply immediately or save the settings to be applied | ||
95 | * at the next resume. | ||
96 | * @param irq_type | ||
97 | * the type of IRQ. Valid values are | ||
98 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
99 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
100 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
101 | * | ||
102 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
103 | */ | ||
104 | static int bma250_set_irq(void *mlsl_handle, | ||
105 | struct ext_slave_platform_data *pdata, | ||
106 | struct bma250_config *config, | ||
107 | int apply, long irq_type) | ||
108 | { | ||
109 | int result = INV_SUCCESS; | ||
110 | unsigned char irqtype_reg; | ||
111 | unsigned char irqdst_reg; | ||
112 | unsigned char irqsrc_reg; | ||
113 | |||
114 | switch (irq_type) { | ||
115 | case MPU_SLAVE_IRQ_TYPE_DATA_READY: | ||
116 | /* data ready int. */ | ||
117 | irqtype_reg = BMA250_INT_TYPE_MASK; | ||
118 | /* routed to interrupt pin 1 */ | ||
119 | irqdst_reg = BMA250_INT_DST_1_MASK; | ||
120 | /* from filtered data */ | ||
121 | irqsrc_reg = BMA250_INT_SRC_MASK; | ||
122 | break; | ||
123 | /* unfinished | ||
124 | case MPU_SLAVE_IRQ_TYPE_MOTION: | ||
125 | reg1 = 0x00; | ||
126 | reg2 = config->mot_int1_cfg; | ||
127 | reg3 = ; | ||
128 | break; | ||
129 | */ | ||
130 | case MPU_SLAVE_IRQ_TYPE_NONE: | ||
131 | irqtype_reg = 0x00; | ||
132 | irqdst_reg = 0x00; | ||
133 | irqsrc_reg = 0x00; | ||
134 | break; | ||
135 | default: | ||
136 | return INV_ERROR_INVALID_PARAMETER; | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | config->irq_type = (unsigned char)irq_type; | ||
141 | |||
142 | if (apply) { | ||
143 | /* select the type of interrupt to use */ | ||
144 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
145 | BMA250_INT_TYPE_REG, irqtype_reg); | ||
146 | if (result) { | ||
147 | LOG_RESULT_LOCATION(result); | ||
148 | return result; | ||
149 | } | ||
150 | /* select to which interrupt pin to route it to */ | ||
151 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
152 | BMA250_INT_DST_REG, irqdst_reg); | ||
153 | if (result) { | ||
154 | LOG_RESULT_LOCATION(result); | ||
155 | return result; | ||
156 | } | ||
157 | /* select whether the interrupt works off filtered or | ||
158 | unfiltered data */ | ||
159 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
160 | BMA250_INT_SRC_REG, irqsrc_reg); | ||
161 | if (result) { | ||
162 | LOG_RESULT_LOCATION(result); | ||
163 | return result; | ||
164 | } | ||
165 | } | ||
166 | return result; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * @brief Set the output data rate for the particular configuration. | ||
171 | * | ||
172 | * @param mlsl_handle | ||
173 | * the handle to the serial channel the device is connected to. | ||
174 | * @param pdata | ||
175 | * a pointer to the slave platform data. | ||
176 | * @param config | ||
177 | * Config to modify with new ODR. | ||
178 | * @param apply | ||
179 | * whether to apply immediately or save the settings to be applied | ||
180 | * at the next resume. | ||
181 | * @param odr | ||
182 | * Output data rate in units of 1/1000Hz (mHz). | ||
183 | * | ||
184 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
185 | */ | ||
186 | static int bma250_set_odr(void *mlsl_handle, | ||
187 | struct ext_slave_platform_data *pdata, | ||
188 | struct bma250_config *config, | ||
189 | int apply, | ||
190 | long odr) | ||
191 | { | ||
192 | int result = INV_SUCCESS; | ||
193 | unsigned char reg_odr; | ||
194 | |||
195 | /* Table uses bandwidth which is half the sample rate */ | ||
196 | odr = odr >> 1; | ||
197 | if (odr >= 1000000) { | ||
198 | reg_odr = 0x0F; | ||
199 | config->odr = 2000000; | ||
200 | } else if (odr >= 500000) { | ||
201 | reg_odr = 0x0E; | ||
202 | config->odr = 1000000; | ||
203 | } else if (odr >= 250000) { | ||
204 | reg_odr = 0x0D; | ||
205 | config->odr = 500000; | ||
206 | } else if (odr >= 125000) { | ||
207 | reg_odr = 0x0C; | ||
208 | config->odr = 250000; | ||
209 | } else if (odr >= 62500) { | ||
210 | reg_odr = 0x0B; | ||
211 | config->odr = 125000; | ||
212 | } else if (odr >= 31250) { | ||
213 | reg_odr = 0x0A; | ||
214 | config->odr = 62500; | ||
215 | } else if (odr >= 15630) { | ||
216 | reg_odr = 0x09; | ||
217 | config->odr = 31250; | ||
218 | } else { | ||
219 | reg_odr = 0x08; | ||
220 | config->odr = 15630; | ||
221 | } | ||
222 | |||
223 | if (apply) { | ||
224 | MPL_LOGV("ODR: %d\n", config->odr); | ||
225 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
226 | BMA250_ODR_REG, reg_odr); | ||
227 | if (result) { | ||
228 | LOG_RESULT_LOCATION(result); | ||
229 | return result; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | return result; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * @brief Set the full scale range of the accels | ||
238 | * | ||
239 | * @param mlsl_handle | ||
240 | * the handle to the serial channel the device is connected to. | ||
241 | * @param pdata | ||
242 | * a pointer to the slave platform data. | ||
243 | * @param config | ||
244 | * pointer to configuration. | ||
245 | * @param apply | ||
246 | * whether to apply immediately or save the settings to be applied | ||
247 | * at the next resume. | ||
248 | * @param fsr | ||
249 | * requested full scale range. | ||
250 | * | ||
251 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
252 | */ | ||
253 | static int bma250_set_fsr(void *mlsl_handle, | ||
254 | struct ext_slave_platform_data *pdata, | ||
255 | struct bma250_config *config, | ||
256 | int apply, | ||
257 | long fsr) | ||
258 | { | ||
259 | int result = INV_SUCCESS; | ||
260 | unsigned char reg_fsr_mask; | ||
261 | |||
262 | if (fsr <= 2000) { | ||
263 | reg_fsr_mask = 0x03; | ||
264 | config->fsr = 2000; | ||
265 | } else if (fsr <= 4000) { | ||
266 | reg_fsr_mask = 0x05; | ||
267 | config->fsr = 4000; | ||
268 | } else if (fsr <= 8000) { | ||
269 | reg_fsr_mask = 0x08; | ||
270 | config->fsr = 8000; | ||
271 | } else { /* 8001 -> oo */ | ||
272 | reg_fsr_mask = 0x0C; | ||
273 | config->fsr = 16000; | ||
274 | } | ||
275 | |||
276 | if (apply) { | ||
277 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
278 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
279 | BMA250_FSR_REG, reg_fsr_mask); | ||
280 | if (result) { | ||
281 | LOG_RESULT_LOCATION(result); | ||
282 | return result; | ||
283 | } | ||
284 | } | ||
285 | return result; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * @brief one-time device driver initialization function. | ||
290 | * If the driver is built as a kernel module, this function will be | ||
291 | * called when the module is loaded in the kernel. | ||
292 | * If the driver is built-in in the kernel, this function will be | ||
293 | * called at boot time. | ||
294 | * | ||
295 | * @param mlsl_handle | ||
296 | * the handle to the serial channel the device is connected to. | ||
297 | * @param slave | ||
298 | * a pointer to the slave descriptor data structure. | ||
299 | * @param pdata | ||
300 | * a pointer to the slave platform data. | ||
301 | * | ||
302 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
303 | */ | ||
304 | static int bma250_init(void *mlsl_handle, | ||
305 | struct ext_slave_descr *slave, | ||
306 | struct ext_slave_platform_data *pdata) | ||
307 | { | ||
308 | int result; | ||
309 | long range; | ||
310 | |||
311 | struct bma250_private_data *private_data; | ||
312 | private_data = kzalloc(sizeof(struct bma250_private_data), GFP_KERNEL); | ||
313 | |||
314 | if (!private_data) | ||
315 | return INV_ERROR_MEMORY_EXAUSTED; | ||
316 | |||
317 | pdata->private_data = private_data; | ||
318 | |||
319 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
320 | BMA250_SOFTRESET_REG, BMA250_SOFTRESET_MASK); | ||
321 | if (result) { | ||
322 | LOG_RESULT_LOCATION(result); | ||
323 | return result; | ||
324 | } | ||
325 | msleep(1); | ||
326 | |||
327 | result = bma250_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
328 | false, 0); | ||
329 | if (result) { | ||
330 | LOG_RESULT_LOCATION(result); | ||
331 | return result; | ||
332 | } | ||
333 | result = bma250_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
334 | false, 200000); | ||
335 | if (result) { | ||
336 | LOG_RESULT_LOCATION(result); | ||
337 | return result; | ||
338 | } | ||
339 | |||
340 | range = range_fixedpoint_to_long_mg(slave->range); | ||
341 | result = bma250_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
342 | false, range); | ||
343 | result = bma250_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
344 | false, range); | ||
345 | if (result) { | ||
346 | LOG_RESULT_LOCATION(result); | ||
347 | return result; | ||
348 | } | ||
349 | |||
350 | result = bma250_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
351 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
352 | if (result) { | ||
353 | LOG_RESULT_LOCATION(result); | ||
354 | return result; | ||
355 | } | ||
356 | result = bma250_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
357 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
358 | if (result) { | ||
359 | LOG_RESULT_LOCATION(result); | ||
360 | return result; | ||
361 | } | ||
362 | |||
363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
364 | BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK); | ||
365 | if (result) { | ||
366 | LOG_RESULT_LOCATION(result); | ||
367 | return result; | ||
368 | } | ||
369 | |||
370 | return result; | ||
371 | } | ||
372 | |||
373 | /** | ||
374 | * @brief one-time device driver exit function. | ||
375 | * If the driver is built as a kernel module, this function will be | ||
376 | * called when the module is removed from the kernel. | ||
377 | * | ||
378 | * @param mlsl_handle | ||
379 | * the handle to the serial channel the device is connected to. | ||
380 | * @param slave | ||
381 | * a pointer to the slave descriptor data structure. | ||
382 | * @param pdata | ||
383 | * a pointer to the slave platform data. | ||
384 | * | ||
385 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
386 | */ | ||
387 | static int bma250_exit(void *mlsl_handle, | ||
388 | struct ext_slave_descr *slave, | ||
389 | struct ext_slave_platform_data *pdata) | ||
390 | { | ||
391 | kfree(pdata->private_data); | ||
392 | return INV_SUCCESS; | ||
393 | } | ||
394 | |||
395 | /** | ||
396 | * @brief device configuration facility. | ||
397 | * | ||
398 | * @param mlsl_handle | ||
399 | * the handle to the serial channel the device is connected to. | ||
400 | * @param slave | ||
401 | * a pointer to the slave descriptor data structure. | ||
402 | * @param pdata | ||
403 | * a pointer to the slave platform data. | ||
404 | * @param data | ||
405 | * a pointer to the configuration data structure. | ||
406 | * | ||
407 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
408 | */ | ||
409 | static int bma250_config(void *mlsl_handle, | ||
410 | struct ext_slave_descr *slave, | ||
411 | struct ext_slave_platform_data *pdata, | ||
412 | struct ext_slave_config *data) | ||
413 | { | ||
414 | struct bma250_private_data *private_data = | ||
415 | (struct bma250_private_data *)(pdata->private_data); | ||
416 | |||
417 | if (!data->data) | ||
418 | return INV_ERROR_INVALID_PARAMETER; | ||
419 | |||
420 | switch (data->key) { | ||
421 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
422 | return bma250_set_odr(mlsl_handle, pdata, | ||
423 | &private_data->suspend, | ||
424 | data->apply, | ||
425 | *((long *)data->data)); | ||
426 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
427 | return bma250_set_odr(mlsl_handle, pdata, | ||
428 | &private_data->resume, | ||
429 | data->apply, | ||
430 | *((long *)data->data)); | ||
431 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
432 | return bma250_set_fsr(mlsl_handle, pdata, | ||
433 | &private_data->suspend, | ||
434 | data->apply, | ||
435 | *((long *)data->data)); | ||
436 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
437 | return bma250_set_fsr(mlsl_handle, pdata, | ||
438 | &private_data->resume, | ||
439 | data->apply, | ||
440 | *((long *)data->data)); | ||
441 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
442 | return bma250_set_irq(mlsl_handle, pdata, | ||
443 | &private_data->suspend, | ||
444 | data->apply, | ||
445 | *((long *)data->data)); | ||
446 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
447 | return bma250_set_irq(mlsl_handle, pdata, | ||
448 | &private_data->resume, | ||
449 | data->apply, | ||
450 | *((long *)data->data)); | ||
451 | default: | ||
452 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
453 | }; | ||
454 | return INV_SUCCESS; | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * @brief facility to retrieve the device configuration. | ||
459 | * | ||
460 | * @param mlsl_handle | ||
461 | * the handle to the serial channel the device is connected to. | ||
462 | * @param slave | ||
463 | * a pointer to the slave descriptor data structure. | ||
464 | * @param pdata | ||
465 | * a pointer to the slave platform data. | ||
466 | * @param data | ||
467 | * a pointer to store the returned configuration data structure. | ||
468 | * | ||
469 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
470 | */ | ||
471 | static int bma250_get_config(void *mlsl_handle, | ||
472 | struct ext_slave_descr *slave, | ||
473 | struct ext_slave_platform_data *pdata, | ||
474 | struct ext_slave_config *data) | ||
475 | { | ||
476 | struct bma250_private_data *private_data = | ||
477 | (struct bma250_private_data *)(pdata->private_data); | ||
478 | |||
479 | if (!data->data) | ||
480 | return INV_ERROR_INVALID_PARAMETER; | ||
481 | |||
482 | switch (data->key) { | ||
483 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
484 | (*(unsigned long *)data->data) = | ||
485 | (unsigned long) private_data->suspend.odr; | ||
486 | break; | ||
487 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
488 | (*(unsigned long *)data->data) = | ||
489 | (unsigned long) private_data->resume.odr; | ||
490 | break; | ||
491 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
492 | (*(unsigned long *)data->data) = | ||
493 | (unsigned long) private_data->suspend.fsr; | ||
494 | break; | ||
495 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
496 | (*(unsigned long *)data->data) = | ||
497 | (unsigned long) private_data->resume.fsr; | ||
498 | break; | ||
499 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
500 | (*(unsigned long *)data->data) = | ||
501 | (unsigned long) private_data->suspend.irq_type; | ||
502 | break; | ||
503 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
504 | (*(unsigned long *)data->data) = | ||
505 | (unsigned long) private_data->resume.irq_type; | ||
506 | break; | ||
507 | default: | ||
508 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
509 | }; | ||
510 | |||
511 | return INV_SUCCESS; | ||
512 | } | ||
513 | |||
514 | /** | ||
515 | * @brief suspends the device to put it in its lowest power mode. | ||
516 | * | ||
517 | * @param mlsl_handle | ||
518 | * the handle to the serial channel the device is connected to. | ||
519 | * @param slave | ||
520 | * a pointer to the slave descriptor data structure. | ||
521 | * @param pdata | ||
522 | * a pointer to the slave platform data. | ||
523 | * | ||
524 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
525 | */ | ||
526 | static int bma250_suspend(void *mlsl_handle, | ||
527 | struct ext_slave_descr *slave, | ||
528 | struct ext_slave_platform_data *pdata) | ||
529 | { | ||
530 | int result; | ||
531 | struct bma250_config *suspend_config = | ||
532 | &((struct bma250_private_data *)pdata->private_data)->suspend; | ||
533 | |||
534 | result = bma250_set_odr(mlsl_handle, pdata, suspend_config, | ||
535 | true, suspend_config->odr); | ||
536 | if (result) { | ||
537 | LOG_RESULT_LOCATION(result); | ||
538 | return result; | ||
539 | } | ||
540 | result = bma250_set_fsr(mlsl_handle, pdata, suspend_config, | ||
541 | true, suspend_config->fsr); | ||
542 | if (result) { | ||
543 | LOG_RESULT_LOCATION(result); | ||
544 | return result; | ||
545 | } | ||
546 | result = bma250_set_irq(mlsl_handle, pdata, suspend_config, | ||
547 | true, suspend_config->irq_type); | ||
548 | if (result) { | ||
549 | LOG_RESULT_LOCATION(result); | ||
550 | return result; | ||
551 | } | ||
552 | |||
553 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
554 | BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK); | ||
555 | if (result) { | ||
556 | LOG_RESULT_LOCATION(result); | ||
557 | return result; | ||
558 | } | ||
559 | |||
560 | msleep(3); /* 3 ms powerup time maximum */ | ||
561 | return result; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * @brief resume the device in the proper power state given the configuration | ||
566 | * chosen. | ||
567 | * | ||
568 | * @param mlsl_handle | ||
569 | * the handle to the serial channel the device is connected to. | ||
570 | * @param slave | ||
571 | * a pointer to the slave descriptor data structure. | ||
572 | * @param pdata | ||
573 | * a pointer to the slave platform data. | ||
574 | * | ||
575 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
576 | */ | ||
577 | static int bma250_resume(void *mlsl_handle, | ||
578 | struct ext_slave_descr *slave, | ||
579 | struct ext_slave_platform_data *pdata) | ||
580 | { | ||
581 | int result; | ||
582 | struct bma250_config *resume_config = | ||
583 | &((struct bma250_private_data *)pdata->private_data)->resume; | ||
584 | |||
585 | result = bma250_set_odr(mlsl_handle, pdata, resume_config, | ||
586 | true, resume_config->odr); | ||
587 | if (result) { | ||
588 | LOG_RESULT_LOCATION(result); | ||
589 | return result; | ||
590 | } | ||
591 | result = bma250_set_fsr(mlsl_handle, pdata, resume_config, | ||
592 | true, resume_config->fsr); | ||
593 | if (result) { | ||
594 | LOG_RESULT_LOCATION(result); | ||
595 | return result; | ||
596 | } | ||
597 | result = bma250_set_irq(mlsl_handle, pdata, resume_config, | ||
598 | true, resume_config->irq_type); | ||
599 | if (result) { | ||
600 | LOG_RESULT_LOCATION(result); | ||
601 | return result; | ||
602 | } | ||
603 | |||
604 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
605 | BMA250_PWR_REG, BMA250_PWR_AWAKE_MASK); | ||
606 | if (result) { | ||
607 | LOG_RESULT_LOCATION(result); | ||
608 | return result; | ||
609 | } | ||
610 | |||
611 | return result; | ||
612 | } | ||
613 | |||
614 | /** | ||
615 | * @brief read the sensor data from the device. | ||
616 | * | ||
617 | * @param mlsl_handle | ||
618 | * the handle to the serial channel the device is connected to. | ||
619 | * @param slave | ||
620 | * a pointer to the slave descriptor data structure. | ||
621 | * @param pdata | ||
622 | * a pointer to the slave platform data. | ||
623 | * @param data | ||
624 | * a buffer to store the data read. | ||
625 | * | ||
626 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
627 | */ | ||
628 | static int bma250_read(void *mlsl_handle, | ||
629 | struct ext_slave_descr *slave, | ||
630 | struct ext_slave_platform_data *pdata, | ||
631 | unsigned char *data) | ||
632 | { | ||
633 | int result = INV_SUCCESS; | ||
634 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
635 | BMA250_STATUS_REG, 1, data); | ||
636 | if (1) { /* KLP - workaroud for small data ready window */ | ||
637 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
638 | slave->read_reg, slave->read_len, data); | ||
639 | return result; | ||
640 | } else | ||
641 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
642 | } | ||
643 | |||
644 | static struct ext_slave_descr bma250_descr = { | ||
645 | .init = bma250_init, | ||
646 | .exit = bma250_exit, | ||
647 | .suspend = bma250_suspend, | ||
648 | .resume = bma250_resume, | ||
649 | .read = bma250_read, | ||
650 | .config = bma250_config, | ||
651 | .get_config = bma250_get_config, | ||
652 | .name = "bma250", | ||
653 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
654 | .id = ACCEL_ID_BMA250, | ||
655 | .read_reg = 0x02, | ||
656 | .read_len = 6, | ||
657 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
658 | .range = {2, 0}, | ||
659 | .trigger = NULL, | ||
660 | }; | ||
661 | |||
662 | static | ||
663 | struct ext_slave_descr *bma250_get_slave_descr(void) | ||
664 | { | ||
665 | return &bma250_descr; | ||
666 | } | ||
667 | |||
668 | /* -------------------------------------------------------------------------- */ | ||
669 | |||
670 | /* Platform data for the MPU */ | ||
671 | struct bma250_mod_private_data { | ||
672 | struct i2c_client *client; | ||
673 | struct ext_slave_platform_data *pdata; | ||
674 | }; | ||
675 | |||
676 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
677 | |||
678 | static int bma250_mod_probe(struct i2c_client *client, | ||
679 | const struct i2c_device_id *devid) | ||
680 | { | ||
681 | struct ext_slave_platform_data *pdata; | ||
682 | struct bma250_mod_private_data *private_data; | ||
683 | int result = 0; | ||
684 | |||
685 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
686 | |||
687 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
688 | result = -ENODEV; | ||
689 | goto out_no_free; | ||
690 | } | ||
691 | |||
692 | pdata = client->dev.platform_data; | ||
693 | if (!pdata) { | ||
694 | dev_err(&client->adapter->dev, | ||
695 | "Missing platform data for slave %s\n", devid->name); | ||
696 | result = -EFAULT; | ||
697 | goto out_no_free; | ||
698 | } | ||
699 | |||
700 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
701 | if (!private_data) { | ||
702 | result = -ENOMEM; | ||
703 | goto out_no_free; | ||
704 | } | ||
705 | |||
706 | i2c_set_clientdata(client, private_data); | ||
707 | private_data->client = client; | ||
708 | private_data->pdata = pdata; | ||
709 | |||
710 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
711 | bma250_get_slave_descr); | ||
712 | if (result) { | ||
713 | dev_err(&client->adapter->dev, | ||
714 | "Slave registration failed: %s, %d\n", | ||
715 | devid->name, result); | ||
716 | goto out_free_memory; | ||
717 | } | ||
718 | |||
719 | return result; | ||
720 | |||
721 | out_free_memory: | ||
722 | kfree(private_data); | ||
723 | out_no_free: | ||
724 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
725 | return result; | ||
726 | |||
727 | } | ||
728 | |||
729 | static int bma250_mod_remove(struct i2c_client *client) | ||
730 | { | ||
731 | struct bma250_mod_private_data *private_data = | ||
732 | i2c_get_clientdata(client); | ||
733 | |||
734 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
735 | |||
736 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
737 | bma250_get_slave_descr); | ||
738 | |||
739 | kfree(private_data); | ||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | static const struct i2c_device_id bma250_mod_id[] = { | ||
744 | { "bma250", ACCEL_ID_BMA250 }, | ||
745 | {} | ||
746 | }; | ||
747 | |||
748 | MODULE_DEVICE_TABLE(i2c, bma250_mod_id); | ||
749 | |||
750 | static struct i2c_driver bma250_mod_driver = { | ||
751 | .class = I2C_CLASS_HWMON, | ||
752 | .probe = bma250_mod_probe, | ||
753 | .remove = bma250_mod_remove, | ||
754 | .id_table = bma250_mod_id, | ||
755 | .driver = { | ||
756 | .owner = THIS_MODULE, | ||
757 | .name = "bma250_mod", | ||
758 | }, | ||
759 | .address_list = normal_i2c, | ||
760 | }; | ||
761 | |||
762 | static int __init bma250_mod_init(void) | ||
763 | { | ||
764 | int res = i2c_add_driver(&bma250_mod_driver); | ||
765 | pr_info("%s: Probe name %s\n", __func__, "bma250_mod"); | ||
766 | if (res) | ||
767 | pr_err("%s failed\n", __func__); | ||
768 | return res; | ||
769 | } | ||
770 | |||
771 | static void __exit bma250_mod_exit(void) | ||
772 | { | ||
773 | pr_info("%s\n", __func__); | ||
774 | i2c_del_driver(&bma250_mod_driver); | ||
775 | } | ||
776 | |||
777 | module_init(bma250_mod_init); | ||
778 | module_exit(bma250_mod_exit); | ||
779 | |||
780 | MODULE_AUTHOR("Invensense Corporation"); | ||
781 | MODULE_DESCRIPTION("Driver to integrate BMA250 sensor with the MPU"); | ||
782 | MODULE_LICENSE("GPL"); | ||
783 | MODULE_ALIAS("bma250_mod"); | ||
784 | |||
785 | /** | ||
786 | * @} | ||
787 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/cma3000.c b/drivers/misc/inv_mpu/accel/cma3000.c new file mode 100644 index 00000000000..496d1f29bdc --- /dev/null +++ b/drivers/misc/inv_mpu/accel/cma3000.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Accelerometer setup and handling methods for VTI CMA3000. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file cma3000.c | ||
26 | * @brief Accelerometer setup and handling methods for VTI CMA3000 | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-acc" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | |||
49 | static int cma3000_suspend(void *mlsl_handle, | ||
50 | struct ext_slave_descr *slave, | ||
51 | struct ext_slave_platform_data *pdata) | ||
52 | { | ||
53 | int result; | ||
54 | /* RAM reset */ | ||
55 | result = | ||
56 | inv_serial_single_write(mlsl_handle, pdata->address, 0x1d, 0xcd); | ||
57 | return result; | ||
58 | } | ||
59 | |||
60 | static int cma3000_resume(void *mlsl_handle, | ||
61 | struct ext_slave_descr *slave, | ||
62 | struct ext_slave_platform_data *pdata) | ||
63 | { | ||
64 | |||
65 | return INV_SUCCESS; | ||
66 | } | ||
67 | |||
68 | static int cma3000_read(void *mlsl_handle, | ||
69 | struct ext_slave_descr *slave, | ||
70 | struct ext_slave_platform_data *pdata, | ||
71 | unsigned char *data) | ||
72 | { | ||
73 | int result; | ||
74 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
75 | slave->reg, slave->len, data); | ||
76 | return result; | ||
77 | } | ||
78 | |||
79 | static struct ext_slave_descr cma3000_descr = { | ||
80 | .init = NULL, | ||
81 | .exit = NULL, | ||
82 | .suspend = cma3000_suspend, | ||
83 | .resume = cma3000_resume, | ||
84 | .read = cma3000_read, | ||
85 | .config = NULL, | ||
86 | .get_config = NULL, | ||
87 | .name = "cma3000", | ||
88 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
89 | .id = ID_INVALID, | ||
90 | .read_reg = 0x06, | ||
91 | .read_len = 6, | ||
92 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
93 | .range = {2, 0}, | ||
94 | .trigger = NULL, | ||
95 | |||
96 | }; | ||
97 | |||
98 | static | ||
99 | struct ext_slave_descr *cma3000_get_slave_descr(void) | ||
100 | { | ||
101 | return &cma3000_descr; | ||
102 | } | ||
103 | |||
104 | /* -------------------------------------------------------------------------- */ | ||
105 | |||
106 | struct cma3000_mod_private_data { | ||
107 | struct i2c_client *client; | ||
108 | struct ext_slave_platform_data *pdata; | ||
109 | }; | ||
110 | |||
111 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
112 | |||
113 | static int cma3000_mod_probe(struct i2c_client *client, | ||
114 | const struct i2c_device_id *devid) | ||
115 | { | ||
116 | struct ext_slave_platform_data *pdata; | ||
117 | struct cma3000_mod_private_data *private_data; | ||
118 | int result = 0; | ||
119 | |||
120 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
121 | |||
122 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
123 | result = -ENODEV; | ||
124 | goto out_no_free; | ||
125 | } | ||
126 | |||
127 | pdata = client->dev.platform_data; | ||
128 | if (!pdata) { | ||
129 | dev_err(&client->adapter->dev, | ||
130 | "Missing platform data for slave %s\n", devid->name); | ||
131 | result = -EFAULT; | ||
132 | goto out_no_free; | ||
133 | } | ||
134 | |||
135 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
136 | if (!private_data) { | ||
137 | result = -ENOMEM; | ||
138 | goto out_no_free; | ||
139 | } | ||
140 | |||
141 | i2c_set_clientdata(client, private_data); | ||
142 | private_data->client = client; | ||
143 | private_data->pdata = pdata; | ||
144 | |||
145 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
146 | cma3000_get_slave_descr); | ||
147 | if (result) { | ||
148 | dev_err(&client->adapter->dev, | ||
149 | "Slave registration failed: %s, %d\n", | ||
150 | devid->name, result); | ||
151 | goto out_free_memory; | ||
152 | } | ||
153 | |||
154 | return result; | ||
155 | |||
156 | out_free_memory: | ||
157 | kfree(private_data); | ||
158 | out_no_free: | ||
159 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
160 | return result; | ||
161 | |||
162 | } | ||
163 | |||
164 | static int cma3000_mod_remove(struct i2c_client *client) | ||
165 | { | ||
166 | struct cma3000_mod_private_data *private_data = | ||
167 | i2c_get_clientdata(client); | ||
168 | |||
169 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
170 | |||
171 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
172 | cma3000_get_slave_descr); | ||
173 | |||
174 | kfree(private_data); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static const struct i2c_device_id cma3000_mod_id[] = { | ||
179 | { "cma3000", ACCEL_ID_CMA3000 }, | ||
180 | {} | ||
181 | }; | ||
182 | |||
183 | MODULE_DEVICE_TABLE(i2c, cma3000_mod_id); | ||
184 | |||
185 | static struct i2c_driver cma3000_mod_driver = { | ||
186 | .class = I2C_CLASS_HWMON, | ||
187 | .probe = cma3000_mod_probe, | ||
188 | .remove = cma3000_mod_remove, | ||
189 | .id_table = cma3000_mod_id, | ||
190 | .driver = { | ||
191 | .owner = THIS_MODULE, | ||
192 | .name = "cma3000_mod", | ||
193 | }, | ||
194 | .address_list = normal_i2c, | ||
195 | }; | ||
196 | |||
197 | static int __init cma3000_mod_init(void) | ||
198 | { | ||
199 | int res = i2c_add_driver(&cma3000_mod_driver); | ||
200 | pr_info("%s: Probe name %s\n", __func__, "cma3000_mod"); | ||
201 | if (res) | ||
202 | pr_err("%s failed\n", __func__); | ||
203 | return res; | ||
204 | } | ||
205 | |||
206 | static void __exit cma3000_mod_exit(void) | ||
207 | { | ||
208 | pr_info("%s\n", __func__); | ||
209 | i2c_del_driver(&cma3000_mod_driver); | ||
210 | } | ||
211 | |||
212 | module_init(cma3000_mod_init); | ||
213 | module_exit(cma3000_mod_exit); | ||
214 | |||
215 | MODULE_AUTHOR("Invensense Corporation"); | ||
216 | MODULE_DESCRIPTION("Driver to integrate CMA3000 sensor with the MPU"); | ||
217 | MODULE_LICENSE("GPL"); | ||
218 | MODULE_ALIAS("cma3000_mod"); | ||
219 | |||
220 | /** | ||
221 | * @} | ||
222 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/kxsd9.c b/drivers/misc/inv_mpu/accel/kxsd9.c new file mode 100644 index 00000000000..5cb4eaf6b4a --- /dev/null +++ b/drivers/misc/inv_mpu/accel/kxsd9.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Accelerometer setup and handling methods for Kionix KXSD9. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file kxsd9.c | ||
26 | * @brief Accelerometer setup and handling methods for Kionix KXSD9. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-acc" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | |||
49 | static int kxsd9_suspend(void *mlsl_handle, | ||
50 | struct ext_slave_descr *slave, | ||
51 | struct ext_slave_platform_data *pdata) | ||
52 | { | ||
53 | int result; | ||
54 | /* CTRL_REGB: low-power standby mode */ | ||
55 | result = | ||
56 | inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x0); | ||
57 | if (result) { | ||
58 | LOG_RESULT_LOCATION(result); | ||
59 | return result; | ||
60 | } | ||
61 | return result; | ||
62 | } | ||
63 | |||
64 | /* full scale setting - register and mask */ | ||
65 | #define ACCEL_KIONIX_CTRL_REG (0x0C) | ||
66 | #define ACCEL_KIONIX_CTRL_MASK (0x3) | ||
67 | |||
68 | static int kxsd9_resume(void *mlsl_handle, | ||
69 | struct ext_slave_descr *slave, | ||
70 | struct ext_slave_platform_data *pdata) | ||
71 | { | ||
72 | int result = INV_SUCCESS; | ||
73 | unsigned char reg; | ||
74 | |||
75 | /* Full Scale */ | ||
76 | reg = 0x0; | ||
77 | reg &= ~ACCEL_KIONIX_CTRL_MASK; | ||
78 | reg |= 0x00; | ||
79 | if (slave->range.mantissa == 4) { /* 4g scale = 4.9951 */ | ||
80 | reg |= 0x2; | ||
81 | slave->range.fraction = 9951; | ||
82 | } else if (slave->range.mantissa == 7) { /* 6g scale = 7.5018 */ | ||
83 | reg |= 0x1; | ||
84 | slave->range.fraction = 5018; | ||
85 | } else if (slave->range.mantissa == 9) { /* 8g scale = 9.9902 */ | ||
86 | reg |= 0x0; | ||
87 | slave->range.fraction = 9902; | ||
88 | } else { | ||
89 | slave->range.mantissa = 2; /* 2g scale = 2.5006 */ | ||
90 | slave->range.fraction = 5006; | ||
91 | reg |= 0x3; | ||
92 | } | ||
93 | reg |= 0xC0; /* 100Hz LPF */ | ||
94 | result = | ||
95 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
96 | ACCEL_KIONIX_CTRL_REG, reg); | ||
97 | if (result) { | ||
98 | LOG_RESULT_LOCATION(result); | ||
99 | return result; | ||
100 | } | ||
101 | /* normal operation */ | ||
102 | result = | ||
103 | inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x40); | ||
104 | if (result) { | ||
105 | LOG_RESULT_LOCATION(result); | ||
106 | return result; | ||
107 | } | ||
108 | |||
109 | return INV_SUCCESS; | ||
110 | } | ||
111 | |||
112 | static int kxsd9_read(void *mlsl_handle, | ||
113 | struct ext_slave_descr *slave, | ||
114 | struct ext_slave_platform_data *pdata, | ||
115 | unsigned char *data) | ||
116 | { | ||
117 | int result; | ||
118 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
119 | slave->read_reg, slave->read_len, data); | ||
120 | return result; | ||
121 | } | ||
122 | |||
123 | static struct ext_slave_descr kxsd9_descr = { | ||
124 | .init = NULL, | ||
125 | .exit = NULL, | ||
126 | .suspend = kxsd9_suspend, | ||
127 | .resume = kxsd9_resume, | ||
128 | .read = kxsd9_read, | ||
129 | .config = NULL, | ||
130 | .get_config = NULL, | ||
131 | .name = "kxsd9", | ||
132 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
133 | .id = ACCEL_ID_KXSD9, | ||
134 | .read_reg = 0x00, | ||
135 | .read_len = 6, | ||
136 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
137 | .range = {2, 5006}, | ||
138 | .trigger = NULL, | ||
139 | }; | ||
140 | |||
141 | static | ||
142 | struct ext_slave_descr *kxsd9_get_slave_descr(void) | ||
143 | { | ||
144 | return &kxsd9_descr; | ||
145 | } | ||
146 | |||
147 | /* -------------------------------------------------------------------------- */ | ||
148 | struct kxsd9_mod_private_data { | ||
149 | struct i2c_client *client; | ||
150 | struct ext_slave_platform_data *pdata; | ||
151 | }; | ||
152 | |||
153 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
154 | |||
155 | static int kxsd9_mod_probe(struct i2c_client *client, | ||
156 | const struct i2c_device_id *devid) | ||
157 | { | ||
158 | struct ext_slave_platform_data *pdata; | ||
159 | struct kxsd9_mod_private_data *private_data; | ||
160 | int result = 0; | ||
161 | |||
162 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
163 | |||
164 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
165 | result = -ENODEV; | ||
166 | goto out_no_free; | ||
167 | } | ||
168 | |||
169 | pdata = client->dev.platform_data; | ||
170 | if (!pdata) { | ||
171 | dev_err(&client->adapter->dev, | ||
172 | "Missing platform data for slave %s\n", devid->name); | ||
173 | result = -EFAULT; | ||
174 | goto out_no_free; | ||
175 | } | ||
176 | |||
177 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
178 | if (!private_data) { | ||
179 | result = -ENOMEM; | ||
180 | goto out_no_free; | ||
181 | } | ||
182 | |||
183 | i2c_set_clientdata(client, private_data); | ||
184 | private_data->client = client; | ||
185 | private_data->pdata = pdata; | ||
186 | |||
187 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
188 | kxsd9_get_slave_descr); | ||
189 | if (result) { | ||
190 | dev_err(&client->adapter->dev, | ||
191 | "Slave registration failed: %s, %d\n", | ||
192 | devid->name, result); | ||
193 | goto out_free_memory; | ||
194 | } | ||
195 | |||
196 | return result; | ||
197 | |||
198 | out_free_memory: | ||
199 | kfree(private_data); | ||
200 | out_no_free: | ||
201 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
202 | return result; | ||
203 | |||
204 | } | ||
205 | |||
206 | static int kxsd9_mod_remove(struct i2c_client *client) | ||
207 | { | ||
208 | struct kxsd9_mod_private_data *private_data = | ||
209 | i2c_get_clientdata(client); | ||
210 | |||
211 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
212 | |||
213 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
214 | kxsd9_get_slave_descr); | ||
215 | |||
216 | kfree(private_data); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static const struct i2c_device_id kxsd9_mod_id[] = { | ||
221 | { "kxsd9", ACCEL_ID_KXSD9 }, | ||
222 | {} | ||
223 | }; | ||
224 | |||
225 | MODULE_DEVICE_TABLE(i2c, kxsd9_mod_id); | ||
226 | |||
227 | static struct i2c_driver kxsd9_mod_driver = { | ||
228 | .class = I2C_CLASS_HWMON, | ||
229 | .probe = kxsd9_mod_probe, | ||
230 | .remove = kxsd9_mod_remove, | ||
231 | .id_table = kxsd9_mod_id, | ||
232 | .driver = { | ||
233 | .owner = THIS_MODULE, | ||
234 | .name = "kxsd9_mod", | ||
235 | }, | ||
236 | .address_list = normal_i2c, | ||
237 | }; | ||
238 | |||
239 | static int __init kxsd9_mod_init(void) | ||
240 | { | ||
241 | int res = i2c_add_driver(&kxsd9_mod_driver); | ||
242 | pr_info("%s: Probe name %s\n", __func__, "kxsd9_mod"); | ||
243 | if (res) | ||
244 | pr_err("%s failed\n", __func__); | ||
245 | return res; | ||
246 | } | ||
247 | |||
248 | static void __exit kxsd9_mod_exit(void) | ||
249 | { | ||
250 | pr_info("%s\n", __func__); | ||
251 | i2c_del_driver(&kxsd9_mod_driver); | ||
252 | } | ||
253 | |||
254 | module_init(kxsd9_mod_init); | ||
255 | module_exit(kxsd9_mod_exit); | ||
256 | |||
257 | MODULE_AUTHOR("Invensense Corporation"); | ||
258 | MODULE_DESCRIPTION("Driver to integrate KXSD9 sensor with the MPU"); | ||
259 | MODULE_LICENSE("GPL"); | ||
260 | MODULE_ALIAS("kxsd9_mod"); | ||
261 | |||
262 | /** | ||
263 | * @} | ||
264 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/kxtf9.c b/drivers/misc/inv_mpu/accel/kxtf9.c new file mode 100644 index 00000000000..80776f249c6 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/kxtf9.c | |||
@@ -0,0 +1,841 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Accelerometer setup and handling methods for Kionix KXTF9. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file kxtf9.c | ||
26 | * @brief Accelerometer setup and handling methods for Kionix KXTF9. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #undef MPL_LOG_NDEBUG | ||
32 | #define MPL_LOG_NDEBUG 1 | ||
33 | |||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include "mpu-dev.h" | ||
42 | |||
43 | #include <log.h> | ||
44 | #include <linux/mpu.h> | ||
45 | #include "mlsl.h" | ||
46 | #include "mldl_cfg.h" | ||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-acc" | ||
49 | |||
50 | #define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */ | ||
51 | #define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */ | ||
52 | #define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */ | ||
53 | #define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */ | ||
54 | #define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */ | ||
55 | #define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */ | ||
56 | #define KXTF9_XOUT_L (0x06) /* 0000 0110 */ | ||
57 | #define KXTF9_XOUT_H (0x07) /* 0000 0111 */ | ||
58 | #define KXTF9_YOUT_L (0x08) /* 0000 1000 */ | ||
59 | #define KXTF9_YOUT_H (0x09) /* 0000 1001 */ | ||
60 | #define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */ | ||
61 | #define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */ | ||
62 | #define KXTF9_ST_RESP (0x0C) /* 0000 1100 */ | ||
63 | #define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */ | ||
64 | #define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */ | ||
65 | #define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */ | ||
66 | #define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */ | ||
67 | #define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */ | ||
68 | #define KXTF9_STATUS_REG (0x18) /* 0001 1000 */ | ||
69 | #define KXTF9_INT_REL (0x1A) /* 0001 1010 */ | ||
70 | #define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */ | ||
71 | #define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */ | ||
72 | #define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */ | ||
73 | #define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ | ||
74 | #define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ | ||
75 | #define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */ | ||
76 | #define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */ | ||
77 | #define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */ | ||
78 | #define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */ | ||
79 | #define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */ | ||
80 | #define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */ | ||
81 | #define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */ | ||
82 | #define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ | ||
83 | #define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ | ||
84 | #define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ | ||
85 | #define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ | ||
86 | #define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */ | ||
87 | #define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */ | ||
88 | #define KXTF9_HYST_SET (0x5F) /* 0101 1111 */ | ||
89 | |||
90 | #define KXTF9_MAX_DUR (0xFF) | ||
91 | #define KXTF9_MAX_THS (0xFF) | ||
92 | #define KXTF9_THS_COUNTS_P_G (32) | ||
93 | |||
94 | /* -------------------------------------------------------------------------- */ | ||
95 | |||
96 | struct kxtf9_config { | ||
97 | unsigned long odr; /* Output data rate mHz */ | ||
98 | unsigned int fsr; /* full scale range mg */ | ||
99 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
100 | unsigned int dur; /* Motion no-motion duration ms */ | ||
101 | unsigned int irq_type; | ||
102 | unsigned char reg_ths; | ||
103 | unsigned char reg_dur; | ||
104 | unsigned char reg_odr; | ||
105 | unsigned char reg_int_cfg1; | ||
106 | unsigned char reg_int_cfg2; | ||
107 | unsigned char ctrl_reg1; | ||
108 | }; | ||
109 | |||
110 | struct kxtf9_private_data { | ||
111 | struct kxtf9_config suspend; | ||
112 | struct kxtf9_config resume; | ||
113 | }; | ||
114 | |||
115 | static int kxtf9_set_ths(void *mlsl_handle, | ||
116 | struct ext_slave_platform_data *pdata, | ||
117 | struct kxtf9_config *config, int apply, long ths) | ||
118 | { | ||
119 | int result = INV_SUCCESS; | ||
120 | if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS) | ||
121 | ths = (long)(KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G; | ||
122 | |||
123 | if (ths < 0) | ||
124 | ths = 0; | ||
125 | |||
126 | config->ths = ths; | ||
127 | config->reg_ths = (unsigned char) | ||
128 | ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000); | ||
129 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
130 | if (apply) | ||
131 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
132 | KXTF9_WUF_THRESH, | ||
133 | config->reg_ths); | ||
134 | return result; | ||
135 | } | ||
136 | |||
137 | static int kxtf9_set_dur(void *mlsl_handle, | ||
138 | struct ext_slave_platform_data *pdata, | ||
139 | struct kxtf9_config *config, int apply, long dur) | ||
140 | { | ||
141 | int result = INV_SUCCESS; | ||
142 | long reg_dur = (dur * config->odr) / 1000000L; | ||
143 | config->dur = dur; | ||
144 | |||
145 | if (reg_dur > KXTF9_MAX_DUR) | ||
146 | reg_dur = KXTF9_MAX_DUR; | ||
147 | |||
148 | config->reg_dur = (unsigned char)reg_dur; | ||
149 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
150 | if (apply) | ||
151 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
152 | KXTF9_WUF_TIMER, | ||
153 | (unsigned char)reg_dur); | ||
154 | return result; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
159 | * duration will not be used uless the type is MOT or NMOT. | ||
160 | * | ||
161 | * @param config configuration to apply to, suspend or resume | ||
162 | * @param irq_type The type of IRQ. Valid values are | ||
163 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
164 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
165 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
166 | */ | ||
167 | static int kxtf9_set_irq(void *mlsl_handle, | ||
168 | struct ext_slave_platform_data *pdata, | ||
169 | struct kxtf9_config *config, int apply, long irq_type) | ||
170 | { | ||
171 | int result = INV_SUCCESS; | ||
172 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
173 | |||
174 | config->irq_type = (unsigned char)irq_type; | ||
175 | config->ctrl_reg1 &= ~0x22; | ||
176 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
177 | config->ctrl_reg1 |= 0x20; | ||
178 | config->reg_int_cfg1 = 0x38; | ||
179 | config->reg_int_cfg2 = 0x00; | ||
180 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
181 | config->ctrl_reg1 |= 0x02; | ||
182 | if ((unsigned long)config == | ||
183 | (unsigned long)&private_data->suspend) | ||
184 | config->reg_int_cfg1 = 0x34; | ||
185 | else | ||
186 | config->reg_int_cfg1 = 0x24; | ||
187 | config->reg_int_cfg2 = 0xE0; | ||
188 | } else { | ||
189 | config->reg_int_cfg1 = 0x00; | ||
190 | config->reg_int_cfg2 = 0x00; | ||
191 | } | ||
192 | |||
193 | if (apply) { | ||
194 | /* Must clear bit 7 before writing new configuration */ | ||
195 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
196 | KXTF9_CTRL_REG1, 0x40); | ||
197 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
198 | KXTF9_INT_CTRL_REG1, | ||
199 | config->reg_int_cfg1); | ||
200 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
201 | KXTF9_INT_CTRL_REG2, | ||
202 | config->reg_int_cfg2); | ||
203 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
204 | KXTF9_CTRL_REG1, | ||
205 | config->ctrl_reg1); | ||
206 | } | ||
207 | MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n", | ||
208 | (unsigned long)config->ctrl_reg1, | ||
209 | (unsigned long)config->reg_int_cfg1, | ||
210 | (unsigned long)config->reg_int_cfg2); | ||
211 | |||
212 | return result; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Set the Output data rate for the particular configuration | ||
217 | * | ||
218 | * @param config Config to modify with new ODR | ||
219 | * @param odr Output data rate in units of 1/1000Hz | ||
220 | */ | ||
221 | static int kxtf9_set_odr(void *mlsl_handle, | ||
222 | struct ext_slave_platform_data *pdata, | ||
223 | struct kxtf9_config *config, int apply, long odr) | ||
224 | { | ||
225 | unsigned char bits; | ||
226 | int result = INV_SUCCESS; | ||
227 | |||
228 | /* Data sheet says there is 12.5 hz, but that seems to produce a single | ||
229 | * correct data value, thus we remove it from the table */ | ||
230 | if (odr > 400000L) { | ||
231 | config->odr = 800000L; | ||
232 | bits = 0x06; | ||
233 | } else if (odr > 200000L) { | ||
234 | config->odr = 400000L; | ||
235 | bits = 0x05; | ||
236 | } else if (odr > 100000L) { | ||
237 | config->odr = 200000L; | ||
238 | bits = 0x04; | ||
239 | } else if (odr > 50000) { | ||
240 | config->odr = 100000L; | ||
241 | bits = 0x03; | ||
242 | } else if (odr > 25000) { | ||
243 | config->odr = 50000; | ||
244 | bits = 0x02; | ||
245 | } else if (odr != 0) { | ||
246 | config->odr = 25000; | ||
247 | bits = 0x01; | ||
248 | } else { | ||
249 | config->odr = 0; | ||
250 | bits = 0; | ||
251 | } | ||
252 | |||
253 | if (odr != 0) | ||
254 | config->ctrl_reg1 |= 0x80; | ||
255 | else | ||
256 | config->ctrl_reg1 &= ~0x80; | ||
257 | |||
258 | config->reg_odr = bits; | ||
259 | kxtf9_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
260 | MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
261 | if (apply) { | ||
262 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
263 | KXTF9_DATA_CTRL_REG, | ||
264 | config->reg_odr); | ||
265 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
266 | KXTF9_CTRL_REG1, 0x40); | ||
267 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
268 | KXTF9_CTRL_REG1, | ||
269 | config->ctrl_reg1); | ||
270 | } | ||
271 | return result; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * Set the full scale range of the accels | ||
276 | * | ||
277 | * @param config pointer to configuration | ||
278 | * @param fsr requested full scale range | ||
279 | */ | ||
280 | static int kxtf9_set_fsr(void *mlsl_handle, | ||
281 | struct ext_slave_platform_data *pdata, | ||
282 | struct kxtf9_config *config, int apply, long fsr) | ||
283 | { | ||
284 | int result = INV_SUCCESS; | ||
285 | |||
286 | config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7); | ||
287 | if (fsr <= 2000) { | ||
288 | config->fsr = 2000; | ||
289 | config->ctrl_reg1 |= 0x00; | ||
290 | } else if (fsr <= 4000) { | ||
291 | config->fsr = 4000; | ||
292 | config->ctrl_reg1 |= 0x08; | ||
293 | } else { | ||
294 | config->fsr = 8000; | ||
295 | config->ctrl_reg1 |= 0x10; | ||
296 | } | ||
297 | |||
298 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
299 | if (apply) { | ||
300 | /* Must clear bit 7 before writing new configuration */ | ||
301 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
302 | KXTF9_CTRL_REG1, 0x40); | ||
303 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
304 | KXTF9_CTRL_REG1, | ||
305 | config->ctrl_reg1); | ||
306 | } | ||
307 | return result; | ||
308 | } | ||
309 | |||
310 | static int kxtf9_suspend(void *mlsl_handle, | ||
311 | struct ext_slave_descr *slave, | ||
312 | struct ext_slave_platform_data *pdata) | ||
313 | { | ||
314 | int result; | ||
315 | unsigned char data; | ||
316 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
317 | |||
318 | /* Wake up */ | ||
319 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
320 | KXTF9_CTRL_REG1, 0x40); | ||
321 | if (result) { | ||
322 | LOG_RESULT_LOCATION(result); | ||
323 | return result; | ||
324 | } | ||
325 | /* INT_CTRL_REG1: */ | ||
326 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
327 | KXTF9_INT_CTRL_REG1, | ||
328 | private_data->suspend.reg_int_cfg1); | ||
329 | if (result) { | ||
330 | LOG_RESULT_LOCATION(result); | ||
331 | return result; | ||
332 | } | ||
333 | /* WUF_THRESH: */ | ||
334 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
335 | KXTF9_WUF_THRESH, | ||
336 | private_data->suspend.reg_ths); | ||
337 | if (result) { | ||
338 | LOG_RESULT_LOCATION(result); | ||
339 | return result; | ||
340 | } | ||
341 | /* DATA_CTRL_REG */ | ||
342 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
343 | KXTF9_DATA_CTRL_REG, | ||
344 | private_data->suspend.reg_odr); | ||
345 | if (result) { | ||
346 | LOG_RESULT_LOCATION(result); | ||
347 | return result; | ||
348 | } | ||
349 | /* WUF_TIMER */ | ||
350 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
351 | KXTF9_WUF_TIMER, | ||
352 | private_data->suspend.reg_dur); | ||
353 | if (result) { | ||
354 | LOG_RESULT_LOCATION(result); | ||
355 | return result; | ||
356 | } | ||
357 | |||
358 | /* Normal operation */ | ||
359 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
360 | KXTF9_CTRL_REG1, | ||
361 | private_data->suspend.ctrl_reg1); | ||
362 | if (result) { | ||
363 | LOG_RESULT_LOCATION(result); | ||
364 | return result; | ||
365 | } | ||
366 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
367 | KXTF9_INT_REL, 1, &data); | ||
368 | if (result) { | ||
369 | LOG_RESULT_LOCATION(result); | ||
370 | return result; | ||
371 | } | ||
372 | |||
373 | return result; | ||
374 | } | ||
375 | |||
376 | /* full scale setting - register and mask */ | ||
377 | #define ACCEL_KIONIX_CTRL_REG (0x1b) | ||
378 | #define ACCEL_KIONIX_CTRL_MASK (0x18) | ||
379 | |||
380 | static int kxtf9_resume(void *mlsl_handle, | ||
381 | struct ext_slave_descr *slave, | ||
382 | struct ext_slave_platform_data *pdata) | ||
383 | { | ||
384 | int result = INV_SUCCESS; | ||
385 | unsigned char data; | ||
386 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
387 | |||
388 | /* Wake up */ | ||
389 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
390 | KXTF9_CTRL_REG1, 0x40); | ||
391 | if (result) { | ||
392 | LOG_RESULT_LOCATION(result); | ||
393 | return result; | ||
394 | } | ||
395 | /* INT_CTRL_REG1: */ | ||
396 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
397 | KXTF9_INT_CTRL_REG1, | ||
398 | private_data->resume.reg_int_cfg1); | ||
399 | if (result) { | ||
400 | LOG_RESULT_LOCATION(result); | ||
401 | return result; | ||
402 | } | ||
403 | /* WUF_THRESH: */ | ||
404 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
405 | KXTF9_WUF_THRESH, | ||
406 | private_data->resume.reg_ths); | ||
407 | if (result) { | ||
408 | LOG_RESULT_LOCATION(result); | ||
409 | return result; | ||
410 | } | ||
411 | /* DATA_CTRL_REG */ | ||
412 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
413 | KXTF9_DATA_CTRL_REG, | ||
414 | private_data->resume.reg_odr); | ||
415 | if (result) { | ||
416 | LOG_RESULT_LOCATION(result); | ||
417 | return result; | ||
418 | } | ||
419 | /* WUF_TIMER */ | ||
420 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
421 | KXTF9_WUF_TIMER, | ||
422 | private_data->resume.reg_dur); | ||
423 | if (result) { | ||
424 | LOG_RESULT_LOCATION(result); | ||
425 | return result; | ||
426 | } | ||
427 | |||
428 | /* Normal operation */ | ||
429 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
430 | KXTF9_CTRL_REG1, | ||
431 | private_data->resume.ctrl_reg1); | ||
432 | if (result) { | ||
433 | LOG_RESULT_LOCATION(result); | ||
434 | return result; | ||
435 | } | ||
436 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
437 | KXTF9_INT_REL, 1, &data); | ||
438 | if (result) { | ||
439 | LOG_RESULT_LOCATION(result); | ||
440 | return result; | ||
441 | } | ||
442 | |||
443 | return INV_SUCCESS; | ||
444 | } | ||
445 | |||
446 | static int kxtf9_init(void *mlsl_handle, | ||
447 | struct ext_slave_descr *slave, | ||
448 | struct ext_slave_platform_data *pdata) | ||
449 | { | ||
450 | |||
451 | struct kxtf9_private_data *private_data; | ||
452 | int result = INV_SUCCESS; | ||
453 | |||
454 | private_data = (struct kxtf9_private_data *) | ||
455 | kzalloc(sizeof(struct kxtf9_private_data), GFP_KERNEL); | ||
456 | |||
457 | if (!private_data) | ||
458 | return INV_ERROR_MEMORY_EXAUSTED; | ||
459 | |||
460 | /* RAM reset */ | ||
461 | /* Fastest Reset */ | ||
462 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
463 | KXTF9_CTRL_REG1, 0x40); | ||
464 | if (result) { | ||
465 | LOG_RESULT_LOCATION(result); | ||
466 | return result; | ||
467 | } | ||
468 | /* Fastest Reset */ | ||
469 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
470 | KXTF9_DATA_CTRL_REG, 0x36); | ||
471 | if (result) { | ||
472 | LOG_RESULT_LOCATION(result); | ||
473 | return result; | ||
474 | } | ||
475 | /* Reset */ | ||
476 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
477 | KXTF9_CTRL_REG3, 0xcd); | ||
478 | if (result) { | ||
479 | LOG_RESULT_LOCATION(result); | ||
480 | return result; | ||
481 | } | ||
482 | msleep(2); | ||
483 | |||
484 | pdata->private_data = private_data; | ||
485 | |||
486 | private_data->resume.ctrl_reg1 = 0xC0; | ||
487 | private_data->suspend.ctrl_reg1 = 0x40; | ||
488 | |||
489 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
490 | false, 1000); | ||
491 | if (result) { | ||
492 | LOG_RESULT_LOCATION(result); | ||
493 | return result; | ||
494 | } | ||
495 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
496 | false, 2540); | ||
497 | if (result) { | ||
498 | LOG_RESULT_LOCATION(result); | ||
499 | return result; | ||
500 | } | ||
501 | |||
502 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
503 | false, 50000); | ||
504 | if (result) { | ||
505 | LOG_RESULT_LOCATION(result); | ||
506 | return result; | ||
507 | } | ||
508 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
509 | false, 200000L); | ||
510 | |||
511 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
512 | false, 2000); | ||
513 | if (result) { | ||
514 | LOG_RESULT_LOCATION(result); | ||
515 | return result; | ||
516 | } | ||
517 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
518 | false, 2000); | ||
519 | if (result) { | ||
520 | LOG_RESULT_LOCATION(result); | ||
521 | return result; | ||
522 | } | ||
523 | |||
524 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
525 | false, 80); | ||
526 | if (result) { | ||
527 | LOG_RESULT_LOCATION(result); | ||
528 | return result; | ||
529 | } | ||
530 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
531 | false, 40); | ||
532 | if (result) { | ||
533 | LOG_RESULT_LOCATION(result); | ||
534 | return result; | ||
535 | } | ||
536 | |||
537 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
538 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
539 | if (result) { | ||
540 | LOG_RESULT_LOCATION(result); | ||
541 | return result; | ||
542 | } | ||
543 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
544 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
545 | if (result) { | ||
546 | LOG_RESULT_LOCATION(result); | ||
547 | return result; | ||
548 | } | ||
549 | return result; | ||
550 | } | ||
551 | |||
552 | static int kxtf9_exit(void *mlsl_handle, | ||
553 | struct ext_slave_descr *slave, | ||
554 | struct ext_slave_platform_data *pdata) | ||
555 | { | ||
556 | kfree(pdata->private_data); | ||
557 | return INV_SUCCESS; | ||
558 | } | ||
559 | |||
560 | static int kxtf9_config(void *mlsl_handle, | ||
561 | struct ext_slave_descr *slave, | ||
562 | struct ext_slave_platform_data *pdata, | ||
563 | struct ext_slave_config *data) | ||
564 | { | ||
565 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
566 | if (!data->data) | ||
567 | return INV_ERROR_INVALID_PARAMETER; | ||
568 | |||
569 | switch (data->key) { | ||
570 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
571 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
572 | &private_data->suspend, | ||
573 | data->apply, *((long *)data->data)); | ||
574 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
575 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
576 | &private_data->resume, | ||
577 | data->apply, *((long *)data->data)); | ||
578 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
579 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
580 | &private_data->suspend, | ||
581 | data->apply, *((long *)data->data)); | ||
582 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
583 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
584 | &private_data->resume, | ||
585 | data->apply, *((long *)data->data)); | ||
586 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
587 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
588 | &private_data->suspend, | ||
589 | data->apply, *((long *)data->data)); | ||
590 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
591 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
592 | &private_data->resume, | ||
593 | data->apply, *((long *)data->data)); | ||
594 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
595 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
596 | &private_data->suspend, | ||
597 | data->apply, *((long *)data->data)); | ||
598 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
599 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
600 | &private_data->resume, | ||
601 | data->apply, *((long *)data->data)); | ||
602 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
603 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
604 | &private_data->suspend, | ||
605 | data->apply, *((long *)data->data)); | ||
606 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
607 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
608 | &private_data->resume, | ||
609 | data->apply, *((long *)data->data)); | ||
610 | default: | ||
611 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
612 | }; | ||
613 | |||
614 | return INV_SUCCESS; | ||
615 | } | ||
616 | |||
617 | static int kxtf9_get_config(void *mlsl_handle, | ||
618 | struct ext_slave_descr *slave, | ||
619 | struct ext_slave_platform_data *pdata, | ||
620 | struct ext_slave_config *data) | ||
621 | { | ||
622 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
623 | if (!data->data) | ||
624 | return INV_ERROR_INVALID_PARAMETER; | ||
625 | |||
626 | switch (data->key) { | ||
627 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
628 | (*(unsigned long *)data->data) = | ||
629 | (unsigned long)private_data->suspend.odr; | ||
630 | break; | ||
631 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
632 | (*(unsigned long *)data->data) = | ||
633 | (unsigned long)private_data->resume.odr; | ||
634 | break; | ||
635 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
636 | (*(unsigned long *)data->data) = | ||
637 | (unsigned long)private_data->suspend.fsr; | ||
638 | break; | ||
639 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
640 | (*(unsigned long *)data->data) = | ||
641 | (unsigned long)private_data->resume.fsr; | ||
642 | break; | ||
643 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
644 | (*(unsigned long *)data->data) = | ||
645 | (unsigned long)private_data->suspend.ths; | ||
646 | break; | ||
647 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
648 | (*(unsigned long *)data->data) = | ||
649 | (unsigned long)private_data->resume.ths; | ||
650 | break; | ||
651 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
652 | (*(unsigned long *)data->data) = | ||
653 | (unsigned long)private_data->suspend.dur; | ||
654 | break; | ||
655 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
656 | (*(unsigned long *)data->data) = | ||
657 | (unsigned long)private_data->resume.dur; | ||
658 | break; | ||
659 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
660 | (*(unsigned long *)data->data) = | ||
661 | (unsigned long)private_data->suspend.irq_type; | ||
662 | break; | ||
663 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
664 | (*(unsigned long *)data->data) = | ||
665 | (unsigned long)private_data->resume.irq_type; | ||
666 | break; | ||
667 | default: | ||
668 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
669 | }; | ||
670 | |||
671 | return INV_SUCCESS; | ||
672 | } | ||
673 | |||
674 | static int kxtf9_read(void *mlsl_handle, | ||
675 | struct ext_slave_descr *slave, | ||
676 | struct ext_slave_platform_data *pdata, | ||
677 | unsigned char *data) | ||
678 | { | ||
679 | int result; | ||
680 | unsigned char reg; | ||
681 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
682 | KXTF9_INT_SRC_REG2, 1, ®); | ||
683 | if (result) { | ||
684 | LOG_RESULT_LOCATION(result); | ||
685 | return result; | ||
686 | } | ||
687 | |||
688 | if (!(reg & 0x10)) | ||
689 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
690 | |||
691 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
692 | slave->read_reg, slave->read_len, data); | ||
693 | if (result) { | ||
694 | LOG_RESULT_LOCATION(result); | ||
695 | return result; | ||
696 | } | ||
697 | return result; | ||
698 | } | ||
699 | |||
700 | static struct ext_slave_descr kxtf9_descr = { | ||
701 | .init = kxtf9_init, | ||
702 | .exit = kxtf9_exit, | ||
703 | .suspend = kxtf9_suspend, | ||
704 | .resume = kxtf9_resume, | ||
705 | .read = kxtf9_read, | ||
706 | .config = kxtf9_config, | ||
707 | .get_config = kxtf9_get_config, | ||
708 | .name = "kxtf9", | ||
709 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
710 | .id = ACCEL_ID_KXTF9, | ||
711 | .read_reg = 0x06, | ||
712 | .read_len = 6, | ||
713 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
714 | .range = {2, 0}, | ||
715 | .trigger = NULL, | ||
716 | }; | ||
717 | |||
718 | static | ||
719 | struct ext_slave_descr *kxtf9_get_slave_descr(void) | ||
720 | { | ||
721 | return &kxtf9_descr; | ||
722 | } | ||
723 | |||
724 | /* -------------------------------------------------------------------------- */ | ||
725 | struct kxtf9_mod_private_data { | ||
726 | struct i2c_client *client; | ||
727 | struct ext_slave_platform_data *pdata; | ||
728 | }; | ||
729 | |||
730 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
731 | |||
732 | static int kxtf9_mod_probe(struct i2c_client *client, | ||
733 | const struct i2c_device_id *devid) | ||
734 | { | ||
735 | struct ext_slave_platform_data *pdata; | ||
736 | struct kxtf9_mod_private_data *private_data; | ||
737 | int result = 0; | ||
738 | |||
739 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
740 | |||
741 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
742 | result = -ENODEV; | ||
743 | goto out_no_free; | ||
744 | } | ||
745 | |||
746 | pdata = client->dev.platform_data; | ||
747 | if (!pdata) { | ||
748 | dev_err(&client->adapter->dev, | ||
749 | "Missing platform data for slave %s\n", devid->name); | ||
750 | result = -EFAULT; | ||
751 | goto out_no_free; | ||
752 | } | ||
753 | |||
754 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
755 | if (!private_data) { | ||
756 | result = -ENOMEM; | ||
757 | goto out_no_free; | ||
758 | } | ||
759 | |||
760 | i2c_set_clientdata(client, private_data); | ||
761 | private_data->client = client; | ||
762 | private_data->pdata = pdata; | ||
763 | |||
764 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
765 | kxtf9_get_slave_descr); | ||
766 | if (result) { | ||
767 | dev_err(&client->adapter->dev, | ||
768 | "Slave registration failed: %s, %d\n", | ||
769 | devid->name, result); | ||
770 | goto out_free_memory; | ||
771 | } | ||
772 | |||
773 | return result; | ||
774 | |||
775 | out_free_memory: | ||
776 | kfree(private_data); | ||
777 | out_no_free: | ||
778 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
779 | return result; | ||
780 | |||
781 | } | ||
782 | |||
783 | static int kxtf9_mod_remove(struct i2c_client *client) | ||
784 | { | ||
785 | struct kxtf9_mod_private_data *private_data = | ||
786 | i2c_get_clientdata(client); | ||
787 | |||
788 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
789 | |||
790 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
791 | kxtf9_get_slave_descr); | ||
792 | |||
793 | kfree(private_data); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static const struct i2c_device_id kxtf9_mod_id[] = { | ||
798 | { "kxtf9", ACCEL_ID_KXTF9 }, | ||
799 | {} | ||
800 | }; | ||
801 | |||
802 | MODULE_DEVICE_TABLE(i2c, kxtf9_mod_id); | ||
803 | |||
804 | static struct i2c_driver kxtf9_mod_driver = { | ||
805 | .class = I2C_CLASS_HWMON, | ||
806 | .probe = kxtf9_mod_probe, | ||
807 | .remove = kxtf9_mod_remove, | ||
808 | .id_table = kxtf9_mod_id, | ||
809 | .driver = { | ||
810 | .owner = THIS_MODULE, | ||
811 | .name = "kxtf9_mod", | ||
812 | }, | ||
813 | .address_list = normal_i2c, | ||
814 | }; | ||
815 | |||
816 | static int __init kxtf9_mod_init(void) | ||
817 | { | ||
818 | int res = i2c_add_driver(&kxtf9_mod_driver); | ||
819 | pr_info("%s: Probe name %s\n", __func__, "kxtf9_mod"); | ||
820 | if (res) | ||
821 | pr_err("%s failed\n", __func__); | ||
822 | return res; | ||
823 | } | ||
824 | |||
825 | static void __exit kxtf9_mod_exit(void) | ||
826 | { | ||
827 | pr_info("%s\n", __func__); | ||
828 | i2c_del_driver(&kxtf9_mod_driver); | ||
829 | } | ||
830 | |||
831 | module_init(kxtf9_mod_init); | ||
832 | module_exit(kxtf9_mod_exit); | ||
833 | |||
834 | MODULE_AUTHOR("Invensense Corporation"); | ||
835 | MODULE_DESCRIPTION("Driver to integrate KXTF9 sensor with the MPU"); | ||
836 | MODULE_LICENSE("GPL"); | ||
837 | MODULE_ALIAS("kxtf9_mod"); | ||
838 | |||
839 | /** | ||
840 | * @} | ||
841 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lis331.c b/drivers/misc/inv_mpu/accel/lis331.c new file mode 100644 index 00000000000..bcbec252af9 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lis331.c | |||
@@ -0,0 +1,745 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file lis331.c | ||
26 | * @brief Accelerometer setup and handling methods for ST LIS331DLH. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #undef MPL_LOG_NDEBUG | ||
32 | #define MPL_LOG_NDEBUG 1 | ||
33 | |||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include "mpu-dev.h" | ||
42 | |||
43 | #include <log.h> | ||
44 | #include <linux/mpu.h> | ||
45 | #include "mlsl.h" | ||
46 | #include "mldl_cfg.h" | ||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-acc" | ||
49 | |||
50 | /* full scale setting - register & mask */ | ||
51 | #define LIS331DLH_CTRL_REG1 (0x20) | ||
52 | #define LIS331DLH_CTRL_REG2 (0x21) | ||
53 | #define LIS331DLH_CTRL_REG3 (0x22) | ||
54 | #define LIS331DLH_CTRL_REG4 (0x23) | ||
55 | #define LIS331DLH_CTRL_REG5 (0x24) | ||
56 | #define LIS331DLH_HP_FILTER_RESET (0x25) | ||
57 | #define LIS331DLH_REFERENCE (0x26) | ||
58 | #define LIS331DLH_STATUS_REG (0x27) | ||
59 | #define LIS331DLH_OUT_X_L (0x28) | ||
60 | #define LIS331DLH_OUT_X_H (0x29) | ||
61 | #define LIS331DLH_OUT_Y_L (0x2a) | ||
62 | #define LIS331DLH_OUT_Y_H (0x2b) | ||
63 | #define LIS331DLH_OUT_Z_L (0x2b) | ||
64 | #define LIS331DLH_OUT_Z_H (0x2d) | ||
65 | |||
66 | #define LIS331DLH_INT1_CFG (0x30) | ||
67 | #define LIS331DLH_INT1_SRC (0x31) | ||
68 | #define LIS331DLH_INT1_THS (0x32) | ||
69 | #define LIS331DLH_INT1_DURATION (0x33) | ||
70 | |||
71 | #define LIS331DLH_INT2_CFG (0x34) | ||
72 | #define LIS331DLH_INT2_SRC (0x35) | ||
73 | #define LIS331DLH_INT2_THS (0x36) | ||
74 | #define LIS331DLH_INT2_DURATION (0x37) | ||
75 | |||
76 | /* CTRL_REG1 */ | ||
77 | #define LIS331DLH_CTRL_MASK (0x30) | ||
78 | #define LIS331DLH_SLEEP_MASK (0x20) | ||
79 | #define LIS331DLH_PWR_MODE_NORMAL (0x20) | ||
80 | |||
81 | #define LIS331DLH_MAX_DUR (0x7F) | ||
82 | |||
83 | |||
84 | /* -------------------------------------------------------------------------- */ | ||
85 | |||
86 | struct lis331dlh_config { | ||
87 | unsigned int odr; | ||
88 | unsigned int fsr; /* full scale range mg */ | ||
89 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
90 | unsigned int dur; /* Motion no-motion duration ms */ | ||
91 | unsigned char reg_ths; | ||
92 | unsigned char reg_dur; | ||
93 | unsigned char ctrl_reg1; | ||
94 | unsigned char irq_type; | ||
95 | unsigned char mot_int1_cfg; | ||
96 | }; | ||
97 | |||
98 | struct lis331dlh_private_data { | ||
99 | struct lis331dlh_config suspend; | ||
100 | struct lis331dlh_config resume; | ||
101 | }; | ||
102 | |||
103 | /* -------------------------------------------------------------------------- */ | ||
104 | static int lis331dlh_set_ths(void *mlsl_handle, | ||
105 | struct ext_slave_platform_data *pdata, | ||
106 | struct lis331dlh_config *config, | ||
107 | int apply, long ths) | ||
108 | { | ||
109 | int result = INV_SUCCESS; | ||
110 | if ((unsigned int)ths >= config->fsr) | ||
111 | ths = (long)config->fsr - 1; | ||
112 | |||
113 | if (ths < 0) | ||
114 | ths = 0; | ||
115 | |||
116 | config->ths = ths; | ||
117 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
118 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
119 | if (apply) | ||
120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
121 | LIS331DLH_INT1_THS, | ||
122 | config->reg_ths); | ||
123 | return result; | ||
124 | } | ||
125 | |||
126 | static int lis331dlh_set_dur(void *mlsl_handle, | ||
127 | struct ext_slave_platform_data *pdata, | ||
128 | struct lis331dlh_config *config, | ||
129 | int apply, long dur) | ||
130 | { | ||
131 | int result = INV_SUCCESS; | ||
132 | long reg_dur = (dur * config->odr) / 1000000L; | ||
133 | config->dur = dur; | ||
134 | |||
135 | if (reg_dur > LIS331DLH_MAX_DUR) | ||
136 | reg_dur = LIS331DLH_MAX_DUR; | ||
137 | |||
138 | config->reg_dur = (unsigned char)reg_dur; | ||
139 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
140 | if (apply) | ||
141 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
142 | LIS331DLH_INT1_DURATION, | ||
143 | (unsigned char)reg_dur); | ||
144 | return result; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
149 | * duration will not be used uless the type is MOT or NMOT. | ||
150 | * | ||
151 | * @param config configuration to apply to, suspend or resume | ||
152 | * @param irq_type The type of IRQ. Valid values are | ||
153 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
154 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
155 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
156 | */ | ||
157 | static int lis331dlh_set_irq(void *mlsl_handle, | ||
158 | struct ext_slave_platform_data *pdata, | ||
159 | struct lis331dlh_config *config, | ||
160 | int apply, long irq_type) | ||
161 | { | ||
162 | int result = INV_SUCCESS; | ||
163 | unsigned char reg1; | ||
164 | unsigned char reg2; | ||
165 | |||
166 | config->irq_type = (unsigned char)irq_type; | ||
167 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
168 | reg1 = 0x02; | ||
169 | reg2 = 0x00; | ||
170 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
171 | reg1 = 0x00; | ||
172 | reg2 = config->mot_int1_cfg; | ||
173 | } else { | ||
174 | reg1 = 0x00; | ||
175 | reg2 = 0x00; | ||
176 | } | ||
177 | |||
178 | if (apply) { | ||
179 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
180 | LIS331DLH_CTRL_REG3, reg1); | ||
181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
182 | LIS331DLH_INT1_CFG, reg2); | ||
183 | } | ||
184 | |||
185 | return result; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Set the Output data rate for the particular configuration | ||
190 | * | ||
191 | * @param config Config to modify with new ODR | ||
192 | * @param odr Output data rate in units of 1/1000Hz | ||
193 | */ | ||
194 | static int lis331dlh_set_odr(void *mlsl_handle, | ||
195 | struct ext_slave_platform_data *pdata, | ||
196 | struct lis331dlh_config *config, | ||
197 | int apply, long odr) | ||
198 | { | ||
199 | unsigned char bits; | ||
200 | int result = INV_SUCCESS; | ||
201 | |||
202 | /* normal power modes */ | ||
203 | if (odr > 400000) { | ||
204 | config->odr = 1000000; | ||
205 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x18; | ||
206 | } else if (odr > 100000) { | ||
207 | config->odr = 400000; | ||
208 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x10; | ||
209 | } else if (odr > 50000) { | ||
210 | config->odr = 100000; | ||
211 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x08; | ||
212 | } else if (odr > 10000) { | ||
213 | config->odr = 50000; | ||
214 | bits = LIS331DLH_PWR_MODE_NORMAL | 0x00; | ||
215 | /* low power modes */ | ||
216 | } else if (odr > 5000) { | ||
217 | config->odr = 10000; | ||
218 | bits = 0xC0; | ||
219 | } else if (odr > 2000) { | ||
220 | config->odr = 5000; | ||
221 | bits = 0xA0; | ||
222 | } else if (odr > 1000) { | ||
223 | config->odr = 2000; | ||
224 | bits = 0x80; | ||
225 | } else if (odr > 500) { | ||
226 | config->odr = 1000; | ||
227 | bits = 0x60; | ||
228 | } else if (odr > 0) { | ||
229 | config->odr = 500; | ||
230 | bits = 0x40; | ||
231 | } else { | ||
232 | config->odr = 0; | ||
233 | bits = 0; | ||
234 | } | ||
235 | |||
236 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7); | ||
237 | lis331dlh_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
238 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
239 | if (apply) | ||
240 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
241 | LIS331DLH_CTRL_REG1, | ||
242 | config->ctrl_reg1); | ||
243 | return result; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * Set the full scale range of the accels | ||
248 | * | ||
249 | * @param config pointer to configuration | ||
250 | * @param fsr requested full scale range | ||
251 | */ | ||
252 | static int lis331dlh_set_fsr(void *mlsl_handle, | ||
253 | struct ext_slave_platform_data *pdata, | ||
254 | struct lis331dlh_config *config, | ||
255 | int apply, long fsr) | ||
256 | { | ||
257 | unsigned char reg1 = 0x40; | ||
258 | int result = INV_SUCCESS; | ||
259 | |||
260 | if (fsr <= 2048) { | ||
261 | config->fsr = 2048; | ||
262 | } else if (fsr <= 4096) { | ||
263 | reg1 |= 0x30; | ||
264 | config->fsr = 4096; | ||
265 | } else { | ||
266 | reg1 |= 0x10; | ||
267 | config->fsr = 8192; | ||
268 | } | ||
269 | |||
270 | lis331dlh_set_ths(mlsl_handle, pdata, config, apply, config->ths); | ||
271 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
272 | if (apply) | ||
273 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
274 | LIS331DLH_CTRL_REG4, reg1); | ||
275 | |||
276 | return result; | ||
277 | } | ||
278 | |||
279 | static int lis331dlh_suspend(void *mlsl_handle, | ||
280 | struct ext_slave_descr *slave, | ||
281 | struct ext_slave_platform_data *pdata) | ||
282 | { | ||
283 | int result = INV_SUCCESS; | ||
284 | unsigned char reg1; | ||
285 | unsigned char reg2; | ||
286 | struct lis331dlh_private_data *private_data = | ||
287 | (struct lis331dlh_private_data *)(pdata->private_data); | ||
288 | |||
289 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
290 | LIS331DLH_CTRL_REG1, | ||
291 | private_data->suspend.ctrl_reg1); | ||
292 | |||
293 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
294 | LIS331DLH_CTRL_REG2, 0x0f); | ||
295 | reg1 = 0x40; | ||
296 | if (private_data->suspend.fsr == 8192) | ||
297 | reg1 |= 0x30; | ||
298 | else if (private_data->suspend.fsr == 4096) | ||
299 | reg1 |= 0x10; | ||
300 | /* else bits [4..5] are already zero */ | ||
301 | |||
302 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
303 | LIS331DLH_CTRL_REG4, reg1); | ||
304 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
305 | LIS331DLH_INT1_THS, | ||
306 | private_data->suspend.reg_ths); | ||
307 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
308 | LIS331DLH_INT1_DURATION, | ||
309 | private_data->suspend.reg_dur); | ||
310 | |||
311 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
312 | reg1 = 0x02; | ||
313 | reg2 = 0x00; | ||
314 | } else if (private_data->suspend.irq_type == | ||
315 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
316 | reg1 = 0x00; | ||
317 | reg2 = private_data->suspend.mot_int1_cfg; | ||
318 | } else { | ||
319 | reg1 = 0x00; | ||
320 | reg2 = 0x00; | ||
321 | } | ||
322 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
323 | LIS331DLH_CTRL_REG3, reg1); | ||
324 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
325 | LIS331DLH_INT1_CFG, reg2); | ||
326 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
327 | LIS331DLH_HP_FILTER_RESET, 1, ®1); | ||
328 | return result; | ||
329 | } | ||
330 | |||
331 | static int lis331dlh_resume(void *mlsl_handle, | ||
332 | struct ext_slave_descr *slave, | ||
333 | struct ext_slave_platform_data *pdata) | ||
334 | { | ||
335 | int result = INV_SUCCESS; | ||
336 | unsigned char reg1; | ||
337 | unsigned char reg2; | ||
338 | struct lis331dlh_private_data *private_data = | ||
339 | (struct lis331dlh_private_data *)(pdata->private_data); | ||
340 | |||
341 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
342 | LIS331DLH_CTRL_REG1, | ||
343 | private_data->resume.ctrl_reg1); | ||
344 | if (result) { | ||
345 | LOG_RESULT_LOCATION(result); | ||
346 | return result; | ||
347 | } | ||
348 | msleep(6); | ||
349 | |||
350 | /* Full Scale */ | ||
351 | reg1 = 0x40; | ||
352 | if (private_data->resume.fsr == 8192) | ||
353 | reg1 |= 0x30; | ||
354 | else if (private_data->resume.fsr == 4096) | ||
355 | reg1 |= 0x10; | ||
356 | |||
357 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
358 | LIS331DLH_CTRL_REG4, reg1); | ||
359 | if (result) { | ||
360 | LOG_RESULT_LOCATION(result); | ||
361 | return result; | ||
362 | } | ||
363 | |||
364 | /* Configure high pass filter */ | ||
365 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
366 | LIS331DLH_CTRL_REG2, 0x0F); | ||
367 | if (result) { | ||
368 | LOG_RESULT_LOCATION(result); | ||
369 | return result; | ||
370 | } | ||
371 | |||
372 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
373 | reg1 = 0x02; | ||
374 | reg2 = 0x00; | ||
375 | } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
376 | reg1 = 0x00; | ||
377 | reg2 = private_data->resume.mot_int1_cfg; | ||
378 | } else { | ||
379 | reg1 = 0x00; | ||
380 | reg2 = 0x00; | ||
381 | } | ||
382 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
383 | LIS331DLH_CTRL_REG3, reg1); | ||
384 | if (result) { | ||
385 | LOG_RESULT_LOCATION(result); | ||
386 | return result; | ||
387 | } | ||
388 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
389 | LIS331DLH_INT1_THS, | ||
390 | private_data->resume.reg_ths); | ||
391 | if (result) { | ||
392 | LOG_RESULT_LOCATION(result); | ||
393 | return result; | ||
394 | } | ||
395 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
396 | LIS331DLH_INT1_DURATION, | ||
397 | private_data->resume.reg_dur); | ||
398 | if (result) { | ||
399 | LOG_RESULT_LOCATION(result); | ||
400 | return result; | ||
401 | } | ||
402 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
403 | LIS331DLH_INT1_CFG, reg2); | ||
404 | if (result) { | ||
405 | LOG_RESULT_LOCATION(result); | ||
406 | return result; | ||
407 | } | ||
408 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
409 | LIS331DLH_HP_FILTER_RESET, 1, ®1); | ||
410 | if (result) { | ||
411 | LOG_RESULT_LOCATION(result); | ||
412 | return result; | ||
413 | } | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | static int lis331dlh_read(void *mlsl_handle, | ||
418 | struct ext_slave_descr *slave, | ||
419 | struct ext_slave_platform_data *pdata, | ||
420 | unsigned char *data) | ||
421 | { | ||
422 | int result = INV_SUCCESS; | ||
423 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
424 | LIS331DLH_STATUS_REG, 1, data); | ||
425 | if (data[0] & 0x0F) { | ||
426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
427 | slave->read_reg, slave->read_len, | ||
428 | data); | ||
429 | return result; | ||
430 | } else | ||
431 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
432 | } | ||
433 | |||
434 | static int lis331dlh_init(void *mlsl_handle, | ||
435 | struct ext_slave_descr *slave, | ||
436 | struct ext_slave_platform_data *pdata) | ||
437 | { | ||
438 | struct lis331dlh_private_data *private_data; | ||
439 | long range; | ||
440 | private_data = (struct lis331dlh_private_data *) | ||
441 | kzalloc(sizeof(struct lis331dlh_private_data), GFP_KERNEL); | ||
442 | |||
443 | if (!private_data) | ||
444 | return INV_ERROR_MEMORY_EXAUSTED; | ||
445 | |||
446 | pdata->private_data = private_data; | ||
447 | |||
448 | private_data->resume.ctrl_reg1 = 0x37; | ||
449 | private_data->suspend.ctrl_reg1 = 0x47; | ||
450 | private_data->resume.mot_int1_cfg = 0x95; | ||
451 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
452 | |||
453 | lis331dlh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0); | ||
454 | lis331dlh_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
455 | false, 200000); | ||
456 | |||
457 | range = range_fixedpoint_to_long_mg(slave->range); | ||
458 | lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
459 | false, range); | ||
460 | lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
461 | false, range); | ||
462 | |||
463 | lis331dlh_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
464 | false, 80); | ||
465 | lis331dlh_set_ths(mlsl_handle, pdata, &private_data->resume, false, 40); | ||
466 | |||
467 | |||
468 | lis331dlh_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
469 | false, 1000); | ||
470 | lis331dlh_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
471 | false, 2540); | ||
472 | |||
473 | lis331dlh_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
474 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
475 | lis331dlh_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
476 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
477 | return INV_SUCCESS; | ||
478 | } | ||
479 | |||
480 | static int lis331dlh_exit(void *mlsl_handle, | ||
481 | struct ext_slave_descr *slave, | ||
482 | struct ext_slave_platform_data *pdata) | ||
483 | { | ||
484 | kfree(pdata->private_data); | ||
485 | return INV_SUCCESS; | ||
486 | } | ||
487 | |||
488 | static int lis331dlh_config(void *mlsl_handle, | ||
489 | struct ext_slave_descr *slave, | ||
490 | struct ext_slave_platform_data *pdata, | ||
491 | struct ext_slave_config *data) | ||
492 | { | ||
493 | struct lis331dlh_private_data *private_data = pdata->private_data; | ||
494 | if (!data->data) | ||
495 | return INV_ERROR_INVALID_PARAMETER; | ||
496 | |||
497 | switch (data->key) { | ||
498 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
499 | return lis331dlh_set_odr(mlsl_handle, pdata, | ||
500 | &private_data->suspend, | ||
501 | data->apply, *((long *)data->data)); | ||
502 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
503 | return lis331dlh_set_odr(mlsl_handle, pdata, | ||
504 | &private_data->resume, | ||
505 | data->apply, *((long *)data->data)); | ||
506 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
507 | return lis331dlh_set_fsr(mlsl_handle, pdata, | ||
508 | &private_data->suspend, | ||
509 | data->apply, *((long *)data->data)); | ||
510 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
511 | return lis331dlh_set_fsr(mlsl_handle, pdata, | ||
512 | &private_data->resume, | ||
513 | data->apply, *((long *)data->data)); | ||
514 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
515 | return lis331dlh_set_ths(mlsl_handle, pdata, | ||
516 | &private_data->suspend, | ||
517 | data->apply, *((long *)data->data)); | ||
518 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
519 | return lis331dlh_set_ths(mlsl_handle, pdata, | ||
520 | &private_data->resume, | ||
521 | data->apply, *((long *)data->data)); | ||
522 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
523 | return lis331dlh_set_dur(mlsl_handle, pdata, | ||
524 | &private_data->suspend, | ||
525 | data->apply, *((long *)data->data)); | ||
526 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
527 | return lis331dlh_set_dur(mlsl_handle, pdata, | ||
528 | &private_data->resume, | ||
529 | data->apply, *((long *)data->data)); | ||
530 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
531 | return lis331dlh_set_irq(mlsl_handle, pdata, | ||
532 | &private_data->suspend, | ||
533 | data->apply, *((long *)data->data)); | ||
534 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
535 | return lis331dlh_set_irq(mlsl_handle, pdata, | ||
536 | &private_data->resume, | ||
537 | data->apply, *((long *)data->data)); | ||
538 | default: | ||
539 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
540 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
541 | }; | ||
542 | |||
543 | return INV_SUCCESS; | ||
544 | } | ||
545 | |||
546 | static int lis331dlh_get_config(void *mlsl_handle, | ||
547 | struct ext_slave_descr *slave, | ||
548 | struct ext_slave_platform_data *pdata, | ||
549 | struct ext_slave_config *data) | ||
550 | { | ||
551 | struct lis331dlh_private_data *private_data = pdata->private_data; | ||
552 | if (!data->data) | ||
553 | return INV_ERROR_INVALID_PARAMETER; | ||
554 | |||
555 | switch (data->key) { | ||
556 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
557 | (*(unsigned long *)data->data) = | ||
558 | (unsigned long)private_data->suspend.odr; | ||
559 | break; | ||
560 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
561 | (*(unsigned long *)data->data) = | ||
562 | (unsigned long)private_data->resume.odr; | ||
563 | break; | ||
564 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
565 | (*(unsigned long *)data->data) = | ||
566 | (unsigned long)private_data->suspend.fsr; | ||
567 | break; | ||
568 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
569 | (*(unsigned long *)data->data) = | ||
570 | (unsigned long)private_data->resume.fsr; | ||
571 | break; | ||
572 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
573 | (*(unsigned long *)data->data) = | ||
574 | (unsigned long)private_data->suspend.ths; | ||
575 | break; | ||
576 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
577 | (*(unsigned long *)data->data) = | ||
578 | (unsigned long)private_data->resume.ths; | ||
579 | break; | ||
580 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
581 | (*(unsigned long *)data->data) = | ||
582 | (unsigned long)private_data->suspend.dur; | ||
583 | break; | ||
584 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
585 | (*(unsigned long *)data->data) = | ||
586 | (unsigned long)private_data->resume.dur; | ||
587 | break; | ||
588 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
589 | (*(unsigned long *)data->data) = | ||
590 | (unsigned long)private_data->suspend.irq_type; | ||
591 | break; | ||
592 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
593 | (*(unsigned long *)data->data) = | ||
594 | (unsigned long)private_data->resume.irq_type; | ||
595 | break; | ||
596 | default: | ||
597 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
598 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
599 | }; | ||
600 | |||
601 | return INV_SUCCESS; | ||
602 | } | ||
603 | |||
604 | static struct ext_slave_descr lis331dlh_descr = { | ||
605 | .init = lis331dlh_init, | ||
606 | .exit = lis331dlh_exit, | ||
607 | .suspend = lis331dlh_suspend, | ||
608 | .resume = lis331dlh_resume, | ||
609 | .read = lis331dlh_read, | ||
610 | .config = lis331dlh_config, | ||
611 | .get_config = lis331dlh_get_config, | ||
612 | .name = "lis331dlh", | ||
613 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
614 | .id = ACCEL_ID_LIS331, | ||
615 | .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */ | ||
616 | .read_len = 6, | ||
617 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
618 | .range = {2, 480}, | ||
619 | .trigger = NULL, | ||
620 | }; | ||
621 | |||
622 | static | ||
623 | struct ext_slave_descr *lis331_get_slave_descr(void) | ||
624 | { | ||
625 | return &lis331dlh_descr; | ||
626 | } | ||
627 | |||
628 | /* -------------------------------------------------------------------------- */ | ||
629 | struct lis331_mod_private_data { | ||
630 | struct i2c_client *client; | ||
631 | struct ext_slave_platform_data *pdata; | ||
632 | }; | ||
633 | |||
634 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
635 | |||
636 | static int lis331_mod_probe(struct i2c_client *client, | ||
637 | const struct i2c_device_id *devid) | ||
638 | { | ||
639 | struct ext_slave_platform_data *pdata; | ||
640 | struct lis331_mod_private_data *private_data; | ||
641 | int result = 0; | ||
642 | |||
643 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
644 | |||
645 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
646 | result = -ENODEV; | ||
647 | goto out_no_free; | ||
648 | } | ||
649 | |||
650 | pdata = client->dev.platform_data; | ||
651 | if (!pdata) { | ||
652 | dev_err(&client->adapter->dev, | ||
653 | "Missing platform data for slave %s\n", devid->name); | ||
654 | result = -EFAULT; | ||
655 | goto out_no_free; | ||
656 | } | ||
657 | |||
658 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
659 | if (!private_data) { | ||
660 | result = -ENOMEM; | ||
661 | goto out_no_free; | ||
662 | } | ||
663 | |||
664 | i2c_set_clientdata(client, private_data); | ||
665 | private_data->client = client; | ||
666 | private_data->pdata = pdata; | ||
667 | |||
668 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
669 | lis331_get_slave_descr); | ||
670 | if (result) { | ||
671 | dev_err(&client->adapter->dev, | ||
672 | "Slave registration failed: %s, %d\n", | ||
673 | devid->name, result); | ||
674 | goto out_free_memory; | ||
675 | } | ||
676 | |||
677 | return result; | ||
678 | |||
679 | out_free_memory: | ||
680 | kfree(private_data); | ||
681 | out_no_free: | ||
682 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
683 | return result; | ||
684 | |||
685 | } | ||
686 | |||
687 | static int lis331_mod_remove(struct i2c_client *client) | ||
688 | { | ||
689 | struct lis331_mod_private_data *private_data = | ||
690 | i2c_get_clientdata(client); | ||
691 | |||
692 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
693 | |||
694 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
695 | lis331_get_slave_descr); | ||
696 | |||
697 | kfree(private_data); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static const struct i2c_device_id lis331_mod_id[] = { | ||
702 | { "lis331", ACCEL_ID_LIS331 }, | ||
703 | {} | ||
704 | }; | ||
705 | |||
706 | MODULE_DEVICE_TABLE(i2c, lis331_mod_id); | ||
707 | |||
708 | static struct i2c_driver lis331_mod_driver = { | ||
709 | .class = I2C_CLASS_HWMON, | ||
710 | .probe = lis331_mod_probe, | ||
711 | .remove = lis331_mod_remove, | ||
712 | .id_table = lis331_mod_id, | ||
713 | .driver = { | ||
714 | .owner = THIS_MODULE, | ||
715 | .name = "lis331_mod", | ||
716 | }, | ||
717 | .address_list = normal_i2c, | ||
718 | }; | ||
719 | |||
720 | static int __init lis331_mod_init(void) | ||
721 | { | ||
722 | int res = i2c_add_driver(&lis331_mod_driver); | ||
723 | pr_info("%s: Probe name %s\n", __func__, "lis331_mod"); | ||
724 | if (res) | ||
725 | pr_err("%s failed\n", __func__); | ||
726 | return res; | ||
727 | } | ||
728 | |||
729 | static void __exit lis331_mod_exit(void) | ||
730 | { | ||
731 | pr_info("%s\n", __func__); | ||
732 | i2c_del_driver(&lis331_mod_driver); | ||
733 | } | ||
734 | |||
735 | module_init(lis331_mod_init); | ||
736 | module_exit(lis331_mod_exit); | ||
737 | |||
738 | MODULE_AUTHOR("Invensense Corporation"); | ||
739 | MODULE_DESCRIPTION("Driver to integrate LIS331 sensor with the MPU"); | ||
740 | MODULE_LICENSE("GPL"); | ||
741 | MODULE_ALIAS("lis331_mod"); | ||
742 | |||
743 | /** | ||
744 | * @} | ||
745 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lis3dh.c b/drivers/misc/inv_mpu/accel/lis3dh.c new file mode 100644 index 00000000000..27206e4b847 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lis3dh.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file lis3dh.c | ||
26 | * @brief Accelerometer setup and handling methods for ST LIS3DH. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #undef MPL_LOG_NDEBUG | ||
32 | #define MPL_LOG_NDEBUG 0 | ||
33 | |||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include "mpu-dev.h" | ||
42 | |||
43 | #include <log.h> | ||
44 | #include <linux/mpu.h> | ||
45 | #include "mlsl.h" | ||
46 | #include "mldl_cfg.h" | ||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-acc" | ||
49 | |||
50 | /* full scale setting - register & mask */ | ||
51 | #define LIS3DH_CTRL_REG1 (0x20) | ||
52 | #define LIS3DH_CTRL_REG2 (0x21) | ||
53 | #define LIS3DH_CTRL_REG3 (0x22) | ||
54 | #define LIS3DH_CTRL_REG4 (0x23) | ||
55 | #define LIS3DH_CTRL_REG5 (0x24) | ||
56 | #define LIS3DH_CTRL_REG6 (0x25) | ||
57 | #define LIS3DH_REFERENCE (0x26) | ||
58 | #define LIS3DH_STATUS_REG (0x27) | ||
59 | #define LIS3DH_OUT_X_L (0x28) | ||
60 | #define LIS3DH_OUT_X_H (0x29) | ||
61 | #define LIS3DH_OUT_Y_L (0x2a) | ||
62 | #define LIS3DH_OUT_Y_H (0x2b) | ||
63 | #define LIS3DH_OUT_Z_L (0x2c) | ||
64 | #define LIS3DH_OUT_Z_H (0x2d) | ||
65 | |||
66 | #define LIS3DH_INT1_CFG (0x30) | ||
67 | #define LIS3DH_INT1_SRC (0x31) | ||
68 | #define LIS3DH_INT1_THS (0x32) | ||
69 | #define LIS3DH_INT1_DURATION (0x33) | ||
70 | |||
71 | #define LIS3DH_MAX_DUR (0x7F) | ||
72 | |||
73 | /* -------------------------------------------------------------------------- */ | ||
74 | |||
75 | struct lis3dh_config { | ||
76 | unsigned long odr; | ||
77 | unsigned int fsr; /* full scale range mg */ | ||
78 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
79 | unsigned int dur; /* Motion no-motion duration ms */ | ||
80 | unsigned char reg_ths; | ||
81 | unsigned char reg_dur; | ||
82 | unsigned char ctrl_reg1; | ||
83 | unsigned char irq_type; | ||
84 | unsigned char mot_int1_cfg; | ||
85 | }; | ||
86 | |||
87 | struct lis3dh_private_data { | ||
88 | struct lis3dh_config suspend; | ||
89 | struct lis3dh_config resume; | ||
90 | }; | ||
91 | |||
92 | /* -------------------------------------------------------------------------- */ | ||
93 | |||
94 | static int lis3dh_set_ths(void *mlsl_handle, | ||
95 | struct ext_slave_platform_data *pdata, | ||
96 | struct lis3dh_config *config, int apply, long ths) | ||
97 | { | ||
98 | int result = INV_SUCCESS; | ||
99 | if ((unsigned int)ths > 1000 * config->fsr) | ||
100 | ths = (long)1000 * config->fsr; | ||
101 | |||
102 | if (ths < 0) | ||
103 | ths = 0; | ||
104 | |||
105 | config->ths = ths; | ||
106 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
107 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
108 | if (apply) | ||
109 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
110 | LIS3DH_INT1_THS, | ||
111 | config->reg_ths); | ||
112 | return result; | ||
113 | } | ||
114 | |||
115 | static int lis3dh_set_dur(void *mlsl_handle, | ||
116 | struct ext_slave_platform_data *pdata, | ||
117 | struct lis3dh_config *config, int apply, long dur) | ||
118 | { | ||
119 | int result = INV_SUCCESS; | ||
120 | long reg_dur = (dur * config->odr) / 1000000L; | ||
121 | config->dur = dur; | ||
122 | |||
123 | if (reg_dur > LIS3DH_MAX_DUR) | ||
124 | reg_dur = LIS3DH_MAX_DUR; | ||
125 | |||
126 | config->reg_dur = (unsigned char)reg_dur; | ||
127 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
128 | if (apply) | ||
129 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
130 | LIS3DH_INT1_DURATION, | ||
131 | (unsigned char)reg_dur); | ||
132 | return result; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
137 | * duration will not be used uless the type is MOT or NMOT. | ||
138 | * | ||
139 | * @param config configuration to apply to, suspend or resume | ||
140 | * @param irq_type The type of IRQ. Valid values are | ||
141 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
142 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
143 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
144 | */ | ||
145 | static int lis3dh_set_irq(void *mlsl_handle, | ||
146 | struct ext_slave_platform_data *pdata, | ||
147 | struct lis3dh_config *config, | ||
148 | int apply, long irq_type) | ||
149 | { | ||
150 | int result = INV_SUCCESS; | ||
151 | unsigned char reg1; | ||
152 | unsigned char reg2; | ||
153 | |||
154 | config->irq_type = (unsigned char)irq_type; | ||
155 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
156 | reg1 = 0x10; | ||
157 | reg2 = 0x00; | ||
158 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
159 | reg1 = 0x40; | ||
160 | reg2 = config->mot_int1_cfg; | ||
161 | } else { | ||
162 | reg1 = 0x00; | ||
163 | reg2 = 0x00; | ||
164 | } | ||
165 | |||
166 | if (apply) { | ||
167 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
168 | LIS3DH_CTRL_REG3, reg1); | ||
169 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
170 | LIS3DH_INT1_CFG, reg2); | ||
171 | } | ||
172 | |||
173 | return result; | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * Set the Output data rate for the particular configuration | ||
178 | * | ||
179 | * @param config Config to modify with new ODR | ||
180 | * @param odr Output data rate in units of 1/1000Hz | ||
181 | */ | ||
182 | static int lis3dh_set_odr(void *mlsl_handle, | ||
183 | struct ext_slave_platform_data *pdata, | ||
184 | struct lis3dh_config *config, int apply, long odr) | ||
185 | { | ||
186 | unsigned char bits; | ||
187 | int result = INV_SUCCESS; | ||
188 | |||
189 | if (odr > 400000L) { | ||
190 | config->odr = 1250000L; | ||
191 | bits = 0x90; | ||
192 | } else if (odr > 200000L) { | ||
193 | config->odr = 400000L; | ||
194 | bits = 0x70; | ||
195 | } else if (odr > 100000L) { | ||
196 | config->odr = 200000L; | ||
197 | bits = 0x60; | ||
198 | } else if (odr > 50000) { | ||
199 | config->odr = 100000L; | ||
200 | bits = 0x50; | ||
201 | } else if (odr > 25000) { | ||
202 | config->odr = 50000; | ||
203 | bits = 0x40; | ||
204 | } else if (odr > 10000) { | ||
205 | config->odr = 25000; | ||
206 | bits = 0x30; | ||
207 | } else if (odr > 1000) { | ||
208 | config->odr = 10000; | ||
209 | bits = 0x20; | ||
210 | } else if (odr > 500) { | ||
211 | config->odr = 1000; | ||
212 | bits = 0x10; | ||
213 | } else { | ||
214 | config->odr = 0; | ||
215 | bits = 0; | ||
216 | } | ||
217 | |||
218 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xf); | ||
219 | lis3dh_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
220 | MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
221 | if (apply) | ||
222 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
223 | LIS3DH_CTRL_REG1, | ||
224 | config->ctrl_reg1); | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * Set the full scale range of the accels | ||
230 | * | ||
231 | * @param config pointer to configuration | ||
232 | * @param fsr requested full scale range | ||
233 | */ | ||
234 | static int lis3dh_set_fsr(void *mlsl_handle, | ||
235 | struct ext_slave_platform_data *pdata, | ||
236 | struct lis3dh_config *config, int apply, long fsr) | ||
237 | { | ||
238 | int result = INV_SUCCESS; | ||
239 | unsigned char reg1 = 0x48; | ||
240 | |||
241 | if (fsr <= 2048) { | ||
242 | config->fsr = 2048; | ||
243 | } else if (fsr <= 4096) { | ||
244 | reg1 |= 0x10; | ||
245 | config->fsr = 4096; | ||
246 | } else if (fsr <= 8192) { | ||
247 | reg1 |= 0x20; | ||
248 | config->fsr = 8192; | ||
249 | } else { | ||
250 | reg1 |= 0x30; | ||
251 | config->fsr = 16348; | ||
252 | } | ||
253 | |||
254 | lis3dh_set_ths(mlsl_handle, pdata, config, apply, config->ths); | ||
255 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
256 | if (apply) | ||
257 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
258 | LIS3DH_CTRL_REG4, reg1); | ||
259 | |||
260 | return result; | ||
261 | } | ||
262 | |||
263 | static int lis3dh_suspend(void *mlsl_handle, | ||
264 | struct ext_slave_descr *slave, | ||
265 | struct ext_slave_platform_data *pdata) | ||
266 | { | ||
267 | int result = INV_SUCCESS; | ||
268 | unsigned char reg1; | ||
269 | unsigned char reg2; | ||
270 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
271 | |||
272 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
273 | LIS3DH_CTRL_REG1, | ||
274 | private_data->suspend.ctrl_reg1); | ||
275 | |||
276 | reg1 = 0x48; | ||
277 | if (private_data->suspend.fsr == 16384) | ||
278 | reg1 |= 0x30; | ||
279 | else if (private_data->suspend.fsr == 8192) | ||
280 | reg1 |= 0x20; | ||
281 | else if (private_data->suspend.fsr == 4096) | ||
282 | reg1 |= 0x10; | ||
283 | else if (private_data->suspend.fsr == 2048) | ||
284 | reg1 |= 0x00; | ||
285 | |||
286 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
287 | LIS3DH_CTRL_REG4, reg1); | ||
288 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
289 | LIS3DH_INT1_THS, | ||
290 | private_data->suspend.reg_ths); | ||
291 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
292 | LIS3DH_INT1_DURATION, | ||
293 | private_data->suspend.reg_dur); | ||
294 | |||
295 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
296 | reg1 = 0x10; | ||
297 | reg2 = 0x00; | ||
298 | } else if (private_data->suspend.irq_type == | ||
299 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
300 | reg1 = 0x40; | ||
301 | reg2 = private_data->suspend.mot_int1_cfg; | ||
302 | } else { | ||
303 | reg1 = 0x00; | ||
304 | reg2 = 0x00; | ||
305 | } | ||
306 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
307 | LIS3DH_CTRL_REG3, reg1); | ||
308 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
309 | LIS3DH_INT1_CFG, reg2); | ||
310 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
311 | LIS3DH_CTRL_REG6, 1, ®1); | ||
312 | |||
313 | return result; | ||
314 | } | ||
315 | |||
316 | static int lis3dh_resume(void *mlsl_handle, | ||
317 | struct ext_slave_descr *slave, | ||
318 | struct ext_slave_platform_data *pdata) | ||
319 | { | ||
320 | int result; | ||
321 | unsigned char reg1; | ||
322 | unsigned char reg2; | ||
323 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
324 | |||
325 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
326 | LIS3DH_CTRL_REG1, | ||
327 | private_data->resume.ctrl_reg1); | ||
328 | if (result) { | ||
329 | LOG_RESULT_LOCATION(result); | ||
330 | return result; | ||
331 | } | ||
332 | msleep(6); | ||
333 | |||
334 | /* Full Scale */ | ||
335 | reg1 = 0x48; | ||
336 | if (private_data->suspend.fsr == 16384) | ||
337 | reg1 |= 0x30; | ||
338 | else if (private_data->suspend.fsr == 8192) | ||
339 | reg1 |= 0x20; | ||
340 | else if (private_data->suspend.fsr == 4096) | ||
341 | reg1 |= 0x10; | ||
342 | else if (private_data->suspend.fsr == 2048) | ||
343 | reg1 |= 0x00; | ||
344 | |||
345 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
346 | LIS3DH_CTRL_REG4, reg1); | ||
347 | if (result) { | ||
348 | LOG_RESULT_LOCATION(result); | ||
349 | return result; | ||
350 | } | ||
351 | |||
352 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
353 | reg1 = 0x10; | ||
354 | reg2 = 0x00; | ||
355 | } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
356 | reg1 = 0x40; | ||
357 | reg2 = private_data->resume.mot_int1_cfg; | ||
358 | } else { | ||
359 | reg1 = 0x00; | ||
360 | reg2 = 0x00; | ||
361 | } | ||
362 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
363 | LIS3DH_CTRL_REG3, reg1); | ||
364 | if (result) { | ||
365 | LOG_RESULT_LOCATION(result); | ||
366 | return result; | ||
367 | } | ||
368 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
369 | LIS3DH_INT1_THS, | ||
370 | private_data->resume.reg_ths); | ||
371 | if (result) { | ||
372 | LOG_RESULT_LOCATION(result); | ||
373 | return result; | ||
374 | } | ||
375 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
376 | LIS3DH_INT1_DURATION, | ||
377 | private_data->resume.reg_dur); | ||
378 | if (result) { | ||
379 | LOG_RESULT_LOCATION(result); | ||
380 | return result; | ||
381 | } | ||
382 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
383 | LIS3DH_INT1_CFG, reg2); | ||
384 | if (result) { | ||
385 | LOG_RESULT_LOCATION(result); | ||
386 | return result; | ||
387 | } | ||
388 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
389 | LIS3DH_CTRL_REG6, 1, ®1); | ||
390 | if (result) { | ||
391 | LOG_RESULT_LOCATION(result); | ||
392 | return result; | ||
393 | } | ||
394 | return result; | ||
395 | } | ||
396 | |||
397 | static int lis3dh_read(void *mlsl_handle, | ||
398 | struct ext_slave_descr *slave, | ||
399 | struct ext_slave_platform_data *pdata, | ||
400 | unsigned char *data) | ||
401 | { | ||
402 | int result = INV_SUCCESS; | ||
403 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
404 | LIS3DH_STATUS_REG, 1, data); | ||
405 | if (data[0] & 0x0F) { | ||
406 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
407 | slave->read_reg, slave->read_len, | ||
408 | data); | ||
409 | return result; | ||
410 | } else | ||
411 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
412 | } | ||
413 | |||
414 | static int lis3dh_init(void *mlsl_handle, | ||
415 | struct ext_slave_descr *slave, | ||
416 | struct ext_slave_platform_data *pdata) | ||
417 | { | ||
418 | int result; | ||
419 | long range; | ||
420 | struct lis3dh_private_data *private_data; | ||
421 | private_data = (struct lis3dh_private_data *) | ||
422 | kzalloc(sizeof(struct lis3dh_private_data), GFP_KERNEL); | ||
423 | |||
424 | if (!private_data) | ||
425 | return INV_ERROR_MEMORY_EXAUSTED; | ||
426 | |||
427 | pdata->private_data = private_data; | ||
428 | |||
429 | private_data->resume.ctrl_reg1 = 0x67; | ||
430 | private_data->suspend.ctrl_reg1 = 0x18; | ||
431 | private_data->resume.mot_int1_cfg = 0x95; | ||
432 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
433 | |||
434 | lis3dh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0); | ||
435 | lis3dh_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
436 | false, 200000L); | ||
437 | |||
438 | range = range_fixedpoint_to_long_mg(slave->range); | ||
439 | lis3dh_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
440 | false, range); | ||
441 | lis3dh_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
442 | false, range); | ||
443 | |||
444 | lis3dh_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
445 | false, 80); | ||
446 | lis3dh_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
447 | false, 40); | ||
448 | |||
449 | lis3dh_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
450 | false, 1000); | ||
451 | lis3dh_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
452 | false, 2540); | ||
453 | |||
454 | lis3dh_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
455 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
456 | lis3dh_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
457 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
458 | |||
459 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
460 | LIS3DH_CTRL_REG1, 0x07); | ||
461 | msleep(6); | ||
462 | |||
463 | return INV_SUCCESS; | ||
464 | } | ||
465 | |||
466 | static int lis3dh_exit(void *mlsl_handle, | ||
467 | struct ext_slave_descr *slave, | ||
468 | struct ext_slave_platform_data *pdata) | ||
469 | { | ||
470 | kfree(pdata->private_data); | ||
471 | return INV_SUCCESS; | ||
472 | } | ||
473 | |||
474 | static int lis3dh_config(void *mlsl_handle, | ||
475 | struct ext_slave_descr *slave, | ||
476 | struct ext_slave_platform_data *pdata, | ||
477 | struct ext_slave_config *data) | ||
478 | { | ||
479 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
480 | if (!data->data) | ||
481 | return INV_ERROR_INVALID_PARAMETER; | ||
482 | |||
483 | switch (data->key) { | ||
484 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
485 | return lis3dh_set_odr(mlsl_handle, pdata, | ||
486 | &private_data->suspend, | ||
487 | data->apply, *((long *)data->data)); | ||
488 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
489 | return lis3dh_set_odr(mlsl_handle, pdata, | ||
490 | &private_data->resume, | ||
491 | data->apply, *((long *)data->data)); | ||
492 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
493 | return lis3dh_set_fsr(mlsl_handle, pdata, | ||
494 | &private_data->suspend, | ||
495 | data->apply, *((long *)data->data)); | ||
496 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
497 | return lis3dh_set_fsr(mlsl_handle, pdata, | ||
498 | &private_data->resume, | ||
499 | data->apply, *((long *)data->data)); | ||
500 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
501 | return lis3dh_set_ths(mlsl_handle, pdata, | ||
502 | &private_data->suspend, | ||
503 | data->apply, *((long *)data->data)); | ||
504 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
505 | return lis3dh_set_ths(mlsl_handle, pdata, | ||
506 | &private_data->resume, | ||
507 | data->apply, *((long *)data->data)); | ||
508 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
509 | return lis3dh_set_dur(mlsl_handle, pdata, | ||
510 | &private_data->suspend, | ||
511 | data->apply, *((long *)data->data)); | ||
512 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
513 | return lis3dh_set_dur(mlsl_handle, pdata, | ||
514 | &private_data->resume, | ||
515 | data->apply, *((long *)data->data)); | ||
516 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
517 | return lis3dh_set_irq(mlsl_handle, pdata, | ||
518 | &private_data->suspend, | ||
519 | data->apply, *((long *)data->data)); | ||
520 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
521 | return lis3dh_set_irq(mlsl_handle, pdata, | ||
522 | &private_data->resume, | ||
523 | data->apply, *((long *)data->data)); | ||
524 | default: | ||
525 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
526 | }; | ||
527 | return INV_SUCCESS; | ||
528 | } | ||
529 | |||
530 | static int lis3dh_get_config(void *mlsl_handle, | ||
531 | struct ext_slave_descr *slave, | ||
532 | struct ext_slave_platform_data *pdata, | ||
533 | struct ext_slave_config *data) | ||
534 | { | ||
535 | struct lis3dh_private_data *private_data = pdata->private_data; | ||
536 | if (!data->data) | ||
537 | return INV_ERROR_INVALID_PARAMETER; | ||
538 | |||
539 | switch (data->key) { | ||
540 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
541 | (*(unsigned long *)data->data) = | ||
542 | (unsigned long)private_data->suspend.odr; | ||
543 | break; | ||
544 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
545 | (*(unsigned long *)data->data) = | ||
546 | (unsigned long)private_data->resume.odr; | ||
547 | break; | ||
548 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
549 | (*(unsigned long *)data->data) = | ||
550 | (unsigned long)private_data->suspend.fsr; | ||
551 | break; | ||
552 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
553 | (*(unsigned long *)data->data) = | ||
554 | (unsigned long)private_data->resume.fsr; | ||
555 | break; | ||
556 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
557 | (*(unsigned long *)data->data) = | ||
558 | (unsigned long)private_data->suspend.ths; | ||
559 | break; | ||
560 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
561 | (*(unsigned long *)data->data) = | ||
562 | (unsigned long)private_data->resume.ths; | ||
563 | break; | ||
564 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
565 | (*(unsigned long *)data->data) = | ||
566 | (unsigned long)private_data->suspend.dur; | ||
567 | break; | ||
568 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
569 | (*(unsigned long *)data->data) = | ||
570 | (unsigned long)private_data->resume.dur; | ||
571 | break; | ||
572 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
573 | (*(unsigned long *)data->data) = | ||
574 | (unsigned long)private_data->suspend.irq_type; | ||
575 | break; | ||
576 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
577 | (*(unsigned long *)data->data) = | ||
578 | (unsigned long)private_data->resume.irq_type; | ||
579 | break; | ||
580 | default: | ||
581 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
582 | }; | ||
583 | |||
584 | return INV_SUCCESS; | ||
585 | } | ||
586 | |||
587 | static struct ext_slave_descr lis3dh_descr = { | ||
588 | .init = lis3dh_init, | ||
589 | .exit = lis3dh_exit, | ||
590 | .suspend = lis3dh_suspend, | ||
591 | .resume = lis3dh_resume, | ||
592 | .read = lis3dh_read, | ||
593 | .config = lis3dh_config, | ||
594 | .get_config = lis3dh_get_config, | ||
595 | .name = "lis3dh", | ||
596 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
597 | .id = ACCEL_ID_LIS3DH, | ||
598 | .read_reg = 0x28 | 0x80, /* 0x80 for burst reads */ | ||
599 | .read_len = 6, | ||
600 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
601 | .range = {2, 480}, | ||
602 | .trigger = NULL, | ||
603 | }; | ||
604 | |||
605 | static | ||
606 | struct ext_slave_descr *lis3dh_get_slave_descr(void) | ||
607 | { | ||
608 | return &lis3dh_descr; | ||
609 | } | ||
610 | |||
611 | /* -------------------------------------------------------------------------- */ | ||
612 | struct lis3dh_mod_private_data { | ||
613 | struct i2c_client *client; | ||
614 | struct ext_slave_platform_data *pdata; | ||
615 | }; | ||
616 | |||
617 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
618 | |||
619 | static int lis3dh_mod_probe(struct i2c_client *client, | ||
620 | const struct i2c_device_id *devid) | ||
621 | { | ||
622 | struct ext_slave_platform_data *pdata; | ||
623 | struct lis3dh_mod_private_data *private_data; | ||
624 | int result = 0; | ||
625 | |||
626 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
627 | |||
628 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
629 | result = -ENODEV; | ||
630 | goto out_no_free; | ||
631 | } | ||
632 | |||
633 | pdata = client->dev.platform_data; | ||
634 | if (!pdata) { | ||
635 | dev_err(&client->adapter->dev, | ||
636 | "Missing platform data for slave %s\n", devid->name); | ||
637 | result = -EFAULT; | ||
638 | goto out_no_free; | ||
639 | } | ||
640 | |||
641 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
642 | if (!private_data) { | ||
643 | result = -ENOMEM; | ||
644 | goto out_no_free; | ||
645 | } | ||
646 | |||
647 | i2c_set_clientdata(client, private_data); | ||
648 | private_data->client = client; | ||
649 | private_data->pdata = pdata; | ||
650 | |||
651 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
652 | lis3dh_get_slave_descr); | ||
653 | if (result) { | ||
654 | dev_err(&client->adapter->dev, | ||
655 | "Slave registration failed: %s, %d\n", | ||
656 | devid->name, result); | ||
657 | goto out_free_memory; | ||
658 | } | ||
659 | |||
660 | return result; | ||
661 | |||
662 | out_free_memory: | ||
663 | kfree(private_data); | ||
664 | out_no_free: | ||
665 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
666 | return result; | ||
667 | |||
668 | } | ||
669 | |||
670 | static int lis3dh_mod_remove(struct i2c_client *client) | ||
671 | { | ||
672 | struct lis3dh_mod_private_data *private_data = | ||
673 | i2c_get_clientdata(client); | ||
674 | |||
675 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
676 | |||
677 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
678 | lis3dh_get_slave_descr); | ||
679 | |||
680 | kfree(private_data); | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static const struct i2c_device_id lis3dh_mod_id[] = { | ||
685 | { "lis3dh", ACCEL_ID_LIS3DH }, | ||
686 | {} | ||
687 | }; | ||
688 | |||
689 | MODULE_DEVICE_TABLE(i2c, lis3dh_mod_id); | ||
690 | |||
691 | static struct i2c_driver lis3dh_mod_driver = { | ||
692 | .class = I2C_CLASS_HWMON, | ||
693 | .probe = lis3dh_mod_probe, | ||
694 | .remove = lis3dh_mod_remove, | ||
695 | .id_table = lis3dh_mod_id, | ||
696 | .driver = { | ||
697 | .owner = THIS_MODULE, | ||
698 | .name = "lis3dh_mod", | ||
699 | }, | ||
700 | .address_list = normal_i2c, | ||
701 | }; | ||
702 | |||
703 | static int __init lis3dh_mod_init(void) | ||
704 | { | ||
705 | int res = i2c_add_driver(&lis3dh_mod_driver); | ||
706 | pr_info("%s: Probe name %s\n", __func__, "lis3dh_mod"); | ||
707 | if (res) | ||
708 | pr_err("%s failed\n", __func__); | ||
709 | return res; | ||
710 | } | ||
711 | |||
712 | static void __exit lis3dh_mod_exit(void) | ||
713 | { | ||
714 | pr_info("%s\n", __func__); | ||
715 | i2c_del_driver(&lis3dh_mod_driver); | ||
716 | } | ||
717 | |||
718 | module_init(lis3dh_mod_init); | ||
719 | module_exit(lis3dh_mod_exit); | ||
720 | |||
721 | MODULE_AUTHOR("Invensense Corporation"); | ||
722 | MODULE_DESCRIPTION("Driver to integrate LIS3DH sensor with the MPU"); | ||
723 | MODULE_LICENSE("GPL"); | ||
724 | MODULE_ALIAS("lis3dh_mod"); | ||
725 | |||
726 | /* | ||
727 | * @} | ||
728 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/lsm303dlx_a.c b/drivers/misc/inv_mpu/accel/lsm303dlx_a.c new file mode 100644 index 00000000000..576282a0fb1 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/lsm303dlx_a.c | |||
@@ -0,0 +1,881 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file lsm303dlx_a.c | ||
26 | * @brief Accelerometer setup and handling methods for ST LSM303DLH | ||
27 | * or LSM303DLM accel. | ||
28 | */ | ||
29 | |||
30 | /* -------------------------------------------------------------------------- */ | ||
31 | |||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/moduleparam.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include "mpu-dev.h" | ||
40 | |||
41 | #include <log.h> | ||
42 | #include <linux/mpu.h> | ||
43 | #include "mlsl.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | #undef MPL_LOG_TAG | ||
46 | #define MPL_LOG_TAG "MPL-acc" | ||
47 | |||
48 | /* -------------------------------------------------------------------------- */ | ||
49 | |||
50 | /* full scale setting - register & mask */ | ||
51 | #define LSM303DLx_CTRL_REG1 (0x20) | ||
52 | #define LSM303DLx_CTRL_REG2 (0x21) | ||
53 | #define LSM303DLx_CTRL_REG3 (0x22) | ||
54 | #define LSM303DLx_CTRL_REG4 (0x23) | ||
55 | #define LSM303DLx_CTRL_REG5 (0x24) | ||
56 | #define LSM303DLx_HP_FILTER_RESET (0x25) | ||
57 | #define LSM303DLx_REFERENCE (0x26) | ||
58 | #define LSM303DLx_STATUS_REG (0x27) | ||
59 | #define LSM303DLx_OUT_X_L (0x28) | ||
60 | #define LSM303DLx_OUT_X_H (0x29) | ||
61 | #define LSM303DLx_OUT_Y_L (0x2a) | ||
62 | #define LSM303DLx_OUT_Y_H (0x2b) | ||
63 | #define LSM303DLx_OUT_Z_L (0x2b) | ||
64 | #define LSM303DLx_OUT_Z_H (0x2d) | ||
65 | |||
66 | #define LSM303DLx_INT1_CFG (0x30) | ||
67 | #define LSM303DLx_INT1_SRC (0x31) | ||
68 | #define LSM303DLx_INT1_THS (0x32) | ||
69 | #define LSM303DLx_INT1_DURATION (0x33) | ||
70 | |||
71 | #define LSM303DLx_INT2_CFG (0x34) | ||
72 | #define LSM303DLx_INT2_SRC (0x35) | ||
73 | #define LSM303DLx_INT2_THS (0x36) | ||
74 | #define LSM303DLx_INT2_DURATION (0x37) | ||
75 | |||
76 | #define LSM303DLx_CTRL_MASK (0x30) | ||
77 | #define LSM303DLx_SLEEP_MASK (0x20) | ||
78 | #define LSM303DLx_PWR_MODE_NORMAL (0x20) | ||
79 | |||
80 | #define LSM303DLx_MAX_DUR (0x7F) | ||
81 | |||
82 | /* -------------------------------------------------------------------------- */ | ||
83 | |||
84 | struct lsm303dlx_a_config { | ||
85 | unsigned int odr; | ||
86 | unsigned int fsr; /** < full scale range mg */ | ||
87 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
88 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
89 | unsigned char reg_ths; | ||
90 | unsigned char reg_dur; | ||
91 | unsigned char ctrl_reg1; | ||
92 | unsigned char irq_type; | ||
93 | unsigned char mot_int1_cfg; | ||
94 | }; | ||
95 | |||
96 | struct lsm303dlx_a_private_data { | ||
97 | struct lsm303dlx_a_config suspend; | ||
98 | struct lsm303dlx_a_config resume; | ||
99 | }; | ||
100 | |||
101 | /* -------------------------------------------------------------------------- */ | ||
102 | |||
103 | static int lsm303dlx_a_set_ths(void *mlsl_handle, | ||
104 | struct ext_slave_platform_data *pdata, | ||
105 | struct lsm303dlx_a_config *config, | ||
106 | int apply, | ||
107 | long ths) | ||
108 | { | ||
109 | int result = INV_SUCCESS; | ||
110 | if ((unsigned int) ths >= config->fsr) | ||
111 | ths = (long) config->fsr - 1; | ||
112 | |||
113 | if (ths < 0) | ||
114 | ths = 0; | ||
115 | |||
116 | config->ths = ths; | ||
117 | config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr)); | ||
118 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
119 | if (apply) | ||
120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
121 | LSM303DLx_INT1_THS, | ||
122 | config->reg_ths); | ||
123 | return result; | ||
124 | } | ||
125 | |||
126 | static int lsm303dlx_a_set_dur(void *mlsl_handle, | ||
127 | struct ext_slave_platform_data *pdata, | ||
128 | struct lsm303dlx_a_config *config, | ||
129 | int apply, | ||
130 | long dur) | ||
131 | { | ||
132 | int result = INV_SUCCESS; | ||
133 | long reg_dur = (dur * config->odr) / 1000000L; | ||
134 | config->dur = dur; | ||
135 | |||
136 | if (reg_dur > LSM303DLx_MAX_DUR) | ||
137 | reg_dur = LSM303DLx_MAX_DUR; | ||
138 | |||
139 | config->reg_dur = (unsigned char) reg_dur; | ||
140 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
141 | if (apply) | ||
142 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
143 | LSM303DLx_INT1_DURATION, | ||
144 | (unsigned char)reg_dur); | ||
145 | return result; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
150 | * duration will not be used uless the type is MOT or NMOT. | ||
151 | * | ||
152 | * @param config configuration to apply to, suspend or resume | ||
153 | * @param irq_type The type of IRQ. Valid values are | ||
154 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
155 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
156 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
157 | */ | ||
158 | static int lsm303dlx_a_set_irq(void *mlsl_handle, | ||
159 | struct ext_slave_platform_data *pdata, | ||
160 | struct lsm303dlx_a_config *config, | ||
161 | int apply, | ||
162 | long irq_type) | ||
163 | { | ||
164 | int result = INV_SUCCESS; | ||
165 | unsigned char reg1; | ||
166 | unsigned char reg2; | ||
167 | |||
168 | config->irq_type = (unsigned char)irq_type; | ||
169 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
170 | reg1 = 0x02; | ||
171 | reg2 = 0x00; | ||
172 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
173 | reg1 = 0x00; | ||
174 | reg2 = config->mot_int1_cfg; | ||
175 | } else { | ||
176 | reg1 = 0x00; | ||
177 | reg2 = 0x00; | ||
178 | } | ||
179 | |||
180 | if (apply) { | ||
181 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
182 | LSM303DLx_CTRL_REG3, reg1); | ||
183 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
184 | LSM303DLx_INT1_CFG, reg2); | ||
185 | } | ||
186 | |||
187 | return result; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * @brief Set the output data rate for the particular configuration. | ||
192 | * | ||
193 | * @param mlsl_handle | ||
194 | * the handle to the serial channel the device is connected to. | ||
195 | * @param pdata | ||
196 | * a pointer to the slave platform data. | ||
197 | * @param config | ||
198 | * Config to modify with new ODR. | ||
199 | * @param apply | ||
200 | * whether to apply immediately or save the settings to be applied | ||
201 | * at the next resume. | ||
202 | * @param odr | ||
203 | * Output data rate in units of 1/1000Hz (mHz). | ||
204 | * | ||
205 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
206 | */ | ||
207 | static int lsm303dlx_a_set_odr(void *mlsl_handle, | ||
208 | struct ext_slave_platform_data *pdata, | ||
209 | struct lsm303dlx_a_config *config, | ||
210 | int apply, | ||
211 | long odr) | ||
212 | { | ||
213 | unsigned char bits; | ||
214 | int result = INV_SUCCESS; | ||
215 | |||
216 | /* normal power modes */ | ||
217 | if (odr > 400000) { | ||
218 | config->odr = 1000000; | ||
219 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x18; | ||
220 | } else if (odr > 100000) { | ||
221 | config->odr = 400000; | ||
222 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x10; | ||
223 | } else if (odr > 50000) { | ||
224 | config->odr = 100000; | ||
225 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x08; | ||
226 | } else if (odr > 10000) { | ||
227 | config->odr = 50000; | ||
228 | bits = LSM303DLx_PWR_MODE_NORMAL | 0x00; | ||
229 | /* low power modes */ | ||
230 | } else if (odr > 5000) { | ||
231 | config->odr = 10000; | ||
232 | bits = 0xC0; | ||
233 | } else if (odr > 2000) { | ||
234 | config->odr = 5000; | ||
235 | bits = 0xA0; | ||
236 | } else if (odr > 1000) { | ||
237 | config->odr = 2000; | ||
238 | bits = 0x80; | ||
239 | } else if (odr > 500) { | ||
240 | config->odr = 1000; | ||
241 | bits = 0x60; | ||
242 | } else if (odr > 0) { | ||
243 | config->odr = 500; | ||
244 | bits = 0x40; | ||
245 | } else { | ||
246 | config->odr = 0; | ||
247 | bits = 0; | ||
248 | } | ||
249 | |||
250 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7); | ||
251 | lsm303dlx_a_set_dur(mlsl_handle, pdata, config, apply, config->dur); | ||
252 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
253 | if (apply) | ||
254 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
255 | LSM303DLx_CTRL_REG1, | ||
256 | config->ctrl_reg1); | ||
257 | return result; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * @brief Set the full scale range of the accels | ||
262 | * | ||
263 | * @param mlsl_handle | ||
264 | * the handle to the serial channel the device is connected to. | ||
265 | * @param pdata | ||
266 | * a pointer to the slave platform data. | ||
267 | * @param config | ||
268 | * pointer to configuration. | ||
269 | * @param apply | ||
270 | * whether to apply immediately or save the settings to be applied | ||
271 | * at the next resume. | ||
272 | * @param fsr | ||
273 | * requested full scale range. | ||
274 | * | ||
275 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
276 | */ | ||
277 | static int lsm303dlx_a_set_fsr(void *mlsl_handle, | ||
278 | struct ext_slave_platform_data *pdata, | ||
279 | struct lsm303dlx_a_config *config, | ||
280 | int apply, | ||
281 | long fsr) | ||
282 | { | ||
283 | unsigned char reg1 = 0x40; | ||
284 | int result = INV_SUCCESS; | ||
285 | |||
286 | if (fsr <= 2048) { | ||
287 | config->fsr = 2048; | ||
288 | } else if (fsr <= 4096) { | ||
289 | reg1 |= 0x30; | ||
290 | config->fsr = 4096; | ||
291 | } else { | ||
292 | reg1 |= 0x10; | ||
293 | config->fsr = 8192; | ||
294 | } | ||
295 | |||
296 | lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
297 | config, apply, config->ths); | ||
298 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
299 | if (apply) | ||
300 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
301 | LSM303DLx_CTRL_REG4, reg1); | ||
302 | |||
303 | return result; | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * @brief suspends the device to put it in its lowest power mode. | ||
308 | * | ||
309 | * @param mlsl_handle | ||
310 | * the handle to the serial channel the device is connected to. | ||
311 | * @param slave | ||
312 | * a pointer to the slave descriptor data structure. | ||
313 | * @param pdata | ||
314 | * a pointer to the slave platform data. | ||
315 | * | ||
316 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
317 | */ | ||
318 | static int lsm303dlx_a_suspend(void *mlsl_handle, | ||
319 | struct ext_slave_descr *slave, | ||
320 | struct ext_slave_platform_data *pdata) | ||
321 | { | ||
322 | int result = INV_SUCCESS; | ||
323 | unsigned char reg1; | ||
324 | unsigned char reg2; | ||
325 | struct lsm303dlx_a_private_data *private_data = | ||
326 | (struct lsm303dlx_a_private_data *)(pdata->private_data); | ||
327 | |||
328 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
329 | LSM303DLx_CTRL_REG1, | ||
330 | private_data->suspend.ctrl_reg1); | ||
331 | |||
332 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
333 | LSM303DLx_CTRL_REG2, 0x0f); | ||
334 | reg1 = 0x40; | ||
335 | if (private_data->suspend.fsr == 8192) | ||
336 | reg1 |= 0x30; | ||
337 | else if (private_data->suspend.fsr == 4096) | ||
338 | reg1 |= 0x10; | ||
339 | /* else bits [4..5] are already zero */ | ||
340 | |||
341 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
342 | LSM303DLx_CTRL_REG4, reg1); | ||
343 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
344 | LSM303DLx_INT1_THS, | ||
345 | private_data->suspend.reg_ths); | ||
346 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
347 | LSM303DLx_INT1_DURATION, | ||
348 | private_data->suspend.reg_dur); | ||
349 | |||
350 | if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
351 | reg1 = 0x02; | ||
352 | reg2 = 0x00; | ||
353 | } else if (private_data->suspend.irq_type == | ||
354 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
355 | reg1 = 0x00; | ||
356 | reg2 = private_data->suspend.mot_int1_cfg; | ||
357 | } else { | ||
358 | reg1 = 0x00; | ||
359 | reg2 = 0x00; | ||
360 | } | ||
361 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
362 | LSM303DLx_CTRL_REG3, reg1); | ||
363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
364 | LSM303DLx_INT1_CFG, reg2); | ||
365 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
366 | LSM303DLx_HP_FILTER_RESET, 1, ®1); | ||
367 | return result; | ||
368 | } | ||
369 | |||
370 | /** | ||
371 | * @brief resume the device in the proper power state given the configuration | ||
372 | * chosen. | ||
373 | * | ||
374 | * @param mlsl_handle | ||
375 | * the handle to the serial channel the device is connected to. | ||
376 | * @param slave | ||
377 | * a pointer to the slave descriptor data structure. | ||
378 | * @param pdata | ||
379 | * a pointer to the slave platform data. | ||
380 | * | ||
381 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
382 | */ | ||
383 | static int lsm303dlx_a_resume(void *mlsl_handle, | ||
384 | struct ext_slave_descr *slave, | ||
385 | struct ext_slave_platform_data *pdata) | ||
386 | { | ||
387 | int result = INV_SUCCESS; | ||
388 | unsigned char reg1; | ||
389 | unsigned char reg2; | ||
390 | struct lsm303dlx_a_private_data *private_data = | ||
391 | (struct lsm303dlx_a_private_data *)(pdata->private_data); | ||
392 | |||
393 | |||
394 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
395 | LSM303DLx_CTRL_REG1, | ||
396 | private_data->resume.ctrl_reg1); | ||
397 | if (result) { | ||
398 | LOG_RESULT_LOCATION(result); | ||
399 | return result; | ||
400 | } | ||
401 | msleep(6); | ||
402 | |||
403 | /* Full Scale */ | ||
404 | reg1 = 0x40; | ||
405 | if (private_data->resume.fsr == 8192) | ||
406 | reg1 |= 0x30; | ||
407 | else if (private_data->resume.fsr == 4096) | ||
408 | reg1 |= 0x10; | ||
409 | |||
410 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
411 | LSM303DLx_CTRL_REG4, reg1); | ||
412 | if (result) { | ||
413 | LOG_RESULT_LOCATION(result); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | /* Configure high pass filter */ | ||
418 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
419 | LSM303DLx_CTRL_REG2, 0x0F); | ||
420 | if (result) { | ||
421 | LOG_RESULT_LOCATION(result); | ||
422 | return result; | ||
423 | } | ||
424 | |||
425 | if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
426 | reg1 = 0x02; | ||
427 | reg2 = 0x00; | ||
428 | } else if (private_data->resume.irq_type == | ||
429 | MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
430 | reg1 = 0x00; | ||
431 | reg2 = private_data->resume.mot_int1_cfg; | ||
432 | } else { | ||
433 | reg1 = 0x00; | ||
434 | reg2 = 0x00; | ||
435 | } | ||
436 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
437 | LSM303DLx_CTRL_REG3, reg1); | ||
438 | if (result) { | ||
439 | LOG_RESULT_LOCATION(result); | ||
440 | return result; | ||
441 | } | ||
442 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
443 | LSM303DLx_INT1_THS, | ||
444 | private_data->resume.reg_ths); | ||
445 | if (result) { | ||
446 | LOG_RESULT_LOCATION(result); | ||
447 | return result; | ||
448 | } | ||
449 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
450 | LSM303DLx_INT1_DURATION, | ||
451 | private_data->resume.reg_dur); | ||
452 | if (result) { | ||
453 | LOG_RESULT_LOCATION(result); | ||
454 | return result; | ||
455 | } | ||
456 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
457 | LSM303DLx_INT1_CFG, reg2); | ||
458 | if (result) { | ||
459 | LOG_RESULT_LOCATION(result); | ||
460 | return result; | ||
461 | } | ||
462 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
463 | LSM303DLx_HP_FILTER_RESET, 1, ®1); | ||
464 | if (result) { | ||
465 | LOG_RESULT_LOCATION(result); | ||
466 | return result; | ||
467 | } | ||
468 | return result; | ||
469 | } | ||
470 | |||
471 | /** | ||
472 | * @brief read the sensor data from the device. | ||
473 | * | ||
474 | * @param mlsl_handle | ||
475 | * the handle to the serial channel the device is connected to. | ||
476 | * @param slave | ||
477 | * a pointer to the slave descriptor data structure. | ||
478 | * @param pdata | ||
479 | * a pointer to the slave platform data. | ||
480 | * @param data | ||
481 | * a buffer to store the data read. | ||
482 | * | ||
483 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
484 | */ | ||
485 | static int lsm303dlx_a_read(void *mlsl_handle, | ||
486 | struct ext_slave_descr *slave, | ||
487 | struct ext_slave_platform_data *pdata, | ||
488 | unsigned char *data) | ||
489 | { | ||
490 | int result = INV_SUCCESS; | ||
491 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
492 | LSM303DLx_STATUS_REG, 1, data); | ||
493 | if (data[0] & 0x0F) { | ||
494 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
495 | slave->read_reg, slave->read_len, data); | ||
496 | return result; | ||
497 | } else | ||
498 | return INV_ERROR_ACCEL_DATA_NOT_READY; | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * @brief one-time device driver initialization function. | ||
503 | * If the driver is built as a kernel module, this function will be | ||
504 | * called when the module is loaded in the kernel. | ||
505 | * If the driver is built-in in the kernel, this function will be | ||
506 | * called at boot time. | ||
507 | * | ||
508 | * @param mlsl_handle | ||
509 | * the handle to the serial channel the device is connected to. | ||
510 | * @param slave | ||
511 | * a pointer to the slave descriptor data structure. | ||
512 | * @param pdata | ||
513 | * a pointer to the slave platform data. | ||
514 | * | ||
515 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
516 | */ | ||
517 | static int lsm303dlx_a_init(void *mlsl_handle, | ||
518 | struct ext_slave_descr *slave, | ||
519 | struct ext_slave_platform_data *pdata) | ||
520 | { | ||
521 | long range; | ||
522 | struct lsm303dlx_a_private_data *private_data; | ||
523 | private_data = (struct lsm303dlx_a_private_data *) | ||
524 | kzalloc(sizeof(struct lsm303dlx_a_private_data), GFP_KERNEL); | ||
525 | |||
526 | if (!private_data) | ||
527 | return INV_ERROR_MEMORY_EXAUSTED; | ||
528 | |||
529 | pdata->private_data = private_data; | ||
530 | |||
531 | private_data->resume.ctrl_reg1 = 0x37; | ||
532 | private_data->suspend.ctrl_reg1 = 0x47; | ||
533 | private_data->resume.mot_int1_cfg = 0x95; | ||
534 | private_data->suspend.mot_int1_cfg = 0x2a; | ||
535 | |||
536 | lsm303dlx_a_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
537 | false, 0); | ||
538 | lsm303dlx_a_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
539 | false, 200000); | ||
540 | |||
541 | range = range_fixedpoint_to_long_mg(slave->range); | ||
542 | lsm303dlx_a_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
543 | false, range); | ||
544 | lsm303dlx_a_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
545 | false, range); | ||
546 | |||
547 | lsm303dlx_a_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
548 | false, 80); | ||
549 | lsm303dlx_a_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
550 | false, 40); | ||
551 | |||
552 | lsm303dlx_a_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
553 | false, 1000); | ||
554 | lsm303dlx_a_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
555 | false, 2540); | ||
556 | |||
557 | lsm303dlx_a_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
558 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
559 | lsm303dlx_a_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
560 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
561 | return INV_SUCCESS; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * @brief one-time device driver exit function. | ||
566 | * If the driver is built as a kernel module, this function will be | ||
567 | * called when the module is removed from the kernel. | ||
568 | * | ||
569 | * @param mlsl_handle | ||
570 | * the handle to the serial channel the device is connected to. | ||
571 | * @param slave | ||
572 | * a pointer to the slave descriptor data structure. | ||
573 | * @param pdata | ||
574 | * a pointer to the slave platform data. | ||
575 | * | ||
576 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
577 | */ | ||
578 | static int lsm303dlx_a_exit(void *mlsl_handle, | ||
579 | struct ext_slave_descr *slave, | ||
580 | struct ext_slave_platform_data *pdata) | ||
581 | { | ||
582 | kfree(pdata->private_data); | ||
583 | return INV_SUCCESS; | ||
584 | } | ||
585 | |||
586 | /** | ||
587 | * @brief device configuration facility. | ||
588 | * | ||
589 | * @param mlsl_handle | ||
590 | * the handle to the serial channel the device is connected to. | ||
591 | * @param slave | ||
592 | * a pointer to the slave descriptor data structure. | ||
593 | * @param pdata | ||
594 | * a pointer to the slave platform data. | ||
595 | * @param data | ||
596 | * a pointer to the configuration data structure. | ||
597 | * | ||
598 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
599 | */ | ||
600 | static int lsm303dlx_a_config(void *mlsl_handle, | ||
601 | struct ext_slave_descr *slave, | ||
602 | struct ext_slave_platform_data *pdata, | ||
603 | struct ext_slave_config *data) | ||
604 | { | ||
605 | struct lsm303dlx_a_private_data *private_data = pdata->private_data; | ||
606 | if (!data->data) | ||
607 | return INV_ERROR_INVALID_PARAMETER; | ||
608 | |||
609 | switch (data->key) { | ||
610 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
611 | return lsm303dlx_a_set_odr(mlsl_handle, pdata, | ||
612 | &private_data->suspend, | ||
613 | data->apply, | ||
614 | *((long *)data->data)); | ||
615 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
616 | return lsm303dlx_a_set_odr(mlsl_handle, pdata, | ||
617 | &private_data->resume, | ||
618 | data->apply, | ||
619 | *((long *)data->data)); | ||
620 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
621 | return lsm303dlx_a_set_fsr(mlsl_handle, pdata, | ||
622 | &private_data->suspend, | ||
623 | data->apply, | ||
624 | *((long *)data->data)); | ||
625 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
626 | return lsm303dlx_a_set_fsr(mlsl_handle, pdata, | ||
627 | &private_data->resume, | ||
628 | data->apply, | ||
629 | *((long *)data->data)); | ||
630 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
631 | return lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
632 | &private_data->suspend, | ||
633 | data->apply, | ||
634 | *((long *)data->data)); | ||
635 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
636 | return lsm303dlx_a_set_ths(mlsl_handle, pdata, | ||
637 | &private_data->resume, | ||
638 | data->apply, | ||
639 | *((long *)data->data)); | ||
640 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
641 | return lsm303dlx_a_set_dur(mlsl_handle, pdata, | ||
642 | &private_data->suspend, | ||
643 | data->apply, | ||
644 | *((long *)data->data)); | ||
645 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
646 | return lsm303dlx_a_set_dur(mlsl_handle, pdata, | ||
647 | &private_data->resume, | ||
648 | data->apply, | ||
649 | *((long *)data->data)); | ||
650 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
651 | return lsm303dlx_a_set_irq(mlsl_handle, pdata, | ||
652 | &private_data->suspend, | ||
653 | data->apply, | ||
654 | *((long *)data->data)); | ||
655 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
656 | return lsm303dlx_a_set_irq(mlsl_handle, pdata, | ||
657 | &private_data->resume, | ||
658 | data->apply, | ||
659 | *((long *)data->data)); | ||
660 | default: | ||
661 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
662 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
663 | }; | ||
664 | |||
665 | return INV_SUCCESS; | ||
666 | } | ||
667 | |||
668 | /** | ||
669 | * @brief facility to retrieve the device configuration. | ||
670 | * | ||
671 | * @param mlsl_handle | ||
672 | * the handle to the serial channel the device is connected to. | ||
673 | * @param slave | ||
674 | * a pointer to the slave descriptor data structure. | ||
675 | * @param pdata | ||
676 | * a pointer to the slave platform data. | ||
677 | * @param data | ||
678 | * a pointer to store the returned configuration data structure. | ||
679 | * | ||
680 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
681 | */ | ||
682 | static int lsm303dlx_a_get_config(void *mlsl_handle, | ||
683 | struct ext_slave_descr *slave, | ||
684 | struct ext_slave_platform_data *pdata, | ||
685 | struct ext_slave_config *data) | ||
686 | { | ||
687 | struct lsm303dlx_a_private_data *private_data = pdata->private_data; | ||
688 | if (!data->data) | ||
689 | return INV_ERROR_INVALID_PARAMETER; | ||
690 | |||
691 | switch (data->key) { | ||
692 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
693 | (*(unsigned long *)data->data) = | ||
694 | (unsigned long) private_data->suspend.odr; | ||
695 | break; | ||
696 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
697 | (*(unsigned long *)data->data) = | ||
698 | (unsigned long) private_data->resume.odr; | ||
699 | break; | ||
700 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
701 | (*(unsigned long *)data->data) = | ||
702 | (unsigned long) private_data->suspend.fsr; | ||
703 | break; | ||
704 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
705 | (*(unsigned long *)data->data) = | ||
706 | (unsigned long) private_data->resume.fsr; | ||
707 | break; | ||
708 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
709 | (*(unsigned long *)data->data) = | ||
710 | (unsigned long) private_data->suspend.ths; | ||
711 | break; | ||
712 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
713 | (*(unsigned long *)data->data) = | ||
714 | (unsigned long) private_data->resume.ths; | ||
715 | break; | ||
716 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
717 | (*(unsigned long *)data->data) = | ||
718 | (unsigned long) private_data->suspend.dur; | ||
719 | break; | ||
720 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
721 | (*(unsigned long *)data->data) = | ||
722 | (unsigned long) private_data->resume.dur; | ||
723 | break; | ||
724 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
725 | (*(unsigned long *)data->data) = | ||
726 | (unsigned long) private_data->suspend.irq_type; | ||
727 | break; | ||
728 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
729 | (*(unsigned long *)data->data) = | ||
730 | (unsigned long) private_data->resume.irq_type; | ||
731 | break; | ||
732 | default: | ||
733 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
734 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
735 | }; | ||
736 | |||
737 | return INV_SUCCESS; | ||
738 | } | ||
739 | |||
740 | static struct ext_slave_descr lsm303dlx_a_descr = { | ||
741 | .init = lsm303dlx_a_init, | ||
742 | .exit = lsm303dlx_a_exit, | ||
743 | .suspend = lsm303dlx_a_suspend, | ||
744 | .resume = lsm303dlx_a_resume, | ||
745 | .read = lsm303dlx_a_read, | ||
746 | .config = lsm303dlx_a_config, | ||
747 | .get_config = lsm303dlx_a_get_config, | ||
748 | .name = "lsm303dlx_a", | ||
749 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
750 | .id = ACCEL_ID_LSM303DLX, | ||
751 | .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */ | ||
752 | .read_len = 6, | ||
753 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
754 | .range = {2, 480}, | ||
755 | .trigger = NULL, | ||
756 | }; | ||
757 | |||
758 | static | ||
759 | struct ext_slave_descr *lsm303dlx_a_get_slave_descr(void) | ||
760 | { | ||
761 | return &lsm303dlx_a_descr; | ||
762 | } | ||
763 | |||
764 | /* -------------------------------------------------------------------------- */ | ||
765 | struct lsm303dlx_a_mod_private_data { | ||
766 | struct i2c_client *client; | ||
767 | struct ext_slave_platform_data *pdata; | ||
768 | }; | ||
769 | |||
770 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
771 | |||
772 | static int lsm303dlx_a_mod_probe(struct i2c_client *client, | ||
773 | const struct i2c_device_id *devid) | ||
774 | { | ||
775 | struct ext_slave_platform_data *pdata; | ||
776 | struct lsm303dlx_a_mod_private_data *private_data; | ||
777 | int result = 0; | ||
778 | |||
779 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
780 | |||
781 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
782 | result = -ENODEV; | ||
783 | goto out_no_free; | ||
784 | } | ||
785 | |||
786 | pdata = client->dev.platform_data; | ||
787 | if (!pdata) { | ||
788 | dev_err(&client->adapter->dev, | ||
789 | "Missing platform data for slave %s\n", devid->name); | ||
790 | result = -EFAULT; | ||
791 | goto out_no_free; | ||
792 | } | ||
793 | |||
794 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
795 | if (!private_data) { | ||
796 | result = -ENOMEM; | ||
797 | goto out_no_free; | ||
798 | } | ||
799 | |||
800 | i2c_set_clientdata(client, private_data); | ||
801 | private_data->client = client; | ||
802 | private_data->pdata = pdata; | ||
803 | |||
804 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
805 | lsm303dlx_a_get_slave_descr); | ||
806 | if (result) { | ||
807 | dev_err(&client->adapter->dev, | ||
808 | "Slave registration failed: %s, %d\n", | ||
809 | devid->name, result); | ||
810 | goto out_free_memory; | ||
811 | } | ||
812 | |||
813 | return result; | ||
814 | |||
815 | out_free_memory: | ||
816 | kfree(private_data); | ||
817 | out_no_free: | ||
818 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
819 | return result; | ||
820 | |||
821 | } | ||
822 | |||
823 | static int lsm303dlx_a_mod_remove(struct i2c_client *client) | ||
824 | { | ||
825 | struct lsm303dlx_a_mod_private_data *private_data = | ||
826 | i2c_get_clientdata(client); | ||
827 | |||
828 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
829 | |||
830 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
831 | lsm303dlx_a_get_slave_descr); | ||
832 | |||
833 | kfree(private_data); | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | static const struct i2c_device_id lsm303dlx_a_mod_id[] = { | ||
838 | { "lsm303dlx", ACCEL_ID_LSM303DLX }, | ||
839 | {} | ||
840 | }; | ||
841 | |||
842 | MODULE_DEVICE_TABLE(i2c, lsm303dlx_a_mod_id); | ||
843 | |||
844 | static struct i2c_driver lsm303dlx_a_mod_driver = { | ||
845 | .class = I2C_CLASS_HWMON, | ||
846 | .probe = lsm303dlx_a_mod_probe, | ||
847 | .remove = lsm303dlx_a_mod_remove, | ||
848 | .id_table = lsm303dlx_a_mod_id, | ||
849 | .driver = { | ||
850 | .owner = THIS_MODULE, | ||
851 | .name = "lsm303dlx_a_mod", | ||
852 | }, | ||
853 | .address_list = normal_i2c, | ||
854 | }; | ||
855 | |||
856 | static int __init lsm303dlx_a_mod_init(void) | ||
857 | { | ||
858 | int res = i2c_add_driver(&lsm303dlx_a_mod_driver); | ||
859 | pr_info("%s: Probe name %s\n", __func__, "lsm303dlx_a_mod"); | ||
860 | if (res) | ||
861 | pr_err("%s failed\n", __func__); | ||
862 | return res; | ||
863 | } | ||
864 | |||
865 | static void __exit lsm303dlx_a_mod_exit(void) | ||
866 | { | ||
867 | pr_info("%s\n", __func__); | ||
868 | i2c_del_driver(&lsm303dlx_a_mod_driver); | ||
869 | } | ||
870 | |||
871 | module_init(lsm303dlx_a_mod_init); | ||
872 | module_exit(lsm303dlx_a_mod_exit); | ||
873 | |||
874 | MODULE_AUTHOR("Invensense Corporation"); | ||
875 | MODULE_DESCRIPTION("Driver to integrate LSM303DLX_A sensor with the MPU"); | ||
876 | MODULE_LICENSE("GPL"); | ||
877 | MODULE_ALIAS("lsm303dlx_a_mod"); | ||
878 | |||
879 | /** | ||
880 | * @} | ||
881 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mma8450.c b/drivers/misc/inv_mpu/accel/mma8450.c new file mode 100644 index 00000000000..f698ee98bf5 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mma8450.c | |||
@@ -0,0 +1,804 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file mma8450.c | ||
26 | * @brief Accelerometer setup and handling methods for Freescale MMA8450. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-acc" | ||
46 | |||
47 | /* full scale setting - register & mask */ | ||
48 | #define ACCEL_MMA8450_XYZ_DATA_CFG (0x16) | ||
49 | |||
50 | #define ACCEL_MMA8450_CTRL_REG1 (0x38) | ||
51 | #define ACCEL_MMA8450_CTRL_REG2 (0x39) | ||
52 | #define ACCEL_MMA8450_CTRL_REG4 (0x3B) | ||
53 | #define ACCEL_MMA8450_CTRL_REG5 (0x3C) | ||
54 | |||
55 | #define ACCEL_MMA8450_CTRL_REG (0x38) | ||
56 | #define ACCEL_MMA8450_CTRL_MASK (0x03) | ||
57 | |||
58 | #define ACCEL_MMA8450_SLEEP_MASK (0x03) | ||
59 | |||
60 | /* -------------------------------------------------------------------------- */ | ||
61 | |||
62 | struct mma8450_config { | ||
63 | unsigned int odr; | ||
64 | unsigned int fsr; /** < full scale range mg */ | ||
65 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
66 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
67 | unsigned char reg_ths; | ||
68 | unsigned char reg_dur; | ||
69 | unsigned char ctrl_reg1; | ||
70 | unsigned char irq_type; | ||
71 | unsigned char mot_int1_cfg; | ||
72 | }; | ||
73 | |||
74 | struct mma8450_private_data { | ||
75 | struct mma8450_config suspend; | ||
76 | struct mma8450_config resume; | ||
77 | }; | ||
78 | |||
79 | |||
80 | /* -------------------------------------------------------------------------- */ | ||
81 | |||
82 | static int mma8450_set_ths(void *mlsl_handle, | ||
83 | struct ext_slave_platform_data *pdata, | ||
84 | struct mma8450_config *config, | ||
85 | int apply, | ||
86 | long ths) | ||
87 | { | ||
88 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
89 | } | ||
90 | |||
91 | static int mma8450_set_dur(void *mlsl_handle, | ||
92 | struct ext_slave_platform_data *pdata, | ||
93 | struct mma8450_config *config, | ||
94 | int apply, | ||
95 | long dur) | ||
96 | { | ||
97 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
102 | * Threshold and duration will not be used unless the type is MOT or | ||
103 | * NMOT. | ||
104 | * | ||
105 | * @param mlsl_handle | ||
106 | * the handle to the serial channel the device is connected to. | ||
107 | * @param pdata | ||
108 | * a pointer to the slave platform data. | ||
109 | * @param config | ||
110 | * configuration to apply to, suspend or resume | ||
111 | * @param apply | ||
112 | * whether to apply immediately or save the settings to be applied | ||
113 | * at the next resume. | ||
114 | * @param irq_type | ||
115 | * the type of IRQ. Valid values are | ||
116 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
117 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
118 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
119 | * | ||
120 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
121 | */ | ||
122 | static int mma8450_set_irq(void *mlsl_handle, | ||
123 | struct ext_slave_platform_data *pdata, | ||
124 | struct mma8450_config *config, | ||
125 | int apply, | ||
126 | long irq_type) | ||
127 | { | ||
128 | int result = INV_SUCCESS; | ||
129 | unsigned char reg1; | ||
130 | unsigned char reg2; | ||
131 | unsigned char reg3; | ||
132 | |||
133 | config->irq_type = (unsigned char)irq_type; | ||
134 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
135 | reg1 = 0x01; | ||
136 | reg2 = 0x01; | ||
137 | reg3 = 0x07; | ||
138 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) { | ||
139 | reg1 = 0x00; | ||
140 | reg2 = 0x00; | ||
141 | reg3 = 0x00; | ||
142 | } else { | ||
143 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
144 | } | ||
145 | |||
146 | if (apply) { | ||
147 | /* XYZ_DATA_CFG: event flag enabled on Z axis */ | ||
148 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
149 | ACCEL_MMA8450_XYZ_DATA_CFG, reg3); | ||
150 | if (result) { | ||
151 | LOG_RESULT_LOCATION(result); | ||
152 | return result; | ||
153 | } | ||
154 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
155 | ACCEL_MMA8450_CTRL_REG4, reg1); | ||
156 | if (result) { | ||
157 | LOG_RESULT_LOCATION(result); | ||
158 | return result; | ||
159 | } | ||
160 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
161 | ACCEL_MMA8450_CTRL_REG5, reg2); | ||
162 | if (result) { | ||
163 | LOG_RESULT_LOCATION(result); | ||
164 | return result; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | return result; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * @brief Set the output data rate for the particular configuration. | ||
173 | * | ||
174 | * @param mlsl_handle | ||
175 | * the handle to the serial channel the device is connected to. | ||
176 | * @param pdata | ||
177 | * a pointer to the slave platform data. | ||
178 | * @param config | ||
179 | * Config to modify with new ODR. | ||
180 | * @param apply | ||
181 | * whether to apply immediately or save the settings to be applied | ||
182 | * at the next resume. | ||
183 | * @param odr | ||
184 | * Output data rate in units of 1/1000Hz (mHz). | ||
185 | * | ||
186 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
187 | */ | ||
188 | static int mma8450_set_odr(void *mlsl_handle, | ||
189 | struct ext_slave_platform_data *pdata, | ||
190 | struct mma8450_config *config, | ||
191 | int apply, | ||
192 | long odr) | ||
193 | { | ||
194 | unsigned char bits; | ||
195 | int result = INV_SUCCESS; | ||
196 | |||
197 | if (odr > 200000) { | ||
198 | config->odr = 400000; | ||
199 | bits = 0x00; | ||
200 | } else if (odr > 100000) { | ||
201 | config->odr = 200000; | ||
202 | bits = 0x04; | ||
203 | } else if (odr > 50000) { | ||
204 | config->odr = 100000; | ||
205 | bits = 0x08; | ||
206 | } else if (odr > 25000) { | ||
207 | config->odr = 50000; | ||
208 | bits = 0x0C; | ||
209 | } else if (odr > 12500) { | ||
210 | config->odr = 25000; | ||
211 | bits = 0x40; /* Sleep -> Auto wake mode */ | ||
212 | } else if (odr > 1563) { | ||
213 | config->odr = 12500; | ||
214 | bits = 0x10; | ||
215 | } else if (odr > 0) { | ||
216 | config->odr = 1563; | ||
217 | bits = 0x14; | ||
218 | } else { | ||
219 | config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */ | ||
220 | config->odr = 0; | ||
221 | bits = 0; | ||
222 | } | ||
223 | |||
224 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x3); | ||
225 | if (apply) { | ||
226 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
227 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
228 | if (result) { | ||
229 | LOG_RESULT_LOCATION(result); | ||
230 | return result; | ||
231 | } | ||
232 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
233 | ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1); | ||
234 | if (result) { | ||
235 | LOG_RESULT_LOCATION(result); | ||
236 | return result; | ||
237 | } | ||
238 | MPL_LOGV("ODR: %d mHz, 0x%02x\n", | ||
239 | config->odr, (int)config->ctrl_reg1); | ||
240 | } | ||
241 | return result; | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * @brief Set the full scale range of the accels | ||
246 | * | ||
247 | * @param mlsl_handle | ||
248 | * the handle to the serial channel the device is connected to. | ||
249 | * @param pdata | ||
250 | * a pointer to the slave platform data. | ||
251 | * @param config | ||
252 | * pointer to configuration. | ||
253 | * @param apply | ||
254 | * whether to apply immediately or save the settings to be applied | ||
255 | * at the next resume. | ||
256 | * @param fsr | ||
257 | * requested full scale range. | ||
258 | * | ||
259 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
260 | */ | ||
261 | static int mma8450_set_fsr(void *mlsl_handle, | ||
262 | struct ext_slave_platform_data *pdata, | ||
263 | struct mma8450_config *config, | ||
264 | int apply, | ||
265 | long fsr) | ||
266 | { | ||
267 | unsigned char bits; | ||
268 | int result = INV_SUCCESS; | ||
269 | |||
270 | if (fsr <= 2000) { | ||
271 | bits = 0x01; | ||
272 | config->fsr = 2000; | ||
273 | } else if (fsr <= 4000) { | ||
274 | bits = 0x02; | ||
275 | config->fsr = 4000; | ||
276 | } else { | ||
277 | bits = 0x03; | ||
278 | config->fsr = 8000; | ||
279 | } | ||
280 | |||
281 | config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xFC); | ||
282 | if (apply) { | ||
283 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
284 | ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1); | ||
285 | if (result) { | ||
286 | LOG_RESULT_LOCATION(result); | ||
287 | return result; | ||
288 | } | ||
289 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
290 | } | ||
291 | return result; | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * @brief suspends the device to put it in its lowest power mode. | ||
296 | * | ||
297 | * @param mlsl_handle | ||
298 | * the handle to the serial channel the device is connected to. | ||
299 | * @param slave | ||
300 | * a pointer to the slave descriptor data structure. | ||
301 | * @param pdata | ||
302 | * a pointer to the slave platform data. | ||
303 | * | ||
304 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
305 | */ | ||
306 | static int mma8450_suspend(void *mlsl_handle, | ||
307 | struct ext_slave_descr *slave, | ||
308 | struct ext_slave_platform_data *pdata) | ||
309 | { | ||
310 | int result; | ||
311 | struct mma8450_private_data *private_data = pdata->private_data; | ||
312 | |||
313 | if (private_data->suspend.fsr == 4000) | ||
314 | slave->range.mantissa = 4; | ||
315 | else if (private_data->suspend.fsr == 8000) | ||
316 | slave->range.mantissa = 8; | ||
317 | else | ||
318 | slave->range.mantissa = 2; | ||
319 | slave->range.fraction = 0; | ||
320 | |||
321 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
322 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
323 | if (result) { | ||
324 | LOG_RESULT_LOCATION(result); | ||
325 | return result; | ||
326 | } | ||
327 | if (private_data->suspend.ctrl_reg1) { | ||
328 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
329 | ACCEL_MMA8450_CTRL_REG1, | ||
330 | private_data->suspend.ctrl_reg1); | ||
331 | if (result) { | ||
332 | LOG_RESULT_LOCATION(result); | ||
333 | return result; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | result = mma8450_set_irq(mlsl_handle, pdata, | ||
338 | &private_data->suspend, | ||
339 | true, private_data->suspend.irq_type); | ||
340 | if (result) { | ||
341 | LOG_RESULT_LOCATION(result); | ||
342 | return result; | ||
343 | } | ||
344 | return result; | ||
345 | } | ||
346 | |||
347 | /** | ||
348 | * @brief resume the device in the proper power state given the configuration | ||
349 | * chosen. | ||
350 | * | ||
351 | * @param mlsl_handle | ||
352 | * the handle to the serial channel the device is connected to. | ||
353 | * @param slave | ||
354 | * a pointer to the slave descriptor data structure. | ||
355 | * @param pdata | ||
356 | * a pointer to the slave platform data. | ||
357 | * | ||
358 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
359 | */ | ||
360 | static int mma8450_resume(void *mlsl_handle, | ||
361 | struct ext_slave_descr *slave, | ||
362 | struct ext_slave_platform_data *pdata) | ||
363 | { | ||
364 | int result = INV_SUCCESS; | ||
365 | struct mma8450_private_data *private_data = pdata->private_data; | ||
366 | |||
367 | /* Full Scale */ | ||
368 | if (private_data->resume.fsr == 4000) | ||
369 | slave->range.mantissa = 4; | ||
370 | else if (private_data->resume.fsr == 8000) | ||
371 | slave->range.mantissa = 8; | ||
372 | else | ||
373 | slave->range.mantissa = 2; | ||
374 | slave->range.fraction = 0; | ||
375 | |||
376 | if (result) { | ||
377 | LOG_RESULT_LOCATION(result); | ||
378 | return result; | ||
379 | } | ||
380 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
381 | ACCEL_MMA8450_CTRL_REG1, 0); | ||
382 | if (result) { | ||
383 | LOG_RESULT_LOCATION(result); | ||
384 | return result; | ||
385 | } | ||
386 | if (private_data->resume.ctrl_reg1) { | ||
387 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
388 | ACCEL_MMA8450_CTRL_REG1, | ||
389 | private_data->resume.ctrl_reg1); | ||
390 | if (result) { | ||
391 | LOG_RESULT_LOCATION(result); | ||
392 | return result; | ||
393 | } | ||
394 | } | ||
395 | result = mma8450_set_irq(mlsl_handle, pdata, | ||
396 | &private_data->resume, | ||
397 | true, private_data->resume.irq_type); | ||
398 | if (result) { | ||
399 | LOG_RESULT_LOCATION(result); | ||
400 | return result; | ||
401 | } | ||
402 | |||
403 | return result; | ||
404 | } | ||
405 | |||
406 | /** | ||
407 | * @brief read the sensor data from the device. | ||
408 | * | ||
409 | * @param mlsl_handle | ||
410 | * the handle to the serial channel the device is connected to. | ||
411 | * @param slave | ||
412 | * a pointer to the slave descriptor data structure. | ||
413 | * @param pdata | ||
414 | * a pointer to the slave platform data. | ||
415 | * @param data | ||
416 | * a buffer to store the data read. | ||
417 | * | ||
418 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
419 | */ | ||
420 | static int mma8450_read(void *mlsl_handle, | ||
421 | struct ext_slave_descr *slave, | ||
422 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
423 | { | ||
424 | int result; | ||
425 | unsigned char local_data[4]; /* Status register + 3 bytes data */ | ||
426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
427 | 0x00, sizeof(local_data), local_data); | ||
428 | if (result) { | ||
429 | LOG_RESULT_LOCATION(result); | ||
430 | return result; | ||
431 | } | ||
432 | memcpy(data, &local_data[1], (slave->read_len) - 1); | ||
433 | |||
434 | MPL_LOGV("Data Not Ready: %02x %02x %02x %02x\n", | ||
435 | local_data[0], local_data[1], | ||
436 | local_data[2], local_data[3]); | ||
437 | |||
438 | return result; | ||
439 | } | ||
440 | |||
441 | /** | ||
442 | * @brief one-time device driver initialization function. | ||
443 | * If the driver is built as a kernel module, this function will be | ||
444 | * called when the module is loaded in the kernel. | ||
445 | * If the driver is built-in in the kernel, this function will be | ||
446 | * called at boot time. | ||
447 | * | ||
448 | * @param mlsl_handle | ||
449 | * the handle to the serial channel the device is connected to. | ||
450 | * @param slave | ||
451 | * a pointer to the slave descriptor data structure. | ||
452 | * @param pdata | ||
453 | * a pointer to the slave platform data. | ||
454 | * | ||
455 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
456 | */ | ||
457 | static int mma8450_init(void *mlsl_handle, | ||
458 | struct ext_slave_descr *slave, | ||
459 | struct ext_slave_platform_data *pdata) | ||
460 | { | ||
461 | struct mma8450_private_data *private_data; | ||
462 | private_data = (struct mma8450_private_data *) | ||
463 | kzalloc(sizeof(struct mma8450_private_data), GFP_KERNEL); | ||
464 | |||
465 | if (!private_data) | ||
466 | return INV_ERROR_MEMORY_EXAUSTED; | ||
467 | |||
468 | pdata->private_data = private_data; | ||
469 | |||
470 | mma8450_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
471 | false, 0); | ||
472 | mma8450_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
473 | false, 200000); | ||
474 | mma8450_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
475 | false, 2000); | ||
476 | mma8450_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
477 | false, 2000); | ||
478 | mma8450_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
479 | false, | ||
480 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
481 | mma8450_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
482 | false, | ||
483 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
484 | return INV_SUCCESS; | ||
485 | } | ||
486 | |||
487 | /** | ||
488 | * @brief one-time device driver exit function. | ||
489 | * If the driver is built as a kernel module, this function will be | ||
490 | * called when the module is removed from the kernel. | ||
491 | * | ||
492 | * @param mlsl_handle | ||
493 | * the handle to the serial channel the device is connected to. | ||
494 | * @param slave | ||
495 | * a pointer to the slave descriptor data structure. | ||
496 | * @param pdata | ||
497 | * a pointer to the slave platform data. | ||
498 | * | ||
499 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
500 | */ | ||
501 | static int mma8450_exit(void *mlsl_handle, | ||
502 | struct ext_slave_descr *slave, | ||
503 | struct ext_slave_platform_data *pdata) | ||
504 | { | ||
505 | kfree(pdata->private_data); | ||
506 | return INV_SUCCESS; | ||
507 | } | ||
508 | |||
509 | /** | ||
510 | * @brief device configuration facility. | ||
511 | * | ||
512 | * @param mlsl_handle | ||
513 | * the handle to the serial channel the device is connected to. | ||
514 | * @param slave | ||
515 | * a pointer to the slave descriptor data structure. | ||
516 | * @param pdata | ||
517 | * a pointer to the slave platform data. | ||
518 | * @param data | ||
519 | * a pointer to the configuration data structure. | ||
520 | * | ||
521 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
522 | */ | ||
523 | static int mma8450_config(void *mlsl_handle, | ||
524 | struct ext_slave_descr *slave, | ||
525 | struct ext_slave_platform_data *pdata, | ||
526 | struct ext_slave_config *data) | ||
527 | { | ||
528 | struct mma8450_private_data *private_data = pdata->private_data; | ||
529 | if (!data->data) | ||
530 | return INV_ERROR_INVALID_PARAMETER; | ||
531 | |||
532 | switch (data->key) { | ||
533 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
534 | return mma8450_set_odr(mlsl_handle, pdata, | ||
535 | &private_data->suspend, | ||
536 | data->apply, | ||
537 | *((long *)data->data)); | ||
538 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
539 | return mma8450_set_odr(mlsl_handle, pdata, | ||
540 | &private_data->resume, | ||
541 | data->apply, | ||
542 | *((long *)data->data)); | ||
543 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
544 | return mma8450_set_fsr(mlsl_handle, pdata, | ||
545 | &private_data->suspend, | ||
546 | data->apply, | ||
547 | *((long *)data->data)); | ||
548 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
549 | return mma8450_set_fsr(mlsl_handle, pdata, | ||
550 | &private_data->resume, | ||
551 | data->apply, | ||
552 | *((long *)data->data)); | ||
553 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
554 | return mma8450_set_ths(mlsl_handle, pdata, | ||
555 | &private_data->suspend, | ||
556 | data->apply, | ||
557 | *((long *)data->data)); | ||
558 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
559 | return mma8450_set_ths(mlsl_handle, pdata, | ||
560 | &private_data->resume, | ||
561 | data->apply, | ||
562 | *((long *)data->data)); | ||
563 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
564 | return mma8450_set_dur(mlsl_handle, pdata, | ||
565 | &private_data->suspend, | ||
566 | data->apply, | ||
567 | *((long *)data->data)); | ||
568 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
569 | return mma8450_set_dur(mlsl_handle, pdata, | ||
570 | &private_data->resume, | ||
571 | data->apply, | ||
572 | *((long *)data->data)); | ||
573 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
574 | return mma8450_set_irq(mlsl_handle, pdata, | ||
575 | &private_data->suspend, | ||
576 | data->apply, | ||
577 | *((long *)data->data)); | ||
578 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
579 | return mma8450_set_irq(mlsl_handle, pdata, | ||
580 | &private_data->resume, | ||
581 | data->apply, | ||
582 | *((long *)data->data)); | ||
583 | default: | ||
584 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
585 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
586 | }; | ||
587 | |||
588 | return INV_SUCCESS; | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * @brief facility to retrieve the device configuration. | ||
593 | * | ||
594 | * @param mlsl_handle | ||
595 | * the handle to the serial channel the device is connected to. | ||
596 | * @param slave | ||
597 | * a pointer to the slave descriptor data structure. | ||
598 | * @param pdata | ||
599 | * a pointer to the slave platform data. | ||
600 | * @param data | ||
601 | * a pointer to store the returned configuration data structure. | ||
602 | * | ||
603 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
604 | */ | ||
605 | static int mma8450_get_config(void *mlsl_handle, | ||
606 | struct ext_slave_descr *slave, | ||
607 | struct ext_slave_platform_data *pdata, | ||
608 | struct ext_slave_config *data) | ||
609 | { | ||
610 | struct mma8450_private_data *private_data = pdata->private_data; | ||
611 | if (!data->data) | ||
612 | return INV_ERROR_INVALID_PARAMETER; | ||
613 | |||
614 | switch (data->key) { | ||
615 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
616 | (*(unsigned long *)data->data) = | ||
617 | (unsigned long) private_data->suspend.odr; | ||
618 | break; | ||
619 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
620 | (*(unsigned long *)data->data) = | ||
621 | (unsigned long) private_data->resume.odr; | ||
622 | break; | ||
623 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
624 | (*(unsigned long *)data->data) = | ||
625 | (unsigned long) private_data->suspend.fsr; | ||
626 | break; | ||
627 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
628 | (*(unsigned long *)data->data) = | ||
629 | (unsigned long) private_data->resume.fsr; | ||
630 | break; | ||
631 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
632 | (*(unsigned long *)data->data) = | ||
633 | (unsigned long) private_data->suspend.ths; | ||
634 | break; | ||
635 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
636 | (*(unsigned long *)data->data) = | ||
637 | (unsigned long) private_data->resume.ths; | ||
638 | break; | ||
639 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
640 | (*(unsigned long *)data->data) = | ||
641 | (unsigned long) private_data->suspend.dur; | ||
642 | break; | ||
643 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
644 | (*(unsigned long *)data->data) = | ||
645 | (unsigned long) private_data->resume.dur; | ||
646 | break; | ||
647 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
648 | (*(unsigned long *)data->data) = | ||
649 | (unsigned long) private_data->suspend.irq_type; | ||
650 | break; | ||
651 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
652 | (*(unsigned long *)data->data) = | ||
653 | (unsigned long) private_data->resume.irq_type; | ||
654 | break; | ||
655 | default: | ||
656 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
657 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
658 | }; | ||
659 | |||
660 | return INV_SUCCESS; | ||
661 | } | ||
662 | |||
663 | static struct ext_slave_descr mma8450_descr = { | ||
664 | .init = mma8450_init, | ||
665 | .exit = mma8450_exit, | ||
666 | .suspend = mma8450_suspend, | ||
667 | .resume = mma8450_resume, | ||
668 | .read = mma8450_read, | ||
669 | .config = mma8450_config, | ||
670 | .get_config = mma8450_get_config, | ||
671 | .name = "mma8450", | ||
672 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
673 | .id = ACCEL_ID_MMA8450, | ||
674 | .read_reg = 0x00, | ||
675 | .read_len = 4, | ||
676 | .endian = EXT_SLAVE_FS8_BIG_ENDIAN, | ||
677 | .range = {2, 0}, | ||
678 | .trigger = NULL, | ||
679 | }; | ||
680 | |||
681 | static | ||
682 | struct ext_slave_descr *mma8450_get_slave_descr(void) | ||
683 | { | ||
684 | return &mma8450_descr; | ||
685 | } | ||
686 | |||
687 | /* -------------------------------------------------------------------------- */ | ||
688 | struct mma8450_mod_private_data { | ||
689 | struct i2c_client *client; | ||
690 | struct ext_slave_platform_data *pdata; | ||
691 | }; | ||
692 | |||
693 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
694 | |||
695 | static int mma8450_mod_probe(struct i2c_client *client, | ||
696 | const struct i2c_device_id *devid) | ||
697 | { | ||
698 | struct ext_slave_platform_data *pdata; | ||
699 | struct mma8450_mod_private_data *private_data; | ||
700 | int result = 0; | ||
701 | |||
702 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
703 | |||
704 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
705 | result = -ENODEV; | ||
706 | goto out_no_free; | ||
707 | } | ||
708 | |||
709 | pdata = client->dev.platform_data; | ||
710 | if (!pdata) { | ||
711 | dev_err(&client->adapter->dev, | ||
712 | "Missing platform data for slave %s\n", devid->name); | ||
713 | result = -EFAULT; | ||
714 | goto out_no_free; | ||
715 | } | ||
716 | |||
717 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
718 | if (!private_data) { | ||
719 | result = -ENOMEM; | ||
720 | goto out_no_free; | ||
721 | } | ||
722 | |||
723 | i2c_set_clientdata(client, private_data); | ||
724 | private_data->client = client; | ||
725 | private_data->pdata = pdata; | ||
726 | |||
727 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
728 | mma8450_get_slave_descr); | ||
729 | if (result) { | ||
730 | dev_err(&client->adapter->dev, | ||
731 | "Slave registration failed: %s, %d\n", | ||
732 | devid->name, result); | ||
733 | goto out_free_memory; | ||
734 | } | ||
735 | |||
736 | return result; | ||
737 | |||
738 | out_free_memory: | ||
739 | kfree(private_data); | ||
740 | out_no_free: | ||
741 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
742 | return result; | ||
743 | |||
744 | } | ||
745 | |||
746 | static int mma8450_mod_remove(struct i2c_client *client) | ||
747 | { | ||
748 | struct mma8450_mod_private_data *private_data = | ||
749 | i2c_get_clientdata(client); | ||
750 | |||
751 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
752 | |||
753 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
754 | mma8450_get_slave_descr); | ||
755 | |||
756 | kfree(private_data); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | static const struct i2c_device_id mma8450_mod_id[] = { | ||
761 | { "mma8450", ACCEL_ID_MMA8450 }, | ||
762 | {} | ||
763 | }; | ||
764 | |||
765 | MODULE_DEVICE_TABLE(i2c, mma8450_mod_id); | ||
766 | |||
767 | static struct i2c_driver mma8450_mod_driver = { | ||
768 | .class = I2C_CLASS_HWMON, | ||
769 | .probe = mma8450_mod_probe, | ||
770 | .remove = mma8450_mod_remove, | ||
771 | .id_table = mma8450_mod_id, | ||
772 | .driver = { | ||
773 | .owner = THIS_MODULE, | ||
774 | .name = "mma8450_mod", | ||
775 | }, | ||
776 | .address_list = normal_i2c, | ||
777 | }; | ||
778 | |||
779 | static int __init mma8450_mod_init(void) | ||
780 | { | ||
781 | int res = i2c_add_driver(&mma8450_mod_driver); | ||
782 | pr_info("%s: Probe name %s\n", __func__, "mma8450_mod"); | ||
783 | if (res) | ||
784 | pr_err("%s failed\n", __func__); | ||
785 | return res; | ||
786 | } | ||
787 | |||
788 | static void __exit mma8450_mod_exit(void) | ||
789 | { | ||
790 | pr_info("%s\n", __func__); | ||
791 | i2c_del_driver(&mma8450_mod_driver); | ||
792 | } | ||
793 | |||
794 | module_init(mma8450_mod_init); | ||
795 | module_exit(mma8450_mod_exit); | ||
796 | |||
797 | MODULE_AUTHOR("Invensense Corporation"); | ||
798 | MODULE_DESCRIPTION("Driver to integrate MMA8450 sensor with the MPU"); | ||
799 | MODULE_LICENSE("GPL"); | ||
800 | MODULE_ALIAS("mma8450_mod"); | ||
801 | |||
802 | /** | ||
803 | * @} | ||
804 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mma845x.c b/drivers/misc/inv_mpu/accel/mma845x.c new file mode 100644 index 00000000000..5f62b22388b --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mma845x.c | |||
@@ -0,0 +1,713 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file mma845x.c | ||
26 | * @brief Accelerometer setup and handling methods for Freescale MMA845X | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-acc" | ||
46 | |||
47 | #define ACCEL_MMA845X_XYZ_DATA_CFG (0x0E) | ||
48 | #define ACCEL_MMA845X_CTRL_REG1 (0x2A) | ||
49 | #define ACCEL_MMA845X_CTRL_REG4 (0x2D) | ||
50 | #define ACCEL_MMA845X_CTRL_REG5 (0x2E) | ||
51 | |||
52 | #define ACCEL_MMA845X_SLEEP_MASK (0x01) | ||
53 | |||
54 | /* full scale setting - register & mask */ | ||
55 | #define ACCEL_MMA845X_CFG_REG (0x0E) | ||
56 | #define ACCEL_MMA845X_CTRL_MASK (0x03) | ||
57 | |||
58 | /* -------------------------------------------------------------------------- */ | ||
59 | |||
60 | struct mma845x_config { | ||
61 | unsigned int odr; | ||
62 | unsigned int fsr; /** < full scale range mg */ | ||
63 | unsigned int ths; /** < Motion no-motion thseshold mg */ | ||
64 | unsigned int dur; /** < Motion no-motion duration ms */ | ||
65 | unsigned char reg_ths; | ||
66 | unsigned char reg_dur; | ||
67 | unsigned char ctrl_reg1; | ||
68 | unsigned char irq_type; | ||
69 | unsigned char mot_int1_cfg; | ||
70 | }; | ||
71 | |||
72 | struct mma845x_private_data { | ||
73 | struct mma845x_config suspend; | ||
74 | struct mma845x_config resume; | ||
75 | }; | ||
76 | |||
77 | /* -------------------------------------------------------------------------- */ | ||
78 | |||
79 | static int mma845x_set_ths(void *mlsl_handle, | ||
80 | struct ext_slave_platform_data *pdata, | ||
81 | struct mma845x_config *config, | ||
82 | int apply, | ||
83 | long ths) | ||
84 | { | ||
85 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
86 | } | ||
87 | |||
88 | static int mma845x_set_dur(void *mlsl_handle, | ||
89 | struct ext_slave_platform_data *pdata, | ||
90 | struct mma845x_config *config, | ||
91 | int apply, | ||
92 | long dur) | ||
93 | { | ||
94 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * @brief Sets the IRQ to fire when one of the IRQ events occur. | ||
99 | * Threshold and duration will not be used unless the type is MOT or | ||
100 | * NMOT. | ||
101 | * | ||
102 | * @param mlsl_handle | ||
103 | * the handle to the serial channel the device is connected to. | ||
104 | * @param pdata | ||
105 | * a pointer to the slave platform data. | ||
106 | * @param config | ||
107 | * configuration to apply to, suspend or resume | ||
108 | * @param apply | ||
109 | * whether to apply immediately or save the settings to be applied | ||
110 | * at the next resume. | ||
111 | * @param irq_type | ||
112 | * the type of IRQ. Valid values are | ||
113 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
114 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
115 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
116 | * | ||
117 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
118 | */ | ||
119 | static int mma845x_set_irq(void *mlsl_handle, | ||
120 | struct ext_slave_platform_data *pdata, | ||
121 | struct mma845x_config *config, | ||
122 | int apply, | ||
123 | long irq_type) | ||
124 | { | ||
125 | int result = INV_SUCCESS; | ||
126 | unsigned char reg1; | ||
127 | unsigned char reg2; | ||
128 | |||
129 | config->irq_type = (unsigned char)irq_type; | ||
130 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
131 | reg1 = 0x01; | ||
132 | reg2 = 0x01; | ||
133 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) { | ||
134 | reg1 = 0x00; | ||
135 | reg2 = 0x00; | ||
136 | } else { | ||
137 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
138 | } | ||
139 | |||
140 | if (apply) { | ||
141 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
142 | ACCEL_MMA845X_CTRL_REG4, reg1); | ||
143 | if (result) { | ||
144 | LOG_RESULT_LOCATION(result); | ||
145 | return result; | ||
146 | } | ||
147 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
148 | ACCEL_MMA845X_CTRL_REG5, reg2); | ||
149 | if (result) { | ||
150 | LOG_RESULT_LOCATION(result); | ||
151 | return result; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return result; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * @brief Set the output data rate for the particular configuration. | ||
160 | * | ||
161 | * @param mlsl_handle | ||
162 | * the handle to the serial channel the device is connected to. | ||
163 | * @param pdata | ||
164 | * a pointer to the slave platform data. | ||
165 | * @param config | ||
166 | * Config to modify with new ODR. | ||
167 | * @param apply | ||
168 | * whether to apply immediately or save the settings to be applied | ||
169 | * at the next resume. | ||
170 | * @param odr | ||
171 | * Output data rate in units of 1/1000Hz (mHz). | ||
172 | * | ||
173 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
174 | */ | ||
175 | static int mma845x_set_odr(void *mlsl_handle, | ||
176 | struct ext_slave_platform_data *pdata, | ||
177 | struct mma845x_config *config, | ||
178 | int apply, | ||
179 | long odr) | ||
180 | { | ||
181 | unsigned char bits; | ||
182 | int result = INV_SUCCESS; | ||
183 | |||
184 | if (odr > 400000) { | ||
185 | config->odr = 800000; | ||
186 | bits = 0x01; | ||
187 | } else if (odr > 200000) { | ||
188 | config->odr = 400000; | ||
189 | bits = 0x09; | ||
190 | } else if (odr > 100000) { | ||
191 | config->odr = 200000; | ||
192 | bits = 0x11; | ||
193 | } else if (odr > 50000) { | ||
194 | config->odr = 100000; | ||
195 | bits = 0x19; | ||
196 | } else if (odr > 12500) { | ||
197 | config->odr = 50000; | ||
198 | bits = 0x21; | ||
199 | } else if (odr > 6250) { | ||
200 | config->odr = 12500; | ||
201 | bits = 0x29; | ||
202 | } else if (odr > 1560) { | ||
203 | config->odr = 6250; | ||
204 | bits = 0x31; | ||
205 | } else if (odr > 0) { | ||
206 | config->odr = 1560; | ||
207 | bits = 0x39; | ||
208 | } else { | ||
209 | config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */ | ||
210 | config->odr = 0; | ||
211 | bits = 0; | ||
212 | } | ||
213 | |||
214 | config->ctrl_reg1 = bits; | ||
215 | if (apply) { | ||
216 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
217 | ACCEL_MMA845X_CTRL_REG1, | ||
218 | config->ctrl_reg1); | ||
219 | if (result) { | ||
220 | LOG_RESULT_LOCATION(result); | ||
221 | return result; | ||
222 | } | ||
223 | MPL_LOGV("ODR: %d mHz, 0x%02x\n", config->odr, | ||
224 | (int)config->ctrl_reg1); | ||
225 | } | ||
226 | return result; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * @brief Set the full scale range of the accels | ||
231 | * | ||
232 | * @param mlsl_handle | ||
233 | * the handle to the serial channel the device is connected to. | ||
234 | * @param pdata | ||
235 | * a pointer to the slave platform data. | ||
236 | * @param config | ||
237 | * pointer to configuration. | ||
238 | * @param apply | ||
239 | * whether to apply immediately or save the settings to be applied | ||
240 | * at the next resume. | ||
241 | * @param fsr | ||
242 | * requested full scale range. | ||
243 | * | ||
244 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
245 | */ | ||
246 | static int mma845x_set_fsr(void *mlsl_handle, | ||
247 | struct ext_slave_platform_data *pdata, | ||
248 | struct mma845x_config *config, | ||
249 | int apply, | ||
250 | long fsr) | ||
251 | { | ||
252 | unsigned char bits; | ||
253 | int result = INV_SUCCESS; | ||
254 | |||
255 | if (fsr <= 2000) { | ||
256 | bits = 0x00; | ||
257 | config->fsr = 2000; | ||
258 | } else if (fsr <= 4000) { | ||
259 | bits = 0x01; | ||
260 | config->fsr = 4000; | ||
261 | } else { | ||
262 | bits = 0x02; | ||
263 | config->fsr = 8000; | ||
264 | } | ||
265 | |||
266 | if (apply) { | ||
267 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
268 | ACCEL_MMA845X_XYZ_DATA_CFG, | ||
269 | bits); | ||
270 | if (result) { | ||
271 | LOG_RESULT_LOCATION(result); | ||
272 | return result; | ||
273 | } | ||
274 | MPL_LOGV("FSR: %d mg\n", config->fsr); | ||
275 | } | ||
276 | return result; | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * @brief suspends the device to put it in its lowest power mode. | ||
281 | * | ||
282 | * @param mlsl_handle | ||
283 | * the handle to the serial channel the device is connected to. | ||
284 | * @param slave | ||
285 | * a pointer to the slave descriptor data structure. | ||
286 | * @param pdata | ||
287 | * a pointer to the slave platform data. | ||
288 | * | ||
289 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
290 | */ | ||
291 | static int mma845x_suspend(void *mlsl_handle, | ||
292 | struct ext_slave_descr *slave, | ||
293 | struct ext_slave_platform_data *pdata) | ||
294 | { | ||
295 | int result; | ||
296 | struct mma845x_private_data *private_data = pdata->private_data; | ||
297 | |||
298 | /* Full Scale */ | ||
299 | if (private_data->suspend.fsr == 4000) | ||
300 | slave->range.mantissa = 4; | ||
301 | else if (private_data->suspend.fsr == 8000) | ||
302 | slave->range.mantissa = 8; | ||
303 | else | ||
304 | slave->range.mantissa = 2; | ||
305 | |||
306 | slave->range.fraction = 0; | ||
307 | |||
308 | result = mma845x_set_fsr(mlsl_handle, pdata, | ||
309 | &private_data->suspend, | ||
310 | true, private_data->suspend.fsr); | ||
311 | if (result) { | ||
312 | LOG_RESULT_LOCATION(result); | ||
313 | return result; | ||
314 | } | ||
315 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
316 | ACCEL_MMA845X_CTRL_REG1, | ||
317 | private_data->suspend.ctrl_reg1); | ||
318 | if (result) { | ||
319 | LOG_RESULT_LOCATION(result); | ||
320 | return result; | ||
321 | } | ||
322 | |||
323 | return result; | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * @brief resume the device in the proper power state given the configuration | ||
328 | * chosen. | ||
329 | * | ||
330 | * @param mlsl_handle | ||
331 | * the handle to the serial channel the device is connected to. | ||
332 | * @param slave | ||
333 | * a pointer to the slave descriptor data structure. | ||
334 | * @param pdata | ||
335 | * a pointer to the slave platform data. | ||
336 | * | ||
337 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
338 | */ | ||
339 | static int mma845x_resume(void *mlsl_handle, | ||
340 | struct ext_slave_descr *slave, | ||
341 | struct ext_slave_platform_data *pdata) | ||
342 | { | ||
343 | int result = INV_SUCCESS; | ||
344 | struct mma845x_private_data *private_data = pdata->private_data; | ||
345 | |||
346 | /* Full Scale */ | ||
347 | if (private_data->resume.fsr == 4000) | ||
348 | slave->range.mantissa = 4; | ||
349 | else if (private_data->resume.fsr == 8000) | ||
350 | slave->range.mantissa = 8; | ||
351 | else | ||
352 | slave->range.mantissa = 2; | ||
353 | |||
354 | slave->range.fraction = 0; | ||
355 | |||
356 | result = mma845x_set_fsr(mlsl_handle, pdata, | ||
357 | &private_data->resume, | ||
358 | true, private_data->resume.fsr); | ||
359 | if (result) { | ||
360 | LOG_RESULT_LOCATION(result); | ||
361 | return result; | ||
362 | } | ||
363 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
364 | ACCEL_MMA845X_CTRL_REG1, | ||
365 | private_data->resume.ctrl_reg1); | ||
366 | if (result) { | ||
367 | LOG_RESULT_LOCATION(result); | ||
368 | return result; | ||
369 | } | ||
370 | |||
371 | return result; | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * @brief read the sensor data from the device. | ||
376 | * | ||
377 | * @param mlsl_handle | ||
378 | * the handle to the serial channel the device is connected to. | ||
379 | * @param slave | ||
380 | * a pointer to the slave descriptor data structure. | ||
381 | * @param pdata | ||
382 | * a pointer to the slave platform data. | ||
383 | * @param data | ||
384 | * a buffer to store the data read. | ||
385 | * | ||
386 | * @return INV_SUCCESS if successful or a non-zero error code. | ||
387 | */ | ||
388 | static int mma845x_read(void *mlsl_handle, | ||
389 | struct ext_slave_descr *slave, | ||
390 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
391 | { | ||
392 | int result; | ||
393 | unsigned char local_data[7]; /* Status register + 6 bytes data */ | ||
394 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
395 | slave->read_reg, sizeof(local_data), | ||
396 | local_data); | ||
397 | if (result) { | ||
398 | LOG_RESULT_LOCATION(result); | ||
399 | return result; | ||
400 | } | ||
401 | memcpy(data, &local_data[1], slave->read_len); | ||
402 | return result; | ||
403 | } | ||
404 | |||
405 | static int mma845x_init(void *mlsl_handle, | ||
406 | struct ext_slave_descr *slave, | ||
407 | struct ext_slave_platform_data *pdata) | ||
408 | { | ||
409 | long range; | ||
410 | struct mma845x_private_data *private_data; | ||
411 | private_data = (struct mma845x_private_data *) | ||
412 | kzalloc(sizeof(struct mma845x_private_data), GFP_KERNEL); | ||
413 | |||
414 | if (!private_data) | ||
415 | return INV_ERROR_MEMORY_EXAUSTED; | ||
416 | |||
417 | pdata->private_data = private_data; | ||
418 | |||
419 | mma845x_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
420 | false, 0); | ||
421 | mma845x_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
422 | false, 200000); | ||
423 | |||
424 | range = range_fixedpoint_to_long_mg(slave->range); | ||
425 | mma845x_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
426 | false, range); | ||
427 | mma845x_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
428 | false, range); | ||
429 | |||
430 | mma845x_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
431 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
432 | mma845x_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
433 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
434 | return INV_SUCCESS; | ||
435 | } | ||
436 | |||
437 | static int mma845x_exit(void *mlsl_handle, | ||
438 | struct ext_slave_descr *slave, | ||
439 | struct ext_slave_platform_data *pdata) | ||
440 | { | ||
441 | kfree(pdata->private_data); | ||
442 | return INV_SUCCESS; | ||
443 | } | ||
444 | |||
445 | static int mma845x_config(void *mlsl_handle, | ||
446 | struct ext_slave_descr *slave, | ||
447 | struct ext_slave_platform_data *pdata, | ||
448 | struct ext_slave_config *data) | ||
449 | { | ||
450 | struct mma845x_private_data *private_data = pdata->private_data; | ||
451 | if (!data->data) | ||
452 | return INV_ERROR_INVALID_PARAMETER; | ||
453 | |||
454 | switch (data->key) { | ||
455 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
456 | return mma845x_set_odr(mlsl_handle, pdata, | ||
457 | &private_data->suspend, | ||
458 | data->apply, | ||
459 | *((long *)data->data)); | ||
460 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
461 | return mma845x_set_odr(mlsl_handle, pdata, | ||
462 | &private_data->resume, | ||
463 | data->apply, | ||
464 | *((long *)data->data)); | ||
465 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
466 | return mma845x_set_fsr(mlsl_handle, pdata, | ||
467 | &private_data->suspend, | ||
468 | data->apply, | ||
469 | *((long *)data->data)); | ||
470 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
471 | return mma845x_set_fsr(mlsl_handle, pdata, | ||
472 | &private_data->resume, | ||
473 | data->apply, | ||
474 | *((long *)data->data)); | ||
475 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
476 | return mma845x_set_ths(mlsl_handle, pdata, | ||
477 | &private_data->suspend, | ||
478 | data->apply, | ||
479 | *((long *)data->data)); | ||
480 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
481 | return mma845x_set_ths(mlsl_handle, pdata, | ||
482 | &private_data->resume, | ||
483 | data->apply, | ||
484 | *((long *)data->data)); | ||
485 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
486 | return mma845x_set_dur(mlsl_handle, pdata, | ||
487 | &private_data->suspend, | ||
488 | data->apply, | ||
489 | *((long *)data->data)); | ||
490 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
491 | return mma845x_set_dur(mlsl_handle, pdata, | ||
492 | &private_data->resume, | ||
493 | data->apply, | ||
494 | *((long *)data->data)); | ||
495 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
496 | return mma845x_set_irq(mlsl_handle, pdata, | ||
497 | &private_data->suspend, | ||
498 | data->apply, | ||
499 | *((long *)data->data)); | ||
500 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
501 | return mma845x_set_irq(mlsl_handle, pdata, | ||
502 | &private_data->resume, | ||
503 | data->apply, | ||
504 | *((long *)data->data)); | ||
505 | default: | ||
506 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
507 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
508 | }; | ||
509 | |||
510 | return INV_SUCCESS; | ||
511 | } | ||
512 | |||
513 | static int mma845x_get_config(void *mlsl_handle, | ||
514 | struct ext_slave_descr *slave, | ||
515 | struct ext_slave_platform_data *pdata, | ||
516 | struct ext_slave_config *data) | ||
517 | { | ||
518 | struct mma845x_private_data *private_data = pdata->private_data; | ||
519 | if (!data->data) | ||
520 | return INV_ERROR_INVALID_PARAMETER; | ||
521 | |||
522 | switch (data->key) { | ||
523 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
524 | (*(unsigned long *)data->data) = | ||
525 | (unsigned long) private_data->suspend.odr; | ||
526 | break; | ||
527 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
528 | (*(unsigned long *)data->data) = | ||
529 | (unsigned long) private_data->resume.odr; | ||
530 | break; | ||
531 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
532 | (*(unsigned long *)data->data) = | ||
533 | (unsigned long) private_data->suspend.fsr; | ||
534 | break; | ||
535 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
536 | (*(unsigned long *)data->data) = | ||
537 | (unsigned long) private_data->resume.fsr; | ||
538 | break; | ||
539 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
540 | (*(unsigned long *)data->data) = | ||
541 | (unsigned long) private_data->suspend.ths; | ||
542 | break; | ||
543 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
544 | (*(unsigned long *)data->data) = | ||
545 | (unsigned long) private_data->resume.ths; | ||
546 | break; | ||
547 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
548 | (*(unsigned long *)data->data) = | ||
549 | (unsigned long) private_data->suspend.dur; | ||
550 | break; | ||
551 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
552 | (*(unsigned long *)data->data) = | ||
553 | (unsigned long) private_data->resume.dur; | ||
554 | break; | ||
555 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
556 | (*(unsigned long *)data->data) = | ||
557 | (unsigned long) private_data->suspend.irq_type; | ||
558 | break; | ||
559 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
560 | (*(unsigned long *)data->data) = | ||
561 | (unsigned long) private_data->resume.irq_type; | ||
562 | break; | ||
563 | default: | ||
564 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
565 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
566 | }; | ||
567 | |||
568 | return INV_SUCCESS; | ||
569 | } | ||
570 | |||
571 | static struct ext_slave_descr mma845x_descr = { | ||
572 | .init = mma845x_init, | ||
573 | .exit = mma845x_exit, | ||
574 | .suspend = mma845x_suspend, | ||
575 | .resume = mma845x_resume, | ||
576 | .read = mma845x_read, | ||
577 | .config = mma845x_config, | ||
578 | .get_config = mma845x_get_config, | ||
579 | .name = "mma845x", | ||
580 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
581 | .id = ACCEL_ID_MMA845X, | ||
582 | .read_reg = 0x00, | ||
583 | .read_len = 6, | ||
584 | .endian = EXT_SLAVE_FS16_BIG_ENDIAN, | ||
585 | .range = {2, 0}, | ||
586 | .trigger = NULL, | ||
587 | }; | ||
588 | |||
589 | static | ||
590 | struct ext_slave_descr *mma845x_get_slave_descr(void) | ||
591 | { | ||
592 | return &mma845x_descr; | ||
593 | } | ||
594 | |||
595 | /* -------------------------------------------------------------------------- */ | ||
596 | struct mma845x_mod_private_data { | ||
597 | struct i2c_client *client; | ||
598 | struct ext_slave_platform_data *pdata; | ||
599 | }; | ||
600 | |||
601 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
602 | |||
603 | static int mma845x_mod_probe(struct i2c_client *client, | ||
604 | const struct i2c_device_id *devid) | ||
605 | { | ||
606 | struct ext_slave_platform_data *pdata; | ||
607 | struct mma845x_mod_private_data *private_data; | ||
608 | int result = 0; | ||
609 | |||
610 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
611 | |||
612 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
613 | result = -ENODEV; | ||
614 | goto out_no_free; | ||
615 | } | ||
616 | |||
617 | pdata = client->dev.platform_data; | ||
618 | if (!pdata) { | ||
619 | dev_err(&client->adapter->dev, | ||
620 | "Missing platform data for slave %s\n", devid->name); | ||
621 | result = -EFAULT; | ||
622 | goto out_no_free; | ||
623 | } | ||
624 | |||
625 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
626 | if (!private_data) { | ||
627 | result = -ENOMEM; | ||
628 | goto out_no_free; | ||
629 | } | ||
630 | |||
631 | i2c_set_clientdata(client, private_data); | ||
632 | private_data->client = client; | ||
633 | private_data->pdata = pdata; | ||
634 | |||
635 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
636 | mma845x_get_slave_descr); | ||
637 | if (result) { | ||
638 | dev_err(&client->adapter->dev, | ||
639 | "Slave registration failed: %s, %d\n", | ||
640 | devid->name, result); | ||
641 | goto out_free_memory; | ||
642 | } | ||
643 | |||
644 | return result; | ||
645 | |||
646 | out_free_memory: | ||
647 | kfree(private_data); | ||
648 | out_no_free: | ||
649 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
650 | return result; | ||
651 | |||
652 | } | ||
653 | |||
654 | static int mma845x_mod_remove(struct i2c_client *client) | ||
655 | { | ||
656 | struct mma845x_mod_private_data *private_data = | ||
657 | i2c_get_clientdata(client); | ||
658 | |||
659 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
660 | |||
661 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
662 | mma845x_get_slave_descr); | ||
663 | |||
664 | kfree(private_data); | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static const struct i2c_device_id mma845x_mod_id[] = { | ||
669 | { "mma845x", ACCEL_ID_MMA845X }, | ||
670 | {} | ||
671 | }; | ||
672 | |||
673 | MODULE_DEVICE_TABLE(i2c, mma845x_mod_id); | ||
674 | |||
675 | static struct i2c_driver mma845x_mod_driver = { | ||
676 | .class = I2C_CLASS_HWMON, | ||
677 | .probe = mma845x_mod_probe, | ||
678 | .remove = mma845x_mod_remove, | ||
679 | .id_table = mma845x_mod_id, | ||
680 | .driver = { | ||
681 | .owner = THIS_MODULE, | ||
682 | .name = "mma845x_mod", | ||
683 | }, | ||
684 | .address_list = normal_i2c, | ||
685 | }; | ||
686 | |||
687 | static int __init mma845x_mod_init(void) | ||
688 | { | ||
689 | int res = i2c_add_driver(&mma845x_mod_driver); | ||
690 | pr_info("%s: Probe name %s\n", __func__, "mma845x_mod"); | ||
691 | if (res) | ||
692 | pr_err("%s failed\n", __func__); | ||
693 | return res; | ||
694 | } | ||
695 | |||
696 | static void __exit mma845x_mod_exit(void) | ||
697 | { | ||
698 | pr_info("%s\n", __func__); | ||
699 | i2c_del_driver(&mma845x_mod_driver); | ||
700 | } | ||
701 | |||
702 | module_init(mma845x_mod_init); | ||
703 | module_exit(mma845x_mod_exit); | ||
704 | |||
705 | MODULE_AUTHOR("Invensense Corporation"); | ||
706 | MODULE_DESCRIPTION("Driver to integrate MMA845X sensor with the MPU"); | ||
707 | MODULE_LICENSE("GPL"); | ||
708 | MODULE_ALIAS("mma845x_mod"); | ||
709 | |||
710 | |||
711 | /** | ||
712 | * @} | ||
713 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.c b/drivers/misc/inv_mpu/accel/mpu6050.c new file mode 100644 index 00000000000..c5bb6784a41 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mpu6050.c | |||
@@ -0,0 +1,695 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup ACCELDL | ||
22 | * @brief Provides the interface to setup and handle an accelerometer. | ||
23 | * | ||
24 | * @{ | ||
25 | * @file mpu6050.c | ||
26 | * @brief Accelerometer setup and handling methods for Invensense MPU6050 | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mpu6050b1.h" | ||
43 | #include "mlsl.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | #undef MPL_LOG_TAG | ||
46 | #define MPL_LOG_TAG "MPL-acc" | ||
47 | |||
48 | /* -------------------------------------------------------------------------- */ | ||
49 | |||
50 | struct mpu6050_config { | ||
51 | unsigned int odr; /**< output data rate 1/1000 Hz */ | ||
52 | unsigned int fsr; /**< full scale range mg */ | ||
53 | unsigned int ths; /**< mot/no-mot thseshold mg */ | ||
54 | unsigned int dur; /**< mot/no-mot duration ms */ | ||
55 | unsigned int irq_type; /**< irq type */ | ||
56 | }; | ||
57 | |||
58 | struct mpu6050_private_data { | ||
59 | struct mpu6050_config suspend; | ||
60 | struct mpu6050_config resume; | ||
61 | struct mldl_cfg *mldl_cfg_ref; | ||
62 | }; | ||
63 | |||
64 | /* -------------------------------------------------------------------------- */ | ||
65 | |||
66 | static int mpu6050_set_mldl_cfg_ref(void *mlsl_handle, | ||
67 | struct ext_slave_platform_data *pdata, | ||
68 | struct mldl_cfg *mldl_cfg_ref) | ||
69 | { | ||
70 | struct mpu6050_private_data *private_data = | ||
71 | (struct mpu6050_private_data *)pdata->private_data; | ||
72 | private_data->mldl_cfg_ref = mldl_cfg_ref; | ||
73 | return 0; | ||
74 | } | ||
75 | static int mpu6050_set_lp_mode(void *mlsl_handle, | ||
76 | struct ext_slave_platform_data *pdata, | ||
77 | unsigned char lpa_freq) | ||
78 | { | ||
79 | unsigned char b = 0; | ||
80 | /* Reducing the duration setting for lp mode */ | ||
81 | b = 1; | ||
82 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
83 | MPUREG_ACCEL_MOT_DUR, b); | ||
84 | /* Setting the cycle bit and LPA wake up freq */ | ||
85 | inv_serial_read(mlsl_handle, pdata->address, MPUREG_PWR_MGMT_1, 1, | ||
86 | &b); | ||
87 | b |= BIT_CYCLE | BIT_PD_PTAT; | ||
88 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
89 | MPUREG_PWR_MGMT_1, | ||
90 | b); | ||
91 | inv_serial_read(mlsl_handle, pdata->address, | ||
92 | MPUREG_PWR_MGMT_2, 1, &b); | ||
93 | b |= lpa_freq & BITS_LPA_WAKE_CTRL; | ||
94 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
95 | MPUREG_PWR_MGMT_2, b); | ||
96 | |||
97 | return INV_SUCCESS; | ||
98 | } | ||
99 | |||
100 | static int mpu6050_set_fp_mode(void *mlsl_handle, | ||
101 | struct ext_slave_platform_data *pdata) | ||
102 | { | ||
103 | unsigned char b; | ||
104 | struct mpu6050_private_data *private_data = | ||
105 | (struct mpu6050_private_data *)pdata->private_data; | ||
106 | /* Resetting the cycle bit and LPA wake up freq */ | ||
107 | inv_serial_read(mlsl_handle, pdata->address, | ||
108 | MPUREG_PWR_MGMT_1, 1, &b); | ||
109 | b &= ~BIT_CYCLE & ~BIT_PD_PTAT; | ||
110 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
111 | MPUREG_PWR_MGMT_1, b); | ||
112 | inv_serial_read(mlsl_handle, pdata->address, | ||
113 | MPUREG_PWR_MGMT_2, 1, &b); | ||
114 | b &= ~BITS_LPA_WAKE_CTRL; | ||
115 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
116 | MPUREG_PWR_MGMT_2, b); | ||
117 | /* Resetting the duration setting for fp mode */ | ||
118 | b = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB; | ||
119 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
120 | MPUREG_ACCEL_MOT_DUR, b); | ||
121 | |||
122 | return INV_SUCCESS; | ||
123 | } | ||
124 | /** | ||
125 | * Record the odr for use in computing duration values. | ||
126 | * | ||
127 | * @param config Config to set, suspend or resume structure | ||
128 | * @param odr output data rate in 1/1000 hz | ||
129 | */ | ||
130 | static int mpu6050_set_odr(void *mlsl_handle, | ||
131 | struct ext_slave_platform_data *pdata, | ||
132 | struct mpu6050_config *config, long apply, long odr) | ||
133 | { | ||
134 | int result; | ||
135 | unsigned char b; | ||
136 | unsigned char lpa_freq = 1; /* Default value */ | ||
137 | long base; | ||
138 | int total_divider; | ||
139 | struct mpu6050_private_data *private_data = | ||
140 | (struct mpu6050_private_data *)pdata->private_data; | ||
141 | struct mldl_cfg *mldl_cfg_ref = | ||
142 | (struct mldl_cfg *)private_data->mldl_cfg_ref; | ||
143 | |||
144 | if (mldl_cfg_ref) { | ||
145 | base = 1000 * | ||
146 | inv_mpu_get_sampling_rate_hz(mldl_cfg_ref->mpu_gyro_cfg) | ||
147 | * (mldl_cfg_ref->mpu_gyro_cfg->divider + 1); | ||
148 | } else { | ||
149 | /* have no reference to mldl_cfg => assume base rate is 1000 */ | ||
150 | base = 1000000L; | ||
151 | } | ||
152 | |||
153 | if (odr != 0) { | ||
154 | total_divider = (base / odr) - 1; | ||
155 | /* final odr MAY be different from requested odr due to | ||
156 | integer truncation */ | ||
157 | config->odr = base / (total_divider + 1); | ||
158 | } else { | ||
159 | config->odr = 0; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* if the DMP and/or gyros are on, don't set the ODR => | ||
164 | the DMP/gyro mldl_cfg->divider setting will handle it */ | ||
165 | if (apply | ||
166 | && (mldl_cfg_ref && | ||
167 | !(mldl_cfg_ref->inv_mpu_cfg->requested_sensors & | ||
168 | INV_DMP_PROCESSOR))) { | ||
169 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
170 | MPUREG_SMPLRT_DIV, | ||
171 | (unsigned char)total_divider); | ||
172 | if (result) { | ||
173 | LOG_RESULT_LOCATION(result); | ||
174 | return result; | ||
175 | } | ||
176 | MPL_LOGI("ODR : %d mHz\n", config->odr); | ||
177 | } | ||
178 | /* Decide whether to put accel in LP mode or pull out of LP mode | ||
179 | based on the odr. */ | ||
180 | switch (odr) { | ||
181 | case 1000: | ||
182 | lpa_freq = BITS_LPA_WAKE_1HZ; | ||
183 | break; | ||
184 | case 2000: | ||
185 | lpa_freq = BITS_LPA_WAKE_2HZ; | ||
186 | break; | ||
187 | case 10000: | ||
188 | lpa_freq = BITS_LPA_WAKE_10HZ; | ||
189 | break; | ||
190 | case 40000: | ||
191 | lpa_freq = BITS_LPA_WAKE_40HZ; | ||
192 | break; | ||
193 | default: | ||
194 | inv_serial_read(mlsl_handle, pdata->address, | ||
195 | MPUREG_PWR_MGMT_1, 1, &b); | ||
196 | b &= BIT_CYCLE; | ||
197 | if (b == BIT_CYCLE) { | ||
198 | MPL_LOGI(" Accel LP - > FP mode. \n "); | ||
199 | mpu6050_set_fp_mode(mlsl_handle, pdata); | ||
200 | } | ||
201 | } | ||
202 | /* If lpa_freq default value was changed, set into LP mode */ | ||
203 | if (lpa_freq != 1) { | ||
204 | MPL_LOGI(" Accel FP - > LP mode. \n "); | ||
205 | mpu6050_set_lp_mode(mlsl_handle, pdata, lpa_freq); | ||
206 | } | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int mpu6050_set_fsr(void *mlsl_handle, | ||
211 | struct ext_slave_platform_data *pdata, | ||
212 | struct mpu6050_config *config, long apply, long fsr) | ||
213 | { | ||
214 | unsigned char fsr_mask; | ||
215 | int result; | ||
216 | |||
217 | if (fsr <= 2000) { | ||
218 | config->fsr = 2000; | ||
219 | fsr_mask = 0x00; | ||
220 | } else if (fsr <= 4000) { | ||
221 | config->fsr = 4000; | ||
222 | fsr_mask = 0x08; | ||
223 | } else if (fsr <= 8000) { | ||
224 | config->fsr = 8000; | ||
225 | fsr_mask = 0x10; | ||
226 | } else { /* fsr = [8001, oo) */ | ||
227 | config->fsr = 16000; | ||
228 | fsr_mask = 0x18; | ||
229 | } | ||
230 | |||
231 | if (apply) { | ||
232 | unsigned char reg; | ||
233 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
234 | MPUREG_ACCEL_CONFIG, 1, ®); | ||
235 | if (result) { | ||
236 | LOG_RESULT_LOCATION(result); | ||
237 | return result; | ||
238 | } | ||
239 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
240 | MPUREG_ACCEL_CONFIG, | ||
241 | reg | fsr_mask); | ||
242 | if (result) { | ||
243 | LOG_RESULT_LOCATION(result); | ||
244 | return result; | ||
245 | } | ||
246 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
247 | } | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int mpu6050_set_irq(void *mlsl_handle, | ||
252 | struct ext_slave_platform_data *pdata, | ||
253 | struct mpu6050_config *config, long apply, | ||
254 | long irq_type) | ||
255 | { | ||
256 | int result; | ||
257 | unsigned char reg_int_cfg; | ||
258 | |||
259 | switch (irq_type) { | ||
260 | case MPU_SLAVE_IRQ_TYPE_DATA_READY: | ||
261 | config->irq_type = irq_type; | ||
262 | reg_int_cfg = BIT_RAW_RDY_EN; | ||
263 | break; | ||
264 | /* todo: add MOTION, NO_MOTION, and FREEFALL */ | ||
265 | case MPU_SLAVE_IRQ_TYPE_NONE: | ||
266 | /* Do nothing, not even set the interrupt because it is | ||
267 | shared with the gyro */ | ||
268 | config->irq_type = irq_type; | ||
269 | return 0; | ||
270 | default: | ||
271 | return INV_ERROR_INVALID_PARAMETER; | ||
272 | } | ||
273 | |||
274 | if (apply) { | ||
275 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
276 | MPUREG_INT_ENABLE, | ||
277 | reg_int_cfg); | ||
278 | if (result) { | ||
279 | LOG_RESULT_LOCATION(result); | ||
280 | return result; | ||
281 | } | ||
282 | MPL_LOGV("irq_type: %d\n", config->irq_type); | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int mpu6050_set_ths(void *mlsl_handle, | ||
289 | struct ext_slave_platform_data *slave, | ||
290 | struct mpu6050_config *config, long apply, long ths) | ||
291 | { | ||
292 | if (ths < 0) | ||
293 | ths = 0; | ||
294 | |||
295 | config->ths = ths; | ||
296 | MPL_LOGV("THS: %d\n", config->ths); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int mpu6050_set_dur(void *mlsl_handle, | ||
301 | struct ext_slave_platform_data *slave, | ||
302 | struct mpu6050_config *config, long apply, long dur) | ||
303 | { | ||
304 | if (dur < 0) | ||
305 | dur = 0; | ||
306 | |||
307 | config->dur = dur; | ||
308 | MPL_LOGV("DUR: %d\n", config->dur); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | |||
313 | static int mpu6050_init(void *mlsl_handle, | ||
314 | struct ext_slave_descr *slave, | ||
315 | struct ext_slave_platform_data *pdata) | ||
316 | { | ||
317 | int result; | ||
318 | struct mpu6050_private_data *private_data; | ||
319 | |||
320 | |||
321 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
322 | |||
323 | if (!private_data) | ||
324 | return INV_ERROR_MEMORY_EXAUSTED; | ||
325 | |||
326 | pdata->private_data = private_data; | ||
327 | |||
328 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
329 | false, 0); | ||
330 | if (result) { | ||
331 | LOG_RESULT_LOCATION(result); | ||
332 | return result; | ||
333 | } | ||
334 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
335 | false, 200000); | ||
336 | if (result) { | ||
337 | LOG_RESULT_LOCATION(result); | ||
338 | return result; | ||
339 | } | ||
340 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
341 | false, 2000); | ||
342 | if (result) { | ||
343 | LOG_RESULT_LOCATION(result); | ||
344 | return result; | ||
345 | } | ||
346 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
347 | false, 2000); | ||
348 | if (result) { | ||
349 | LOG_RESULT_LOCATION(result); | ||
350 | return result; | ||
351 | } | ||
352 | |||
353 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
354 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
355 | if (result) { | ||
356 | LOG_RESULT_LOCATION(result); | ||
357 | return result; | ||
358 | } | ||
359 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
360 | false, MPU_SLAVE_IRQ_TYPE_NONE); | ||
361 | if (result) { | ||
362 | LOG_RESULT_LOCATION(result); | ||
363 | return result; | ||
364 | } | ||
365 | |||
366 | result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
367 | false, 80); | ||
368 | if (result) { | ||
369 | LOG_RESULT_LOCATION(result); | ||
370 | return result; | ||
371 | } | ||
372 | result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
373 | false, 40); | ||
374 | if (result) { | ||
375 | LOG_RESULT_LOCATION(result); | ||
376 | return result; | ||
377 | } | ||
378 | result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
379 | false, 1000); | ||
380 | if (result) { | ||
381 | LOG_RESULT_LOCATION(result); | ||
382 | return result; | ||
383 | } | ||
384 | result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
385 | false, 2540); | ||
386 | if (result) { | ||
387 | LOG_RESULT_LOCATION(result); | ||
388 | return result; | ||
389 | } | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int mpu6050_exit(void *mlsl_handle, | ||
395 | struct ext_slave_descr *slave, | ||
396 | struct ext_slave_platform_data *pdata) | ||
397 | { | ||
398 | kfree(pdata->private_data); | ||
399 | pdata->private_data = NULL; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int mpu6050_suspend(void *mlsl_handle, | ||
404 | struct ext_slave_descr *slave, | ||
405 | struct ext_slave_platform_data *pdata) | ||
406 | { | ||
407 | unsigned char reg; | ||
408 | int result; | ||
409 | struct mpu6050_private_data *private_data = | ||
410 | (struct mpu6050_private_data *)pdata->private_data; | ||
411 | |||
412 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
413 | true, private_data->suspend.odr); | ||
414 | if (result) { | ||
415 | LOG_RESULT_LOCATION(result); | ||
416 | return result; | ||
417 | } | ||
418 | |||
419 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
420 | true, private_data->suspend.irq_type); | ||
421 | if (result) { | ||
422 | LOG_RESULT_LOCATION(result); | ||
423 | return result; | ||
424 | } | ||
425 | |||
426 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
427 | MPUREG_PWR_MGMT_2, 1, ®); | ||
428 | if (result) { | ||
429 | LOG_RESULT_LOCATION(result); | ||
430 | return result; | ||
431 | } | ||
432 | reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
433 | |||
434 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
435 | MPUREG_PWR_MGMT_2, reg); | ||
436 | if (result) { | ||
437 | LOG_RESULT_LOCATION(result); | ||
438 | return result; | ||
439 | } | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static int mpu6050_resume(void *mlsl_handle, | ||
445 | struct ext_slave_descr *slave, | ||
446 | struct ext_slave_platform_data *pdata) | ||
447 | { | ||
448 | int result; | ||
449 | unsigned char reg; | ||
450 | struct mpu6050_private_data *private_data = | ||
451 | (struct mpu6050_private_data *)pdata->private_data; | ||
452 | |||
453 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
454 | MPUREG_PWR_MGMT_1, 1, ®); | ||
455 | if (result) { | ||
456 | LOG_RESULT_LOCATION(result); | ||
457 | return result; | ||
458 | } | ||
459 | |||
460 | if (reg & BIT_SLEEP) { | ||
461 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
462 | MPUREG_PWR_MGMT_1, reg & ~BIT_SLEEP); | ||
463 | if (result) { | ||
464 | LOG_RESULT_LOCATION(result); | ||
465 | return result; | ||
466 | } | ||
467 | } | ||
468 | msleep(2); | ||
469 | |||
470 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
471 | MPUREG_PWR_MGMT_2, 1, ®); | ||
472 | if (result) { | ||
473 | LOG_RESULT_LOCATION(result); | ||
474 | return result; | ||
475 | } | ||
476 | reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
477 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
478 | MPUREG_PWR_MGMT_2, reg); | ||
479 | if (result) { | ||
480 | LOG_RESULT_LOCATION(result); | ||
481 | return result; | ||
482 | } | ||
483 | |||
484 | /* settings */ | ||
485 | |||
486 | result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
487 | true, private_data->resume.fsr); | ||
488 | if (result) { | ||
489 | LOG_RESULT_LOCATION(result); | ||
490 | return result; | ||
491 | } | ||
492 | result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
493 | true, private_data->resume.odr); | ||
494 | if (result) { | ||
495 | LOG_RESULT_LOCATION(result); | ||
496 | return result; | ||
497 | } | ||
498 | result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
499 | true, private_data->resume.irq_type); | ||
500 | |||
501 | /* motion, no_motion */ | ||
502 | /* TODO : port these in their respective _set_thrs and _set_dur | ||
503 | functions and use the APPLY paremeter to apply just like | ||
504 | _set_odr, _set_irq, and _set_fsr. */ | ||
505 | reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB; | ||
506 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
507 | MPUREG_ACCEL_MOT_THR, reg); | ||
508 | if (result) { | ||
509 | LOG_RESULT_LOCATION(result); | ||
510 | return result; | ||
511 | } | ||
512 | reg = (unsigned char) | ||
513 | ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths); | ||
514 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
515 | MPUREG_ACCEL_ZRMOT_THR, reg); | ||
516 | if (result) { | ||
517 | LOG_RESULT_LOCATION(result); | ||
518 | return result; | ||
519 | } | ||
520 | reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB; | ||
521 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
522 | MPUREG_ACCEL_MOT_DUR, reg); | ||
523 | if (result) { | ||
524 | LOG_RESULT_LOCATION(result); | ||
525 | return result; | ||
526 | } | ||
527 | reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB; | ||
528 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
529 | MPUREG_ACCEL_ZRMOT_DUR, reg); | ||
530 | if (result) { | ||
531 | LOG_RESULT_LOCATION(result); | ||
532 | return result; | ||
533 | } | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int mpu6050_read(void *mlsl_handle, | ||
538 | struct ext_slave_descr *slave, | ||
539 | struct ext_slave_platform_data *pdata, | ||
540 | unsigned char *data) | ||
541 | { | ||
542 | int result; | ||
543 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
544 | slave->read_reg, slave->read_len, data); | ||
545 | return result; | ||
546 | } | ||
547 | |||
548 | static int mpu6050_config(void *mlsl_handle, | ||
549 | struct ext_slave_descr *slave, | ||
550 | struct ext_slave_platform_data *pdata, | ||
551 | struct ext_slave_config *data) | ||
552 | { | ||
553 | struct mpu6050_private_data *private_data = | ||
554 | (struct mpu6050_private_data *)pdata->private_data; | ||
555 | if (!data->data) | ||
556 | return INV_ERROR_INVALID_PARAMETER; | ||
557 | |||
558 | switch (data->key) { | ||
559 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
560 | return mpu6050_set_odr(mlsl_handle, pdata, | ||
561 | &private_data->suspend, | ||
562 | data->apply, *((long *)data->data)); | ||
563 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
564 | return mpu6050_set_odr(mlsl_handle, pdata, | ||
565 | &private_data->resume, | ||
566 | data->apply, *((long *)data->data)); | ||
567 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
568 | return mpu6050_set_fsr(mlsl_handle, pdata, | ||
569 | &private_data->suspend, | ||
570 | data->apply, *((long *)data->data)); | ||
571 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
572 | return mpu6050_set_fsr(mlsl_handle, pdata, | ||
573 | &private_data->resume, | ||
574 | data->apply, *((long *)data->data)); | ||
575 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
576 | return mpu6050_set_ths(mlsl_handle, pdata, | ||
577 | &private_data->suspend, | ||
578 | data->apply, *((long *)data->data)); | ||
579 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
580 | return mpu6050_set_ths(mlsl_handle, pdata, | ||
581 | &private_data->resume, | ||
582 | data->apply, *((long *)data->data)); | ||
583 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
584 | return mpu6050_set_dur(mlsl_handle, pdata, | ||
585 | &private_data->suspend, | ||
586 | data->apply, *((long *)data->data)); | ||
587 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
588 | return mpu6050_set_dur(mlsl_handle, pdata, | ||
589 | &private_data->resume, | ||
590 | data->apply, *((long *)data->data)); | ||
591 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
592 | return mpu6050_set_irq(mlsl_handle, pdata, | ||
593 | &private_data->suspend, | ||
594 | data->apply, *((long *)data->data)); | ||
595 | break; | ||
596 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
597 | return mpu6050_set_irq(mlsl_handle, pdata, | ||
598 | &private_data->resume, | ||
599 | data->apply, *((long *)data->data)); | ||
600 | case MPU_SLAVE_CONFIG_INTERNAL_REFERENCE: | ||
601 | return mpu6050_set_mldl_cfg_ref(mlsl_handle, pdata, | ||
602 | (struct mldl_cfg *)data->data); | ||
603 | break; | ||
604 | |||
605 | default: | ||
606 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
607 | }; | ||
608 | |||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | static int mpu6050_get_config(void *mlsl_handle, | ||
613 | struct ext_slave_descr *slave, | ||
614 | struct ext_slave_platform_data *pdata, | ||
615 | struct ext_slave_config *data) | ||
616 | { | ||
617 | struct mpu6050_private_data *private_data = | ||
618 | (struct mpu6050_private_data *)pdata->private_data; | ||
619 | if (!data->data) | ||
620 | return INV_ERROR_INVALID_PARAMETER; | ||
621 | |||
622 | switch (data->key) { | ||
623 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
624 | (*(unsigned long *)data->data) = | ||
625 | (unsigned long)private_data->suspend.odr; | ||
626 | break; | ||
627 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
628 | (*(unsigned long *)data->data) = | ||
629 | (unsigned long)private_data->resume.odr; | ||
630 | break; | ||
631 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
632 | (*(unsigned long *)data->data) = | ||
633 | (unsigned long)private_data->suspend.fsr; | ||
634 | break; | ||
635 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
636 | (*(unsigned long *)data->data) = | ||
637 | (unsigned long)private_data->resume.fsr; | ||
638 | break; | ||
639 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
640 | (*(unsigned long *)data->data) = | ||
641 | (unsigned long)private_data->suspend.ths; | ||
642 | break; | ||
643 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
644 | (*(unsigned long *)data->data) = | ||
645 | (unsigned long)private_data->resume.ths; | ||
646 | break; | ||
647 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
648 | (*(unsigned long *)data->data) = | ||
649 | (unsigned long)private_data->suspend.dur; | ||
650 | break; | ||
651 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
652 | (*(unsigned long *)data->data) = | ||
653 | (unsigned long)private_data->resume.dur; | ||
654 | break; | ||
655 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
656 | (*(unsigned long *)data->data) = | ||
657 | (unsigned long)private_data->suspend.irq_type; | ||
658 | break; | ||
659 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
660 | (*(unsigned long *)data->data) = | ||
661 | (unsigned long)private_data->resume.irq_type; | ||
662 | break; | ||
663 | default: | ||
664 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
665 | }; | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static struct ext_slave_descr mpu6050_descr = { | ||
671 | .init = mpu6050_init, | ||
672 | .exit = mpu6050_exit, | ||
673 | .suspend = mpu6050_suspend, | ||
674 | .resume = mpu6050_resume, | ||
675 | .read = mpu6050_read, | ||
676 | .config = mpu6050_config, | ||
677 | .get_config = mpu6050_get_config, | ||
678 | .name = "mpu6050", | ||
679 | .type = EXT_SLAVE_TYPE_ACCEL, | ||
680 | .id = ACCEL_ID_MPU6050, | ||
681 | .read_reg = 0x3B, | ||
682 | .read_len = 6, | ||
683 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
684 | .range = {2, 0}, | ||
685 | .trigger = NULL, | ||
686 | }; | ||
687 | |||
688 | struct ext_slave_descr *mpu6050_get_slave_descr(void) | ||
689 | { | ||
690 | return &mpu6050_descr; | ||
691 | } | ||
692 | |||
693 | /** | ||
694 | * @} | ||
695 | */ | ||
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.h b/drivers/misc/inv_mpu/accel/mpu6050.h new file mode 100644 index 00000000000..c347bcb4d77 --- /dev/null +++ b/drivers/misc/inv_mpu/accel/mpu6050.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | |||
21 | #ifndef __MPU6050_H__ | ||
22 | #define __MPU6050_H__ | ||
23 | |||
24 | #include <linux/mpu.h> | ||
25 | |||
26 | struct ext_slave_descr *mpu6050_get_slave_descr(void); | ||
27 | |||
28 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/Kconfig b/drivers/misc/inv_mpu/compass/Kconfig new file mode 100644 index 00000000000..7e6bac87d31 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/Kconfig | |||
@@ -0,0 +1,130 @@ | |||
1 | menuconfig INV_SENSORS_COMPASS | ||
2 | bool "Compass Slave Sensors" | ||
3 | default y | ||
4 | ---help--- | ||
5 | Say Y here to get to see options for device drivers for various | ||
6 | compasses. This option alone does not add any kernel code. | ||
7 | |||
8 | If you say N, all options in this submenu will be skipped and disabled. | ||
9 | |||
10 | if INV_SENSORS_COMPASS | ||
11 | |||
12 | config MPU_SENSORS_AK8963 | ||
13 | tristate "AKM ak8963" | ||
14 | help | ||
15 | This enables support for the AKM ak8963 compass | ||
16 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
17 | device driver. Only one compass can be registered at a time. | ||
18 | Specifying more that one compass in the board file will result | ||
19 | in runtime errors. | ||
20 | |||
21 | config MPU_SENSORS_AK8975 | ||
22 | tristate "AKM ak8975" | ||
23 | help | ||
24 | This enables support for the AKM ak8975 compass | ||
25 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
26 | device driver. Only one compass can be registered at a time. | ||
27 | Specifying more that one compass in the board file will result | ||
28 | in runtime errors. | ||
29 | |||
30 | config MPU_SENSORS_AK8972 | ||
31 | tristate "AKM ak8972" | ||
32 | help | ||
33 | This enables support for the AKM ak8972 compass | ||
34 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
35 | device driver. Only one compass can be registered at a time. | ||
36 | Specifying more that one compass in the board file will result | ||
37 | in runtime errors. | ||
38 | |||
39 | config MPU_SENSORS_MMC314X | ||
40 | tristate "MEMSIC mmc314x" | ||
41 | help | ||
42 | This enables support for the MEMSIC mmc314x compass | ||
43 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
44 | device driver. Only one compass can be registered at a time. | ||
45 | Specifying more that one compass in the board file will result | ||
46 | in runtime errors. | ||
47 | |||
48 | config MPU_SENSORS_AMI30X | ||
49 | tristate "Aichi Steel ami30X" | ||
50 | help | ||
51 | This enables support for the Aichi Steel ami304/ami305 compass | ||
52 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
53 | device driver. Only one compass can be registered at a time. | ||
54 | Specifying more that one compass in the board file will result | ||
55 | in runtime errors. | ||
56 | |||
57 | config MPU_SENSORS_AMI306 | ||
58 | tristate "Aichi Steel ami306" | ||
59 | help | ||
60 | This enables support for the Aichi Steel ami306 compass | ||
61 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
62 | device driver. Only one compass can be registered at a time. | ||
63 | Specifying more that one compass in the board file will result | ||
64 | in runtime errors. | ||
65 | |||
66 | config MPU_SENSORS_HMC5883 | ||
67 | tristate "Honeywell hmc5883" | ||
68 | help | ||
69 | This enables support for the Honeywell hmc5883 compass | ||
70 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
71 | device driver. Only one compass can be registered at a time. | ||
72 | Specifying more that one compass in the board file will result | ||
73 | in runtime errors. | ||
74 | |||
75 | config MPU_SENSORS_LSM303DLX_M | ||
76 | tristate "ST lsm303dlx" | ||
77 | help | ||
78 | This enables support for the ST lsm303dlh and lsm303dlm compasses | ||
79 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
80 | device driver. Only one compass can be registered at a time. | ||
81 | Specifying more that one compass in the board file will result | ||
82 | in runtime errors. | ||
83 | |||
84 | config MPU_SENSORS_MMC314XMS | ||
85 | tristate "MEMSIC mmc314xMS" | ||
86 | help | ||
87 | This enables support for the MEMSIC mmc314xMS compass | ||
88 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
89 | device driver. Only one compass can be registered at a time. | ||
90 | Specifying more that one compass in the board file will result | ||
91 | in runtime errors. | ||
92 | |||
93 | config MPU_SENSORS_YAS529 | ||
94 | tristate "Yamaha yas529" | ||
95 | depends on INPUT_YAS_MAGNETOMETER | ||
96 | help | ||
97 | This enables support for the Yamaha yas529 compass | ||
98 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
99 | device driver. Only one compass can be registered at a time. | ||
100 | Specifying more that one compass in the board file will result | ||
101 | in runtime errors. | ||
102 | |||
103 | config MPU_SENSORS_YAS530 | ||
104 | tristate "Yamaha yas530" | ||
105 | help | ||
106 | This enables support for the Yamaha yas530 compass | ||
107 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
108 | device driver. Only one compass can be registered at a time. | ||
109 | Specifying more that one compass in the board file will result | ||
110 | in runtime errors. | ||
111 | |||
112 | config MPU_SENSORS_HSCDTD002B | ||
113 | tristate "Alps hscdtd002b" | ||
114 | help | ||
115 | This enables support for the Alps hscdtd002b compass | ||
116 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
117 | device driver. Only one compass can be registered at a time. | ||
118 | Specifying more that one compass in the board file will result | ||
119 | in runtime errors. | ||
120 | |||
121 | config MPU_SENSORS_HSCDTD004A | ||
122 | tristate "Alps hscdtd004a" | ||
123 | help | ||
124 | This enables support for the Alps hscdtd004a compass | ||
125 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
126 | device driver. Only one compass can be registered at a time. | ||
127 | Specifying more that one compass in the board file will result | ||
128 | in runtime errors. | ||
129 | |||
130 | endif | ||
diff --git a/drivers/misc/inv_mpu/compass/Makefile b/drivers/misc/inv_mpu/compass/Makefile new file mode 100644 index 00000000000..d76ce1b06f3 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/Makefile | |||
@@ -0,0 +1,41 @@ | |||
1 | # | ||
2 | # Compass Slaves MPUxxxx | ||
3 | # | ||
4 | obj-$(CONFIG_MPU_SENSORS_AMI30X) += inv_mpu_ami30x.o | ||
5 | inv_mpu_ami30x-objs += ami30x.o | ||
6 | |||
7 | obj-$(CONFIG_MPU_SENSORS_AMI306) += inv_mpu_ami306.o | ||
8 | inv_mpu_ami306-objs += ami306.o | ||
9 | |||
10 | obj-$(CONFIG_MPU_SENSORS_HMC5883) += inv_mpu_hmc5883.o | ||
11 | inv_mpu_hmc5883-objs += hmc5883.o | ||
12 | |||
13 | obj-$(CONFIG_MPU_SENSORS_LSM303DLX_M) += inv_mpu_lsm303dlx_m.o | ||
14 | inv_mpu_lsm303dlx_m-objs += lsm303dlx_m.o | ||
15 | |||
16 | obj-$(CONFIG_MPU_SENSORS_MMC314X) += inv_mpu_mmc314x.o | ||
17 | inv_mpu_mmc314x-objs += mmc314x.o | ||
18 | |||
19 | obj-$(CONFIG_MPU_SENSORS_YAS529) += inv_mpu_yas529.o | ||
20 | inv_mpu_yas529-objs += yas529-kernel.o | ||
21 | |||
22 | obj-$(CONFIG_MPU_SENSORS_YAS530) += inv_mpu_yas530.o | ||
23 | inv_mpu_yas530-objs += yas530.o | ||
24 | |||
25 | obj-$(CONFIG_MPU_SENSORS_HSCDTD002B) += inv_mpu_hscdtd002b.o | ||
26 | inv_mpu_hscdtd002b-objs += hscdtd002b.o | ||
27 | |||
28 | obj-$(CONFIG_MPU_SENSORS_HSCDTD004A) += inv_mpu_hscdtd004a.o | ||
29 | inv_mpu_hscdtd004a-objs += hscdtd004a.o | ||
30 | |||
31 | obj-$(CONFIG_MPU_SENSORS_AK8963) += inv_mpu_ak89xx.o | ||
32 | inv_mpu_ak89xx-objs += ak89xx.o | ||
33 | |||
34 | obj-$(CONFIG_MPU_SENSORS_AK8975) += inv_mpu_ak8975.o | ||
35 | inv_mpu_ak8975-objs += ak8975.o | ||
36 | |||
37 | obj-$(CONFIG_MPU_SENSORS_AK8972) += inv_mpu_ak8972.o | ||
38 | inv_mpu_ak8972-objs += ak8972.o | ||
39 | |||
40 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
41 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/compass/ak8972.c b/drivers/misc/inv_mpu/compass/ak8972.c new file mode 100644 index 00000000000..7eb15b44039 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak8972.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file ak8972.c | ||
25 | * @brief Magnetometer setup and handling methods for the AKM AK8972 compass device. | ||
26 | */ | ||
27 | |||
28 | /* -------------------------------------------------------------------------- */ | ||
29 | |||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/errno.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include "mpu-dev.h" | ||
38 | |||
39 | #include <log.h> | ||
40 | #include <linux/mpu.h> | ||
41 | #include "mlsl.h" | ||
42 | #include "mldl_cfg.h" | ||
43 | #undef MPL_LOG_TAG | ||
44 | #define MPL_LOG_TAG "MPL-compass" | ||
45 | |||
46 | /* -------------------------------------------------------------------------- */ | ||
47 | #define AK8972_REG_ST1 (0x02) | ||
48 | #define AK8972_REG_HXL (0x03) | ||
49 | #define AK8972_REG_ST2 (0x09) | ||
50 | |||
51 | #define AK8972_REG_CNTL (0x0A) | ||
52 | #define AK8972_REG_ASAX (0x10) | ||
53 | #define AK8972_REG_ASAY (0x11) | ||
54 | #define AK8972_REG_ASAZ (0x12) | ||
55 | |||
56 | #define AK8972_CNTL_MODE_POWER_DOWN (0x00) | ||
57 | #define AK8972_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
58 | #define AK8972_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
59 | |||
60 | /* -------------------------------------------------------------------------- */ | ||
61 | struct ak8972_config { | ||
62 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
63 | }; | ||
64 | |||
65 | struct ak8972_private_data { | ||
66 | struct ak8972_config init; | ||
67 | }; | ||
68 | |||
69 | /* -------------------------------------------------------------------------- */ | ||
70 | static int ak8972_init(void *mlsl_handle, | ||
71 | struct ext_slave_descr *slave, | ||
72 | struct ext_slave_platform_data *pdata) | ||
73 | { | ||
74 | int result; | ||
75 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
76 | |||
77 | struct ak8972_private_data *private_data; | ||
78 | private_data = (struct ak8972_private_data *) | ||
79 | kzalloc(sizeof(struct ak8972_private_data), GFP_KERNEL); | ||
80 | |||
81 | if (!private_data) | ||
82 | return INV_ERROR_MEMORY_EXAUSTED; | ||
83 | |||
84 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
85 | AK8972_REG_CNTL, | ||
86 | AK8972_CNTL_MODE_POWER_DOWN); | ||
87 | if (result) { | ||
88 | LOG_RESULT_LOCATION(result); | ||
89 | return result; | ||
90 | } | ||
91 | /* Wait at least 100us */ | ||
92 | udelay(100); | ||
93 | |||
94 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
95 | AK8972_REG_CNTL, | ||
96 | AK8972_CNTL_MODE_FUSE_ROM_ACCESS); | ||
97 | if (result) { | ||
98 | LOG_RESULT_LOCATION(result); | ||
99 | return result; | ||
100 | } | ||
101 | |||
102 | /* Wait at least 200us */ | ||
103 | udelay(200); | ||
104 | |||
105 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
106 | AK8972_REG_ASAX, | ||
107 | COMPASS_NUM_AXES, serial_data); | ||
108 | if (result) { | ||
109 | LOG_RESULT_LOCATION(result); | ||
110 | return result; | ||
111 | } | ||
112 | |||
113 | pdata->private_data = private_data; | ||
114 | |||
115 | private_data->init.asa[0] = serial_data[0]; | ||
116 | private_data->init.asa[1] = serial_data[1]; | ||
117 | private_data->init.asa[2] = serial_data[2]; | ||
118 | |||
119 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
120 | AK8972_REG_CNTL, | ||
121 | AK8972_CNTL_MODE_POWER_DOWN); | ||
122 | if (result) { | ||
123 | LOG_RESULT_LOCATION(result); | ||
124 | return result; | ||
125 | } | ||
126 | |||
127 | udelay(100); | ||
128 | return INV_SUCCESS; | ||
129 | } | ||
130 | |||
131 | static int ak8972_exit(void *mlsl_handle, | ||
132 | struct ext_slave_descr *slave, | ||
133 | struct ext_slave_platform_data *pdata) | ||
134 | { | ||
135 | kfree(pdata->private_data); | ||
136 | return INV_SUCCESS; | ||
137 | } | ||
138 | |||
139 | static int ak8972_suspend(void *mlsl_handle, | ||
140 | struct ext_slave_descr *slave, | ||
141 | struct ext_slave_platform_data *pdata) | ||
142 | { | ||
143 | int result = INV_SUCCESS; | ||
144 | result = | ||
145 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
146 | AK8972_REG_CNTL, | ||
147 | AK8972_CNTL_MODE_POWER_DOWN); | ||
148 | msleep(1); /* wait at least 100us */ | ||
149 | if (result) { | ||
150 | LOG_RESULT_LOCATION(result); | ||
151 | return result; | ||
152 | } | ||
153 | return result; | ||
154 | } | ||
155 | |||
156 | static int ak8972_resume(void *mlsl_handle, | ||
157 | struct ext_slave_descr *slave, | ||
158 | struct ext_slave_platform_data *pdata) | ||
159 | { | ||
160 | int result = INV_SUCCESS; | ||
161 | result = | ||
162 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
163 | AK8972_REG_CNTL, | ||
164 | AK8972_CNTL_MODE_SINGLE_MEASUREMENT); | ||
165 | if (result) { | ||
166 | LOG_RESULT_LOCATION(result); | ||
167 | return result; | ||
168 | } | ||
169 | return result; | ||
170 | } | ||
171 | |||
172 | static int ak8972_read(void *mlsl_handle, | ||
173 | struct ext_slave_descr *slave, | ||
174 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
175 | { | ||
176 | unsigned char regs[8]; | ||
177 | unsigned char *stat = ®s[0]; | ||
178 | unsigned char *stat2 = ®s[7]; | ||
179 | int result = INV_SUCCESS; | ||
180 | int status = INV_SUCCESS; | ||
181 | |||
182 | result = | ||
183 | inv_serial_read(mlsl_handle, pdata->address, AK8972_REG_ST1, | ||
184 | 8, regs); | ||
185 | if (result) { | ||
186 | LOG_RESULT_LOCATION(result); | ||
187 | return result; | ||
188 | } | ||
189 | |||
190 | /* Always return the data and the status registers */ | ||
191 | memcpy(data, ®s[1], 6); | ||
192 | data[6] = regs[0]; | ||
193 | data[7] = regs[7]; | ||
194 | |||
195 | /* | ||
196 | * ST : data ready - | ||
197 | * Measurement has been completed and data is ready to be read. | ||
198 | */ | ||
199 | if (*stat & 0x01) | ||
200 | status = INV_SUCCESS; | ||
201 | |||
202 | /* | ||
203 | * ST2 : data error - | ||
204 | * occurs when data read is started outside of a readable period; | ||
205 | * data read would not be correct. | ||
206 | * Valid in continuous measurement mode only. | ||
207 | * In single measurement mode this error should not occour but we | ||
208 | * stil account for it and return an error, since the data would be | ||
209 | * corrupted. | ||
210 | * DERR bit is self-clearing when ST2 register is read. | ||
211 | */ | ||
212 | if (*stat2 & 0x04) | ||
213 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
214 | /* | ||
215 | * ST2 : overflow - | ||
216 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
217 | * This is likely to happen in presence of an external magnetic | ||
218 | * disturbance; it indicates, the sensor data is incorrect and should | ||
219 | * be ignored. | ||
220 | * An error is returned. | ||
221 | * HOFL bit clears when a new measurement starts. | ||
222 | */ | ||
223 | if (*stat2 & 0x08) | ||
224 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
225 | /* | ||
226 | * ST : overrun - | ||
227 | * the previous sample was not fetched and lost. | ||
228 | * Valid in continuous measurement mode only. | ||
229 | * In single measurement mode this error should not occour and we | ||
230 | * don't consider this condition an error. | ||
231 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
232 | * read. | ||
233 | */ | ||
234 | if (*stat & 0x02) { | ||
235 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
236 | status = INV_SUCCESS; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * trigger next measurement if: | ||
241 | * - stat is non zero; | ||
242 | * - if stat is zero and stat2 is non zero. | ||
243 | * Won't trigger if data is not ready and there was no error. | ||
244 | */ | ||
245 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
246 | result = inv_serial_single_write( | ||
247 | mlsl_handle, pdata->address, | ||
248 | AK8972_REG_CNTL, AK8972_CNTL_MODE_SINGLE_MEASUREMENT); | ||
249 | if (result) { | ||
250 | LOG_RESULT_LOCATION(result); | ||
251 | return result; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return status; | ||
256 | } | ||
257 | |||
258 | static int ak8972_config(void *mlsl_handle, | ||
259 | struct ext_slave_descr *slave, | ||
260 | struct ext_slave_platform_data *pdata, | ||
261 | struct ext_slave_config *data) | ||
262 | { | ||
263 | int result; | ||
264 | if (!data->data) | ||
265 | return INV_ERROR_INVALID_PARAMETER; | ||
266 | |||
267 | switch (data->key) { | ||
268 | case MPU_SLAVE_WRITE_REGISTERS: | ||
269 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
270 | data->len, | ||
271 | (unsigned char *)data->data); | ||
272 | if (result) { | ||
273 | LOG_RESULT_LOCATION(result); | ||
274 | return result; | ||
275 | } | ||
276 | break; | ||
277 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
278 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
279 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
280 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
281 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
282 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
283 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
284 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
285 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
286 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
287 | default: | ||
288 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
289 | }; | ||
290 | |||
291 | return INV_SUCCESS; | ||
292 | } | ||
293 | |||
294 | static int ak8972_get_config(void *mlsl_handle, | ||
295 | struct ext_slave_descr *slave, | ||
296 | struct ext_slave_platform_data *pdata, | ||
297 | struct ext_slave_config *data) | ||
298 | { | ||
299 | struct ak8972_private_data *private_data = pdata->private_data; | ||
300 | int result; | ||
301 | if (!data->data) | ||
302 | return INV_ERROR_INVALID_PARAMETER; | ||
303 | |||
304 | switch (data->key) { | ||
305 | case MPU_SLAVE_READ_REGISTERS: | ||
306 | { | ||
307 | unsigned char *serial_data = | ||
308 | (unsigned char *)data->data; | ||
309 | result = | ||
310 | inv_serial_read(mlsl_handle, pdata->address, | ||
311 | serial_data[0], data->len - 1, | ||
312 | &serial_data[1]); | ||
313 | if (result) { | ||
314 | LOG_RESULT_LOCATION(result); | ||
315 | return result; | ||
316 | } | ||
317 | break; | ||
318 | } | ||
319 | case MPU_SLAVE_READ_SCALE: | ||
320 | { | ||
321 | unsigned char *serial_data = | ||
322 | (unsigned char *)data->data; | ||
323 | serial_data[0] = private_data->init.asa[0]; | ||
324 | serial_data[1] = private_data->init.asa[1]; | ||
325 | serial_data[2] = private_data->init.asa[2]; | ||
326 | result = INV_SUCCESS; | ||
327 | if (result) { | ||
328 | LOG_RESULT_LOCATION(result); | ||
329 | return result; | ||
330 | } | ||
331 | break; | ||
332 | } | ||
333 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
334 | (*(unsigned long *)data->data) = 0; | ||
335 | break; | ||
336 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
337 | (*(unsigned long *)data->data) = 8000; | ||
338 | break; | ||
339 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
340 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
341 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
342 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
343 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
344 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
345 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
346 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
347 | default: | ||
348 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
349 | }; | ||
350 | |||
351 | return INV_SUCCESS; | ||
352 | } | ||
353 | |||
354 | static struct ext_slave_read_trigger ak8972_read_trigger = { | ||
355 | /*.reg = */ 0x0A, | ||
356 | /*.value = */ 0x01 | ||
357 | }; | ||
358 | |||
359 | static struct ext_slave_descr ak8972_descr = { | ||
360 | .init = ak8972_init, | ||
361 | .exit = ak8972_exit, | ||
362 | .suspend = ak8972_suspend, | ||
363 | .resume = ak8972_resume, | ||
364 | .read = ak8972_read, | ||
365 | .config = ak8972_config, | ||
366 | .get_config = ak8972_get_config, | ||
367 | .name = "ak8972", | ||
368 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
369 | .id = COMPASS_ID_AK8972, | ||
370 | .read_reg = 0x01, | ||
371 | .read_len = 9, | ||
372 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
373 | .range = {39321, 6000}, | ||
374 | .trigger = &ak8972_read_trigger, | ||
375 | }; | ||
376 | |||
377 | static | ||
378 | struct ext_slave_descr *ak8972_get_slave_descr(void) | ||
379 | { | ||
380 | return &ak8972_descr; | ||
381 | } | ||
382 | |||
383 | /* -------------------------------------------------------------------------- */ | ||
384 | struct ak8972_mod_private_data { | ||
385 | struct i2c_client *client; | ||
386 | struct ext_slave_platform_data *pdata; | ||
387 | }; | ||
388 | |||
389 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
390 | |||
391 | static int ak8972_mod_probe(struct i2c_client *client, | ||
392 | const struct i2c_device_id *devid) | ||
393 | { | ||
394 | struct ext_slave_platform_data *pdata; | ||
395 | struct ak8972_mod_private_data *private_data; | ||
396 | int result = 0; | ||
397 | |||
398 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
399 | |||
400 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
401 | result = -ENODEV; | ||
402 | goto out_no_free; | ||
403 | } | ||
404 | |||
405 | pdata = client->dev.platform_data; | ||
406 | if (!pdata) { | ||
407 | dev_err(&client->adapter->dev, | ||
408 | "Missing platform data for slave %s\n", devid->name); | ||
409 | result = -EFAULT; | ||
410 | goto out_no_free; | ||
411 | } | ||
412 | |||
413 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
414 | if (!private_data) { | ||
415 | result = -ENOMEM; | ||
416 | goto out_no_free; | ||
417 | } | ||
418 | |||
419 | i2c_set_clientdata(client, private_data); | ||
420 | private_data->client = client; | ||
421 | private_data->pdata = pdata; | ||
422 | |||
423 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
424 | ak8972_get_slave_descr); | ||
425 | if (result) { | ||
426 | dev_err(&client->adapter->dev, | ||
427 | "Slave registration failed: %s, %d\n", | ||
428 | devid->name, result); | ||
429 | goto out_free_memory; | ||
430 | } | ||
431 | |||
432 | return result; | ||
433 | |||
434 | out_free_memory: | ||
435 | kfree(private_data); | ||
436 | out_no_free: | ||
437 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
438 | return result; | ||
439 | |||
440 | } | ||
441 | |||
442 | static int ak8972_mod_remove(struct i2c_client *client) | ||
443 | { | ||
444 | struct ak8972_mod_private_data *private_data = | ||
445 | i2c_get_clientdata(client); | ||
446 | |||
447 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
448 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
449 | ak8972_get_slave_descr); | ||
450 | |||
451 | kfree(private_data); | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static const struct i2c_device_id ak8972_mod_id[] = { | ||
456 | { "ak8972", COMPASS_ID_AK8972 }, | ||
457 | {} | ||
458 | }; | ||
459 | |||
460 | MODULE_DEVICE_TABLE(i2c, ak8972_mod_id); | ||
461 | |||
462 | static struct i2c_driver ak8972_mod_driver = { | ||
463 | .class = I2C_CLASS_HWMON, | ||
464 | .probe = ak8972_mod_probe, | ||
465 | .remove = ak8972_mod_remove, | ||
466 | .id_table = ak8972_mod_id, | ||
467 | .driver = { | ||
468 | .owner = THIS_MODULE, | ||
469 | .name = "ak8972_mod", | ||
470 | }, | ||
471 | .address_list = normal_i2c, | ||
472 | }; | ||
473 | |||
474 | static int __init ak8972_mod_init(void) | ||
475 | { | ||
476 | int res = i2c_add_driver(&ak8972_mod_driver); | ||
477 | pr_info("%s: Probe name %s\n", __func__, "ak8972_mod"); | ||
478 | if (res) | ||
479 | pr_err("%s failed\n", __func__); | ||
480 | return res; | ||
481 | } | ||
482 | |||
483 | static void __exit ak8972_mod_exit(void) | ||
484 | { | ||
485 | pr_info("%s\n", __func__); | ||
486 | i2c_del_driver(&ak8972_mod_driver); | ||
487 | } | ||
488 | |||
489 | module_init(ak8972_mod_init); | ||
490 | module_exit(ak8972_mod_exit); | ||
491 | |||
492 | MODULE_AUTHOR("Invensense Corporation"); | ||
493 | MODULE_DESCRIPTION("Driver to integrate AK8972 sensor with the MPU"); | ||
494 | MODULE_LICENSE("GPL"); | ||
495 | MODULE_ALIAS("ak8972_mod"); | ||
496 | |||
497 | /** | ||
498 | * @} | ||
499 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ak8975.c b/drivers/misc/inv_mpu/compass/ak8975.c new file mode 100644 index 00000000000..3642e29e89a --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak8975.c | |||
@@ -0,0 +1,500 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file ak8975.c | ||
25 | * @brief Magnetometer setup and handling methods for the AKM AK8975, | ||
26 | * AKM AK8975B, and AKM AK8975C compass devices. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | #define AK8975_REG_ST1 (0x02) | ||
49 | #define AK8975_REG_HXL (0x03) | ||
50 | #define AK8975_REG_ST2 (0x09) | ||
51 | |||
52 | #define AK8975_REG_CNTL (0x0A) | ||
53 | #define AK8975_REG_ASAX (0x10) | ||
54 | #define AK8975_REG_ASAY (0x11) | ||
55 | #define AK8975_REG_ASAZ (0x12) | ||
56 | |||
57 | #define AK8975_CNTL_MODE_POWER_DOWN (0x00) | ||
58 | #define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
59 | #define AK8975_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
60 | |||
61 | /* -------------------------------------------------------------------------- */ | ||
62 | struct ak8975_config { | ||
63 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
64 | }; | ||
65 | |||
66 | struct ak8975_private_data { | ||
67 | struct ak8975_config init; | ||
68 | }; | ||
69 | |||
70 | /* -------------------------------------------------------------------------- */ | ||
71 | static int ak8975_init(void *mlsl_handle, | ||
72 | struct ext_slave_descr *slave, | ||
73 | struct ext_slave_platform_data *pdata) | ||
74 | { | ||
75 | int result; | ||
76 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
77 | |||
78 | struct ak8975_private_data *private_data; | ||
79 | private_data = (struct ak8975_private_data *) | ||
80 | kzalloc(sizeof(struct ak8975_private_data), GFP_KERNEL); | ||
81 | |||
82 | if (!private_data) | ||
83 | return INV_ERROR_MEMORY_EXAUSTED; | ||
84 | |||
85 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
86 | AK8975_REG_CNTL, | ||
87 | AK8975_CNTL_MODE_POWER_DOWN); | ||
88 | if (result) { | ||
89 | LOG_RESULT_LOCATION(result); | ||
90 | return result; | ||
91 | } | ||
92 | /* Wait at least 100us */ | ||
93 | udelay(100); | ||
94 | |||
95 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
96 | AK8975_REG_CNTL, | ||
97 | AK8975_CNTL_MODE_FUSE_ROM_ACCESS); | ||
98 | if (result) { | ||
99 | LOG_RESULT_LOCATION(result); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | /* Wait at least 200us */ | ||
104 | udelay(200); | ||
105 | |||
106 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
107 | AK8975_REG_ASAX, | ||
108 | COMPASS_NUM_AXES, serial_data); | ||
109 | if (result) { | ||
110 | LOG_RESULT_LOCATION(result); | ||
111 | return result; | ||
112 | } | ||
113 | |||
114 | pdata->private_data = private_data; | ||
115 | |||
116 | private_data->init.asa[0] = serial_data[0]; | ||
117 | private_data->init.asa[1] = serial_data[1]; | ||
118 | private_data->init.asa[2] = serial_data[2]; | ||
119 | |||
120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
121 | AK8975_REG_CNTL, | ||
122 | AK8975_CNTL_MODE_POWER_DOWN); | ||
123 | if (result) { | ||
124 | LOG_RESULT_LOCATION(result); | ||
125 | return result; | ||
126 | } | ||
127 | |||
128 | udelay(100); | ||
129 | return INV_SUCCESS; | ||
130 | } | ||
131 | |||
132 | static int ak8975_exit(void *mlsl_handle, | ||
133 | struct ext_slave_descr *slave, | ||
134 | struct ext_slave_platform_data *pdata) | ||
135 | { | ||
136 | kfree(pdata->private_data); | ||
137 | return INV_SUCCESS; | ||
138 | } | ||
139 | |||
140 | static int ak8975_suspend(void *mlsl_handle, | ||
141 | struct ext_slave_descr *slave, | ||
142 | struct ext_slave_platform_data *pdata) | ||
143 | { | ||
144 | int result = INV_SUCCESS; | ||
145 | result = | ||
146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
147 | AK8975_REG_CNTL, | ||
148 | AK8975_CNTL_MODE_POWER_DOWN); | ||
149 | msleep(1); /* wait at least 100us */ | ||
150 | if (result) { | ||
151 | LOG_RESULT_LOCATION(result); | ||
152 | return result; | ||
153 | } | ||
154 | return result; | ||
155 | } | ||
156 | |||
157 | static int ak8975_resume(void *mlsl_handle, | ||
158 | struct ext_slave_descr *slave, | ||
159 | struct ext_slave_platform_data *pdata) | ||
160 | { | ||
161 | int result = INV_SUCCESS; | ||
162 | result = | ||
163 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
164 | AK8975_REG_CNTL, | ||
165 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
166 | if (result) { | ||
167 | LOG_RESULT_LOCATION(result); | ||
168 | return result; | ||
169 | } | ||
170 | return result; | ||
171 | } | ||
172 | |||
173 | static int ak8975_read(void *mlsl_handle, | ||
174 | struct ext_slave_descr *slave, | ||
175 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
176 | { | ||
177 | unsigned char regs[8]; | ||
178 | unsigned char *stat = ®s[0]; | ||
179 | unsigned char *stat2 = ®s[7]; | ||
180 | int result = INV_SUCCESS; | ||
181 | int status = INV_SUCCESS; | ||
182 | |||
183 | result = | ||
184 | inv_serial_read(mlsl_handle, pdata->address, AK8975_REG_ST1, | ||
185 | 8, regs); | ||
186 | if (result) { | ||
187 | LOG_RESULT_LOCATION(result); | ||
188 | return result; | ||
189 | } | ||
190 | |||
191 | /* Always return the data and the status registers */ | ||
192 | memcpy(data, ®s[1], 6); | ||
193 | data[6] = regs[0]; | ||
194 | data[7] = regs[7]; | ||
195 | |||
196 | /* | ||
197 | * ST : data ready - | ||
198 | * Measurement has been completed and data is ready to be read. | ||
199 | */ | ||
200 | if (*stat & 0x01) | ||
201 | status = INV_SUCCESS; | ||
202 | |||
203 | /* | ||
204 | * ST2 : data error - | ||
205 | * occurs when data read is started outside of a readable period; | ||
206 | * data read would not be correct. | ||
207 | * Valid in continuous measurement mode only. | ||
208 | * In single measurement mode this error should not occour but we | ||
209 | * stil account for it and return an error, since the data would be | ||
210 | * corrupted. | ||
211 | * DERR bit is self-clearing when ST2 register is read. | ||
212 | */ | ||
213 | if (*stat2 & 0x04) | ||
214 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
215 | /* | ||
216 | * ST2 : overflow - | ||
217 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
218 | * This is likely to happen in presence of an external magnetic | ||
219 | * disturbance; it indicates, the sensor data is incorrect and should | ||
220 | * be ignored. | ||
221 | * An error is returned. | ||
222 | * HOFL bit clears when a new measurement starts. | ||
223 | */ | ||
224 | if (*stat2 & 0x08) | ||
225 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
226 | /* | ||
227 | * ST : overrun - | ||
228 | * the previous sample was not fetched and lost. | ||
229 | * Valid in continuous measurement mode only. | ||
230 | * In single measurement mode this error should not occour and we | ||
231 | * don't consider this condition an error. | ||
232 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
233 | * read. | ||
234 | */ | ||
235 | if (*stat & 0x02) { | ||
236 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
237 | status = INV_SUCCESS; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * trigger next measurement if: | ||
242 | * - stat is non zero; | ||
243 | * - if stat is zero and stat2 is non zero. | ||
244 | * Won't trigger if data is not ready and there was no error. | ||
245 | */ | ||
246 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
247 | result = inv_serial_single_write( | ||
248 | mlsl_handle, pdata->address, | ||
249 | AK8975_REG_CNTL, AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
250 | if (result) { | ||
251 | LOG_RESULT_LOCATION(result); | ||
252 | return result; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | return status; | ||
257 | } | ||
258 | |||
259 | static int ak8975_config(void *mlsl_handle, | ||
260 | struct ext_slave_descr *slave, | ||
261 | struct ext_slave_platform_data *pdata, | ||
262 | struct ext_slave_config *data) | ||
263 | { | ||
264 | int result; | ||
265 | if (!data->data) | ||
266 | return INV_ERROR_INVALID_PARAMETER; | ||
267 | |||
268 | switch (data->key) { | ||
269 | case MPU_SLAVE_WRITE_REGISTERS: | ||
270 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
271 | data->len, | ||
272 | (unsigned char *)data->data); | ||
273 | if (result) { | ||
274 | LOG_RESULT_LOCATION(result); | ||
275 | return result; | ||
276 | } | ||
277 | break; | ||
278 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
279 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
280 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
281 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
282 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
283 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
284 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
285 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
286 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
287 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
288 | default: | ||
289 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
290 | }; | ||
291 | |||
292 | return INV_SUCCESS; | ||
293 | } | ||
294 | |||
295 | static int ak8975_get_config(void *mlsl_handle, | ||
296 | struct ext_slave_descr *slave, | ||
297 | struct ext_slave_platform_data *pdata, | ||
298 | struct ext_slave_config *data) | ||
299 | { | ||
300 | struct ak8975_private_data *private_data = pdata->private_data; | ||
301 | int result; | ||
302 | if (!data->data) | ||
303 | return INV_ERROR_INVALID_PARAMETER; | ||
304 | |||
305 | switch (data->key) { | ||
306 | case MPU_SLAVE_READ_REGISTERS: | ||
307 | { | ||
308 | unsigned char *serial_data = | ||
309 | (unsigned char *)data->data; | ||
310 | result = | ||
311 | inv_serial_read(mlsl_handle, pdata->address, | ||
312 | serial_data[0], data->len - 1, | ||
313 | &serial_data[1]); | ||
314 | if (result) { | ||
315 | LOG_RESULT_LOCATION(result); | ||
316 | return result; | ||
317 | } | ||
318 | break; | ||
319 | } | ||
320 | case MPU_SLAVE_READ_SCALE: | ||
321 | { | ||
322 | unsigned char *serial_data = | ||
323 | (unsigned char *)data->data; | ||
324 | serial_data[0] = private_data->init.asa[0]; | ||
325 | serial_data[1] = private_data->init.asa[1]; | ||
326 | serial_data[2] = private_data->init.asa[2]; | ||
327 | result = INV_SUCCESS; | ||
328 | if (result) { | ||
329 | LOG_RESULT_LOCATION(result); | ||
330 | return result; | ||
331 | } | ||
332 | break; | ||
333 | } | ||
334 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
335 | (*(unsigned long *)data->data) = 0; | ||
336 | break; | ||
337 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
338 | (*(unsigned long *)data->data) = 8000; | ||
339 | break; | ||
340 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
341 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
342 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
343 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
344 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
345 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
346 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
347 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
348 | default: | ||
349 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
350 | }; | ||
351 | |||
352 | return INV_SUCCESS; | ||
353 | } | ||
354 | |||
355 | static struct ext_slave_read_trigger ak8975_read_trigger = { | ||
356 | /*.reg = */ 0x0A, | ||
357 | /*.value = */ 0x01 | ||
358 | }; | ||
359 | |||
360 | static struct ext_slave_descr ak8975_descr = { | ||
361 | .init = ak8975_init, | ||
362 | .exit = ak8975_exit, | ||
363 | .suspend = ak8975_suspend, | ||
364 | .resume = ak8975_resume, | ||
365 | .read = ak8975_read, | ||
366 | .config = ak8975_config, | ||
367 | .get_config = ak8975_get_config, | ||
368 | .name = "ak8975", | ||
369 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
370 | .id = COMPASS_ID_AK8975, | ||
371 | .read_reg = 0x01, | ||
372 | .read_len = 10, | ||
373 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
374 | .range = {9830, 4000}, | ||
375 | .trigger = &ak8975_read_trigger, | ||
376 | }; | ||
377 | |||
378 | static | ||
379 | struct ext_slave_descr *ak8975_get_slave_descr(void) | ||
380 | { | ||
381 | return &ak8975_descr; | ||
382 | } | ||
383 | |||
384 | /* -------------------------------------------------------------------------- */ | ||
385 | struct ak8975_mod_private_data { | ||
386 | struct i2c_client *client; | ||
387 | struct ext_slave_platform_data *pdata; | ||
388 | }; | ||
389 | |||
390 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
391 | |||
392 | static int ak8975_mod_probe(struct i2c_client *client, | ||
393 | const struct i2c_device_id *devid) | ||
394 | { | ||
395 | struct ext_slave_platform_data *pdata; | ||
396 | struct ak8975_mod_private_data *private_data; | ||
397 | int result = 0; | ||
398 | |||
399 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
400 | |||
401 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
402 | result = -ENODEV; | ||
403 | goto out_no_free; | ||
404 | } | ||
405 | |||
406 | pdata = client->dev.platform_data; | ||
407 | if (!pdata) { | ||
408 | dev_err(&client->adapter->dev, | ||
409 | "Missing platform data for slave %s\n", devid->name); | ||
410 | result = -EFAULT; | ||
411 | goto out_no_free; | ||
412 | } | ||
413 | |||
414 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
415 | if (!private_data) { | ||
416 | result = -ENOMEM; | ||
417 | goto out_no_free; | ||
418 | } | ||
419 | |||
420 | i2c_set_clientdata(client, private_data); | ||
421 | private_data->client = client; | ||
422 | private_data->pdata = pdata; | ||
423 | |||
424 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
425 | ak8975_get_slave_descr); | ||
426 | if (result) { | ||
427 | dev_err(&client->adapter->dev, | ||
428 | "Slave registration failed: %s, %d\n", | ||
429 | devid->name, result); | ||
430 | goto out_free_memory; | ||
431 | } | ||
432 | |||
433 | return result; | ||
434 | |||
435 | out_free_memory: | ||
436 | kfree(private_data); | ||
437 | out_no_free: | ||
438 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
439 | return result; | ||
440 | |||
441 | } | ||
442 | |||
443 | static int ak8975_mod_remove(struct i2c_client *client) | ||
444 | { | ||
445 | struct ak8975_mod_private_data *private_data = | ||
446 | i2c_get_clientdata(client); | ||
447 | |||
448 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
449 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
450 | ak8975_get_slave_descr); | ||
451 | |||
452 | kfree(private_data); | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static const struct i2c_device_id ak8975_mod_id[] = { | ||
457 | { "ak8975", COMPASS_ID_AK8975 }, | ||
458 | {} | ||
459 | }; | ||
460 | |||
461 | MODULE_DEVICE_TABLE(i2c, ak8975_mod_id); | ||
462 | |||
463 | static struct i2c_driver ak8975_mod_driver = { | ||
464 | .class = I2C_CLASS_HWMON, | ||
465 | .probe = ak8975_mod_probe, | ||
466 | .remove = ak8975_mod_remove, | ||
467 | .id_table = ak8975_mod_id, | ||
468 | .driver = { | ||
469 | .owner = THIS_MODULE, | ||
470 | .name = "ak8975_mod", | ||
471 | }, | ||
472 | .address_list = normal_i2c, | ||
473 | }; | ||
474 | |||
475 | static int __init ak8975_mod_init(void) | ||
476 | { | ||
477 | int res = i2c_add_driver(&ak8975_mod_driver); | ||
478 | pr_info("%s: Probe name %s\n", __func__, "ak8975_mod"); | ||
479 | if (res) | ||
480 | pr_err("%s failed\n", __func__); | ||
481 | return res; | ||
482 | } | ||
483 | |||
484 | static void __exit ak8975_mod_exit(void) | ||
485 | { | ||
486 | pr_info("%s\n", __func__); | ||
487 | i2c_del_driver(&ak8975_mod_driver); | ||
488 | } | ||
489 | |||
490 | module_init(ak8975_mod_init); | ||
491 | module_exit(ak8975_mod_exit); | ||
492 | |||
493 | MODULE_AUTHOR("Invensense Corporation"); | ||
494 | MODULE_DESCRIPTION("Driver to integrate AK8975 sensor with the MPU"); | ||
495 | MODULE_LICENSE("GPL"); | ||
496 | MODULE_ALIAS("ak8975_mod"); | ||
497 | |||
498 | /** | ||
499 | * @} | ||
500 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ak89xx.c b/drivers/misc/inv_mpu/compass/ak89xx.c new file mode 100644 index 00000000000..d15b0b8dbe6 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ak89xx.c | |||
@@ -0,0 +1,522 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file ak89xx.c | ||
25 | * @brief Magnetometer setup and handling methods for the AKM | ||
26 | * compass devices. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | #define AK89XX_REG_ST1 (0x02) | ||
49 | #define AK89XX_REG_HXL (0x03) | ||
50 | #define AK89XX_REG_ST2 (0x09) | ||
51 | |||
52 | #define AK89XX_REG_CNTL (0x0A) | ||
53 | #define AK89XX_REG_ASAX (0x10) | ||
54 | #define AK89XX_REG_ASAY (0x11) | ||
55 | #define AK89XX_REG_ASAZ (0x12) | ||
56 | |||
57 | #define AK89XX_CNTL_MODE_POWER_DOWN (0x00) | ||
58 | #define AK89XX_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
59 | #define AK89XX_CNTL_MODE_FUSE_ROM_ACCESS (0x0f) | ||
60 | |||
61 | /* -------------------------------------------------------------------------- */ | ||
62 | struct ak89xx_config { | ||
63 | char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */ | ||
64 | }; | ||
65 | |||
66 | struct ak89xx_private_data { | ||
67 | struct ak89xx_config init; | ||
68 | }; | ||
69 | |||
70 | /* -------------------------------------------------------------------------- */ | ||
71 | static int ak89xx_init(void *mlsl_handle, | ||
72 | struct ext_slave_descr *slave, | ||
73 | struct ext_slave_platform_data *pdata) | ||
74 | { | ||
75 | int result; | ||
76 | unsigned char serial_data[COMPASS_NUM_AXES]; | ||
77 | |||
78 | struct ak89xx_private_data *private_data; | ||
79 | private_data = (struct ak89xx_private_data *) | ||
80 | kzalloc(sizeof(struct ak89xx_private_data), GFP_KERNEL); | ||
81 | |||
82 | if (!private_data) | ||
83 | return INV_ERROR_MEMORY_EXAUSTED; | ||
84 | |||
85 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
86 | AK89XX_REG_CNTL, | ||
87 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
88 | if (result) { | ||
89 | LOG_RESULT_LOCATION(result); | ||
90 | return result; | ||
91 | } | ||
92 | /* Wait at least 100us */ | ||
93 | udelay(100); | ||
94 | |||
95 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
96 | AK89XX_REG_CNTL, | ||
97 | AK89XX_CNTL_MODE_FUSE_ROM_ACCESS); | ||
98 | if (result) { | ||
99 | LOG_RESULT_LOCATION(result); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | /* Wait at least 200us */ | ||
104 | udelay(200); | ||
105 | |||
106 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
107 | AK89XX_REG_ASAX, | ||
108 | COMPASS_NUM_AXES, serial_data); | ||
109 | if (result) { | ||
110 | LOG_RESULT_LOCATION(result); | ||
111 | return result; | ||
112 | } | ||
113 | |||
114 | pdata->private_data = private_data; | ||
115 | |||
116 | private_data->init.asa[0] = serial_data[0]; | ||
117 | private_data->init.asa[1] = serial_data[1]; | ||
118 | private_data->init.asa[2] = serial_data[2]; | ||
119 | |||
120 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
121 | AK89XX_REG_CNTL, | ||
122 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
123 | if (result) { | ||
124 | LOG_RESULT_LOCATION(result); | ||
125 | return result; | ||
126 | } | ||
127 | |||
128 | udelay(100); | ||
129 | return INV_SUCCESS; | ||
130 | } | ||
131 | |||
132 | static int ak89xx_exit(void *mlsl_handle, | ||
133 | struct ext_slave_descr *slave, | ||
134 | struct ext_slave_platform_data *pdata) | ||
135 | { | ||
136 | kfree(pdata->private_data); | ||
137 | return INV_SUCCESS; | ||
138 | } | ||
139 | |||
140 | static int ak89xx_suspend(void *mlsl_handle, | ||
141 | struct ext_slave_descr *slave, | ||
142 | struct ext_slave_platform_data *pdata) | ||
143 | { | ||
144 | int result = INV_SUCCESS; | ||
145 | result = | ||
146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
147 | AK89XX_REG_CNTL, | ||
148 | AK89XX_CNTL_MODE_POWER_DOWN); | ||
149 | msleep(1); /* wait at least 100us */ | ||
150 | if (result) { | ||
151 | LOG_RESULT_LOCATION(result); | ||
152 | return result; | ||
153 | } | ||
154 | return result; | ||
155 | } | ||
156 | |||
157 | static int ak89xx_resume(void *mlsl_handle, | ||
158 | struct ext_slave_descr *slave, | ||
159 | struct ext_slave_platform_data *pdata) | ||
160 | { | ||
161 | int result = INV_SUCCESS; | ||
162 | result = | ||
163 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
164 | AK89XX_REG_CNTL, | ||
165 | AK89XX_CNTL_MODE_SINGLE_MEASUREMENT); | ||
166 | if (result) { | ||
167 | LOG_RESULT_LOCATION(result); | ||
168 | return result; | ||
169 | } | ||
170 | return result; | ||
171 | } | ||
172 | |||
173 | static int ak89xx_read(void *mlsl_handle, | ||
174 | struct ext_slave_descr *slave, | ||
175 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
176 | { | ||
177 | unsigned char regs[8]; | ||
178 | unsigned char *stat = ®s[0]; | ||
179 | unsigned char *stat2 = ®s[7]; | ||
180 | int result = INV_SUCCESS; | ||
181 | int status = INV_SUCCESS; | ||
182 | |||
183 | result = | ||
184 | inv_serial_read(mlsl_handle, pdata->address, AK89XX_REG_ST1, | ||
185 | 8, regs); | ||
186 | if (result) { | ||
187 | LOG_RESULT_LOCATION(result); | ||
188 | return result; | ||
189 | } | ||
190 | |||
191 | /* Always return the data and the status registers */ | ||
192 | memcpy(data, ®s[1], 6); | ||
193 | data[6] = regs[0]; | ||
194 | data[7] = regs[7]; | ||
195 | |||
196 | /* | ||
197 | * ST : data ready - | ||
198 | * Measurement has been completed and data is ready to be read. | ||
199 | */ | ||
200 | if (*stat & 0x01) | ||
201 | status = INV_SUCCESS; | ||
202 | |||
203 | /* | ||
204 | * ST2 : data error - | ||
205 | * occurs when data read is started outside of a readable period; | ||
206 | * data read would not be correct. | ||
207 | * Valid in continuous measurement mode only. | ||
208 | * In single measurement mode this error should not occour but we | ||
209 | * stil account for it and return an error, since the data would be | ||
210 | * corrupted. | ||
211 | * DERR bit is self-clearing when ST2 register is read. | ||
212 | * | ||
213 | * This bit is always zero on the AK8963. | ||
214 | */ | ||
215 | if (*stat2 & 0x04) | ||
216 | status = INV_ERROR_COMPASS_DATA_ERROR; | ||
217 | /* | ||
218 | * ST2 : overflow - | ||
219 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
220 | * This is likely to happen in presence of an external magnetic | ||
221 | * disturbance; it indicates, the sensor data is incorrect and should | ||
222 | * be ignored. | ||
223 | * An error is returned. | ||
224 | * HOFL bit clears when a new measurement starts. | ||
225 | */ | ||
226 | if (*stat2 & 0x08) | ||
227 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
228 | /* | ||
229 | * ST : overrun - | ||
230 | * the previous sample was not fetched and lost. | ||
231 | * Valid in continuous measurement mode only. | ||
232 | * In single measurement mode this error should not occour and we | ||
233 | * don't consider this condition an error. | ||
234 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
235 | * read. | ||
236 | */ | ||
237 | if (*stat & 0x02) { | ||
238 | /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
239 | status = INV_SUCCESS; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * trigger next measurement if: | ||
244 | * - stat is non zero; | ||
245 | * - if stat is zero and stat2 is non zero. | ||
246 | * Won't trigger if data is not ready and there was no error. | ||
247 | */ | ||
248 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
249 | result = inv_serial_single_write( | ||
250 | mlsl_handle, pdata->address, | ||
251 | AK89XX_REG_CNTL, AK89XX_CNTL_MODE_SINGLE_MEASUREMENT); | ||
252 | if (result) { | ||
253 | LOG_RESULT_LOCATION(result); | ||
254 | return result; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | return status; | ||
259 | } | ||
260 | |||
261 | static int ak89xx_config(void *mlsl_handle, | ||
262 | struct ext_slave_descr *slave, | ||
263 | struct ext_slave_platform_data *pdata, | ||
264 | struct ext_slave_config *data) | ||
265 | { | ||
266 | int result; | ||
267 | if (!data->data) | ||
268 | return INV_ERROR_INVALID_PARAMETER; | ||
269 | |||
270 | switch (data->key) { | ||
271 | case MPU_SLAVE_WRITE_REGISTERS: | ||
272 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
273 | data->len, | ||
274 | (unsigned char *)data->data); | ||
275 | if (result) { | ||
276 | LOG_RESULT_LOCATION(result); | ||
277 | return result; | ||
278 | } | ||
279 | break; | ||
280 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
281 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
282 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
283 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
284 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
285 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
286 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
287 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
288 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
289 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
290 | default: | ||
291 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
292 | }; | ||
293 | |||
294 | return INV_SUCCESS; | ||
295 | } | ||
296 | |||
297 | static int ak89xx_get_config(void *mlsl_handle, | ||
298 | struct ext_slave_descr *slave, | ||
299 | struct ext_slave_platform_data *pdata, | ||
300 | struct ext_slave_config *data) | ||
301 | { | ||
302 | struct ak89xx_private_data *private_data = pdata->private_data; | ||
303 | int result; | ||
304 | if (!data->data) | ||
305 | return INV_ERROR_INVALID_PARAMETER; | ||
306 | |||
307 | switch (data->key) { | ||
308 | case MPU_SLAVE_READ_REGISTERS: | ||
309 | { | ||
310 | unsigned char *serial_data = | ||
311 | (unsigned char *)data->data; | ||
312 | result = | ||
313 | inv_serial_read(mlsl_handle, pdata->address, | ||
314 | serial_data[0], data->len - 1, | ||
315 | &serial_data[1]); | ||
316 | if (result) { | ||
317 | LOG_RESULT_LOCATION(result); | ||
318 | return result; | ||
319 | } | ||
320 | break; | ||
321 | } | ||
322 | case MPU_SLAVE_READ_SCALE: | ||
323 | { | ||
324 | unsigned char *serial_data = | ||
325 | (unsigned char *)data->data; | ||
326 | serial_data[0] = private_data->init.asa[0]; | ||
327 | serial_data[1] = private_data->init.asa[1]; | ||
328 | serial_data[2] = private_data->init.asa[2]; | ||
329 | result = INV_SUCCESS; | ||
330 | if (result) { | ||
331 | LOG_RESULT_LOCATION(result); | ||
332 | return result; | ||
333 | } | ||
334 | break; | ||
335 | } | ||
336 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
337 | (*(unsigned long *)data->data) = 0; | ||
338 | break; | ||
339 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
340 | (*(unsigned long *)data->data) = 8000; | ||
341 | break; | ||
342 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
343 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
344 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
345 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
346 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
347 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
348 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
349 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
350 | default: | ||
351 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
352 | }; | ||
353 | |||
354 | return INV_SUCCESS; | ||
355 | } | ||
356 | |||
357 | static struct ext_slave_read_trigger ak89xx_read_trigger = { | ||
358 | /*.reg = */ 0x0A, | ||
359 | /*.value = */ 0x01 | ||
360 | }; | ||
361 | |||
362 | static struct ext_slave_descr ak89xx_descr = { | ||
363 | .init = ak89xx_init, | ||
364 | .exit = ak89xx_exit, | ||
365 | .suspend = ak89xx_suspend, | ||
366 | .resume = ak89xx_resume, | ||
367 | .read = ak89xx_read, | ||
368 | .config = ak89xx_config, | ||
369 | .get_config = ak89xx_get_config, | ||
370 | .name = "ak89xx", | ||
371 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
372 | .id = COMPASS_ID_AK8963, | ||
373 | .read_reg = 0x01, | ||
374 | .read_len = 10, | ||
375 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
376 | .range = {9830, 4000}, | ||
377 | .trigger = &ak89xx_read_trigger, | ||
378 | }; | ||
379 | |||
380 | static | ||
381 | struct ext_slave_descr *ak89xx_get_slave_descr(void) | ||
382 | { | ||
383 | return &ak89xx_descr; | ||
384 | } | ||
385 | |||
386 | /* -------------------------------------------------------------------------- */ | ||
387 | struct ak89xx_mod_private_data { | ||
388 | struct i2c_client *client; | ||
389 | struct ext_slave_platform_data *pdata; | ||
390 | }; | ||
391 | |||
392 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
393 | |||
394 | static int ak89xx_mod_probe(struct i2c_client *client, | ||
395 | const struct i2c_device_id *devid) | ||
396 | { | ||
397 | struct ext_slave_platform_data *pdata; | ||
398 | struct ak89xx_mod_private_data *private_data; | ||
399 | int result = 0; | ||
400 | |||
401 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
402 | |||
403 | /* Override default values based on model. */ | ||
404 | ak89xx_descr.id = devid->driver_data; | ||
405 | /* ak89xx_descr.name = devid->name; */ | ||
406 | switch (ak89xx_descr.id) { | ||
407 | case COMPASS_ID_AK8963: | ||
408 | break; | ||
409 | case COMPASS_ID_AK8972: | ||
410 | ak89xx_descr.read_len = 9; | ||
411 | ak89xx_descr.range.mantissa = 39321; | ||
412 | ak89xx_descr.range.fraction = 6000; | ||
413 | break; | ||
414 | case COMPASS_ID_AK8975: | ||
415 | break; | ||
416 | default: | ||
417 | result = -EFAULT; | ||
418 | goto out_no_free; | ||
419 | } | ||
420 | |||
421 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
422 | result = -ENODEV; | ||
423 | goto out_no_free; | ||
424 | } | ||
425 | |||
426 | pdata = client->dev.platform_data; | ||
427 | if (!pdata) { | ||
428 | dev_err(&client->adapter->dev, | ||
429 | "Missing platform data for slave %s\n", devid->name); | ||
430 | result = -EFAULT; | ||
431 | goto out_no_free; | ||
432 | } | ||
433 | |||
434 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
435 | if (!private_data) { | ||
436 | result = -ENOMEM; | ||
437 | goto out_no_free; | ||
438 | } | ||
439 | |||
440 | i2c_set_clientdata(client, private_data); | ||
441 | private_data->client = client; | ||
442 | private_data->pdata = pdata; | ||
443 | |||
444 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
445 | ak89xx_get_slave_descr); | ||
446 | if (result) { | ||
447 | dev_err(&client->adapter->dev, | ||
448 | "Slave registration failed: %s, %d\n", | ||
449 | devid->name, result); | ||
450 | goto out_free_memory; | ||
451 | } | ||
452 | |||
453 | return result; | ||
454 | |||
455 | out_free_memory: | ||
456 | kfree(private_data); | ||
457 | out_no_free: | ||
458 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
459 | return result; | ||
460 | |||
461 | } | ||
462 | |||
463 | static int ak89xx_mod_remove(struct i2c_client *client) | ||
464 | { | ||
465 | struct ak89xx_mod_private_data *private_data = | ||
466 | i2c_get_clientdata(client); | ||
467 | |||
468 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
469 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
470 | ak89xx_get_slave_descr); | ||
471 | |||
472 | kfree(private_data); | ||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static const struct i2c_device_id ak89xx_mod_id[] = { | ||
477 | { "ak8963", COMPASS_ID_AK8963 }, | ||
478 | { "ak8972", COMPASS_ID_AK8972 }, | ||
479 | { "ak8975", COMPASS_ID_AK8975 }, | ||
480 | {} | ||
481 | }; | ||
482 | |||
483 | MODULE_DEVICE_TABLE(i2c, ak89xx_mod_id); | ||
484 | |||
485 | static struct i2c_driver ak89xx_mod_driver = { | ||
486 | .class = I2C_CLASS_HWMON, | ||
487 | .probe = ak89xx_mod_probe, | ||
488 | .remove = ak89xx_mod_remove, | ||
489 | .id_table = ak89xx_mod_id, | ||
490 | .driver = { | ||
491 | .owner = THIS_MODULE, | ||
492 | .name = "ak89xx_mod", | ||
493 | }, | ||
494 | .address_list = normal_i2c, | ||
495 | }; | ||
496 | |||
497 | static int __init ak89xx_mod_init(void) | ||
498 | { | ||
499 | int res = i2c_add_driver(&ak89xx_mod_driver); | ||
500 | pr_info("%s: Probe name %s\n", __func__, "ak89xx_mod"); | ||
501 | if (res) | ||
502 | pr_err("%s failed\n", __func__); | ||
503 | return res; | ||
504 | } | ||
505 | |||
506 | static void __exit ak89xx_mod_exit(void) | ||
507 | { | ||
508 | pr_info("%s\n", __func__); | ||
509 | i2c_del_driver(&ak89xx_mod_driver); | ||
510 | } | ||
511 | |||
512 | module_init(ak89xx_mod_init); | ||
513 | module_exit(ak89xx_mod_exit); | ||
514 | |||
515 | MODULE_AUTHOR("Invensense Corporation"); | ||
516 | MODULE_DESCRIPTION("Driver to integrate AKM AK89XX sensor with the MPU"); | ||
517 | MODULE_LICENSE("GPL"); | ||
518 | MODULE_ALIAS("ak89xx_mod"); | ||
519 | |||
520 | /** | ||
521 | * @} | ||
522 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami306.c b/drivers/misc/inv_mpu/compass/ami306.c new file mode 100644 index 00000000000..f645457d161 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami306.c | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file ami306.c | ||
25 | * @brief Magnetometer setup and handling methods for Aichi AMI306 | ||
26 | * compass. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include "ami_hw.h" | ||
41 | #include "ami_sensor_def.h" | ||
42 | |||
43 | #include <log.h> | ||
44 | #include <linux/mpu.h> | ||
45 | #include "mlsl.h" | ||
46 | #include "mldl_cfg.h" | ||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-compass" | ||
49 | |||
50 | /* -------------------------------------------------------------------------- */ | ||
51 | #define AMI306_REG_DATAX (0x10) | ||
52 | #define AMI306_REG_STAT1 (0x18) | ||
53 | #define AMI306_REG_CNTL1 (0x1B) | ||
54 | #define AMI306_REG_CNTL2 (0x1C) | ||
55 | #define AMI306_REG_CNTL3 (0x1D) | ||
56 | #define AMI306_REG_CNTL4_1 (0x5C) | ||
57 | #define AMI306_REG_CNTL4_2 (0x5D) | ||
58 | |||
59 | #define AMI306_BIT_CNTL1_PC1 (0x80) | ||
60 | #define AMI306_BIT_CNTL1_ODR1 (0x10) | ||
61 | #define AMI306_BIT_CNTL1_FS1 (0x02) | ||
62 | |||
63 | #define AMI306_BIT_CNTL2_IEN (0x10) | ||
64 | #define AMI306_BIT_CNTL2_DREN (0x08) | ||
65 | #define AMI306_BIT_CNTL2_DRP (0x04) | ||
66 | #define AMI306_BIT_CNTL3_F0RCE (0x40) | ||
67 | |||
68 | #define AMI_FINE_MAX (96) | ||
69 | #define AMI_STANDARD_OFFSET (0x800) | ||
70 | #define AMI_GAIN_COR_DEFAULT (1000) | ||
71 | |||
72 | /* -------------------------------------------------------------------------- */ | ||
73 | struct ami306_private_data { | ||
74 | int isstandby; | ||
75 | unsigned char fine[3]; | ||
76 | struct ami_sensor_parametor param; | ||
77 | struct ami_win_parameter win; | ||
78 | }; | ||
79 | |||
80 | /* -------------------------------------------------------------------------- */ | ||
81 | static inline unsigned short little_u8_to_u16(unsigned char *p_u8) | ||
82 | { | ||
83 | return p_u8[0] | (p_u8[1] << 8); | ||
84 | } | ||
85 | |||
86 | static int ami306_set_bits8(void *mlsl_handle, | ||
87 | struct ext_slave_platform_data *pdata, | ||
88 | unsigned char reg, unsigned char bits) | ||
89 | { | ||
90 | int result; | ||
91 | unsigned char buf; | ||
92 | |||
93 | result = inv_serial_read(mlsl_handle, pdata->address, reg, 1, &buf); | ||
94 | if (result) { | ||
95 | LOG_RESULT_LOCATION(result); | ||
96 | return result; | ||
97 | } | ||
98 | |||
99 | buf |= bits; | ||
100 | result = inv_serial_single_write(mlsl_handle, pdata->address, reg, buf); | ||
101 | if (result) { | ||
102 | LOG_RESULT_LOCATION(result); | ||
103 | return result; | ||
104 | } | ||
105 | return result; | ||
106 | } | ||
107 | |||
108 | static int ami306_wait_data_ready(void *mlsl_handle, | ||
109 | struct ext_slave_platform_data *pdata, | ||
110 | unsigned long usecs, unsigned long times) | ||
111 | { | ||
112 | int result = 0; | ||
113 | unsigned char buf; | ||
114 | |||
115 | for (; 0 < times; --times) { | ||
116 | udelay(usecs); | ||
117 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
118 | AMI_REG_STA1, 1, &buf); | ||
119 | if (buf & AMI_STA1_DRDY_BIT) | ||
120 | return 0; | ||
121 | else if (buf & AMI_STA1_DOR_BIT) | ||
122 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
123 | } | ||
124 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
125 | } | ||
126 | |||
127 | static int ami306_read_raw_data(void *mlsl_handle, | ||
128 | struct ext_slave_platform_data *pdata, | ||
129 | short dat[3]) | ||
130 | { | ||
131 | int result; | ||
132 | unsigned char buf[6]; | ||
133 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
134 | AMI_REG_DATAX, sizeof(buf), buf); | ||
135 | if (result) { | ||
136 | LOG_RESULT_LOCATION(result); | ||
137 | return result; | ||
138 | } | ||
139 | dat[0] = little_u8_to_u16(&buf[0]); | ||
140 | dat[1] = little_u8_to_u16(&buf[2]); | ||
141 | dat[2] = little_u8_to_u16(&buf[4]); | ||
142 | return result; | ||
143 | } | ||
144 | |||
145 | #define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */ | ||
146 | #define AMI_DRDYWAIT 800 /* u(micro) sec */ | ||
147 | static int ami306_force_mesurement(void *mlsl_handle, | ||
148 | struct ext_slave_platform_data *pdata, | ||
149 | short ver[3]) | ||
150 | { | ||
151 | int result; | ||
152 | int status; | ||
153 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
154 | AMI_REG_CTRL3, AMI_CTRL3_FORCE_BIT); | ||
155 | if (result) { | ||
156 | LOG_RESULT_LOCATION(result); | ||
157 | return result; | ||
158 | } | ||
159 | |||
160 | result = ami306_wait_data_ready(mlsl_handle, pdata, | ||
161 | AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY); | ||
162 | if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) { | ||
163 | LOG_RESULT_LOCATION(result); | ||
164 | return result; | ||
165 | } | ||
166 | /* READ DATA X,Y,Z */ | ||
167 | status = ami306_read_raw_data(mlsl_handle, pdata, ver); | ||
168 | if (status) { | ||
169 | LOG_RESULT_LOCATION(status); | ||
170 | return status; | ||
171 | } | ||
172 | |||
173 | return result; | ||
174 | } | ||
175 | |||
176 | static int ami306_mea(void *mlsl_handle, | ||
177 | struct ext_slave_platform_data *pdata, short val[3]) | ||
178 | { | ||
179 | int result = ami306_force_mesurement(mlsl_handle, pdata, val); | ||
180 | if (result) { | ||
181 | LOG_RESULT_LOCATION(result); | ||
182 | return result; | ||
183 | } | ||
184 | val[0] += AMI_STANDARD_OFFSET; | ||
185 | val[1] += AMI_STANDARD_OFFSET; | ||
186 | val[2] += AMI_STANDARD_OFFSET; | ||
187 | return result; | ||
188 | } | ||
189 | |||
190 | static int ami306_write_offset(void *mlsl_handle, | ||
191 | struct ext_slave_platform_data *pdata, | ||
192 | unsigned char *fine) | ||
193 | { | ||
194 | int result = 0; | ||
195 | unsigned char dat[3]; | ||
196 | dat[0] = AMI_REG_OFFX; | ||
197 | dat[1] = 0x7f & fine[0]; | ||
198 | dat[2] = 0; | ||
199 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
200 | sizeof(dat), dat); | ||
201 | dat[0] = AMI_REG_OFFY; | ||
202 | dat[1] = 0x7f & fine[1]; | ||
203 | dat[2] = 0; | ||
204 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
205 | sizeof(dat), dat); | ||
206 | dat[0] = AMI_REG_OFFZ; | ||
207 | dat[1] = 0x7f & fine[2]; | ||
208 | dat[2] = 0; | ||
209 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
210 | sizeof(dat), dat); | ||
211 | return result; | ||
212 | } | ||
213 | |||
214 | static int ami306_start_sensor(void *mlsl_handle, | ||
215 | struct ext_slave_platform_data *pdata) | ||
216 | { | ||
217 | int result = 0; | ||
218 | unsigned char buf[3]; | ||
219 | struct ami306_private_data *private_data = pdata->private_data; | ||
220 | |||
221 | /* Step 1 */ | ||
222 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
223 | AMI_REG_CTRL1, | ||
224 | AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); | ||
225 | if (result) { | ||
226 | LOG_RESULT_LOCATION(result); | ||
227 | return result; | ||
228 | } | ||
229 | /* Step 2 */ | ||
230 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
231 | AMI_REG_CTRL2, AMI_CTRL2_DREN); | ||
232 | if (result) { | ||
233 | LOG_RESULT_LOCATION(result); | ||
234 | return result; | ||
235 | } | ||
236 | /* Step 3 */ | ||
237 | buf[0] = AMI_REG_CTRL4; | ||
238 | buf[1] = AMI_CTRL4_HS & 0xFF; | ||
239 | buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF; | ||
240 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
241 | sizeof(buf), buf); | ||
242 | if (result) { | ||
243 | LOG_RESULT_LOCATION(result); | ||
244 | return result; | ||
245 | } | ||
246 | /* Step 4 */ | ||
247 | result = ami306_write_offset(mlsl_handle, pdata, private_data->fine); | ||
248 | if (result) { | ||
249 | LOG_RESULT_LOCATION(result); | ||
250 | return result; | ||
251 | } | ||
252 | return result; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * This function does this. | ||
257 | * | ||
258 | * @param mlsl_handle this param is this. | ||
259 | * @param slave | ||
260 | * @param pdata | ||
261 | * | ||
262 | * @return INV_SUCCESS or non-zero error code. | ||
263 | */ | ||
264 | static int ami306_read_param(void *mlsl_handle, | ||
265 | struct ext_slave_descr *slave, | ||
266 | struct ext_slave_platform_data *pdata) | ||
267 | { | ||
268 | int result = 0; | ||
269 | unsigned char regs[12]; | ||
270 | struct ami306_private_data *private_data = pdata->private_data; | ||
271 | struct ami_sensor_parametor *param = &private_data->param; | ||
272 | |||
273 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
274 | AMI_REG_SENX, sizeof(regs), regs); | ||
275 | if (result) { | ||
276 | LOG_RESULT_LOCATION(result); | ||
277 | return result; | ||
278 | } | ||
279 | |||
280 | /* Little endian 16 bit registers */ | ||
281 | param->m_gain.x = little_u8_to_u16(®s[0]); | ||
282 | param->m_gain.y = little_u8_to_u16(®s[2]); | ||
283 | param->m_gain.z = little_u8_to_u16(®s[4]); | ||
284 | |||
285 | param->m_interference.xy = regs[7]; | ||
286 | param->m_interference.xz = regs[6]; | ||
287 | param->m_interference.yx = regs[9]; | ||
288 | param->m_interference.yz = regs[8]; | ||
289 | param->m_interference.zx = regs[11]; | ||
290 | param->m_interference.zy = regs[10]; | ||
291 | |||
292 | param->m_offset.x = AMI_STANDARD_OFFSET; | ||
293 | param->m_offset.y = AMI_STANDARD_OFFSET; | ||
294 | param->m_offset.z = AMI_STANDARD_OFFSET; | ||
295 | |||
296 | param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT; | ||
297 | param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT; | ||
298 | param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT; | ||
299 | |||
300 | return result; | ||
301 | } | ||
302 | |||
303 | static int ami306_initial_b0_adjust(void *mlsl_handle, | ||
304 | struct ext_slave_descr *slave, | ||
305 | struct ext_slave_platform_data *pdata) | ||
306 | { | ||
307 | int result; | ||
308 | unsigned char fine[3] = { 0 }; | ||
309 | short data[3]; | ||
310 | int diff[3] = { 0x7fff, 0x7fff, 0x7fff }; | ||
311 | int fn = 0; | ||
312 | int ax = 0; | ||
313 | unsigned char buf[3]; | ||
314 | struct ami306_private_data *private_data = pdata->private_data; | ||
315 | |||
316 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
317 | AMI_REG_CTRL2, AMI_CTRL2_DREN); | ||
318 | if (result) { | ||
319 | LOG_RESULT_LOCATION(result); | ||
320 | return result; | ||
321 | } | ||
322 | |||
323 | buf[0] = AMI_REG_CTRL4; | ||
324 | buf[1] = AMI_CTRL4_HS & 0xFF; | ||
325 | buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF; | ||
326 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
327 | sizeof(buf), buf); | ||
328 | if (result) { | ||
329 | LOG_RESULT_LOCATION(result); | ||
330 | return result; | ||
331 | } | ||
332 | |||
333 | for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */ | ||
334 | fine[0] = fine[1] = fine[2] = fn; | ||
335 | result = ami306_write_offset(mlsl_handle, pdata, fine); | ||
336 | if (result) { | ||
337 | LOG_RESULT_LOCATION(result); | ||
338 | return result; | ||
339 | } | ||
340 | |||
341 | result = ami306_force_mesurement(mlsl_handle, pdata, data); | ||
342 | if (result) { | ||
343 | LOG_RESULT_LOCATION(result); | ||
344 | return result; | ||
345 | } | ||
346 | MPL_LOGV("[%d] x:%-5d y:%-5d z:%-5d\n", | ||
347 | fn, data[0], data[1], data[2]); | ||
348 | |||
349 | for (ax = 0; ax < 3; ax++) { | ||
350 | /* search point most close to zero. */ | ||
351 | if (diff[ax] > abs(data[ax])) { | ||
352 | private_data->fine[ax] = fn; | ||
353 | diff[ax] = abs(data[ax]); | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | MPL_LOGV("fine x:%-5d y:%-5d z:%-5d\n", | ||
358 | private_data->fine[0], private_data->fine[1], | ||
359 | private_data->fine[2]); | ||
360 | |||
361 | result = ami306_write_offset(mlsl_handle, pdata, private_data->fine); | ||
362 | if (result) { | ||
363 | LOG_RESULT_LOCATION(result); | ||
364 | return result; | ||
365 | } | ||
366 | |||
367 | /* Software Reset */ | ||
368 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
369 | AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT); | ||
370 | if (result) { | ||
371 | LOG_RESULT_LOCATION(result); | ||
372 | return result; | ||
373 | } | ||
374 | return result; | ||
375 | } | ||
376 | |||
377 | #define SEH_RANGE_MIN 100 | ||
378 | #define SEH_RANGE_MAX 3950 | ||
379 | static int ami306_search_offset(void *mlsl_handle, | ||
380 | struct ext_slave_descr *slave, | ||
381 | struct ext_slave_platform_data *pdata) | ||
382 | { | ||
383 | int result; | ||
384 | int axis; | ||
385 | unsigned char regs[6]; | ||
386 | unsigned char run_flg[3] = { 1, 1, 1 }; | ||
387 | unsigned char fine[3]; | ||
388 | unsigned char win_range_fine[3]; | ||
389 | unsigned short fine_output[3]; | ||
390 | short val[3]; | ||
391 | unsigned short cnt[3] = { 0 }; | ||
392 | struct ami306_private_data *private_data = pdata->private_data; | ||
393 | |||
394 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
395 | AMI_FINEOUTPUT_X, sizeof(regs), regs); | ||
396 | if (result) { | ||
397 | LOG_RESULT_LOCATION(result); | ||
398 | return result; | ||
399 | } | ||
400 | fine_output[0] = little_u8_to_u16(®s[0]); | ||
401 | fine_output[1] = little_u8_to_u16(®s[2]); | ||
402 | fine_output[2] = little_u8_to_u16(®s[4]); | ||
403 | |||
404 | for (axis = 0; axis < 3; ++axis) { | ||
405 | if (fine_output[axis] == 0) { | ||
406 | MPL_LOGV("error fine_output %d axis:%d\n", | ||
407 | __LINE__, axis); | ||
408 | return -1; | ||
409 | } | ||
410 | /* fines per a window */ | ||
411 | win_range_fine[axis] = (SEH_RANGE_MAX - SEH_RANGE_MIN) | ||
412 | / fine_output[axis]; | ||
413 | } | ||
414 | |||
415 | /* get current fine */ | ||
416 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
417 | AMI_REG_OFFX, 2, ®s[0]); | ||
418 | if (result) { | ||
419 | LOG_RESULT_LOCATION(result); | ||
420 | return result; | ||
421 | } | ||
422 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
423 | AMI_REG_OFFY, 2, ®s[2]); | ||
424 | if (result) { | ||
425 | LOG_RESULT_LOCATION(result); | ||
426 | return result; | ||
427 | } | ||
428 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
429 | AMI_REG_OFFZ, 2, ®s[4]); | ||
430 | if (result) { | ||
431 | LOG_RESULT_LOCATION(result); | ||
432 | return result; | ||
433 | } | ||
434 | |||
435 | fine[0] = (unsigned char)(regs[0] & 0x7f); | ||
436 | fine[1] = (unsigned char)(regs[2] & 0x7f); | ||
437 | fine[2] = (unsigned char)(regs[4] & 0x7f); | ||
438 | |||
439 | while (run_flg[0] == 1 || run_flg[1] == 1 || run_flg[2] == 1) { | ||
440 | |||
441 | result = ami306_mea(mlsl_handle, pdata, val); | ||
442 | if (result) { | ||
443 | LOG_RESULT_LOCATION(result); | ||
444 | return result; | ||
445 | } | ||
446 | MPL_LOGV("val x:%-5d y:%-5d z:%-5d\n", val[0], val[1], val[2]); | ||
447 | MPL_LOGV("now fine x:%-5d y:%-5d z:%-5d\n", | ||
448 | fine[0], fine[1], fine[2]); | ||
449 | |||
450 | for (axis = 0; axis < 3; ++axis) { | ||
451 | if (axis == 0) { /* X-axis is reversed */ | ||
452 | val[axis] = 0x0FFF & ~val[axis]; | ||
453 | } | ||
454 | if (val[axis] < SEH_RANGE_MIN) { | ||
455 | /* At the case of less low limmit. */ | ||
456 | fine[axis] -= win_range_fine[axis]; | ||
457 | MPL_LOGV("min : fine=%d diff=%d\n", | ||
458 | fine[axis], win_range_fine[axis]); | ||
459 | } | ||
460 | if (val[axis] > SEH_RANGE_MAX) { | ||
461 | /* At the case of over high limmit. */ | ||
462 | fine[axis] += win_range_fine[axis]; | ||
463 | MPL_LOGV("max : fine=%d diff=%d\n", | ||
464 | fine[axis], win_range_fine[axis]); | ||
465 | } | ||
466 | if (SEH_RANGE_MIN <= val[axis] && | ||
467 | val[axis] <= SEH_RANGE_MAX) { | ||
468 | /* In the current window. */ | ||
469 | int diff_fine = | ||
470 | (val[axis] - AMI_STANDARD_OFFSET) / | ||
471 | fine_output[axis]; | ||
472 | fine[axis] += diff_fine; | ||
473 | run_flg[axis] = 0; | ||
474 | MPL_LOGV("mid : fine=%d diff=%d\n", | ||
475 | fine[axis], diff_fine); | ||
476 | } | ||
477 | |||
478 | if (!(0 <= fine[axis] && fine[axis] < AMI_FINE_MAX)) { | ||
479 | MPL_LOGE("fine err :%d\n", cnt[axis]); | ||
480 | goto out; | ||
481 | } | ||
482 | if (cnt[axis] > 3) { | ||
483 | MPL_LOGE("cnt err :%d\n", cnt[axis]); | ||
484 | goto out; | ||
485 | } | ||
486 | cnt[axis]++; | ||
487 | } | ||
488 | MPL_LOGV("new fine x:%-5d y:%-5d z:%-5d\n", | ||
489 | fine[0], fine[1], fine[2]); | ||
490 | |||
491 | /* set current fine */ | ||
492 | result = ami306_write_offset(mlsl_handle, pdata, fine); | ||
493 | if (result) { | ||
494 | LOG_RESULT_LOCATION(result); | ||
495 | return result; | ||
496 | } | ||
497 | } | ||
498 | memcpy(private_data->fine, fine, sizeof(fine)); | ||
499 | out: | ||
500 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
501 | AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT); | ||
502 | if (result) { | ||
503 | LOG_RESULT_LOCATION(result); | ||
504 | return result; | ||
505 | } | ||
506 | udelay(250 + 50); | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int ami306_read_win(void *mlsl_handle, | ||
511 | struct ext_slave_descr *slave, | ||
512 | struct ext_slave_platform_data *pdata) | ||
513 | { | ||
514 | int result = 0; | ||
515 | unsigned char regs[6]; | ||
516 | struct ami306_private_data *private_data = pdata->private_data; | ||
517 | struct ami_win_parameter *win = &private_data->win; | ||
518 | |||
519 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
520 | AMI_REG_OFFOTPX, sizeof(regs), regs); | ||
521 | if (result) { | ||
522 | LOG_RESULT_LOCATION(result); | ||
523 | return result; | ||
524 | } | ||
525 | |||
526 | win->m_0Gauss_fine.x = (unsigned char)(regs[0] & 0x7f); | ||
527 | win->m_0Gauss_fine.y = (unsigned char)(regs[2] & 0x7f); | ||
528 | win->m_0Gauss_fine.z = (unsigned char)(regs[4] & 0x7f); | ||
529 | |||
530 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
531 | AMI_REG_OFFX, 2, ®s[0]); | ||
532 | if (result) { | ||
533 | LOG_RESULT_LOCATION(result); | ||
534 | return result; | ||
535 | } | ||
536 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
537 | AMI_REG_OFFY, 2, ®s[2]); | ||
538 | if (result) { | ||
539 | LOG_RESULT_LOCATION(result); | ||
540 | return result; | ||
541 | } | ||
542 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
543 | AMI_REG_OFFZ, 2, ®s[4]); | ||
544 | if (result) { | ||
545 | LOG_RESULT_LOCATION(result); | ||
546 | return result; | ||
547 | } | ||
548 | |||
549 | win->m_fine.x = (unsigned char)(regs[0] & 0x7f); | ||
550 | win->m_fine.y = (unsigned char)(regs[2] & 0x7f); | ||
551 | win->m_fine.z = (unsigned char)(regs[4] & 0x7f); | ||
552 | |||
553 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
554 | AMI_FINEOUTPUT_X, sizeof(regs), regs); | ||
555 | if (result) { | ||
556 | LOG_RESULT_LOCATION(result); | ||
557 | return result; | ||
558 | } | ||
559 | win->m_fine_output.x = little_u8_to_u16(®s[0]); | ||
560 | win->m_fine_output.y = little_u8_to_u16(®s[2]); | ||
561 | win->m_fine_output.z = little_u8_to_u16(®s[4]); | ||
562 | |||
563 | return result; | ||
564 | } | ||
565 | |||
566 | static int ami306_suspend(void *mlsl_handle, | ||
567 | struct ext_slave_descr *slave, | ||
568 | struct ext_slave_platform_data *pdata) | ||
569 | { | ||
570 | int result; | ||
571 | unsigned char reg; | ||
572 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
573 | AMI306_REG_CNTL1, 1, ®); | ||
574 | if (result) { | ||
575 | LOG_RESULT_LOCATION(result); | ||
576 | return result; | ||
577 | } | ||
578 | |||
579 | reg &= ~(AMI306_BIT_CNTL1_PC1 | AMI306_BIT_CNTL1_FS1); | ||
580 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
581 | AMI306_REG_CNTL1, reg); | ||
582 | if (result) { | ||
583 | LOG_RESULT_LOCATION(result); | ||
584 | return result; | ||
585 | } | ||
586 | |||
587 | return result; | ||
588 | } | ||
589 | |||
590 | static int ami306_resume(void *mlsl_handle, | ||
591 | struct ext_slave_descr *slave, | ||
592 | struct ext_slave_platform_data *pdata) | ||
593 | { | ||
594 | int result = INV_SUCCESS; | ||
595 | unsigned char regs[] = { | ||
596 | AMI306_REG_CNTL4_1, | ||
597 | 0x7E, | ||
598 | 0xA0 | ||
599 | }; | ||
600 | /* Step1. Set CNTL1 reg to power model active (Write CNTL1:PC1=1) */ | ||
601 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
602 | AMI306_REG_CNTL1, | ||
603 | AMI306_BIT_CNTL1_PC1 | | ||
604 | AMI306_BIT_CNTL1_FS1); | ||
605 | if (result) { | ||
606 | LOG_RESULT_LOCATION(result); | ||
607 | return result; | ||
608 | } | ||
609 | |||
610 | /* Step2. Set CNTL2 reg to DRDY active high and enabled | ||
611 | (Write CNTL2:DREN=1) */ | ||
612 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
613 | AMI306_REG_CNTL2, | ||
614 | AMI306_BIT_CNTL2_DREN | | ||
615 | AMI306_BIT_CNTL2_DRP); | ||
616 | if (result) { | ||
617 | LOG_RESULT_LOCATION(result); | ||
618 | return result; | ||
619 | } | ||
620 | |||
621 | /* Step3. Set CNTL4 reg to for measurement speed: Write CNTL4, 0xA07E */ | ||
622 | result = inv_serial_write(mlsl_handle, pdata->address, | ||
623 | ARRAY_SIZE(regs), regs); | ||
624 | if (result) { | ||
625 | LOG_RESULT_LOCATION(result); | ||
626 | return result; | ||
627 | } | ||
628 | |||
629 | /* Step4. skipped */ | ||
630 | |||
631 | /* Step5. Set CNTL3 reg to forced measurement period | ||
632 | (Write CNTL3:FORCE=1) */ | ||
633 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
634 | AMI306_REG_CNTL3, | ||
635 | AMI306_BIT_CNTL3_F0RCE); | ||
636 | |||
637 | return result; | ||
638 | } | ||
639 | |||
640 | static int ami306_read(void *mlsl_handle, | ||
641 | struct ext_slave_descr *slave, | ||
642 | struct ext_slave_platform_data *pdata, | ||
643 | unsigned char *data) | ||
644 | { | ||
645 | int result = INV_SUCCESS; | ||
646 | int ii; | ||
647 | short val[COMPASS_NUM_AXES]; | ||
648 | |||
649 | result = ami306_mea(mlsl_handle, pdata, val); | ||
650 | if (result) { | ||
651 | LOG_RESULT_LOCATION(result); | ||
652 | return result; | ||
653 | } | ||
654 | for (ii = 0; ii < COMPASS_NUM_AXES; ii++) { | ||
655 | val[ii] -= AMI_STANDARD_OFFSET; | ||
656 | data[2 * ii] = val[ii] & 0xFF; | ||
657 | data[(2 * ii) + 1] = (val[ii] >> 8) & 0xFF; | ||
658 | } | ||
659 | return result; | ||
660 | } | ||
661 | |||
662 | static int ami306_init(void *mlsl_handle, | ||
663 | struct ext_slave_descr *slave, | ||
664 | struct ext_slave_platform_data *pdata) | ||
665 | { | ||
666 | int result; | ||
667 | struct ami306_private_data *private_data; | ||
668 | private_data = (struct ami306_private_data *) | ||
669 | kzalloc(sizeof(struct ami306_private_data), GFP_KERNEL); | ||
670 | |||
671 | if (!private_data) | ||
672 | return INV_ERROR_MEMORY_EXAUSTED; | ||
673 | |||
674 | pdata->private_data = private_data; | ||
675 | result = ami306_set_bits8(mlsl_handle, pdata, | ||
676 | AMI_REG_CTRL1, | ||
677 | AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); | ||
678 | if (result) { | ||
679 | LOG_RESULT_LOCATION(result); | ||
680 | return result; | ||
681 | } | ||
682 | /* Read Parameters */ | ||
683 | result = ami306_read_param(mlsl_handle, slave, pdata); | ||
684 | if (result) { | ||
685 | LOG_RESULT_LOCATION(result); | ||
686 | return result; | ||
687 | } | ||
688 | /* Read Window */ | ||
689 | result = ami306_initial_b0_adjust(mlsl_handle, slave, pdata); | ||
690 | if (result) { | ||
691 | LOG_RESULT_LOCATION(result); | ||
692 | return result; | ||
693 | } | ||
694 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
695 | if (result) { | ||
696 | LOG_RESULT_LOCATION(result); | ||
697 | return result; | ||
698 | } | ||
699 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
700 | if (result) { | ||
701 | LOG_RESULT_LOCATION(result); | ||
702 | return result; | ||
703 | } | ||
704 | |||
705 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
706 | AMI306_REG_CNTL1, 0); | ||
707 | if (result) { | ||
708 | LOG_RESULT_LOCATION(result); | ||
709 | return result; | ||
710 | } | ||
711 | |||
712 | return INV_SUCCESS; | ||
713 | } | ||
714 | |||
715 | static int ami306_exit(void *mlsl_handle, | ||
716 | struct ext_slave_descr *slave, | ||
717 | struct ext_slave_platform_data *pdata) | ||
718 | { | ||
719 | kfree(pdata->private_data); | ||
720 | return INV_SUCCESS; | ||
721 | } | ||
722 | |||
723 | static int ami306_config(void *mlsl_handle, | ||
724 | struct ext_slave_descr *slave, | ||
725 | struct ext_slave_platform_data *pdata, | ||
726 | struct ext_slave_config *data) | ||
727 | { | ||
728 | if (!data->data) { | ||
729 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
730 | return INV_ERROR_INVALID_PARAMETER; | ||
731 | } | ||
732 | |||
733 | switch (data->key) { | ||
734 | case MPU_SLAVE_PARAM: | ||
735 | case MPU_SLAVE_WINDOW: | ||
736 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
737 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
738 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
739 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
740 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
741 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
742 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
743 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
744 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
745 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
746 | default: | ||
747 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
748 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
749 | }; | ||
750 | |||
751 | return INV_SUCCESS; | ||
752 | } | ||
753 | |||
754 | static int ami306_get_config(void *mlsl_handle, | ||
755 | struct ext_slave_descr *slave, | ||
756 | struct ext_slave_platform_data *pdata, | ||
757 | struct ext_slave_config *data) | ||
758 | { | ||
759 | int result; | ||
760 | struct ami306_private_data *private_data = pdata->private_data; | ||
761 | if (!data->data) { | ||
762 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
763 | return INV_ERROR_INVALID_PARAMETER; | ||
764 | } | ||
765 | |||
766 | switch (data->key) { | ||
767 | case MPU_SLAVE_PARAM: | ||
768 | if (sizeof(struct ami_sensor_parametor) > data->len) { | ||
769 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
770 | return INV_ERROR_INVALID_PARAMETER; | ||
771 | } | ||
772 | if (data->apply) { | ||
773 | result = ami306_read_param(mlsl_handle, slave, pdata); | ||
774 | if (result) { | ||
775 | LOG_RESULT_LOCATION(result); | ||
776 | return result; | ||
777 | } | ||
778 | } | ||
779 | memcpy(data->data, &private_data->param, | ||
780 | sizeof(struct ami_sensor_parametor)); | ||
781 | break; | ||
782 | case MPU_SLAVE_WINDOW: | ||
783 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
784 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
785 | return INV_ERROR_INVALID_PARAMETER; | ||
786 | } | ||
787 | if (data->apply) { | ||
788 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
789 | if (result) { | ||
790 | LOG_RESULT_LOCATION(result); | ||
791 | return result; | ||
792 | } | ||
793 | } | ||
794 | memcpy(data->data, &private_data->win, | ||
795 | sizeof(struct ami_win_parameter)); | ||
796 | break; | ||
797 | case MPU_SLAVE_SEARCHOFFSET: | ||
798 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
799 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
800 | return INV_ERROR_INVALID_PARAMETER; | ||
801 | } | ||
802 | if (data->apply) { | ||
803 | result = ami306_search_offset(mlsl_handle, | ||
804 | slave, pdata); | ||
805 | if (result) { | ||
806 | LOG_RESULT_LOCATION(result); | ||
807 | return result; | ||
808 | } | ||
809 | /* Start sensor */ | ||
810 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
811 | if (result) { | ||
812 | LOG_RESULT_LOCATION(result); | ||
813 | return result; | ||
814 | } | ||
815 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
816 | if (result) { | ||
817 | LOG_RESULT_LOCATION(result); | ||
818 | return result; | ||
819 | } | ||
820 | } | ||
821 | memcpy(data->data, &private_data->win, | ||
822 | sizeof(struct ami_win_parameter)); | ||
823 | break; | ||
824 | case MPU_SLAVE_READWINPARAMS: | ||
825 | if (sizeof(struct ami_win_parameter) > data->len) { | ||
826 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
827 | return INV_ERROR_INVALID_PARAMETER; | ||
828 | } | ||
829 | if (data->apply) { | ||
830 | result = ami306_initial_b0_adjust(mlsl_handle, | ||
831 | slave, pdata); | ||
832 | if (result) { | ||
833 | LOG_RESULT_LOCATION(result); | ||
834 | return result; | ||
835 | } | ||
836 | /* Start sensor */ | ||
837 | result = ami306_start_sensor(mlsl_handle, pdata); | ||
838 | if (result) { | ||
839 | LOG_RESULT_LOCATION(result); | ||
840 | return result; | ||
841 | } | ||
842 | result = ami306_read_win(mlsl_handle, slave, pdata); | ||
843 | if (result) { | ||
844 | LOG_RESULT_LOCATION(result); | ||
845 | return result; | ||
846 | } | ||
847 | } | ||
848 | memcpy(data->data, &private_data->win, | ||
849 | sizeof(struct ami_win_parameter)); | ||
850 | break; | ||
851 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
852 | (*(unsigned long *)data->data) = 0; | ||
853 | break; | ||
854 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
855 | (*(unsigned long *)data->data) = 50000; | ||
856 | break; | ||
857 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
858 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
859 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
860 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
861 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
862 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
863 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
864 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
865 | case MPU_SLAVE_READ_SCALE: | ||
866 | default: | ||
867 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
868 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
869 | }; | ||
870 | |||
871 | return INV_SUCCESS; | ||
872 | } | ||
873 | |||
874 | static struct ext_slave_read_trigger ami306_read_trigger = { | ||
875 | /*.reg = */ AMI_REG_CTRL3, | ||
876 | /*.value = */ AMI_CTRL3_FORCE_BIT | ||
877 | }; | ||
878 | |||
879 | static struct ext_slave_descr ami306_descr = { | ||
880 | .init = ami306_init, | ||
881 | .exit = ami306_exit, | ||
882 | .suspend = ami306_suspend, | ||
883 | .resume = ami306_resume, | ||
884 | .read = ami306_read, | ||
885 | .config = ami306_config, | ||
886 | .get_config = ami306_get_config, | ||
887 | .name = "ami306", | ||
888 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
889 | .id = COMPASS_ID_AMI306, | ||
890 | .read_reg = 0x0E, | ||
891 | .read_len = 13, | ||
892 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
893 | .range = {5461, 3333}, | ||
894 | .trigger = &ami306_read_trigger, | ||
895 | }; | ||
896 | |||
897 | static | ||
898 | struct ext_slave_descr *ami306_get_slave_descr(void) | ||
899 | { | ||
900 | return &ami306_descr; | ||
901 | } | ||
902 | |||
903 | /* -------------------------------------------------------------------------- */ | ||
904 | struct ami306_mod_private_data { | ||
905 | struct i2c_client *client; | ||
906 | struct ext_slave_platform_data *pdata; | ||
907 | }; | ||
908 | |||
909 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
910 | |||
911 | static int ami306_mod_probe(struct i2c_client *client, | ||
912 | const struct i2c_device_id *devid) | ||
913 | { | ||
914 | struct ext_slave_platform_data *pdata; | ||
915 | struct ami306_mod_private_data *private_data; | ||
916 | int result = 0; | ||
917 | |||
918 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
919 | |||
920 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
921 | result = -ENODEV; | ||
922 | goto out_no_free; | ||
923 | } | ||
924 | |||
925 | pdata = client->dev.platform_data; | ||
926 | if (!pdata) { | ||
927 | dev_err(&client->adapter->dev, | ||
928 | "Missing platform data for slave %s\n", devid->name); | ||
929 | result = -EFAULT; | ||
930 | goto out_no_free; | ||
931 | } | ||
932 | |||
933 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
934 | if (!private_data) { | ||
935 | result = -ENOMEM; | ||
936 | goto out_no_free; | ||
937 | } | ||
938 | |||
939 | i2c_set_clientdata(client, private_data); | ||
940 | private_data->client = client; | ||
941 | private_data->pdata = pdata; | ||
942 | |||
943 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
944 | ami306_get_slave_descr); | ||
945 | if (result) { | ||
946 | dev_err(&client->adapter->dev, | ||
947 | "Slave registration failed: %s, %d\n", | ||
948 | devid->name, result); | ||
949 | goto out_free_memory; | ||
950 | } | ||
951 | |||
952 | return result; | ||
953 | |||
954 | out_free_memory: | ||
955 | kfree(private_data); | ||
956 | out_no_free: | ||
957 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
958 | return result; | ||
959 | |||
960 | } | ||
961 | |||
962 | static int ami306_mod_remove(struct i2c_client *client) | ||
963 | { | ||
964 | struct ami306_mod_private_data *private_data = | ||
965 | i2c_get_clientdata(client); | ||
966 | |||
967 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
968 | |||
969 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
970 | ami306_get_slave_descr); | ||
971 | |||
972 | kfree(private_data); | ||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | static const struct i2c_device_id ami306_mod_id[] = { | ||
977 | { "ami306", COMPASS_ID_AMI306 }, | ||
978 | {} | ||
979 | }; | ||
980 | |||
981 | MODULE_DEVICE_TABLE(i2c, ami306_mod_id); | ||
982 | |||
983 | static struct i2c_driver ami306_mod_driver = { | ||
984 | .class = I2C_CLASS_HWMON, | ||
985 | .probe = ami306_mod_probe, | ||
986 | .remove = ami306_mod_remove, | ||
987 | .id_table = ami306_mod_id, | ||
988 | .driver = { | ||
989 | .owner = THIS_MODULE, | ||
990 | .name = "ami306_mod", | ||
991 | }, | ||
992 | .address_list = normal_i2c, | ||
993 | }; | ||
994 | |||
995 | static int __init ami306_mod_init(void) | ||
996 | { | ||
997 | int res = i2c_add_driver(&ami306_mod_driver); | ||
998 | pr_info("%s: Probe name %s\n", __func__, "ami306_mod"); | ||
999 | if (res) | ||
1000 | pr_err("%s failed\n", __func__); | ||
1001 | return res; | ||
1002 | } | ||
1003 | |||
1004 | static void __exit ami306_mod_exit(void) | ||
1005 | { | ||
1006 | pr_info("%s\n", __func__); | ||
1007 | i2c_del_driver(&ami306_mod_driver); | ||
1008 | } | ||
1009 | |||
1010 | module_init(ami306_mod_init); | ||
1011 | module_exit(ami306_mod_exit); | ||
1012 | |||
1013 | MODULE_AUTHOR("Invensense Corporation"); | ||
1014 | MODULE_DESCRIPTION("Driver to integrate AMI306 sensor with the MPU"); | ||
1015 | MODULE_LICENSE("GPL"); | ||
1016 | MODULE_ALIAS("ami306_mod"); | ||
1017 | |||
1018 | /** | ||
1019 | * @} | ||
1020 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami30x.c b/drivers/misc/inv_mpu/compass/ami30x.c new file mode 100644 index 00000000000..0c4937c4426 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami30x.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file ami30x.c | ||
25 | * @brief Magnetometer setup and handling methods for Aichi AMI304 | ||
26 | * and AMI305 compass devices. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | #define AMI30X_REG_DATAX (0x10) | ||
49 | #define AMI30X_REG_STAT1 (0x18) | ||
50 | #define AMI30X_REG_CNTL1 (0x1B) | ||
51 | #define AMI30X_REG_CNTL2 (0x1C) | ||
52 | #define AMI30X_REG_CNTL3 (0x1D) | ||
53 | |||
54 | #define AMI30X_BIT_CNTL1_PC1 (0x80) | ||
55 | #define AMI30X_BIT_CNTL1_ODR1 (0x10) | ||
56 | #define AMI30X_BIT_CNTL1_FS1 (0x02) | ||
57 | |||
58 | #define AMI30X_BIT_CNTL2_IEN (0x10) | ||
59 | #define AMI30X_BIT_CNTL2_DREN (0x08) | ||
60 | #define AMI30X_BIT_CNTL2_DRP (0x04) | ||
61 | #define AMI30X_BIT_CNTL3_F0RCE (0x40) | ||
62 | |||
63 | /* -------------------------------------------------------------------------- */ | ||
64 | static int ami30x_suspend(void *mlsl_handle, | ||
65 | struct ext_slave_descr *slave, | ||
66 | struct ext_slave_platform_data *pdata) | ||
67 | { | ||
68 | int result; | ||
69 | unsigned char reg; | ||
70 | result = | ||
71 | inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_CNTL1, | ||
72 | 1, ®); | ||
73 | if (result) { | ||
74 | LOG_RESULT_LOCATION(result); | ||
75 | return result; | ||
76 | } | ||
77 | |||
78 | reg &= ~(AMI30X_BIT_CNTL1_PC1 | AMI30X_BIT_CNTL1_FS1); | ||
79 | result = | ||
80 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
81 | AMI30X_REG_CNTL1, reg); | ||
82 | if (result) { | ||
83 | LOG_RESULT_LOCATION(result); | ||
84 | return result; | ||
85 | } | ||
86 | |||
87 | return result; | ||
88 | } | ||
89 | |||
90 | static int ami30x_resume(void *mlsl_handle, | ||
91 | struct ext_slave_descr *slave, | ||
92 | struct ext_slave_platform_data *pdata) | ||
93 | { | ||
94 | int result = INV_SUCCESS; | ||
95 | |||
96 | /* Set CNTL1 reg to power model active */ | ||
97 | result = | ||
98 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
99 | AMI30X_REG_CNTL1, | ||
100 | AMI30X_BIT_CNTL1_PC1 | | ||
101 | AMI30X_BIT_CNTL1_FS1); | ||
102 | if (result) { | ||
103 | LOG_RESULT_LOCATION(result); | ||
104 | return result; | ||
105 | } | ||
106 | /* Set CNTL2 reg to DRDY active high and enabled */ | ||
107 | result = | ||
108 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
109 | AMI30X_REG_CNTL2, | ||
110 | AMI30X_BIT_CNTL2_DREN | | ||
111 | AMI30X_BIT_CNTL2_DRP); | ||
112 | if (result) { | ||
113 | LOG_RESULT_LOCATION(result); | ||
114 | return result; | ||
115 | } | ||
116 | /* Set CNTL3 reg to forced measurement period */ | ||
117 | result = | ||
118 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
119 | AMI30X_REG_CNTL3, AMI30X_BIT_CNTL3_F0RCE); | ||
120 | |||
121 | return result; | ||
122 | } | ||
123 | |||
124 | static int ami30x_read(void *mlsl_handle, | ||
125 | struct ext_slave_descr *slave, | ||
126 | struct ext_slave_platform_data *pdata, | ||
127 | unsigned char *data) | ||
128 | { | ||
129 | unsigned char stat; | ||
130 | int result = INV_SUCCESS; | ||
131 | |||
132 | /* Read status reg and check if data ready (DRDY) */ | ||
133 | result = | ||
134 | inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_STAT1, | ||
135 | 1, &stat); | ||
136 | if (result) { | ||
137 | LOG_RESULT_LOCATION(result); | ||
138 | return result; | ||
139 | } | ||
140 | |||
141 | if (stat & 0x40) { | ||
142 | result = | ||
143 | inv_serial_read(mlsl_handle, pdata->address, | ||
144 | AMI30X_REG_DATAX, 6, (unsigned char *)data); | ||
145 | if (result) { | ||
146 | LOG_RESULT_LOCATION(result); | ||
147 | return result; | ||
148 | } | ||
149 | /* start another measurement */ | ||
150 | result = | ||
151 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
152 | AMI30X_REG_CNTL3, | ||
153 | AMI30X_BIT_CNTL3_F0RCE); | ||
154 | if (result) { | ||
155 | LOG_RESULT_LOCATION(result); | ||
156 | return result; | ||
157 | } | ||
158 | |||
159 | return INV_SUCCESS; | ||
160 | } | ||
161 | |||
162 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
163 | } | ||
164 | |||
165 | |||
166 | /* For AMI305,the range field needs to be modified to {9830.4f} */ | ||
167 | static struct ext_slave_descr ami30x_descr = { | ||
168 | .init = NULL, | ||
169 | .exit = NULL, | ||
170 | .suspend = ami30x_suspend, | ||
171 | .resume = ami30x_resume, | ||
172 | .read = ami30x_read, | ||
173 | .config = NULL, | ||
174 | .get_config = NULL, | ||
175 | .name = "ami30x", | ||
176 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
177 | .id = COMPASS_ID_AMI30X, | ||
178 | .read_reg = 0x06, | ||
179 | .read_len = 6, | ||
180 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
181 | .range = {5461, 3333}, | ||
182 | .trigger = NULL, | ||
183 | }; | ||
184 | |||
185 | static | ||
186 | struct ext_slave_descr *ami30x_get_slave_descr(void) | ||
187 | { | ||
188 | return &ami30x_descr; | ||
189 | } | ||
190 | |||
191 | /* -------------------------------------------------------------------------- */ | ||
192 | struct ami30x_mod_private_data { | ||
193 | struct i2c_client *client; | ||
194 | struct ext_slave_platform_data *pdata; | ||
195 | }; | ||
196 | |||
197 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
198 | |||
199 | static int ami30x_mod_probe(struct i2c_client *client, | ||
200 | const struct i2c_device_id *devid) | ||
201 | { | ||
202 | struct ext_slave_platform_data *pdata; | ||
203 | struct ami30x_mod_private_data *private_data; | ||
204 | int result = 0; | ||
205 | |||
206 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
207 | |||
208 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
209 | result = -ENODEV; | ||
210 | goto out_no_free; | ||
211 | } | ||
212 | |||
213 | pdata = client->dev.platform_data; | ||
214 | if (!pdata) { | ||
215 | dev_err(&client->adapter->dev, | ||
216 | "Missing platform data for slave %s\n", devid->name); | ||
217 | result = -EFAULT; | ||
218 | goto out_no_free; | ||
219 | } | ||
220 | |||
221 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
222 | if (!private_data) { | ||
223 | result = -ENOMEM; | ||
224 | goto out_no_free; | ||
225 | } | ||
226 | |||
227 | i2c_set_clientdata(client, private_data); | ||
228 | private_data->client = client; | ||
229 | private_data->pdata = pdata; | ||
230 | |||
231 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
232 | ami30x_get_slave_descr); | ||
233 | if (result) { | ||
234 | dev_err(&client->adapter->dev, | ||
235 | "Slave registration failed: %s, %d\n", | ||
236 | devid->name, result); | ||
237 | goto out_free_memory; | ||
238 | } | ||
239 | |||
240 | return result; | ||
241 | |||
242 | out_free_memory: | ||
243 | kfree(private_data); | ||
244 | out_no_free: | ||
245 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
246 | return result; | ||
247 | |||
248 | } | ||
249 | |||
250 | static int ami30x_mod_remove(struct i2c_client *client) | ||
251 | { | ||
252 | struct ami30x_mod_private_data *private_data = | ||
253 | i2c_get_clientdata(client); | ||
254 | |||
255 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
256 | |||
257 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
258 | ami30x_get_slave_descr); | ||
259 | |||
260 | kfree(private_data); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static const struct i2c_device_id ami30x_mod_id[] = { | ||
265 | { "ami30x", COMPASS_ID_AMI30X }, | ||
266 | {} | ||
267 | }; | ||
268 | |||
269 | MODULE_DEVICE_TABLE(i2c, ami30x_mod_id); | ||
270 | |||
271 | static struct i2c_driver ami30x_mod_driver = { | ||
272 | .class = I2C_CLASS_HWMON, | ||
273 | .probe = ami30x_mod_probe, | ||
274 | .remove = ami30x_mod_remove, | ||
275 | .id_table = ami30x_mod_id, | ||
276 | .driver = { | ||
277 | .owner = THIS_MODULE, | ||
278 | .name = "ami30x_mod", | ||
279 | }, | ||
280 | .address_list = normal_i2c, | ||
281 | }; | ||
282 | |||
283 | static int __init ami30x_mod_init(void) | ||
284 | { | ||
285 | int res = i2c_add_driver(&ami30x_mod_driver); | ||
286 | pr_info("%s: Probe name %s\n", __func__, "ami30x_mod"); | ||
287 | if (res) | ||
288 | pr_err("%s failed\n", __func__); | ||
289 | return res; | ||
290 | } | ||
291 | |||
292 | static void __exit ami30x_mod_exit(void) | ||
293 | { | ||
294 | pr_info("%s\n", __func__); | ||
295 | i2c_del_driver(&ami30x_mod_driver); | ||
296 | } | ||
297 | |||
298 | module_init(ami30x_mod_init); | ||
299 | module_exit(ami30x_mod_exit); | ||
300 | |||
301 | MODULE_AUTHOR("Invensense Corporation"); | ||
302 | MODULE_DESCRIPTION("Driver to integrate AMI30X sensor with the MPU"); | ||
303 | MODULE_LICENSE("GPL"); | ||
304 | MODULE_ALIAS("ami30x_mod"); | ||
305 | |||
306 | /** | ||
307 | * @} | ||
308 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/ami_hw.h b/drivers/misc/inv_mpu/compass/ami_hw.h new file mode 100644 index 00000000000..32a04e91cdc --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami_hw.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Information System Products Co.,Ltd. | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #ifndef AMI_HW_H | ||
18 | #define AMI_HW_H | ||
19 | |||
20 | #define AMI_I2C_BUS_NUM 2 | ||
21 | |||
22 | #ifdef AMI304_MODEL | ||
23 | #define AMI_I2C_ADDRESS 0x0F | ||
24 | #else | ||
25 | #define AMI_I2C_ADDRESS 0x0E | ||
26 | #endif | ||
27 | |||
28 | #define AMI_GPIO_INT 152 | ||
29 | #define AMI_GPIO_DRDY 153 | ||
30 | |||
31 | /* AMI-Sensor Internal Register Address | ||
32 | *(Please refer to AMI-Sensor Specifications) | ||
33 | */ | ||
34 | #define AMI_MOREINFO_CMDCODE 0x0d | ||
35 | #define AMI_WHOIAM_CMDCODE 0x0f | ||
36 | #define AMI_REG_DATAX 0x10 | ||
37 | #define AMI_REG_DATAY 0x12 | ||
38 | #define AMI_REG_DATAZ 0x14 | ||
39 | #define AMI_REG_STA1 0x18 | ||
40 | #define AMI_REG_CTRL1 0x1b | ||
41 | #define AMI_REG_CTRL2 0x1c | ||
42 | #define AMI_REG_CTRL3 0x1d | ||
43 | #define AMI_REG_B0X 0x20 | ||
44 | #define AMI_REG_B0Y 0x22 | ||
45 | #define AMI_REG_B0Z 0x24 | ||
46 | #define AMI_REG_CTRL5 0x40 | ||
47 | #define AMI_REG_CTRL4 0x5c | ||
48 | #define AMI_REG_TEMP 0x60 | ||
49 | #define AMI_REG_DELAYX 0x68 | ||
50 | #define AMI_REG_DELAYY 0x6e | ||
51 | #define AMI_REG_DELAYZ 0x74 | ||
52 | #define AMI_REG_OFFX 0x6c | ||
53 | #define AMI_REG_OFFY 0x72 | ||
54 | #define AMI_REG_OFFZ 0x78 | ||
55 | #define AMI_FINEOUTPUT_X 0x90 | ||
56 | #define AMI_FINEOUTPUT_Y 0x92 | ||
57 | #define AMI_FINEOUTPUT_Z 0x94 | ||
58 | #define AMI_REG_SENX 0x96 | ||
59 | #define AMI_REG_SENY 0x98 | ||
60 | #define AMI_REG_SENZ 0x9a | ||
61 | #define AMI_REG_GAINX 0x9c | ||
62 | #define AMI_REG_GAINY 0x9e | ||
63 | #define AMI_REG_GAINZ 0xa0 | ||
64 | #define AMI_GETVERSION_CMDCODE 0xe8 | ||
65 | #define AMI_SERIALNUMBER_CMDCODE 0xea | ||
66 | #define AMI_REG_B0OTPX 0xa2 | ||
67 | #define AMI_REG_B0OTPY 0xb8 | ||
68 | #define AMI_REG_B0OTPZ 0xce | ||
69 | #define AMI_REG_OFFOTPX 0xf8 | ||
70 | #define AMI_REG_OFFOTPY 0xfa | ||
71 | #define AMI_REG_OFFOTPZ 0xfc | ||
72 | |||
73 | /* AMI-Sensor Control Bit (Please refer to AMI-Sensor Specifications) */ | ||
74 | #define AMI_CTRL1_PC1 0x80 | ||
75 | #define AMI_CTRL1_FS1_FORCE 0x02 | ||
76 | #define AMI_CTRL1_ODR1 0x10 | ||
77 | #define AMI_CTRL2_DREN 0x08 | ||
78 | #define AMI_CTRL2_DRP 0x04 | ||
79 | #define AMI_CTRL3_FORCE_BIT 0x40 | ||
80 | #define AMI_CTRL3_B0_LO_BIT 0x10 | ||
81 | #define AMI_CTRL3_SRST_BIT 0x80 | ||
82 | #define AMI_CTRL4_HS 0xa07e | ||
83 | #define AMI_CTRL4_AB 0x0001 | ||
84 | #define AMI_STA1_DRDY_BIT 0x40 | ||
85 | #define AMI_STA1_DOR_BIT 0x20 | ||
86 | |||
87 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/ami_sensor_def.h b/drivers/misc/inv_mpu/compass/ami_sensor_def.h new file mode 100644 index 00000000000..64032e2bf1f --- /dev/null +++ b/drivers/misc/inv_mpu/compass/ami_sensor_def.h | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Information System Products Co.,Ltd. | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * Definitions for ami306 compass chip. | ||
19 | */ | ||
20 | #ifndef AMI_SENSOR_DEF_H | ||
21 | #define AMI_SENSOR_DEF_H | ||
22 | |||
23 | /********************************************************************* | ||
24 | Constant | ||
25 | *********************************************************************/ | ||
26 | #define AMI_OK 0x00 /**< Normal */ | ||
27 | #define AMI_PARAM_ERR 0x01 /**< Parameter Error */ | ||
28 | #define AMI_SEQ_ERR 0x02 /**< Squence Error */ | ||
29 | #define AMI_SYSTEM_ERR 0x10 /**< System Error */ | ||
30 | #define AMI_BLOCK_ERR 0x20 /**< Block Error */ | ||
31 | #define AMI_ERROR 0x99 /**< other Error */ | ||
32 | |||
33 | /********************************************************************* | ||
34 | Struct definition | ||
35 | *********************************************************************/ | ||
36 | /** axis sensitivity(gain) calibration parameter information */ | ||
37 | struct ami_vector3d { | ||
38 | signed short x; /**< X-axis */ | ||
39 | signed short y; /**< Y-axis */ | ||
40 | signed short z; /**< Z-axis */ | ||
41 | }; | ||
42 | |||
43 | /** axis interference information */ | ||
44 | struct ami_interference { | ||
45 | /**< Y-axis magnetic field for X-axis correction value */ | ||
46 | signed short xy; | ||
47 | /**< Z-axis magnetic field for X-axis correction value */ | ||
48 | signed short xz; | ||
49 | /**< X-axis magnetic field for Y-axis correction value */ | ||
50 | signed short yx; | ||
51 | /**< Z-axis magnetic field for Y-axis correction value */ | ||
52 | signed short yz; | ||
53 | /**< X-axis magnetic field for Z-axis correction value */ | ||
54 | signed short zx; | ||
55 | /**< Y-axis magnetic field for Z-axis correction value */ | ||
56 | signed short zy; | ||
57 | }; | ||
58 | |||
59 | /** sensor calibration Parameter information */ | ||
60 | struct ami_sensor_parametor { | ||
61 | /**< geomagnetic field sensor gain */ | ||
62 | struct ami_vector3d m_gain; | ||
63 | /**< geomagnetic field sensor gain correction parameter */ | ||
64 | struct ami_vector3d m_gain_cor; | ||
65 | /**< geomagnetic field sensor offset */ | ||
66 | struct ami_vector3d m_offset; | ||
67 | /**< geomagnetic field sensor axis interference parameter */ | ||
68 | struct ami_interference m_interference; | ||
69 | #ifdef AMI_6AXIS | ||
70 | /**< acceleration sensor gain */ | ||
71 | struct ami_vector3d a_gain; | ||
72 | /**< acceleration sensor offset */ | ||
73 | struct ami_vector3d a_offset; | ||
74 | /**< acceleration sensor deviation */ | ||
75 | signed short a_deviation; | ||
76 | #endif | ||
77 | }; | ||
78 | |||
79 | /** G2-Sensor measurement value (voltage ADC value ) */ | ||
80 | struct ami_sensor_rawvalue { | ||
81 | /**< geomagnetic field sensor measurement X-axis value | ||
82 | (mounted position/direction reference) */ | ||
83 | unsigned short mx; | ||
84 | /**< geomagnetic field sensor measurement Y-axis value | ||
85 | (mounted position/direction reference) */ | ||
86 | unsigned short my; | ||
87 | /**< geomagnetic field sensor measurement Z-axis value | ||
88 | (mounted position/direction reference) */ | ||
89 | unsigned short mz; | ||
90 | #ifdef AMI_6AXIS | ||
91 | /**< acceleration sensor measurement X-axis value | ||
92 | (mounted position/direction reference) */ | ||
93 | unsigned short ax; | ||
94 | /**< acceleration sensor measurement Y-axis value | ||
95 | (mounted position/direction reference) */ | ||
96 | unsigned short ay; | ||
97 | /**< acceleration sensor measurement Z-axis value | ||
98 | (mounted position/direction reference) */ | ||
99 | unsigned short az; | ||
100 | #endif | ||
101 | /**< temperature sensor measurement value */ | ||
102 | unsigned short temperature; | ||
103 | }; | ||
104 | |||
105 | /** Window function Parameter information */ | ||
106 | struct ami_win_parameter { | ||
107 | /**< current fine value */ | ||
108 | struct ami_vector3d m_fine; | ||
109 | /**< change per 1coarse */ | ||
110 | struct ami_vector3d m_fine_output; | ||
111 | /**< fine value at zero gauss */ | ||
112 | struct ami_vector3d m_0Gauss_fine; | ||
113 | #ifdef AMI304 | ||
114 | /**< current b0 value */ | ||
115 | struct ami_vector3d m_b0; | ||
116 | /**< current coarse value */ | ||
117 | struct ami_vector3d m_coar; | ||
118 | /**< change per 1fine */ | ||
119 | struct ami_vector3d m_coar_output; | ||
120 | /**< coarse value at zero gauss */ | ||
121 | struct ami_vector3d m_0Gauss_coar; | ||
122 | /**< delay value */ | ||
123 | struct ami_vector3d m_delay; | ||
124 | #endif | ||
125 | }; | ||
126 | |||
127 | /** AMI chip information ex) 1)model 2)s/n 3)ver 4)more info in the chip */ | ||
128 | struct ami_chipinfo { | ||
129 | unsigned short info; /* INFO 0x0d/0x0e reg. */ | ||
130 | unsigned short ver; /* VER 0xe8/0xe9 reg. */ | ||
131 | unsigned short sn; /* SN 0xea/0xeb reg. */ | ||
132 | unsigned char wia; /* WIA 0x0f reg. */ | ||
133 | }; | ||
134 | |||
135 | /** AMI Driver Information */ | ||
136 | struct ami_driverinfo { | ||
137 | unsigned char remarks[40]; /* Some Information */ | ||
138 | unsigned char datetime[30]; /* compiled date&time */ | ||
139 | unsigned char ver_major; /* major version */ | ||
140 | unsigned char ver_middle; /* middle.. */ | ||
141 | unsigned char ver_minor; /* minor .. */ | ||
142 | }; | ||
143 | |||
144 | #endif | ||
diff --git a/drivers/misc/inv_mpu/compass/hmc5883.c b/drivers/misc/inv_mpu/compass/hmc5883.c new file mode 100644 index 00000000000..fdf2ac00565 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hmc5883.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file hmc5883.c | ||
25 | * @brief Magnetometer setup and handling methods for Honeywell | ||
26 | * HMC5883 compass. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | enum HMC_REG { | ||
49 | HMC_REG_CONF_A = 0x0, | ||
50 | HMC_REG_CONF_B = 0x1, | ||
51 | HMC_REG_MODE = 0x2, | ||
52 | HMC_REG_X_M = 0x3, | ||
53 | HMC_REG_X_L = 0x4, | ||
54 | HMC_REG_Z_M = 0x5, | ||
55 | HMC_REG_Z_L = 0x6, | ||
56 | HMC_REG_Y_M = 0x7, | ||
57 | HMC_REG_Y_L = 0x8, | ||
58 | HMC_REG_STATUS = 0x9, | ||
59 | HMC_REG_ID_A = 0xA, | ||
60 | HMC_REG_ID_B = 0xB, | ||
61 | HMC_REG_ID_C = 0xC | ||
62 | }; | ||
63 | |||
64 | enum HMC_CONF_A { | ||
65 | HMC_CONF_A_DRATE_MASK = 0x1C, | ||
66 | HMC_CONF_A_DRATE_0_75 = 0x00, | ||
67 | HMC_CONF_A_DRATE_1_5 = 0x04, | ||
68 | HMC_CONF_A_DRATE_3 = 0x08, | ||
69 | HMC_CONF_A_DRATE_7_5 = 0x0C, | ||
70 | HMC_CONF_A_DRATE_15 = 0x10, | ||
71 | HMC_CONF_A_DRATE_30 = 0x14, | ||
72 | HMC_CONF_A_DRATE_75 = 0x18, | ||
73 | HMC_CONF_A_MEAS_MASK = 0x3, | ||
74 | HMC_CONF_A_MEAS_NORM = 0x0, | ||
75 | HMC_CONF_A_MEAS_POS = 0x1, | ||
76 | HMC_CONF_A_MEAS_NEG = 0x2 | ||
77 | }; | ||
78 | |||
79 | enum HMC_CONF_B { | ||
80 | HMC_CONF_B_GAIN_MASK = 0xE0, | ||
81 | HMC_CONF_B_GAIN_0_9 = 0x00, | ||
82 | HMC_CONF_B_GAIN_1_2 = 0x20, | ||
83 | HMC_CONF_B_GAIN_1_9 = 0x40, | ||
84 | HMC_CONF_B_GAIN_2_5 = 0x60, | ||
85 | HMC_CONF_B_GAIN_4_0 = 0x80, | ||
86 | HMC_CONF_B_GAIN_4_6 = 0xA0, | ||
87 | HMC_CONF_B_GAIN_5_5 = 0xC0, | ||
88 | HMC_CONF_B_GAIN_7_9 = 0xE0 | ||
89 | }; | ||
90 | |||
91 | enum HMC_MODE { | ||
92 | HMC_MODE_MASK = 0x3, | ||
93 | HMC_MODE_CONT = 0x0, | ||
94 | HMC_MODE_SINGLE = 0x1, | ||
95 | HMC_MODE_IDLE = 0x2, | ||
96 | HMC_MODE_SLEEP = 0x3 | ||
97 | }; | ||
98 | |||
99 | /* -------------------------------------------------------------------------- */ | ||
100 | static int hmc5883_suspend(void *mlsl_handle, | ||
101 | struct ext_slave_descr *slave, | ||
102 | struct ext_slave_platform_data *pdata) | ||
103 | { | ||
104 | int result = INV_SUCCESS; | ||
105 | |||
106 | result = | ||
107 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
108 | HMC_REG_MODE, HMC_MODE_SLEEP); | ||
109 | if (result) { | ||
110 | LOG_RESULT_LOCATION(result); | ||
111 | return result; | ||
112 | } | ||
113 | msleep(3); | ||
114 | |||
115 | return result; | ||
116 | } | ||
117 | |||
118 | static int hmc5883_resume(void *mlsl_handle, | ||
119 | struct ext_slave_descr *slave, | ||
120 | struct ext_slave_platform_data *pdata) | ||
121 | { | ||
122 | int result = INV_SUCCESS; | ||
123 | |||
124 | /* Use single measurement mode. Start at sleep state. */ | ||
125 | result = | ||
126 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
127 | HMC_REG_MODE, HMC_MODE_SLEEP); | ||
128 | if (result) { | ||
129 | LOG_RESULT_LOCATION(result); | ||
130 | return result; | ||
131 | } | ||
132 | /* Config normal measurement */ | ||
133 | result = | ||
134 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
135 | HMC_REG_CONF_A, 0); | ||
136 | if (result) { | ||
137 | LOG_RESULT_LOCATION(result); | ||
138 | return result; | ||
139 | } | ||
140 | /* Adjust gain to 307 LSB/Gauss */ | ||
141 | result = | ||
142 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
143 | HMC_REG_CONF_B, HMC_CONF_B_GAIN_5_5); | ||
144 | if (result) { | ||
145 | LOG_RESULT_LOCATION(result); | ||
146 | return result; | ||
147 | } | ||
148 | |||
149 | return result; | ||
150 | } | ||
151 | |||
152 | static int hmc5883_read(void *mlsl_handle, | ||
153 | struct ext_slave_descr *slave, | ||
154 | struct ext_slave_platform_data *pdata, | ||
155 | unsigned char *data) | ||
156 | { | ||
157 | unsigned char stat; | ||
158 | int result = INV_SUCCESS; | ||
159 | unsigned char tmp; | ||
160 | short axisFixed; | ||
161 | |||
162 | /* Read status reg. to check if data is ready */ | ||
163 | result = | ||
164 | inv_serial_read(mlsl_handle, pdata->address, HMC_REG_STATUS, 1, | ||
165 | &stat); | ||
166 | if (result) { | ||
167 | LOG_RESULT_LOCATION(result); | ||
168 | return result; | ||
169 | } | ||
170 | if (stat & 0x01) { | ||
171 | result = | ||
172 | inv_serial_read(mlsl_handle, pdata->address, | ||
173 | HMC_REG_X_M, 6, (unsigned char *)data); | ||
174 | if (result) { | ||
175 | LOG_RESULT_LOCATION(result); | ||
176 | return result; | ||
177 | } | ||
178 | |||
179 | /* switch YZ axis to proper position */ | ||
180 | tmp = data[2]; | ||
181 | data[2] = data[4]; | ||
182 | data[4] = tmp; | ||
183 | tmp = data[3]; | ||
184 | data[3] = data[5]; | ||
185 | data[5] = tmp; | ||
186 | |||
187 | /*drop data if overflows */ | ||
188 | if ((data[0] == 0xf0) || (data[2] == 0xf0) | ||
189 | || (data[4] == 0xf0)) { | ||
190 | /* trigger next measurement read */ | ||
191 | result = | ||
192 | inv_serial_single_write(mlsl_handle, | ||
193 | pdata->address, | ||
194 | HMC_REG_MODE, | ||
195 | HMC_MODE_SINGLE); | ||
196 | if (result) { | ||
197 | LOG_RESULT_LOCATION(result); | ||
198 | return result; | ||
199 | } | ||
200 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
201 | } | ||
202 | /* convert to fixed point and apply sensitivity correction for | ||
203 | Z-axis */ | ||
204 | axisFixed = | ||
205 | (short)((unsigned short)data[5] + | ||
206 | (unsigned short)data[4] * 256); | ||
207 | /* scale up by 1.125 (36/32) */ | ||
208 | axisFixed = (short)(axisFixed * 36); | ||
209 | data[4] = axisFixed >> 8; | ||
210 | data[5] = axisFixed & 0xFF; | ||
211 | |||
212 | axisFixed = | ||
213 | (short)((unsigned short)data[3] + | ||
214 | (unsigned short)data[2] * 256); | ||
215 | axisFixed = (short)(axisFixed * 32); | ||
216 | data[2] = axisFixed >> 8; | ||
217 | data[3] = axisFixed & 0xFF; | ||
218 | |||
219 | axisFixed = | ||
220 | (short)((unsigned short)data[1] + | ||
221 | (unsigned short)data[0] * 256); | ||
222 | axisFixed = (short)(axisFixed * 32); | ||
223 | data[0] = axisFixed >> 8; | ||
224 | data[1] = axisFixed & 0xFF; | ||
225 | |||
226 | /* trigger next measurement read */ | ||
227 | result = | ||
228 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
229 | HMC_REG_MODE, HMC_MODE_SINGLE); | ||
230 | if (result) { | ||
231 | LOG_RESULT_LOCATION(result); | ||
232 | return result; | ||
233 | } | ||
234 | |||
235 | return INV_SUCCESS; | ||
236 | } else { | ||
237 | /* trigger next measurement read */ | ||
238 | result = | ||
239 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
240 | HMC_REG_MODE, HMC_MODE_SINGLE); | ||
241 | if (result) { | ||
242 | LOG_RESULT_LOCATION(result); | ||
243 | return result; | ||
244 | } | ||
245 | |||
246 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | static struct ext_slave_descr hmc5883_descr = { | ||
251 | .init = NULL, | ||
252 | .exit = NULL, | ||
253 | .suspend = hmc5883_suspend, | ||
254 | .resume = hmc5883_resume, | ||
255 | .read = hmc5883_read, | ||
256 | .config = NULL, | ||
257 | .get_config = NULL, | ||
258 | .name = "hmc5883", | ||
259 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
260 | .id = COMPASS_ID_HMC5883, | ||
261 | .read_reg = 0x06, | ||
262 | .read_len = 6, | ||
263 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
264 | .range = {10673, 6156}, | ||
265 | .trigger = NULL, | ||
266 | }; | ||
267 | |||
268 | static | ||
269 | struct ext_slave_descr *hmc5883_get_slave_descr(void) | ||
270 | { | ||
271 | return &hmc5883_descr; | ||
272 | } | ||
273 | |||
274 | /* -------------------------------------------------------------------------- */ | ||
275 | struct hmc5883_mod_private_data { | ||
276 | struct i2c_client *client; | ||
277 | struct ext_slave_platform_data *pdata; | ||
278 | }; | ||
279 | |||
280 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
281 | |||
282 | static int hmc5883_mod_probe(struct i2c_client *client, | ||
283 | const struct i2c_device_id *devid) | ||
284 | { | ||
285 | struct ext_slave_platform_data *pdata; | ||
286 | struct hmc5883_mod_private_data *private_data; | ||
287 | int result = 0; | ||
288 | |||
289 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
290 | |||
291 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
292 | result = -ENODEV; | ||
293 | goto out_no_free; | ||
294 | } | ||
295 | |||
296 | pdata = client->dev.platform_data; | ||
297 | if (!pdata) { | ||
298 | dev_err(&client->adapter->dev, | ||
299 | "Missing platform data for slave %s\n", devid->name); | ||
300 | result = -EFAULT; | ||
301 | goto out_no_free; | ||
302 | } | ||
303 | |||
304 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
305 | if (!private_data) { | ||
306 | result = -ENOMEM; | ||
307 | goto out_no_free; | ||
308 | } | ||
309 | |||
310 | i2c_set_clientdata(client, private_data); | ||
311 | private_data->client = client; | ||
312 | private_data->pdata = pdata; | ||
313 | |||
314 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
315 | hmc5883_get_slave_descr); | ||
316 | if (result) { | ||
317 | dev_err(&client->adapter->dev, | ||
318 | "Slave registration failed: %s, %d\n", | ||
319 | devid->name, result); | ||
320 | goto out_free_memory; | ||
321 | } | ||
322 | |||
323 | return result; | ||
324 | |||
325 | out_free_memory: | ||
326 | kfree(private_data); | ||
327 | out_no_free: | ||
328 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
329 | return result; | ||
330 | |||
331 | } | ||
332 | |||
333 | static int hmc5883_mod_remove(struct i2c_client *client) | ||
334 | { | ||
335 | struct hmc5883_mod_private_data *private_data = | ||
336 | i2c_get_clientdata(client); | ||
337 | |||
338 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
339 | |||
340 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
341 | hmc5883_get_slave_descr); | ||
342 | |||
343 | kfree(private_data); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static const struct i2c_device_id hmc5883_mod_id[] = { | ||
348 | { "hmc5883", COMPASS_ID_HMC5883 }, | ||
349 | {} | ||
350 | }; | ||
351 | |||
352 | MODULE_DEVICE_TABLE(i2c, hmc5883_mod_id); | ||
353 | |||
354 | static struct i2c_driver hmc5883_mod_driver = { | ||
355 | .class = I2C_CLASS_HWMON, | ||
356 | .probe = hmc5883_mod_probe, | ||
357 | .remove = hmc5883_mod_remove, | ||
358 | .id_table = hmc5883_mod_id, | ||
359 | .driver = { | ||
360 | .owner = THIS_MODULE, | ||
361 | .name = "hmc5883_mod", | ||
362 | }, | ||
363 | .address_list = normal_i2c, | ||
364 | }; | ||
365 | |||
366 | static int __init hmc5883_mod_init(void) | ||
367 | { | ||
368 | int res = i2c_add_driver(&hmc5883_mod_driver); | ||
369 | pr_info("%s: Probe name %s\n", __func__, "hmc5883_mod"); | ||
370 | if (res) | ||
371 | pr_err("%s failed\n", __func__); | ||
372 | return res; | ||
373 | } | ||
374 | |||
375 | static void __exit hmc5883_mod_exit(void) | ||
376 | { | ||
377 | pr_info("%s\n", __func__); | ||
378 | i2c_del_driver(&hmc5883_mod_driver); | ||
379 | } | ||
380 | |||
381 | module_init(hmc5883_mod_init); | ||
382 | module_exit(hmc5883_mod_exit); | ||
383 | |||
384 | MODULE_AUTHOR("Invensense Corporation"); | ||
385 | MODULE_DESCRIPTION("Driver to integrate HMC5883 sensor with the MPU"); | ||
386 | MODULE_LICENSE("GPL"); | ||
387 | MODULE_ALIAS("hmc5883_mod"); | ||
388 | |||
389 | /** | ||
390 | * @} | ||
391 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/hscdtd002b.c b/drivers/misc/inv_mpu/compass/hscdtd002b.c new file mode 100644 index 00000000000..4f6013cbe3d --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hscdtd002b.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file hscdtd002b.c | ||
25 | * @brief Magnetometer setup and handling methods for Alps HSCDTD002B | ||
26 | * compass. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | #define COMPASS_HSCDTD002B_STAT (0x18) | ||
49 | #define COMPASS_HSCDTD002B_CTRL1 (0x1B) | ||
50 | #define COMPASS_HSCDTD002B_CTRL2 (0x1C) | ||
51 | #define COMPASS_HSCDTD002B_CTRL3 (0x1D) | ||
52 | #define COMPASS_HSCDTD002B_DATAX (0x10) | ||
53 | |||
54 | /* -------------------------------------------------------------------------- */ | ||
55 | static int hscdtd002b_suspend(void *mlsl_handle, | ||
56 | struct ext_slave_descr *slave, | ||
57 | struct ext_slave_platform_data *pdata) | ||
58 | { | ||
59 | int result = INV_SUCCESS; | ||
60 | |||
61 | /* Power mode: stand-by */ | ||
62 | result = | ||
63 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
64 | COMPASS_HSCDTD002B_CTRL1, 0x00); | ||
65 | if (result) { | ||
66 | LOG_RESULT_LOCATION(result); | ||
67 | return result; | ||
68 | } | ||
69 | msleep(1); /* turn-off time */ | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | |||
74 | static int hscdtd002b_resume(void *mlsl_handle, | ||
75 | struct ext_slave_descr *slave, | ||
76 | struct ext_slave_platform_data *pdata) | ||
77 | { | ||
78 | int result = INV_SUCCESS; | ||
79 | |||
80 | /* Soft reset */ | ||
81 | result = | ||
82 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
83 | COMPASS_HSCDTD002B_CTRL3, 0x80); | ||
84 | if (result) { | ||
85 | LOG_RESULT_LOCATION(result); | ||
86 | return result; | ||
87 | } | ||
88 | /* Force state; Power mode: active */ | ||
89 | result = | ||
90 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
91 | COMPASS_HSCDTD002B_CTRL1, 0x82); | ||
92 | if (result) { | ||
93 | LOG_RESULT_LOCATION(result); | ||
94 | return result; | ||
95 | } | ||
96 | /* Data ready enable */ | ||
97 | result = | ||
98 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
99 | COMPASS_HSCDTD002B_CTRL2, 0x08); | ||
100 | if (result) { | ||
101 | LOG_RESULT_LOCATION(result); | ||
102 | return result; | ||
103 | } | ||
104 | msleep(1); /* turn-on time */ | ||
105 | |||
106 | return result; | ||
107 | } | ||
108 | |||
109 | static int hscdtd002b_read(void *mlsl_handle, | ||
110 | struct ext_slave_descr *slave, | ||
111 | struct ext_slave_platform_data *pdata, | ||
112 | unsigned char *data) | ||
113 | { | ||
114 | unsigned char stat; | ||
115 | int result = INV_SUCCESS; | ||
116 | int status = INV_SUCCESS; | ||
117 | |||
118 | /* Read status reg. to check if data is ready */ | ||
119 | result = | ||
120 | inv_serial_read(mlsl_handle, pdata->address, | ||
121 | COMPASS_HSCDTD002B_STAT, 1, &stat); | ||
122 | if (result) { | ||
123 | LOG_RESULT_LOCATION(result); | ||
124 | return result; | ||
125 | } | ||
126 | if (stat & 0x40) { | ||
127 | result = | ||
128 | inv_serial_read(mlsl_handle, pdata->address, | ||
129 | COMPASS_HSCDTD002B_DATAX, 6, | ||
130 | (unsigned char *)data); | ||
131 | if (result) { | ||
132 | LOG_RESULT_LOCATION(result); | ||
133 | return result; | ||
134 | } | ||
135 | status = INV_SUCCESS; | ||
136 | } else if (stat & 0x20) { | ||
137 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
138 | } else { | ||
139 | status = INV_ERROR_COMPASS_DATA_NOT_READY; | ||
140 | } | ||
141 | /* trigger next measurement read */ | ||
142 | result = | ||
143 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
144 | COMPASS_HSCDTD002B_CTRL3, 0x40); | ||
145 | if (result) { | ||
146 | LOG_RESULT_LOCATION(result); | ||
147 | return result; | ||
148 | } | ||
149 | |||
150 | return status; | ||
151 | } | ||
152 | |||
153 | static struct ext_slave_descr hscdtd002b_descr = { | ||
154 | .init = NULL, | ||
155 | .exit = NULL, | ||
156 | .suspend = hscdtd002b_suspend, | ||
157 | .resume = hscdtd002b_resume, | ||
158 | .read = hscdtd002b_read, | ||
159 | .config = NULL, | ||
160 | .get_config = NULL, | ||
161 | .name = "hscdtd002b", | ||
162 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
163 | .id = COMPASS_ID_HSCDTD002B, | ||
164 | .read_reg = 0x10, | ||
165 | .read_len = 6, | ||
166 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
167 | .range = {9830, 4000}, | ||
168 | .trigger = NULL, | ||
169 | }; | ||
170 | |||
171 | static | ||
172 | struct ext_slave_descr *hscdtd002b_get_slave_descr(void) | ||
173 | { | ||
174 | return &hscdtd002b_descr; | ||
175 | } | ||
176 | |||
177 | /* -------------------------------------------------------------------------- */ | ||
178 | struct hscdtd002b_mod_private_data { | ||
179 | struct i2c_client *client; | ||
180 | struct ext_slave_platform_data *pdata; | ||
181 | }; | ||
182 | |||
183 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
184 | |||
185 | static int hscdtd002b_mod_probe(struct i2c_client *client, | ||
186 | const struct i2c_device_id *devid) | ||
187 | { | ||
188 | struct ext_slave_platform_data *pdata; | ||
189 | struct hscdtd002b_mod_private_data *private_data; | ||
190 | int result = 0; | ||
191 | |||
192 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
193 | |||
194 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
195 | result = -ENODEV; | ||
196 | goto out_no_free; | ||
197 | } | ||
198 | |||
199 | pdata = client->dev.platform_data; | ||
200 | if (!pdata) { | ||
201 | dev_err(&client->adapter->dev, | ||
202 | "Missing platform data for slave %s\n", devid->name); | ||
203 | result = -EFAULT; | ||
204 | goto out_no_free; | ||
205 | } | ||
206 | |||
207 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
208 | if (!private_data) { | ||
209 | result = -ENOMEM; | ||
210 | goto out_no_free; | ||
211 | } | ||
212 | |||
213 | i2c_set_clientdata(client, private_data); | ||
214 | private_data->client = client; | ||
215 | private_data->pdata = pdata; | ||
216 | |||
217 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
218 | hscdtd002b_get_slave_descr); | ||
219 | if (result) { | ||
220 | dev_err(&client->adapter->dev, | ||
221 | "Slave registration failed: %s, %d\n", | ||
222 | devid->name, result); | ||
223 | goto out_free_memory; | ||
224 | } | ||
225 | |||
226 | return result; | ||
227 | |||
228 | out_free_memory: | ||
229 | kfree(private_data); | ||
230 | out_no_free: | ||
231 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
232 | return result; | ||
233 | |||
234 | } | ||
235 | |||
236 | static int hscdtd002b_mod_remove(struct i2c_client *client) | ||
237 | { | ||
238 | struct hscdtd002b_mod_private_data *private_data = | ||
239 | i2c_get_clientdata(client); | ||
240 | |||
241 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
242 | |||
243 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
244 | hscdtd002b_get_slave_descr); | ||
245 | |||
246 | kfree(private_data); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static const struct i2c_device_id hscdtd002b_mod_id[] = { | ||
251 | { "hscdtd002b", COMPASS_ID_HSCDTD002B }, | ||
252 | {} | ||
253 | }; | ||
254 | |||
255 | MODULE_DEVICE_TABLE(i2c, hscdtd002b_mod_id); | ||
256 | |||
257 | static struct i2c_driver hscdtd002b_mod_driver = { | ||
258 | .class = I2C_CLASS_HWMON, | ||
259 | .probe = hscdtd002b_mod_probe, | ||
260 | .remove = hscdtd002b_mod_remove, | ||
261 | .id_table = hscdtd002b_mod_id, | ||
262 | .driver = { | ||
263 | .owner = THIS_MODULE, | ||
264 | .name = "hscdtd002b_mod", | ||
265 | }, | ||
266 | .address_list = normal_i2c, | ||
267 | }; | ||
268 | |||
269 | static int __init hscdtd002b_mod_init(void) | ||
270 | { | ||
271 | int res = i2c_add_driver(&hscdtd002b_mod_driver); | ||
272 | pr_info("%s: Probe name %s\n", __func__, "hscdtd002b_mod"); | ||
273 | if (res) | ||
274 | pr_err("%s failed\n", __func__); | ||
275 | return res; | ||
276 | } | ||
277 | |||
278 | static void __exit hscdtd002b_mod_exit(void) | ||
279 | { | ||
280 | pr_info("%s\n", __func__); | ||
281 | i2c_del_driver(&hscdtd002b_mod_driver); | ||
282 | } | ||
283 | |||
284 | module_init(hscdtd002b_mod_init); | ||
285 | module_exit(hscdtd002b_mod_exit); | ||
286 | |||
287 | MODULE_AUTHOR("Invensense Corporation"); | ||
288 | MODULE_DESCRIPTION("Driver to integrate HSCDTD002B sensor with the MPU"); | ||
289 | MODULE_LICENSE("GPL"); | ||
290 | MODULE_ALIAS("hscdtd002b_mod"); | ||
291 | |||
292 | /** | ||
293 | * @} | ||
294 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/hscdtd004a.c b/drivers/misc/inv_mpu/compass/hscdtd004a.c new file mode 100644 index 00000000000..f0915599bd2 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/hscdtd004a.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file hscdtd004a.c | ||
25 | * @brief Magnetometer setup and handling methods for Alps HSCDTD004A | ||
26 | * compass. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | #define COMPASS_HSCDTD004A_STAT (0x18) | ||
49 | #define COMPASS_HSCDTD004A_CTRL1 (0x1B) | ||
50 | #define COMPASS_HSCDTD004A_CTRL2 (0x1C) | ||
51 | #define COMPASS_HSCDTD004A_CTRL3 (0x1D) | ||
52 | #define COMPASS_HSCDTD004A_DATAX (0x10) | ||
53 | |||
54 | /* -------------------------------------------------------------------------- */ | ||
55 | |||
56 | static int hscdtd004a_suspend(void *mlsl_handle, | ||
57 | struct ext_slave_descr *slave, | ||
58 | struct ext_slave_platform_data *pdata) | ||
59 | { | ||
60 | int result = INV_SUCCESS; | ||
61 | |||
62 | /* Power mode: stand-by */ | ||
63 | result = | ||
64 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
65 | COMPASS_HSCDTD004A_CTRL1, 0x00); | ||
66 | if (result) { | ||
67 | LOG_RESULT_LOCATION(result); | ||
68 | return result; | ||
69 | } | ||
70 | msleep(1); /* turn-off time */ | ||
71 | |||
72 | return result; | ||
73 | } | ||
74 | |||
75 | static int hscdtd004a_init(void *mlsl_handle, | ||
76 | struct ext_slave_descr *slave, | ||
77 | struct ext_slave_platform_data *pdata) | ||
78 | { | ||
79 | int result; | ||
80 | unsigned char data1, data2[2]; | ||
81 | |||
82 | result = inv_serial_read(mlsl_handle, pdata->address, 0xf, 1, &data1); | ||
83 | if (result) { | ||
84 | LOG_RESULT_LOCATION(result); | ||
85 | return result; | ||
86 | } | ||
87 | result = inv_serial_read(mlsl_handle, pdata->address, 0xd, 2, data2); | ||
88 | if (result) { | ||
89 | LOG_RESULT_LOCATION(result); | ||
90 | return result; | ||
91 | } | ||
92 | if (data1 != 0x49 || data2[0] != 0x45 || data2[1] != 0x54) { | ||
93 | LOG_RESULT_LOCATION(INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED); | ||
94 | return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED; | ||
95 | } | ||
96 | return result; | ||
97 | } | ||
98 | |||
99 | static int hscdtd004a_resume(void *mlsl_handle, | ||
100 | struct ext_slave_descr *slave, | ||
101 | struct ext_slave_platform_data *pdata) | ||
102 | { | ||
103 | int result = INV_SUCCESS; | ||
104 | |||
105 | /* Soft reset */ | ||
106 | result = | ||
107 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
108 | COMPASS_HSCDTD004A_CTRL3, 0x80); | ||
109 | if (result) { | ||
110 | LOG_RESULT_LOCATION(result); | ||
111 | return result; | ||
112 | } | ||
113 | /* Normal state; Power mode: active */ | ||
114 | result = | ||
115 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
116 | COMPASS_HSCDTD004A_CTRL1, 0x82); | ||
117 | if (result) { | ||
118 | LOG_RESULT_LOCATION(result); | ||
119 | return result; | ||
120 | } | ||
121 | /* Data ready enable */ | ||
122 | result = | ||
123 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
124 | COMPASS_HSCDTD004A_CTRL2, 0x7C); | ||
125 | if (result) { | ||
126 | LOG_RESULT_LOCATION(result); | ||
127 | return result; | ||
128 | } | ||
129 | msleep(1); /* turn-on time */ | ||
130 | return result; | ||
131 | } | ||
132 | |||
133 | static int hscdtd004a_read(void *mlsl_handle, | ||
134 | struct ext_slave_descr *slave, | ||
135 | struct ext_slave_platform_data *pdata, | ||
136 | unsigned char *data) | ||
137 | { | ||
138 | unsigned char stat; | ||
139 | int result = INV_SUCCESS; | ||
140 | int status = INV_SUCCESS; | ||
141 | |||
142 | /* Read status reg. to check if data is ready */ | ||
143 | result = | ||
144 | inv_serial_read(mlsl_handle, pdata->address, | ||
145 | COMPASS_HSCDTD004A_STAT, 1, &stat); | ||
146 | if (result) { | ||
147 | LOG_RESULT_LOCATION(result); | ||
148 | return result; | ||
149 | } | ||
150 | if (stat & 0x48) { | ||
151 | result = | ||
152 | inv_serial_read(mlsl_handle, pdata->address, | ||
153 | COMPASS_HSCDTD004A_DATAX, 6, | ||
154 | (unsigned char *)data); | ||
155 | if (result) { | ||
156 | LOG_RESULT_LOCATION(result); | ||
157 | return result; | ||
158 | } | ||
159 | status = INV_SUCCESS; | ||
160 | } else if (stat & 0x68) { | ||
161 | status = INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
162 | } else { | ||
163 | status = INV_ERROR_COMPASS_DATA_NOT_READY; | ||
164 | } | ||
165 | /* trigger next measurement read */ | ||
166 | result = | ||
167 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
168 | COMPASS_HSCDTD004A_CTRL3, 0x40); | ||
169 | if (result) { | ||
170 | LOG_RESULT_LOCATION(result); | ||
171 | return result; | ||
172 | } | ||
173 | return status; | ||
174 | |||
175 | } | ||
176 | |||
177 | static struct ext_slave_descr hscdtd004a_descr = { | ||
178 | .init = hscdtd004a_init, | ||
179 | .exit = NULL, | ||
180 | .suspend = hscdtd004a_suspend, | ||
181 | .resume = hscdtd004a_resume, | ||
182 | .read = hscdtd004a_read, | ||
183 | .config = NULL, | ||
184 | .get_config = NULL, | ||
185 | .name = "hscdtd004a", | ||
186 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
187 | .id = COMPASS_ID_HSCDTD004A, | ||
188 | .read_reg = 0x10, | ||
189 | .read_len = 6, | ||
190 | .endian = EXT_SLAVE_LITTLE_ENDIAN, | ||
191 | .range = {9830, 4000}, | ||
192 | .trigger = NULL, | ||
193 | }; | ||
194 | |||
195 | static | ||
196 | struct ext_slave_descr *hscdtd004a_get_slave_descr(void) | ||
197 | { | ||
198 | return &hscdtd004a_descr; | ||
199 | } | ||
200 | |||
201 | /* -------------------------------------------------------------------------- */ | ||
202 | struct hscdtd004a_mod_private_data { | ||
203 | struct i2c_client *client; | ||
204 | struct ext_slave_platform_data *pdata; | ||
205 | }; | ||
206 | |||
207 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
208 | |||
209 | static int hscdtd004a_mod_probe(struct i2c_client *client, | ||
210 | const struct i2c_device_id *devid) | ||
211 | { | ||
212 | struct ext_slave_platform_data *pdata; | ||
213 | struct hscdtd004a_mod_private_data *private_data; | ||
214 | int result = 0; | ||
215 | |||
216 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
217 | |||
218 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
219 | result = -ENODEV; | ||
220 | goto out_no_free; | ||
221 | } | ||
222 | |||
223 | pdata = client->dev.platform_data; | ||
224 | if (!pdata) { | ||
225 | dev_err(&client->adapter->dev, | ||
226 | "Missing platform data for slave %s\n", devid->name); | ||
227 | result = -EFAULT; | ||
228 | goto out_no_free; | ||
229 | } | ||
230 | |||
231 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
232 | if (!private_data) { | ||
233 | result = -ENOMEM; | ||
234 | goto out_no_free; | ||
235 | } | ||
236 | |||
237 | i2c_set_clientdata(client, private_data); | ||
238 | private_data->client = client; | ||
239 | private_data->pdata = pdata; | ||
240 | |||
241 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
242 | hscdtd004a_get_slave_descr); | ||
243 | if (result) { | ||
244 | dev_err(&client->adapter->dev, | ||
245 | "Slave registration failed: %s, %d\n", | ||
246 | devid->name, result); | ||
247 | goto out_free_memory; | ||
248 | } | ||
249 | |||
250 | return result; | ||
251 | |||
252 | out_free_memory: | ||
253 | kfree(private_data); | ||
254 | out_no_free: | ||
255 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
256 | return result; | ||
257 | |||
258 | } | ||
259 | |||
260 | static int hscdtd004a_mod_remove(struct i2c_client *client) | ||
261 | { | ||
262 | struct hscdtd004a_mod_private_data *private_data = | ||
263 | i2c_get_clientdata(client); | ||
264 | |||
265 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
266 | |||
267 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
268 | hscdtd004a_get_slave_descr); | ||
269 | |||
270 | kfree(private_data); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static const struct i2c_device_id hscdtd004a_mod_id[] = { | ||
275 | { "hscdtd004a", COMPASS_ID_HSCDTD004A }, | ||
276 | {} | ||
277 | }; | ||
278 | |||
279 | MODULE_DEVICE_TABLE(i2c, hscdtd004a_mod_id); | ||
280 | |||
281 | static struct i2c_driver hscdtd004a_mod_driver = { | ||
282 | .class = I2C_CLASS_HWMON, | ||
283 | .probe = hscdtd004a_mod_probe, | ||
284 | .remove = hscdtd004a_mod_remove, | ||
285 | .id_table = hscdtd004a_mod_id, | ||
286 | .driver = { | ||
287 | .owner = THIS_MODULE, | ||
288 | .name = "hscdtd004a_mod", | ||
289 | }, | ||
290 | .address_list = normal_i2c, | ||
291 | }; | ||
292 | |||
293 | static int __init hscdtd004a_mod_init(void) | ||
294 | { | ||
295 | int res = i2c_add_driver(&hscdtd004a_mod_driver); | ||
296 | pr_info("%s: Probe name %s\n", __func__, "hscdtd004a_mod"); | ||
297 | if (res) | ||
298 | pr_err("%s failed\n", __func__); | ||
299 | return res; | ||
300 | } | ||
301 | |||
302 | static void __exit hscdtd004a_mod_exit(void) | ||
303 | { | ||
304 | pr_info("%s\n", __func__); | ||
305 | i2c_del_driver(&hscdtd004a_mod_driver); | ||
306 | } | ||
307 | |||
308 | module_init(hscdtd004a_mod_init); | ||
309 | module_exit(hscdtd004a_mod_exit); | ||
310 | |||
311 | MODULE_AUTHOR("Invensense Corporation"); | ||
312 | MODULE_DESCRIPTION("Driver to integrate HSCDTD004A sensor with the MPU"); | ||
313 | MODULE_LICENSE("GPL"); | ||
314 | MODULE_ALIAS("hscdtd004a_mod"); | ||
315 | |||
316 | /** | ||
317 | * @} | ||
318 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/lsm303dlx_m.c b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c new file mode 100644 index 00000000000..32f8cdddb00 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file lsm303dlx_m.c | ||
25 | * @brief Magnetometer setup and handling methods for ST LSM303 | ||
26 | * compass. | ||
27 | * This magnetometer device is part of a combo chip with the | ||
28 | * ST LIS331DLH accelerometer and the logic in entirely based | ||
29 | * on the Honeywell HMC5883 magnetometer. | ||
30 | */ | ||
31 | |||
32 | /* -------------------------------------------------------------------------- */ | ||
33 | |||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include "mpu-dev.h" | ||
42 | |||
43 | #include <log.h> | ||
44 | #include <linux/mpu.h> | ||
45 | #include "mlsl.h" | ||
46 | #include "mldl_cfg.h" | ||
47 | #undef MPL_LOG_TAG | ||
48 | #define MPL_LOG_TAG "MPL-compass" | ||
49 | |||
50 | /* -------------------------------------------------------------------------- */ | ||
51 | enum LSM_REG { | ||
52 | LSM_REG_CONF_A = 0x0, | ||
53 | LSM_REG_CONF_B = 0x1, | ||
54 | LSM_REG_MODE = 0x2, | ||
55 | LSM_REG_X_M = 0x3, | ||
56 | LSM_REG_X_L = 0x4, | ||
57 | LSM_REG_Z_M = 0x5, | ||
58 | LSM_REG_Z_L = 0x6, | ||
59 | LSM_REG_Y_M = 0x7, | ||
60 | LSM_REG_Y_L = 0x8, | ||
61 | LSM_REG_STATUS = 0x9, | ||
62 | LSM_REG_ID_A = 0xA, | ||
63 | LSM_REG_ID_B = 0xB, | ||
64 | LSM_REG_ID_C = 0xC | ||
65 | }; | ||
66 | |||
67 | enum LSM_CONF_A { | ||
68 | LSM_CONF_A_DRATE_MASK = 0x1C, | ||
69 | LSM_CONF_A_DRATE_0_75 = 0x00, | ||
70 | LSM_CONF_A_DRATE_1_5 = 0x04, | ||
71 | LSM_CONF_A_DRATE_3 = 0x08, | ||
72 | LSM_CONF_A_DRATE_7_5 = 0x0C, | ||
73 | LSM_CONF_A_DRATE_15 = 0x10, | ||
74 | LSM_CONF_A_DRATE_30 = 0x14, | ||
75 | LSM_CONF_A_DRATE_75 = 0x18, | ||
76 | LSM_CONF_A_MEAS_MASK = 0x3, | ||
77 | LSM_CONF_A_MEAS_NORM = 0x0, | ||
78 | LSM_CONF_A_MEAS_POS = 0x1, | ||
79 | LSM_CONF_A_MEAS_NEG = 0x2 | ||
80 | }; | ||
81 | |||
82 | enum LSM_CONF_B { | ||
83 | LSM_CONF_B_GAIN_MASK = 0xE0, | ||
84 | LSM_CONF_B_GAIN_0_9 = 0x00, | ||
85 | LSM_CONF_B_GAIN_1_2 = 0x20, | ||
86 | LSM_CONF_B_GAIN_1_9 = 0x40, | ||
87 | LSM_CONF_B_GAIN_2_5 = 0x60, | ||
88 | LSM_CONF_B_GAIN_4_0 = 0x80, | ||
89 | LSM_CONF_B_GAIN_4_6 = 0xA0, | ||
90 | LSM_CONF_B_GAIN_5_5 = 0xC0, | ||
91 | LSM_CONF_B_GAIN_7_9 = 0xE0 | ||
92 | }; | ||
93 | |||
94 | enum LSM_MODE { | ||
95 | LSM_MODE_MASK = 0x3, | ||
96 | LSM_MODE_CONT = 0x0, | ||
97 | LSM_MODE_SINGLE = 0x1, | ||
98 | LSM_MODE_IDLE = 0x2, | ||
99 | LSM_MODE_SLEEP = 0x3 | ||
100 | }; | ||
101 | |||
102 | /* -------------------------------------------------------------------------- */ | ||
103 | |||
104 | static int lsm303dlx_m_suspend(void *mlsl_handle, | ||
105 | struct ext_slave_descr *slave, | ||
106 | struct ext_slave_platform_data *pdata) | ||
107 | { | ||
108 | int result = INV_SUCCESS; | ||
109 | |||
110 | result = | ||
111 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
112 | LSM_REG_MODE, LSM_MODE_SLEEP); | ||
113 | if (result) { | ||
114 | LOG_RESULT_LOCATION(result); | ||
115 | return result; | ||
116 | } | ||
117 | msleep(3); | ||
118 | |||
119 | return result; | ||
120 | } | ||
121 | |||
122 | static int lsm303dlx_m_resume(void *mlsl_handle, | ||
123 | struct ext_slave_descr *slave, | ||
124 | struct ext_slave_platform_data *pdata) | ||
125 | { | ||
126 | int result = INV_SUCCESS; | ||
127 | |||
128 | /* Use single measurement mode. Start at sleep state. */ | ||
129 | result = | ||
130 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
131 | LSM_REG_MODE, LSM_MODE_SLEEP); | ||
132 | if (result) { | ||
133 | LOG_RESULT_LOCATION(result); | ||
134 | return result; | ||
135 | } | ||
136 | /* Config normal measurement */ | ||
137 | result = | ||
138 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
139 | LSM_REG_CONF_A, 0); | ||
140 | if (result) { | ||
141 | LOG_RESULT_LOCATION(result); | ||
142 | return result; | ||
143 | } | ||
144 | /* Adjust gain to 320 LSB/Gauss */ | ||
145 | result = | ||
146 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
147 | LSM_REG_CONF_B, LSM_CONF_B_GAIN_5_5); | ||
148 | if (result) { | ||
149 | LOG_RESULT_LOCATION(result); | ||
150 | return result; | ||
151 | } | ||
152 | |||
153 | return result; | ||
154 | } | ||
155 | |||
156 | static int lsm303dlx_m_read(void *mlsl_handle, | ||
157 | struct ext_slave_descr *slave, | ||
158 | struct ext_slave_platform_data *pdata, | ||
159 | unsigned char *data) | ||
160 | { | ||
161 | unsigned char stat; | ||
162 | int result = INV_SUCCESS; | ||
163 | short axis_fixed; | ||
164 | |||
165 | /* Read status reg. to check if data is ready */ | ||
166 | result = | ||
167 | inv_serial_read(mlsl_handle, pdata->address, LSM_REG_STATUS, 1, | ||
168 | &stat); | ||
169 | if (result) { | ||
170 | LOG_RESULT_LOCATION(result); | ||
171 | return result; | ||
172 | } | ||
173 | if (stat & 0x01) { | ||
174 | result = | ||
175 | inv_serial_read(mlsl_handle, pdata->address, | ||
176 | LSM_REG_X_M, 6, (unsigned char *)data); | ||
177 | if (result) { | ||
178 | LOG_RESULT_LOCATION(result); | ||
179 | return result; | ||
180 | } | ||
181 | |||
182 | /*drop data if overflows */ | ||
183 | if ((data[0] == 0xf0) || (data[2] == 0xf0) | ||
184 | || (data[4] == 0xf0)) { | ||
185 | /* trigger next measurement read */ | ||
186 | result = | ||
187 | inv_serial_single_write(mlsl_handle, | ||
188 | pdata->address, | ||
189 | LSM_REG_MODE, | ||
190 | LSM_MODE_SINGLE); | ||
191 | if (result) { | ||
192 | LOG_RESULT_LOCATION(result); | ||
193 | return result; | ||
194 | } | ||
195 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
196 | } | ||
197 | /* convert to fixed point and apply sensitivity correction for | ||
198 | Z-axis */ | ||
199 | axis_fixed = | ||
200 | (short)((unsigned short)data[5] + | ||
201 | (unsigned short)data[4] * 256); | ||
202 | /* scale up by 1.125 (36/32) approximate of 1.122 (320/285) */ | ||
203 | if (slave->id == COMPASS_ID_LSM303DLM) { | ||
204 | /* NOTE/IMPORTANT: | ||
205 | lsm303dlm compass axis definition doesn't | ||
206 | respect the right hand rule. We invert | ||
207 | the sign of the Z axis to fix that. */ | ||
208 | axis_fixed = (short)(-1 * axis_fixed * 36); | ||
209 | } else { | ||
210 | axis_fixed = (short)(axis_fixed * 36); | ||
211 | } | ||
212 | data[4] = axis_fixed >> 8; | ||
213 | data[5] = axis_fixed & 0xFF; | ||
214 | |||
215 | axis_fixed = | ||
216 | (short)((unsigned short)data[3] + | ||
217 | (unsigned short)data[2] * 256); | ||
218 | axis_fixed = (short)(axis_fixed * 32); | ||
219 | data[2] = axis_fixed >> 8; | ||
220 | data[3] = axis_fixed & 0xFF; | ||
221 | |||
222 | axis_fixed = | ||
223 | (short)((unsigned short)data[1] + | ||
224 | (unsigned short)data[0] * 256); | ||
225 | axis_fixed = (short)(axis_fixed * 32); | ||
226 | data[0] = axis_fixed >> 8; | ||
227 | data[1] = axis_fixed & 0xFF; | ||
228 | |||
229 | /* trigger next measurement read */ | ||
230 | result = | ||
231 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
232 | LSM_REG_MODE, LSM_MODE_SINGLE); | ||
233 | if (result) { | ||
234 | LOG_RESULT_LOCATION(result); | ||
235 | return result; | ||
236 | } | ||
237 | |||
238 | return INV_SUCCESS; | ||
239 | } else { | ||
240 | /* trigger next measurement read */ | ||
241 | result = | ||
242 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
243 | LSM_REG_MODE, LSM_MODE_SINGLE); | ||
244 | if (result) { | ||
245 | LOG_RESULT_LOCATION(result); | ||
246 | return result; | ||
247 | } | ||
248 | |||
249 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static struct ext_slave_descr lsm303dlx_m_descr = { | ||
254 | .init = NULL, | ||
255 | .exit = NULL, | ||
256 | .suspend = lsm303dlx_m_suspend, | ||
257 | .resume = lsm303dlx_m_resume, | ||
258 | .read = lsm303dlx_m_read, | ||
259 | .config = NULL, | ||
260 | .get_config = NULL, | ||
261 | .name = "lsm303dlx_m", | ||
262 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
263 | .id = ID_INVALID, | ||
264 | .read_reg = 0x06, | ||
265 | .read_len = 6, | ||
266 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
267 | .range = {10240, 0}, | ||
268 | .trigger = NULL, | ||
269 | }; | ||
270 | |||
271 | static | ||
272 | struct ext_slave_descr *lsm303dlx_m_get_slave_descr(void) | ||
273 | { | ||
274 | return &lsm303dlx_m_descr; | ||
275 | } | ||
276 | |||
277 | /* -------------------------------------------------------------------------- */ | ||
278 | struct lsm303dlx_m_mod_private_data { | ||
279 | struct i2c_client *client; | ||
280 | struct ext_slave_platform_data *pdata; | ||
281 | }; | ||
282 | |||
283 | static const struct i2c_device_id lsm303dlx_m_mod_id[] = { | ||
284 | { "lsm303dlh", COMPASS_ID_LSM303DLH }, | ||
285 | { "lsm303dlm", COMPASS_ID_LSM303DLM }, | ||
286 | {} | ||
287 | }; | ||
288 | MODULE_DEVICE_TABLE(i2c, lsm303dlx_m_mod_id); | ||
289 | |||
290 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
291 | |||
292 | static int lsm303dlx_m_mod_probe(struct i2c_client *client, | ||
293 | const struct i2c_device_id *devid) | ||
294 | { | ||
295 | struct ext_slave_platform_data *pdata; | ||
296 | struct lsm303dlx_m_mod_private_data *private_data; | ||
297 | int result = 0; | ||
298 | |||
299 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
300 | lsm303dlx_m_descr.id = devid->driver_data; | ||
301 | |||
302 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
303 | result = -ENODEV; | ||
304 | goto out_no_free; | ||
305 | } | ||
306 | |||
307 | pdata = client->dev.platform_data; | ||
308 | if (!pdata) { | ||
309 | dev_err(&client->adapter->dev, | ||
310 | "Missing platform data for slave %s\n", devid->name); | ||
311 | result = -EFAULT; | ||
312 | goto out_no_free; | ||
313 | } | ||
314 | |||
315 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
316 | if (!private_data) { | ||
317 | result = -ENOMEM; | ||
318 | goto out_no_free; | ||
319 | } | ||
320 | |||
321 | i2c_set_clientdata(client, private_data); | ||
322 | private_data->client = client; | ||
323 | private_data->pdata = pdata; | ||
324 | |||
325 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
326 | lsm303dlx_m_get_slave_descr); | ||
327 | if (result) { | ||
328 | dev_err(&client->adapter->dev, | ||
329 | "Slave registration failed: %s, %d\n", | ||
330 | devid->name, result); | ||
331 | goto out_free_memory; | ||
332 | } | ||
333 | |||
334 | return result; | ||
335 | |||
336 | out_free_memory: | ||
337 | kfree(private_data); | ||
338 | out_no_free: | ||
339 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
340 | return result; | ||
341 | |||
342 | } | ||
343 | |||
344 | static int lsm303dlx_m_mod_remove(struct i2c_client *client) | ||
345 | { | ||
346 | struct lsm303dlx_m_mod_private_data *private_data = | ||
347 | i2c_get_clientdata(client); | ||
348 | |||
349 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
350 | |||
351 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
352 | lsm303dlx_m_get_slave_descr); | ||
353 | |||
354 | kfree(private_data); | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static struct i2c_driver lsm303dlx_m_mod_driver = { | ||
359 | .class = I2C_CLASS_HWMON, | ||
360 | .probe = lsm303dlx_m_mod_probe, | ||
361 | .remove = lsm303dlx_m_mod_remove, | ||
362 | .id_table = lsm303dlx_m_mod_id, | ||
363 | .driver = { | ||
364 | .owner = THIS_MODULE, | ||
365 | .name = "lsm303dlx_m_mod", | ||
366 | }, | ||
367 | .address_list = normal_i2c, | ||
368 | }; | ||
369 | |||
370 | static int __init lsm303dlx_m_mod_init(void) | ||
371 | { | ||
372 | int res = i2c_add_driver(&lsm303dlx_m_mod_driver); | ||
373 | pr_info("%s: Probe name %s\n", __func__, "lsm303dlx_m_mod"); | ||
374 | if (res) | ||
375 | pr_err("%s failed\n", __func__); | ||
376 | return res; | ||
377 | } | ||
378 | |||
379 | static void __exit lsm303dlx_m_mod_exit(void) | ||
380 | { | ||
381 | pr_info("%s\n", __func__); | ||
382 | i2c_del_driver(&lsm303dlx_m_mod_driver); | ||
383 | } | ||
384 | |||
385 | module_init(lsm303dlx_m_mod_init); | ||
386 | module_exit(lsm303dlx_m_mod_exit); | ||
387 | |||
388 | MODULE_AUTHOR("Invensense Corporation"); | ||
389 | MODULE_DESCRIPTION("Driver to integrate lsm303dlx_m sensor with the MPU"); | ||
390 | MODULE_LICENSE("GPL"); | ||
391 | MODULE_ALIAS("lsm303dlx_m_mod"); | ||
392 | |||
393 | /** | ||
394 | * @} | ||
395 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/mmc314x.c b/drivers/misc/inv_mpu/compass/mmc314x.c new file mode 100644 index 00000000000..786fadcc3e4 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/mmc314x.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mmc314x.c | ||
25 | * @brief Magnetometer setup and handling methods for the | ||
26 | * MEMSIC MMC314x compass. | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include "mpu-dev.h" | ||
39 | |||
40 | #include <log.h> | ||
41 | #include <linux/mpu.h> | ||
42 | #include "mlsl.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #undef MPL_LOG_TAG | ||
45 | #define MPL_LOG_TAG "MPL-compass" | ||
46 | |||
47 | /* -------------------------------------------------------------------------- */ | ||
48 | |||
49 | static int reset_int = 1000; | ||
50 | static int read_count = 1; | ||
51 | static char reset_mode; /* in Z-init section */ | ||
52 | |||
53 | /* -------------------------------------------------------------------------- */ | ||
54 | #define MMC314X_REG_ST (0x00) | ||
55 | #define MMC314X_REG_X_MSB (0x01) | ||
56 | |||
57 | #define MMC314X_CNTL_MODE_WAKE_UP (0x01) | ||
58 | #define MMC314X_CNTL_MODE_SET (0x02) | ||
59 | #define MMC314X_CNTL_MODE_RESET (0x04) | ||
60 | |||
61 | /* -------------------------------------------------------------------------- */ | ||
62 | |||
63 | static int mmc314x_suspend(void *mlsl_handle, | ||
64 | struct ext_slave_descr *slave, | ||
65 | struct ext_slave_platform_data *pdata) | ||
66 | { | ||
67 | int result = INV_SUCCESS; | ||
68 | |||
69 | return result; | ||
70 | } | ||
71 | |||
72 | static int mmc314x_resume(void *mlsl_handle, | ||
73 | struct ext_slave_descr *slave, | ||
74 | struct ext_slave_platform_data *pdata) | ||
75 | { | ||
76 | |||
77 | int result; | ||
78 | result = | ||
79 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
80 | MMC314X_REG_ST, MMC314X_CNTL_MODE_RESET); | ||
81 | if (result) { | ||
82 | LOG_RESULT_LOCATION(result); | ||
83 | return result; | ||
84 | } | ||
85 | msleep(10); | ||
86 | result = | ||
87 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
88 | MMC314X_REG_ST, MMC314X_CNTL_MODE_SET); | ||
89 | if (result) { | ||
90 | LOG_RESULT_LOCATION(result); | ||
91 | return result; | ||
92 | } | ||
93 | msleep(10); | ||
94 | read_count = 1; | ||
95 | return INV_SUCCESS; | ||
96 | } | ||
97 | |||
98 | static int mmc314x_read(void *mlsl_handle, | ||
99 | struct ext_slave_descr *slave, | ||
100 | struct ext_slave_platform_data *pdata, | ||
101 | unsigned char *data) | ||
102 | { | ||
103 | int result, ii; | ||
104 | short tmp[3]; | ||
105 | unsigned char tmpdata[6]; | ||
106 | |||
107 | if (read_count > 1000) | ||
108 | read_count = 1; | ||
109 | |||
110 | result = | ||
111 | inv_serial_read(mlsl_handle, pdata->address, MMC314X_REG_X_MSB, | ||
112 | 6, (unsigned char *)data); | ||
113 | if (result) { | ||
114 | LOG_RESULT_LOCATION(result); | ||
115 | return result; | ||
116 | } | ||
117 | |||
118 | for (ii = 0; ii < 6; ii++) | ||
119 | tmpdata[ii] = data[ii]; | ||
120 | |||
121 | for (ii = 0; ii < 3; ii++) { | ||
122 | tmp[ii] = (short)((tmpdata[2 * ii] << 8) + tmpdata[2 * ii + 1]); | ||
123 | tmp[ii] = tmp[ii] - 4096; | ||
124 | tmp[ii] = tmp[ii] * 16; | ||
125 | } | ||
126 | |||
127 | for (ii = 0; ii < 3; ii++) { | ||
128 | data[2 * ii] = (unsigned char)(tmp[ii] >> 8); | ||
129 | data[2 * ii + 1] = (unsigned char)(tmp[ii]); | ||
130 | } | ||
131 | |||
132 | if (read_count % reset_int == 0) { | ||
133 | if (reset_mode) { | ||
134 | result = | ||
135 | inv_serial_single_write(mlsl_handle, | ||
136 | pdata->address, | ||
137 | MMC314X_REG_ST, | ||
138 | MMC314X_CNTL_MODE_RESET); | ||
139 | if (result) { | ||
140 | LOG_RESULT_LOCATION(result); | ||
141 | return result; | ||
142 | } | ||
143 | reset_mode = 0; | ||
144 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
145 | } else { | ||
146 | result = | ||
147 | inv_serial_single_write(mlsl_handle, | ||
148 | pdata->address, | ||
149 | MMC314X_REG_ST, | ||
150 | MMC314X_CNTL_MODE_SET); | ||
151 | if (result) { | ||
152 | LOG_RESULT_LOCATION(result); | ||
153 | return result; | ||
154 | } | ||
155 | reset_mode = 1; | ||
156 | read_count++; | ||
157 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
158 | } | ||
159 | } | ||
160 | result = | ||
161 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
162 | MMC314X_REG_ST, MMC314X_CNTL_MODE_WAKE_UP); | ||
163 | if (result) { | ||
164 | LOG_RESULT_LOCATION(result); | ||
165 | return result; | ||
166 | } | ||
167 | read_count++; | ||
168 | |||
169 | return INV_SUCCESS; | ||
170 | } | ||
171 | |||
172 | static struct ext_slave_descr mmc314x_descr = { | ||
173 | .init = NULL, | ||
174 | .exit = NULL, | ||
175 | .suspend = mmc314x_suspend, | ||
176 | .resume = mmc314x_resume, | ||
177 | .read = mmc314x_read, | ||
178 | .config = NULL, | ||
179 | .get_config = NULL, | ||
180 | .name = "mmc314x", | ||
181 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
182 | .id = COMPASS_ID_MMC314X, | ||
183 | .read_reg = 0x01, | ||
184 | .read_len = 6, | ||
185 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
186 | .range = {400, 0}, | ||
187 | .trigger = NULL, | ||
188 | }; | ||
189 | |||
190 | static | ||
191 | struct ext_slave_descr *mmc314x_get_slave_descr(void) | ||
192 | { | ||
193 | return &mmc314x_descr; | ||
194 | } | ||
195 | |||
196 | /* -------------------------------------------------------------------------- */ | ||
197 | struct mmc314x_mod_private_data { | ||
198 | struct i2c_client *client; | ||
199 | struct ext_slave_platform_data *pdata; | ||
200 | }; | ||
201 | |||
202 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
203 | |||
204 | static int mmc314x_mod_probe(struct i2c_client *client, | ||
205 | const struct i2c_device_id *devid) | ||
206 | { | ||
207 | struct ext_slave_platform_data *pdata; | ||
208 | struct mmc314x_mod_private_data *private_data; | ||
209 | int result = 0; | ||
210 | |||
211 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
212 | |||
213 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
214 | result = -ENODEV; | ||
215 | goto out_no_free; | ||
216 | } | ||
217 | |||
218 | pdata = client->dev.platform_data; | ||
219 | if (!pdata) { | ||
220 | dev_err(&client->adapter->dev, | ||
221 | "Missing platform data for slave %s\n", devid->name); | ||
222 | result = -EFAULT; | ||
223 | goto out_no_free; | ||
224 | } | ||
225 | |||
226 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
227 | if (!private_data) { | ||
228 | result = -ENOMEM; | ||
229 | goto out_no_free; | ||
230 | } | ||
231 | |||
232 | i2c_set_clientdata(client, private_data); | ||
233 | private_data->client = client; | ||
234 | private_data->pdata = pdata; | ||
235 | |||
236 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
237 | mmc314x_get_slave_descr); | ||
238 | if (result) { | ||
239 | dev_err(&client->adapter->dev, | ||
240 | "Slave registration failed: %s, %d\n", | ||
241 | devid->name, result); | ||
242 | goto out_free_memory; | ||
243 | } | ||
244 | |||
245 | return result; | ||
246 | |||
247 | out_free_memory: | ||
248 | kfree(private_data); | ||
249 | out_no_free: | ||
250 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
251 | return result; | ||
252 | |||
253 | } | ||
254 | |||
255 | static int mmc314x_mod_remove(struct i2c_client *client) | ||
256 | { | ||
257 | struct mmc314x_mod_private_data *private_data = | ||
258 | i2c_get_clientdata(client); | ||
259 | |||
260 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
261 | |||
262 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
263 | mmc314x_get_slave_descr); | ||
264 | |||
265 | kfree(private_data); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static const struct i2c_device_id mmc314x_mod_id[] = { | ||
270 | { "mmc314x", COMPASS_ID_MMC314X }, | ||
271 | {} | ||
272 | }; | ||
273 | |||
274 | MODULE_DEVICE_TABLE(i2c, mmc314x_mod_id); | ||
275 | |||
276 | static struct i2c_driver mmc314x_mod_driver = { | ||
277 | .class = I2C_CLASS_HWMON, | ||
278 | .probe = mmc314x_mod_probe, | ||
279 | .remove = mmc314x_mod_remove, | ||
280 | .id_table = mmc314x_mod_id, | ||
281 | .driver = { | ||
282 | .owner = THIS_MODULE, | ||
283 | .name = "mmc314x_mod", | ||
284 | }, | ||
285 | .address_list = normal_i2c, | ||
286 | }; | ||
287 | |||
288 | static int __init mmc314x_mod_init(void) | ||
289 | { | ||
290 | int res = i2c_add_driver(&mmc314x_mod_driver); | ||
291 | pr_info("%s: Probe name %s\n", __func__, "mmc314x_mod"); | ||
292 | if (res) | ||
293 | pr_err("%s failed\n", __func__); | ||
294 | return res; | ||
295 | } | ||
296 | |||
297 | static void __exit mmc314x_mod_exit(void) | ||
298 | { | ||
299 | pr_info("%s\n", __func__); | ||
300 | i2c_del_driver(&mmc314x_mod_driver); | ||
301 | } | ||
302 | |||
303 | module_init(mmc314x_mod_init); | ||
304 | module_exit(mmc314x_mod_exit); | ||
305 | |||
306 | MODULE_AUTHOR("Invensense Corporation"); | ||
307 | MODULE_DESCRIPTION("Driver to integrate MMC314X sensor with the MPU"); | ||
308 | MODULE_LICENSE("GPL"); | ||
309 | MODULE_ALIAS("mmc314x_mod"); | ||
310 | |||
311 | /** | ||
312 | * @} | ||
313 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/yas529-kernel.c b/drivers/misc/inv_mpu/compass/yas529-kernel.c new file mode 100644 index 00000000000..f53223fba64 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/yas529-kernel.c | |||
@@ -0,0 +1,611 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /* -------------------------------------------------------------------------- */ | ||
21 | |||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include "mpu-dev.h" | ||
30 | |||
31 | #include <log.h> | ||
32 | #include <linux/mpu.h> | ||
33 | #include "mlsl.h" | ||
34 | #include "mldl_cfg.h" | ||
35 | #undef MPL_LOG_TAG | ||
36 | #define MPL_LOG_TAG "MPL-acc" | ||
37 | |||
38 | /*----- YAMAHA YAS529 Registers ------*/ | ||
39 | enum YAS_REG { | ||
40 | YAS_REG_CMDR = 0x00, /* 000 < 5 */ | ||
41 | YAS_REG_XOFFSETR = 0x20, /* 001 < 5 */ | ||
42 | YAS_REG_Y1OFFSETR = 0x40, /* 010 < 5 */ | ||
43 | YAS_REG_Y2OFFSETR = 0x60, /* 011 < 5 */ | ||
44 | YAS_REG_ICOILR = 0x80, /* 100 < 5 */ | ||
45 | YAS_REG_CAL = 0xA0, /* 101 < 5 */ | ||
46 | YAS_REG_CONFR = 0xC0, /* 110 < 5 */ | ||
47 | YAS_REG_DOUTR = 0xE0 /* 111 < 5 */ | ||
48 | }; | ||
49 | |||
50 | /* -------------------------------------------------------------------------- */ | ||
51 | |||
52 | static long a1; | ||
53 | static long a2; | ||
54 | static long a3; | ||
55 | static long a4; | ||
56 | static long a5; | ||
57 | static long a6; | ||
58 | static long a7; | ||
59 | static long a8; | ||
60 | static long a9; | ||
61 | |||
62 | /* -------------------------------------------------------------------------- */ | ||
63 | static int yas529_sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
64 | unsigned char address, | ||
65 | unsigned int len, unsigned char *data) | ||
66 | { | ||
67 | struct i2c_msg msgs[1]; | ||
68 | int res; | ||
69 | |||
70 | if (NULL == data || NULL == i2c_adap) | ||
71 | return -EINVAL; | ||
72 | |||
73 | msgs[0].addr = address; | ||
74 | msgs[0].flags = 0; /* write */ | ||
75 | msgs[0].buf = (unsigned char *)data; | ||
76 | msgs[0].len = len; | ||
77 | |||
78 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
79 | if (res < 1) | ||
80 | return res; | ||
81 | else | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int yas529_sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
86 | unsigned char address, | ||
87 | unsigned char reg, | ||
88 | unsigned int len, unsigned char *data) | ||
89 | { | ||
90 | struct i2c_msg msgs[2]; | ||
91 | int res; | ||
92 | |||
93 | if (NULL == data || NULL == i2c_adap) | ||
94 | return -EINVAL; | ||
95 | |||
96 | msgs[0].addr = address; | ||
97 | msgs[0].flags = I2C_M_RD; | ||
98 | msgs[0].buf = data; | ||
99 | msgs[0].len = len; | ||
100 | |||
101 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
102 | if (res < 1) | ||
103 | return res; | ||
104 | else | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int yas529_suspend(void *mlsl_handle, | ||
109 | struct ext_slave_descr *slave, | ||
110 | struct ext_slave_platform_data *pdata) | ||
111 | { | ||
112 | int result = INV_SUCCESS; | ||
113 | |||
114 | return result; | ||
115 | } | ||
116 | |||
117 | static int yas529_resume(void *mlsl_handle, | ||
118 | struct ext_slave_descr *slave, | ||
119 | struct ext_slave_platform_data *pdata) | ||
120 | { | ||
121 | int result = INV_SUCCESS; | ||
122 | |||
123 | unsigned char dummyData[1] = { 0 }; | ||
124 | unsigned char dummyRegister = 0; | ||
125 | unsigned char rawData[6]; | ||
126 | unsigned char calData[9]; | ||
127 | |||
128 | short xoffset, y1offset, y2offset; | ||
129 | short d2, d3, d4, d5, d6, d7, d8, d9; | ||
130 | |||
131 | /* YAS529 Application Manual MS-3C - Section 4.4.5 */ | ||
132 | /* =============================================== */ | ||
133 | /* Step 1 - register initialization */ | ||
134 | /* zero initialization coil register - "100 00 000" */ | ||
135 | dummyData[0] = YAS_REG_ICOILR | 0x00; | ||
136 | result = | ||
137 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
138 | if (result) { | ||
139 | LOG_RESULT_LOCATION(result); | ||
140 | return result; | ||
141 | } | ||
142 | /* zero config register - "110 00 000" */ | ||
143 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
144 | result = | ||
145 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
146 | if (result) { | ||
147 | LOG_RESULT_LOCATION(result); | ||
148 | return result; | ||
149 | } | ||
150 | |||
151 | /* Step 2 - initialization coil operation */ | ||
152 | dummyData[0] = YAS_REG_ICOILR | 0x11; | ||
153 | result = | ||
154 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
155 | if (result) { | ||
156 | LOG_RESULT_LOCATION(result); | ||
157 | return result; | ||
158 | } | ||
159 | dummyData[0] = YAS_REG_ICOILR | 0x01; | ||
160 | result = | ||
161 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
162 | if (result) { | ||
163 | LOG_RESULT_LOCATION(result); | ||
164 | return result; | ||
165 | } | ||
166 | dummyData[0] = YAS_REG_ICOILR | 0x12; | ||
167 | result = | ||
168 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
169 | if (result) { | ||
170 | LOG_RESULT_LOCATION(result); | ||
171 | return result; | ||
172 | } | ||
173 | dummyData[0] = YAS_REG_ICOILR | 0x02; | ||
174 | result = | ||
175 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
176 | if (result) { | ||
177 | LOG_RESULT_LOCATION(result); | ||
178 | return result; | ||
179 | } | ||
180 | dummyData[0] = YAS_REG_ICOILR | 0x13; | ||
181 | result = | ||
182 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
183 | if (result) { | ||
184 | LOG_RESULT_LOCATION(result); | ||
185 | return result; | ||
186 | } | ||
187 | dummyData[0] = YAS_REG_ICOILR | 0x03; | ||
188 | result = | ||
189 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
190 | if (result) { | ||
191 | LOG_RESULT_LOCATION(result); | ||
192 | return result; | ||
193 | } | ||
194 | dummyData[0] = YAS_REG_ICOILR | 0x14; | ||
195 | result = | ||
196 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
197 | if (result) { | ||
198 | LOG_RESULT_LOCATION(result); | ||
199 | return result; | ||
200 | } | ||
201 | dummyData[0] = YAS_REG_ICOILR | 0x04; | ||
202 | result = | ||
203 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
204 | if (result) { | ||
205 | LOG_RESULT_LOCATION(result); | ||
206 | return result; | ||
207 | } | ||
208 | dummyData[0] = YAS_REG_ICOILR | 0x15; | ||
209 | result = | ||
210 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
211 | if (result) { | ||
212 | LOG_RESULT_LOCATION(result); | ||
213 | return result; | ||
214 | } | ||
215 | dummyData[0] = YAS_REG_ICOILR | 0x05; | ||
216 | result = | ||
217 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
218 | if (result) { | ||
219 | LOG_RESULT_LOCATION(result); | ||
220 | return result; | ||
221 | } | ||
222 | dummyData[0] = YAS_REG_ICOILR | 0x16; | ||
223 | result = | ||
224 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
225 | if (result) { | ||
226 | LOG_RESULT_LOCATION(result); | ||
227 | return result; | ||
228 | } | ||
229 | dummyData[0] = YAS_REG_ICOILR | 0x06; | ||
230 | result = | ||
231 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
232 | if (result) { | ||
233 | LOG_RESULT_LOCATION(result); | ||
234 | return result; | ||
235 | } | ||
236 | dummyData[0] = YAS_REG_ICOILR | 0x17; | ||
237 | result = | ||
238 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
239 | if (result) { | ||
240 | LOG_RESULT_LOCATION(result); | ||
241 | return result; | ||
242 | } | ||
243 | dummyData[0] = YAS_REG_ICOILR | 0x07; | ||
244 | result = | ||
245 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
246 | if (result) { | ||
247 | LOG_RESULT_LOCATION(result); | ||
248 | return result; | ||
249 | } | ||
250 | dummyData[0] = YAS_REG_ICOILR | 0x10; | ||
251 | result = | ||
252 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
253 | if (result) { | ||
254 | LOG_RESULT_LOCATION(result); | ||
255 | return result; | ||
256 | } | ||
257 | dummyData[0] = YAS_REG_ICOILR | 0x00; | ||
258 | result = | ||
259 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
260 | if (result) { | ||
261 | LOG_RESULT_LOCATION(result); | ||
262 | return result; | ||
263 | } | ||
264 | |||
265 | /* Step 3 - rough offset measurement */ | ||
266 | /* Config register - Measurements results - "110 00 000" */ | ||
267 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
268 | result = | ||
269 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
270 | if (result) { | ||
271 | LOG_RESULT_LOCATION(result); | ||
272 | return result; | ||
273 | } | ||
274 | /* Measurements command register - Rough offset measurement - | ||
275 | "000 00001" */ | ||
276 | dummyData[0] = YAS_REG_CMDR | 0x01; | ||
277 | result = | ||
278 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
279 | if (result) { | ||
280 | LOG_RESULT_LOCATION(result); | ||
281 | return result; | ||
282 | } | ||
283 | msleep(2); /* wait at least 1.5ms */ | ||
284 | |||
285 | /* Measurement data read */ | ||
286 | result = | ||
287 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
288 | dummyRegister, 6, rawData); | ||
289 | if (result) { | ||
290 | LOG_RESULT_LOCATION(result); | ||
291 | return result; | ||
292 | } | ||
293 | xoffset = | ||
294 | (short)((unsigned short)rawData[5] + | ||
295 | ((unsigned short)rawData[4] & 0x7) * 256) - 5; | ||
296 | if (xoffset < 0) | ||
297 | xoffset = 0; | ||
298 | y1offset = | ||
299 | (short)((unsigned short)rawData[3] + | ||
300 | ((unsigned short)rawData[2] & 0x7) * 256) - 5; | ||
301 | if (y1offset < 0) | ||
302 | y1offset = 0; | ||
303 | y2offset = | ||
304 | (short)((unsigned short)rawData[1] + | ||
305 | ((unsigned short)rawData[0] & 0x7) * 256) - 5; | ||
306 | if (y2offset < 0) | ||
307 | y2offset = 0; | ||
308 | |||
309 | /* Step 4 - rough offset setting */ | ||
310 | /* Set rough offset register values */ | ||
311 | dummyData[0] = YAS_REG_XOFFSETR | xoffset; | ||
312 | result = | ||
313 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
314 | if (result) { | ||
315 | LOG_RESULT_LOCATION(result); | ||
316 | return result; | ||
317 | } | ||
318 | dummyData[0] = YAS_REG_Y1OFFSETR | y1offset; | ||
319 | result = | ||
320 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
321 | if (result) { | ||
322 | LOG_RESULT_LOCATION(result); | ||
323 | return result; | ||
324 | } | ||
325 | dummyData[0] = YAS_REG_Y2OFFSETR | y2offset; | ||
326 | result = | ||
327 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
328 | if (result) { | ||
329 | LOG_RESULT_LOCATION(result); | ||
330 | return result; | ||
331 | } | ||
332 | |||
333 | /* CAL matrix read (first read is invalid) */ | ||
334 | /* Config register - CAL register read - "110 01 000" */ | ||
335 | dummyData[0] = YAS_REG_CONFR | 0x08; | ||
336 | result = | ||
337 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
338 | if (result) { | ||
339 | LOG_RESULT_LOCATION(result); | ||
340 | return result; | ||
341 | } | ||
342 | /* CAL data read */ | ||
343 | result = | ||
344 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
345 | dummyRegister, 9, calData); | ||
346 | if (result) { | ||
347 | LOG_RESULT_LOCATION(result); | ||
348 | return result; | ||
349 | } | ||
350 | /* Config register - CAL register read - "110 01 000" */ | ||
351 | dummyData[0] = YAS_REG_CONFR | 0x08; | ||
352 | result = | ||
353 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
354 | if (result) { | ||
355 | LOG_RESULT_LOCATION(result); | ||
356 | return result; | ||
357 | } | ||
358 | /* CAL data read */ | ||
359 | result = | ||
360 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
361 | dummyRegister, 9, calData); | ||
362 | if (result) { | ||
363 | LOG_RESULT_LOCATION(result); | ||
364 | return result; | ||
365 | } | ||
366 | |||
367 | /* Calculate coefficients of the sensitivity correction matrix */ | ||
368 | a1 = 100; | ||
369 | d2 = (calData[0] & 0xFC) >> 2; /* [71..66] 6bit */ | ||
370 | a2 = (short)(d2 - 32); | ||
371 | /* [65..62] 4bit */ | ||
372 | d3 = ((calData[0] & 0x03) << 2) | ((calData[1] & 0xC0) >> 6); | ||
373 | a3 = (short)(d3 - 8); | ||
374 | d4 = (calData[1] & 0x3F); /* [61..56] 6bit */ | ||
375 | a4 = (short)(d4 - 32); | ||
376 | d5 = (calData[2] & 0xFC) >> 2; /* [55..50] 6bit */ | ||
377 | a5 = (short)(d5 - 32) + 70; | ||
378 | /* [49..44] 6bit */ | ||
379 | d6 = ((calData[2] & 0x03) << 4) | ((calData[3] & 0xF0) >> 4); | ||
380 | a6 = (short)(d6 - 32); | ||
381 | /* [43..38] 6bit */ | ||
382 | d7 = ((calData[3] & 0x0F) << 2) | ((calData[4] & 0xC0) >> 6); | ||
383 | a7 = (short)(d7 - 32); | ||
384 | d8 = (calData[4] & 0x3F); /* [37..32] 6bit */ | ||
385 | a8 = (short)(d8 - 32); | ||
386 | d9 = (calData[5] & 0xFE) >> 1; /* [31..25] 7bit */ | ||
387 | a9 = (short)(d9 - 64) + 130; | ||
388 | |||
389 | return result; | ||
390 | } | ||
391 | |||
392 | static int yas529_read(void *mlsl_handle, | ||
393 | struct ext_slave_descr *slave, | ||
394 | struct ext_slave_platform_data *pdata, | ||
395 | unsigned char *data) | ||
396 | { | ||
397 | unsigned char stat; | ||
398 | unsigned char rawData[6]; | ||
399 | unsigned char dummyData[1] = { 0 }; | ||
400 | unsigned char dummyRegister = 0; | ||
401 | int result = INV_SUCCESS; | ||
402 | short SX, SY1, SY2, SY, SZ; | ||
403 | short row1fixed, row2fixed, row3fixed; | ||
404 | |||
405 | /* Config register - Measurements results - "110 00 000" */ | ||
406 | dummyData[0] = YAS_REG_CONFR | 0x00; | ||
407 | result = | ||
408 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
409 | if (result) { | ||
410 | LOG_RESULT_LOCATION(result); | ||
411 | return result; | ||
412 | } | ||
413 | /* Measurements command register - Normal magnetic field measurement - | ||
414 | "000 00000" */ | ||
415 | dummyData[0] = YAS_REG_CMDR | 0x00; | ||
416 | result = | ||
417 | yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData); | ||
418 | if (result) { | ||
419 | LOG_RESULT_LOCATION(result); | ||
420 | return result; | ||
421 | } | ||
422 | msleep(10); | ||
423 | /* Measurement data read */ | ||
424 | result = | ||
425 | yas529_sensor_i2c_read(mlsl_handle, pdata->address, | ||
426 | dummyRegister, 6, (unsigned char *)&rawData); | ||
427 | if (result) { | ||
428 | LOG_RESULT_LOCATION(result); | ||
429 | return result; | ||
430 | } | ||
431 | |||
432 | stat = rawData[0] & 0x80; | ||
433 | if (stat == 0x00) { | ||
434 | /* Extract raw data */ | ||
435 | SX = (short)((unsigned short)rawData[5] + | ||
436 | ((unsigned short)rawData[4] & 0x7) * 256); | ||
437 | SY1 = | ||
438 | (short)((unsigned short)rawData[3] + | ||
439 | ((unsigned short)rawData[2] & 0x7) * 256); | ||
440 | SY2 = | ||
441 | (short)((unsigned short)rawData[1] + | ||
442 | ((unsigned short)rawData[0] & 0x7) * 256); | ||
443 | if ((SX <= 1) || (SY1 <= 1) || (SY2 <= 1)) | ||
444 | return INV_ERROR_COMPASS_DATA_UNDERFLOW; | ||
445 | if ((SX >= 1024) || (SY1 >= 1024) || (SY2 >= 1024)) | ||
446 | return INV_ERROR_COMPASS_DATA_OVERFLOW; | ||
447 | /* Convert to XYZ axis */ | ||
448 | SX = -1 * SX; | ||
449 | SY = SY2 - SY1; | ||
450 | SZ = SY1 + SY2; | ||
451 | |||
452 | /* Apply sensitivity correction matrix */ | ||
453 | row1fixed = (short)((a1 * SX + a2 * SY + a3 * SZ) >> 7) * 41; | ||
454 | row2fixed = (short)((a4 * SX + a5 * SY + a6 * SZ) >> 7) * 41; | ||
455 | row3fixed = (short)((a7 * SX + a8 * SY + a9 * SZ) >> 7) * 41; | ||
456 | |||
457 | data[0] = row1fixed >> 8; | ||
458 | data[1] = row1fixed & 0xFF; | ||
459 | data[2] = row2fixed >> 8; | ||
460 | data[3] = row2fixed & 0xFF; | ||
461 | data[4] = row3fixed >> 8; | ||
462 | data[5] = row3fixed & 0xFF; | ||
463 | |||
464 | return INV_SUCCESS; | ||
465 | } else { | ||
466 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | static struct ext_slave_descr yas529_descr = { | ||
471 | .init = NULL, | ||
472 | .exit = NULL, | ||
473 | .suspend = yas529_suspend, | ||
474 | .resume = yas529_resume, | ||
475 | .read = yas529_read, | ||
476 | .config = NULL, | ||
477 | .get_config = NULL, | ||
478 | .name = "yas529", | ||
479 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
480 | .id = COMPASS_ID_YAS529, | ||
481 | .read_reg = 0x06, | ||
482 | .read_len = 6, | ||
483 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
484 | .range = {19660, 8000}, | ||
485 | .trigger = NULL, | ||
486 | }; | ||
487 | |||
488 | static | ||
489 | struct ext_slave_descr *yas529_get_slave_descr(void) | ||
490 | { | ||
491 | return &yas529_descr; | ||
492 | } | ||
493 | |||
494 | /* -------------------------------------------------------------------------- */ | ||
495 | struct yas529_mod_private_data { | ||
496 | struct i2c_client *client; | ||
497 | struct ext_slave_platform_data *pdata; | ||
498 | }; | ||
499 | |||
500 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
501 | |||
502 | static int yas529_mod_probe(struct i2c_client *client, | ||
503 | const struct i2c_device_id *devid) | ||
504 | { | ||
505 | struct ext_slave_platform_data *pdata; | ||
506 | struct yas529_mod_private_data *private_data; | ||
507 | int result = 0; | ||
508 | |||
509 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
510 | |||
511 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
512 | result = -ENODEV; | ||
513 | goto out_no_free; | ||
514 | } | ||
515 | |||
516 | pdata = client->dev.platform_data; | ||
517 | if (!pdata) { | ||
518 | dev_err(&client->adapter->dev, | ||
519 | "Missing platform data for slave %s\n", devid->name); | ||
520 | result = -EFAULT; | ||
521 | goto out_no_free; | ||
522 | } | ||
523 | |||
524 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
525 | if (!private_data) { | ||
526 | result = -ENOMEM; | ||
527 | goto out_no_free; | ||
528 | } | ||
529 | |||
530 | i2c_set_clientdata(client, private_data); | ||
531 | private_data->client = client; | ||
532 | private_data->pdata = pdata; | ||
533 | |||
534 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
535 | yas529_get_slave_descr); | ||
536 | if (result) { | ||
537 | dev_err(&client->adapter->dev, | ||
538 | "Slave registration failed: %s, %d\n", | ||
539 | devid->name, result); | ||
540 | goto out_free_memory; | ||
541 | } | ||
542 | |||
543 | return result; | ||
544 | |||
545 | out_free_memory: | ||
546 | kfree(private_data); | ||
547 | out_no_free: | ||
548 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
549 | return result; | ||
550 | |||
551 | } | ||
552 | |||
553 | static int yas529_mod_remove(struct i2c_client *client) | ||
554 | { | ||
555 | struct yas529_mod_private_data *private_data = | ||
556 | i2c_get_clientdata(client); | ||
557 | |||
558 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
559 | |||
560 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
561 | yas529_get_slave_descr); | ||
562 | |||
563 | kfree(private_data); | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static const struct i2c_device_id yas529_mod_id[] = { | ||
568 | { "yas529", COMPASS_ID_YAS529 }, | ||
569 | {} | ||
570 | }; | ||
571 | |||
572 | MODULE_DEVICE_TABLE(i2c, yas529_mod_id); | ||
573 | |||
574 | static struct i2c_driver yas529_mod_driver = { | ||
575 | .class = I2C_CLASS_HWMON, | ||
576 | .probe = yas529_mod_probe, | ||
577 | .remove = yas529_mod_remove, | ||
578 | .id_table = yas529_mod_id, | ||
579 | .driver = { | ||
580 | .owner = THIS_MODULE, | ||
581 | .name = "yas529_mod", | ||
582 | }, | ||
583 | .address_list = normal_i2c, | ||
584 | }; | ||
585 | |||
586 | static int __init yas529_mod_init(void) | ||
587 | { | ||
588 | int res = i2c_add_driver(&yas529_mod_driver); | ||
589 | pr_info("%s: Probe name %s\n", __func__, "yas529_mod"); | ||
590 | if (res) | ||
591 | pr_err("%s failed\n", __func__); | ||
592 | return res; | ||
593 | } | ||
594 | |||
595 | static void __exit yas529_mod_exit(void) | ||
596 | { | ||
597 | pr_info("%s\n", __func__); | ||
598 | i2c_del_driver(&yas529_mod_driver); | ||
599 | } | ||
600 | |||
601 | module_init(yas529_mod_init); | ||
602 | module_exit(yas529_mod_exit); | ||
603 | |||
604 | MODULE_AUTHOR("Invensense Corporation"); | ||
605 | MODULE_DESCRIPTION("Driver to integrate YAS529 sensor with the MPU"); | ||
606 | MODULE_LICENSE("GPL"); | ||
607 | MODULE_ALIAS("yas529_mod"); | ||
608 | |||
609 | /** | ||
610 | * @} | ||
611 | */ | ||
diff --git a/drivers/misc/inv_mpu/compass/yas530.c b/drivers/misc/inv_mpu/compass/yas530.c new file mode 100644 index 00000000000..fdca05ba8e5 --- /dev/null +++ b/drivers/misc/inv_mpu/compass/yas530.c | |||
@@ -0,0 +1,580 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup COMPASSDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file yas530.c | ||
25 | * @brief Magnetometer setup and handling methods for Yamaha YAS530 | ||
26 | * compass when used in a user-space solution (no kernel driver). | ||
27 | */ | ||
28 | |||
29 | /* -------------------------------------------------------------------------- */ | ||
30 | |||
31 | #include <linux/i2c.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/slab.h> | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include "mpu-dev.h" | ||
41 | |||
42 | #include "log.h" | ||
43 | #include <linux/mpu.h> | ||
44 | #include "mlsl.h" | ||
45 | #include "mldl_cfg.h" | ||
46 | #undef MPL_LOG_TAG | ||
47 | #define MPL_LOG_TAG "MPL-compass" | ||
48 | |||
49 | /* -------------------------------------------------------------------------- */ | ||
50 | #define YAS530_REGADDR_DEVICE_ID (0x80) | ||
51 | #define YAS530_REGADDR_ACTUATE_INIT_COIL (0x81) | ||
52 | #define YAS530_REGADDR_MEASURE_COMMAND (0x82) | ||
53 | #define YAS530_REGADDR_CONFIG (0x83) | ||
54 | #define YAS530_REGADDR_MEASURE_INTERVAL (0x84) | ||
55 | #define YAS530_REGADDR_OFFSET_X (0x85) | ||
56 | #define YAS530_REGADDR_OFFSET_Y1 (0x86) | ||
57 | #define YAS530_REGADDR_OFFSET_Y2 (0x87) | ||
58 | #define YAS530_REGADDR_TEST1 (0x88) | ||
59 | #define YAS530_REGADDR_TEST2 (0x89) | ||
60 | #define YAS530_REGADDR_CAL (0x90) | ||
61 | #define YAS530_REGADDR_MEASURE_DATA (0xb0) | ||
62 | |||
63 | /* -------------------------------------------------------------------------- */ | ||
64 | static int Cx, Cy1, Cy2; | ||
65 | static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9; | ||
66 | static int k; | ||
67 | |||
68 | static unsigned char dx, dy1, dy2; | ||
69 | static unsigned char d2, d3, d4, d5, d6, d7, d8, d9, d0; | ||
70 | static unsigned char dck; | ||
71 | |||
72 | /* -------------------------------------------------------------------------- */ | ||
73 | |||
74 | static int set_hardware_offset(void *mlsl_handle, | ||
75 | struct ext_slave_descr *slave, | ||
76 | struct ext_slave_platform_data *pdata, | ||
77 | char offset_x, char offset_y1, char offset_y2) | ||
78 | { | ||
79 | char data; | ||
80 | int result = INV_SUCCESS; | ||
81 | |||
82 | data = offset_x & 0x3f; | ||
83 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
84 | YAS530_REGADDR_OFFSET_X, data); | ||
85 | if (result) { | ||
86 | LOG_RESULT_LOCATION(result); | ||
87 | return result; | ||
88 | } | ||
89 | |||
90 | data = offset_y1 & 0x3f; | ||
91 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
92 | YAS530_REGADDR_OFFSET_Y1, data); | ||
93 | if (result) { | ||
94 | LOG_RESULT_LOCATION(result); | ||
95 | return result; | ||
96 | } | ||
97 | |||
98 | data = offset_y2 & 0x3f; | ||
99 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
100 | YAS530_REGADDR_OFFSET_Y2, data); | ||
101 | if (result) { | ||
102 | LOG_RESULT_LOCATION(result); | ||
103 | return result; | ||
104 | } | ||
105 | |||
106 | return result; | ||
107 | } | ||
108 | |||
109 | static int set_measure_command(void *mlsl_handle, | ||
110 | struct ext_slave_descr *slave, | ||
111 | struct ext_slave_platform_data *pdata, | ||
112 | int ldtc, int fors, int dlymes) | ||
113 | { | ||
114 | int result = INV_SUCCESS; | ||
115 | |||
116 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
117 | YAS530_REGADDR_MEASURE_COMMAND, 0x01); | ||
118 | if (result) { | ||
119 | LOG_RESULT_LOCATION(result); | ||
120 | return result; | ||
121 | } | ||
122 | |||
123 | return result; | ||
124 | } | ||
125 | |||
126 | static int measure_normal(void *mlsl_handle, | ||
127 | struct ext_slave_descr *slave, | ||
128 | struct ext_slave_platform_data *pdata, | ||
129 | int *busy, unsigned short *t, | ||
130 | unsigned short *x, unsigned short *y1, | ||
131 | unsigned short *y2) | ||
132 | { | ||
133 | unsigned char data[8]; | ||
134 | unsigned short b, to, xo, y1o, y2o; | ||
135 | int result; | ||
136 | ktime_t sleeptime; | ||
137 | result = set_measure_command(mlsl_handle, slave, pdata, 0, 0, 0); | ||
138 | sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC); | ||
139 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
140 | schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL); | ||
141 | |||
142 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
143 | YAS530_REGADDR_MEASURE_DATA, 8, data); | ||
144 | if (result) { | ||
145 | LOG_RESULT_LOCATION(result); | ||
146 | return result; | ||
147 | } | ||
148 | |||
149 | b = (data[0] >> 7) & 0x01; | ||
150 | to = ((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03); | ||
151 | xo = ((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f); | ||
152 | y1o = ((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f); | ||
153 | y2o = ((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f); | ||
154 | |||
155 | *busy = b; | ||
156 | *t = to; | ||
157 | *x = xo; | ||
158 | *y1 = y1o; | ||
159 | *y2 = y2o; | ||
160 | |||
161 | return result; | ||
162 | } | ||
163 | |||
164 | static int check_offset(void *mlsl_handle, | ||
165 | struct ext_slave_descr *slave, | ||
166 | struct ext_slave_platform_data *pdata, | ||
167 | char offset_x, char offset_y1, char offset_y2, | ||
168 | int *flag_x, int *flag_y1, int *flag_y2) | ||
169 | { | ||
170 | int result; | ||
171 | int busy; | ||
172 | short t, x, y1, y2; | ||
173 | |||
174 | result = set_hardware_offset(mlsl_handle, slave, pdata, | ||
175 | offset_x, offset_y1, offset_y2); | ||
176 | if (result) { | ||
177 | LOG_RESULT_LOCATION(result); | ||
178 | return result; | ||
179 | } | ||
180 | |||
181 | result = measure_normal(mlsl_handle, slave, pdata, | ||
182 | &busy, &t, &x, &y1, &y2); | ||
183 | if (result) { | ||
184 | LOG_RESULT_LOCATION(result); | ||
185 | return result; | ||
186 | } | ||
187 | |||
188 | *flag_x = 0; | ||
189 | *flag_y1 = 0; | ||
190 | *flag_y2 = 0; | ||
191 | |||
192 | if (x > 2048) | ||
193 | *flag_x = 1; | ||
194 | if (y1 > 2048) | ||
195 | *flag_y1 = 1; | ||
196 | if (y2 > 2048) | ||
197 | *flag_y2 = 1; | ||
198 | if (x < 2048) | ||
199 | *flag_x = -1; | ||
200 | if (y1 < 2048) | ||
201 | *flag_y1 = -1; | ||
202 | if (y2 < 2048) | ||
203 | *flag_y2 = -1; | ||
204 | |||
205 | return result; | ||
206 | } | ||
207 | |||
208 | static int measure_and_set_offset(void *mlsl_handle, | ||
209 | struct ext_slave_descr *slave, | ||
210 | struct ext_slave_platform_data *pdata, | ||
211 | char *offset) | ||
212 | { | ||
213 | int i; | ||
214 | int result = INV_SUCCESS; | ||
215 | char offset_x = 0, offset_y1 = 0, offset_y2 = 0; | ||
216 | int flag_x = 0, flag_y1 = 0, flag_y2 = 0; | ||
217 | static const int correct[5] = { 16, 8, 4, 2, 1 }; | ||
218 | |||
219 | for (i = 0; i < 5; i++) { | ||
220 | result = check_offset(mlsl_handle, slave, pdata, | ||
221 | offset_x, offset_y1, offset_y2, | ||
222 | &flag_x, &flag_y1, &flag_y2); | ||
223 | if (result) { | ||
224 | LOG_RESULT_LOCATION(result); | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | if (flag_x) | ||
229 | offset_x += flag_x * correct[i]; | ||
230 | if (flag_y1) | ||
231 | offset_y1 += flag_y1 * correct[i]; | ||
232 | if (flag_y2) | ||
233 | offset_y2 += flag_y2 * correct[i]; | ||
234 | } | ||
235 | |||
236 | result = set_hardware_offset(mlsl_handle, slave, pdata, | ||
237 | offset_x, offset_y1, offset_y2); | ||
238 | if (result) { | ||
239 | LOG_RESULT_LOCATION(result); | ||
240 | return result; | ||
241 | } | ||
242 | |||
243 | offset[0] = offset_x; | ||
244 | offset[1] = offset_y1; | ||
245 | offset[2] = offset_y2; | ||
246 | |||
247 | return result; | ||
248 | } | ||
249 | |||
250 | static void coordinate_conversion(short x, short y1, short y2, short t, | ||
251 | int32_t *xo, int32_t *yo, int32_t *zo) | ||
252 | { | ||
253 | int32_t sx, sy1, sy2, sy, sz; | ||
254 | int32_t hx, hy, hz; | ||
255 | |||
256 | sx = x - (Cx * t) / 100; | ||
257 | sy1 = y1 - (Cy1 * t) / 100; | ||
258 | sy2 = y2 - (Cy2 * t) / 100; | ||
259 | |||
260 | sy = sy1 - sy2; | ||
261 | sz = -sy1 - sy2; | ||
262 | |||
263 | hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10); | ||
264 | hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10); | ||
265 | hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10); | ||
266 | |||
267 | *xo = hx; | ||
268 | *yo = hy; | ||
269 | *zo = hz; | ||
270 | } | ||
271 | |||
272 | static int yas530_suspend(void *mlsl_handle, | ||
273 | struct ext_slave_descr *slave, | ||
274 | struct ext_slave_platform_data *pdata) | ||
275 | { | ||
276 | int result = INV_SUCCESS; | ||
277 | |||
278 | return result; | ||
279 | } | ||
280 | |||
281 | static int yas530_resume(void *mlsl_handle, | ||
282 | struct ext_slave_descr *slave, | ||
283 | struct ext_slave_platform_data *pdata) | ||
284 | { | ||
285 | int result = INV_SUCCESS; | ||
286 | |||
287 | unsigned char dummyData = 0x00; | ||
288 | char offset[3] = { 0, 0, 0 }; | ||
289 | unsigned char data[16]; | ||
290 | unsigned char read_reg[1]; | ||
291 | |||
292 | /* =============================================== */ | ||
293 | |||
294 | /* Step 1 - Test register initialization */ | ||
295 | dummyData = 0x00; | ||
296 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
297 | YAS530_REGADDR_TEST1, dummyData); | ||
298 | if (result) { | ||
299 | LOG_RESULT_LOCATION(result); | ||
300 | return result; | ||
301 | } | ||
302 | |||
303 | result = | ||
304 | inv_serial_single_write(mlsl_handle, pdata->address, | ||
305 | YAS530_REGADDR_TEST2, dummyData); | ||
306 | if (result) { | ||
307 | LOG_RESULT_LOCATION(result); | ||
308 | return result; | ||
309 | } | ||
310 | |||
311 | /* Device ID read */ | ||
312 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
313 | YAS530_REGADDR_DEVICE_ID, 1, read_reg); | ||
314 | |||
315 | /*Step 2 Read the CAL register */ | ||
316 | /* CAL data read */ | ||
317 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
318 | YAS530_REGADDR_CAL, 16, data); | ||
319 | if (result) { | ||
320 | LOG_RESULT_LOCATION(result); | ||
321 | return result; | ||
322 | } | ||
323 | /* CAL data Second Read */ | ||
324 | result = inv_serial_read(mlsl_handle, pdata->address, | ||
325 | YAS530_REGADDR_CAL, 16, data); | ||
326 | if (result) { | ||
327 | LOG_RESULT_LOCATION(result); | ||
328 | return result; | ||
329 | } | ||
330 | |||
331 | /*Cal data */ | ||
332 | dx = data[0]; | ||
333 | dy1 = data[1]; | ||
334 | dy2 = data[2]; | ||
335 | d2 = (data[3] >> 2) & 0x03f; | ||
336 | d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03); | ||
337 | d4 = data[4] & 0x3f; | ||
338 | d5 = (data[5] >> 2) & 0x3f; | ||
339 | d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f); | ||
340 | d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07); | ||
341 | d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01); | ||
342 | d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01); | ||
343 | d0 = (data[9] >> 2) & 0x1f; | ||
344 | dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01); | ||
345 | |||
346 | /*Correction Data */ | ||
347 | Cx = (int)dx * 6 - 768; | ||
348 | Cy1 = (int)dy1 * 6 - 768; | ||
349 | Cy2 = (int)dy2 * 6 - 768; | ||
350 | a2 = (int)d2 - 32; | ||
351 | a3 = (int)d3 - 8; | ||
352 | a4 = (int)d4 - 32; | ||
353 | a5 = (int)d5 + 38; | ||
354 | a6 = (int)d6 - 32; | ||
355 | a7 = (int)d7 - 64; | ||
356 | a8 = (int)d8 - 32; | ||
357 | a9 = (int)d9; | ||
358 | k = (int)d0 + 10; | ||
359 | |||
360 | /*Obtain the [49:47] bits */ | ||
361 | dck &= 0x07; | ||
362 | |||
363 | /*Step 3 : Storing the CONFIG with the CLK value */ | ||
364 | dummyData = 0x00 | (dck << 2); | ||
365 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
366 | YAS530_REGADDR_CONFIG, dummyData); | ||
367 | if (result) { | ||
368 | LOG_RESULT_LOCATION(result); | ||
369 | return result; | ||
370 | } | ||
371 | |||
372 | /*Step 4 : Set Acquisition Interval Register */ | ||
373 | dummyData = 0x00; | ||
374 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
375 | YAS530_REGADDR_MEASURE_INTERVAL, | ||
376 | dummyData); | ||
377 | if (result) { | ||
378 | LOG_RESULT_LOCATION(result); | ||
379 | return result; | ||
380 | } | ||
381 | |||
382 | /*Step 5 : Reset Coil */ | ||
383 | dummyData = 0x00; | ||
384 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
385 | YAS530_REGADDR_ACTUATE_INIT_COIL, | ||
386 | dummyData); | ||
387 | if (result) { | ||
388 | LOG_RESULT_LOCATION(result); | ||
389 | return result; | ||
390 | } | ||
391 | |||
392 | /* Offset Measurement and Set */ | ||
393 | result = measure_and_set_offset(mlsl_handle, slave, pdata, offset); | ||
394 | if (result) { | ||
395 | LOG_RESULT_LOCATION(result); | ||
396 | return result; | ||
397 | } | ||
398 | |||
399 | return result; | ||
400 | } | ||
401 | |||
402 | static int yas530_read(void *mlsl_handle, | ||
403 | struct ext_slave_descr *slave, | ||
404 | struct ext_slave_platform_data *pdata, | ||
405 | unsigned char *data) | ||
406 | { | ||
407 | int result = INV_SUCCESS; | ||
408 | |||
409 | int busy; | ||
410 | short t, x, y1, y2; | ||
411 | int32_t xyz[3]; | ||
412 | short rawfixed[3]; | ||
413 | |||
414 | result = measure_normal(mlsl_handle, slave, pdata, | ||
415 | &busy, &t, &x, &y1, &y2); | ||
416 | if (result) { | ||
417 | LOG_RESULT_LOCATION(result); | ||
418 | return result; | ||
419 | } | ||
420 | |||
421 | coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]); | ||
422 | |||
423 | rawfixed[0] = (short)(xyz[0] / 100); | ||
424 | rawfixed[1] = (short)(xyz[1] / 100); | ||
425 | rawfixed[2] = (short)(xyz[2] / 100); | ||
426 | |||
427 | data[0] = rawfixed[0] >> 8; | ||
428 | data[1] = rawfixed[0] & 0xFF; | ||
429 | data[2] = rawfixed[1] >> 8; | ||
430 | data[3] = rawfixed[1] & 0xFF; | ||
431 | data[4] = rawfixed[2] >> 8; | ||
432 | data[5] = rawfixed[2] & 0xFF; | ||
433 | |||
434 | if (busy) | ||
435 | return INV_ERROR_COMPASS_DATA_NOT_READY; | ||
436 | return result; | ||
437 | } | ||
438 | |||
439 | static struct ext_slave_descr yas530_descr = { | ||
440 | .init = NULL, | ||
441 | .exit = NULL, | ||
442 | .suspend = yas530_suspend, | ||
443 | .resume = yas530_resume, | ||
444 | .read = yas530_read, | ||
445 | .config = NULL, | ||
446 | .get_config = NULL, | ||
447 | .name = "yas530", | ||
448 | .type = EXT_SLAVE_TYPE_COMPASS, | ||
449 | .id = COMPASS_ID_YAS530, | ||
450 | .read_reg = 0x06, | ||
451 | .read_len = 6, | ||
452 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
453 | .range = {3276, 8001}, | ||
454 | .trigger = NULL, | ||
455 | }; | ||
456 | |||
457 | static | ||
458 | struct ext_slave_descr *yas530_get_slave_descr(void) | ||
459 | { | ||
460 | return &yas530_descr; | ||
461 | } | ||
462 | |||
463 | /* -------------------------------------------------------------------------- */ | ||
464 | struct yas530_mod_private_data { | ||
465 | struct i2c_client *client; | ||
466 | struct ext_slave_platform_data *pdata; | ||
467 | }; | ||
468 | |||
469 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
470 | |||
471 | static int yas530_mod_probe(struct i2c_client *client, | ||
472 | const struct i2c_device_id *devid) | ||
473 | { | ||
474 | struct ext_slave_platform_data *pdata; | ||
475 | struct yas530_mod_private_data *private_data; | ||
476 | int result = 0; | ||
477 | |||
478 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
479 | |||
480 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
481 | result = -ENODEV; | ||
482 | goto out_no_free; | ||
483 | } | ||
484 | |||
485 | pdata = client->dev.platform_data; | ||
486 | if (!pdata) { | ||
487 | dev_err(&client->adapter->dev, | ||
488 | "Missing platform data for slave %s\n", devid->name); | ||
489 | result = -EFAULT; | ||
490 | goto out_no_free; | ||
491 | } | ||
492 | |||
493 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
494 | if (!private_data) { | ||
495 | result = -ENOMEM; | ||
496 | goto out_no_free; | ||
497 | } | ||
498 | |||
499 | i2c_set_clientdata(client, private_data); | ||
500 | private_data->client = client; | ||
501 | private_data->pdata = pdata; | ||
502 | |||
503 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
504 | yas530_get_slave_descr); | ||
505 | if (result) { | ||
506 | dev_err(&client->adapter->dev, | ||
507 | "Slave registration failed: %s, %d\n", | ||
508 | devid->name, result); | ||
509 | goto out_free_memory; | ||
510 | } | ||
511 | |||
512 | return result; | ||
513 | |||
514 | out_free_memory: | ||
515 | kfree(private_data); | ||
516 | out_no_free: | ||
517 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
518 | return result; | ||
519 | |||
520 | } | ||
521 | |||
522 | static int yas530_mod_remove(struct i2c_client *client) | ||
523 | { | ||
524 | struct yas530_mod_private_data *private_data = | ||
525 | i2c_get_clientdata(client); | ||
526 | |||
527 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
528 | |||
529 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
530 | yas530_get_slave_descr); | ||
531 | |||
532 | kfree(private_data); | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static const struct i2c_device_id yas530_mod_id[] = { | ||
537 | { "yas530", COMPASS_ID_YAS530 }, | ||
538 | {} | ||
539 | }; | ||
540 | |||
541 | MODULE_DEVICE_TABLE(i2c, yas530_mod_id); | ||
542 | |||
543 | static struct i2c_driver yas530_mod_driver = { | ||
544 | .class = I2C_CLASS_HWMON, | ||
545 | .probe = yas530_mod_probe, | ||
546 | .remove = yas530_mod_remove, | ||
547 | .id_table = yas530_mod_id, | ||
548 | .driver = { | ||
549 | .owner = THIS_MODULE, | ||
550 | .name = "yas530_mod", | ||
551 | }, | ||
552 | .address_list = normal_i2c, | ||
553 | }; | ||
554 | |||
555 | static int __init yas530_mod_init(void) | ||
556 | { | ||
557 | int res = i2c_add_driver(&yas530_mod_driver); | ||
558 | pr_info("%s: Probe name %s\n", __func__, "yas530_mod"); | ||
559 | if (res) | ||
560 | pr_err("%s failed\n", __func__); | ||
561 | return res; | ||
562 | } | ||
563 | |||
564 | static void __exit yas530_mod_exit(void) | ||
565 | { | ||
566 | pr_info("%s\n", __func__); | ||
567 | i2c_del_driver(&yas530_mod_driver); | ||
568 | } | ||
569 | |||
570 | module_init(yas530_mod_init); | ||
571 | module_exit(yas530_mod_exit); | ||
572 | |||
573 | MODULE_AUTHOR("Invensense Corporation"); | ||
574 | MODULE_DESCRIPTION("Driver to integrate YAS530 sensor with the MPU"); | ||
575 | MODULE_LICENSE("GPL"); | ||
576 | MODULE_ALIAS("yas530_mod"); | ||
577 | |||
578 | /** | ||
579 | * @} | ||
580 | */ | ||
diff --git a/drivers/misc/inv_mpu/log.h b/drivers/misc/inv_mpu/log.h new file mode 100644 index 00000000000..5630602e3ef --- /dev/null +++ b/drivers/misc/inv_mpu/log.h | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * This file incorporates work covered by the following copyright and | ||
22 | * permission notice: | ||
23 | * | ||
24 | * Copyright (C) 2005 The Android Open Source Project | ||
25 | * | ||
26 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
27 | * you may not use this file except in compliance with the License. | ||
28 | * You may obtain a copy of the License at | ||
29 | * | ||
30 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
31 | * | ||
32 | * Unless required by applicable law or agreed to in writing, software | ||
33 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
35 | * See the License for the specific language governing permissions and | ||
36 | * limitations under the License. | ||
37 | */ | ||
38 | |||
39 | /* | ||
40 | * C/C++ logging functions. See the logging documentation for API details. | ||
41 | * | ||
42 | * We'd like these to be available from C code (in case we import some from | ||
43 | * somewhere), so this has a C interface. | ||
44 | * | ||
45 | * The output will be correct when the log file is shared between multiple | ||
46 | * threads and/or multiple processes so long as the operating system | ||
47 | * supports O_APPEND. These calls have mutex-protected data structures | ||
48 | * and so are NOT reentrant. Do not use MPL_LOG in a signal handler. | ||
49 | */ | ||
50 | #ifndef _LIBS_CUTILS_MPL_LOG_H | ||
51 | #define _LIBS_CUTILS_MPL_LOG_H | ||
52 | |||
53 | #include "mltypes.h" | ||
54 | #include <stdarg.h> | ||
55 | |||
56 | |||
57 | #include <linux/kernel.h> | ||
58 | |||
59 | |||
60 | /* --------------------------------------------------------------------- */ | ||
61 | |||
62 | /* | ||
63 | * Normally we strip MPL_LOGV (VERBOSE messages) from release builds. | ||
64 | * You can modify this (for example with "#define MPL_LOG_NDEBUG 0" | ||
65 | * at the top of your source file) to change that behavior. | ||
66 | */ | ||
67 | #ifndef MPL_LOG_NDEBUG | ||
68 | #ifdef NDEBUG | ||
69 | #define MPL_LOG_NDEBUG 1 | ||
70 | #else | ||
71 | #define MPL_LOG_NDEBUG 0 | ||
72 | #endif | ||
73 | #endif | ||
74 | |||
75 | #define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE | ||
76 | #define MPL_LOG_DEFAULT KERN_DEFAULT | ||
77 | #define MPL_LOG_VERBOSE KERN_CONT | ||
78 | #define MPL_LOG_DEBUG KERN_NOTICE | ||
79 | #define MPL_LOG_INFO KERN_INFO | ||
80 | #define MPL_LOG_WARN KERN_WARNING | ||
81 | #define MPL_LOG_ERROR KERN_ERR | ||
82 | #define MPL_LOG_SILENT MPL_LOG_VERBOSE | ||
83 | |||
84 | |||
85 | |||
86 | /* | ||
87 | * This is the local tag used for the following simplified | ||
88 | * logging macros. You can change this preprocessor definition | ||
89 | * before using the other macros to change the tag. | ||
90 | */ | ||
91 | #ifndef MPL_LOG_TAG | ||
92 | #define MPL_LOG_TAG | ||
93 | #endif | ||
94 | |||
95 | /* --------------------------------------------------------------------- */ | ||
96 | |||
97 | /* | ||
98 | * Simplified macro to send a verbose log message using the current MPL_LOG_TAG. | ||
99 | */ | ||
100 | #ifndef MPL_LOGV | ||
101 | #if MPL_LOG_NDEBUG | ||
102 | #define MPL_LOGV(fmt, ...) \ | ||
103 | do { \ | ||
104 | if (0) \ | ||
105 | MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\ | ||
106 | } while (0) | ||
107 | #else | ||
108 | #define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
109 | #endif | ||
110 | #endif | ||
111 | |||
112 | #ifndef CONDITION | ||
113 | #define CONDITION(cond) ((cond) != 0) | ||
114 | #endif | ||
115 | |||
116 | #ifndef MPL_LOGV_IF | ||
117 | #if MPL_LOG_NDEBUG | ||
118 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
119 | do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0) | ||
120 | #else | ||
121 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
122 | ((CONDITION(cond)) \ | ||
123 | ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
124 | : (void)0) | ||
125 | #endif | ||
126 | #endif | ||
127 | |||
128 | /* | ||
129 | * Simplified macro to send a debug log message using the current MPL_LOG_TAG. | ||
130 | */ | ||
131 | #ifndef MPL_LOGD | ||
132 | #define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
133 | #endif | ||
134 | |||
135 | #ifndef MPL_LOGD_IF | ||
136 | #define MPL_LOGD_IF(cond, fmt, ...) \ | ||
137 | ((CONDITION(cond)) \ | ||
138 | ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
139 | : (void)0) | ||
140 | #endif | ||
141 | |||
142 | /* | ||
143 | * Simplified macro to send an info log message using the current MPL_LOG_TAG. | ||
144 | */ | ||
145 | #ifndef MPL_LOGI | ||
146 | #define MPL_LOGI(fmt, ...) pr_info(KERN_INFO MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
147 | #endif | ||
148 | |||
149 | #ifndef MPL_LOGI_IF | ||
150 | #define MPL_LOGI_IF(cond, fmt, ...) \ | ||
151 | ((CONDITION(cond)) \ | ||
152 | ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
153 | : (void)0) | ||
154 | #endif | ||
155 | |||
156 | /* | ||
157 | * Simplified macro to send a warning log message using the current MPL_LOG_TAG. | ||
158 | */ | ||
159 | #ifndef MPL_LOGW | ||
160 | #define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
161 | #endif | ||
162 | |||
163 | #ifndef MPL_LOGW_IF | ||
164 | #define MPL_LOGW_IF(cond, fmt, ...) \ | ||
165 | ((CONDITION(cond)) \ | ||
166 | ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
167 | : (void)0) | ||
168 | #endif | ||
169 | |||
170 | /* | ||
171 | * Simplified macro to send an error log message using the current MPL_LOG_TAG. | ||
172 | */ | ||
173 | #ifndef MPL_LOGE | ||
174 | #define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
175 | #endif | ||
176 | |||
177 | #ifndef MPL_LOGE_IF | ||
178 | #define MPL_LOGE_IF(cond, fmt, ...) \ | ||
179 | ((CONDITION(cond)) \ | ||
180 | ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
181 | : (void)0) | ||
182 | #endif | ||
183 | |||
184 | /* --------------------------------------------------------------------- */ | ||
185 | |||
186 | /* | ||
187 | * Log a fatal error. If the given condition fails, this stops program | ||
188 | * execution like a normal assertion, but also generating the given message. | ||
189 | * It is NOT stripped from release builds. Note that the condition test | ||
190 | * is -inverted- from the normal assert() semantics. | ||
191 | */ | ||
192 | #define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \ | ||
193 | ((CONDITION(cond)) \ | ||
194 | ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \ | ||
195 | fmt, ##__VA_ARGS__)) \ | ||
196 | : (void)0) | ||
197 | |||
198 | #define MPL_LOG_ALWAYS_FATAL(fmt, ...) \ | ||
199 | (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__))) | ||
200 | |||
201 | /* | ||
202 | * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that | ||
203 | * are stripped out of release builds. | ||
204 | */ | ||
205 | #if MPL_LOG_NDEBUG | ||
206 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
207 | do { \ | ||
208 | if (0) \ | ||
209 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \ | ||
210 | } while (0) | ||
211 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
212 | do { \ | ||
213 | if (0) \ | ||
214 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \ | ||
215 | } while (0) | ||
216 | #else | ||
217 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
218 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__) | ||
219 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
220 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) | ||
221 | #endif | ||
222 | |||
223 | /* | ||
224 | * Assertion that generates a log message when the assertion fails. | ||
225 | * Stripped out of release builds. Uses the current MPL_LOG_TAG. | ||
226 | */ | ||
227 | #define MPL_LOG_ASSERT(cond, fmt, ...) \ | ||
228 | MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__) | ||
229 | |||
230 | /* --------------------------------------------------------------------- */ | ||
231 | |||
232 | /* | ||
233 | * Basic log message macro. | ||
234 | * | ||
235 | * Example: | ||
236 | * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno); | ||
237 | * | ||
238 | * The second argument may be NULL or "" to indicate the "global" tag. | ||
239 | */ | ||
240 | #ifndef MPL_LOG | ||
241 | #define MPL_LOG(priority, tag, fmt, ...) \ | ||
242 | MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__) | ||
243 | #endif | ||
244 | |||
245 | /* | ||
246 | * Log macro that allows you to specify a number for the priority. | ||
247 | */ | ||
248 | #ifndef MPL_LOG_PRI | ||
249 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
250 | pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__) | ||
251 | #endif | ||
252 | |||
253 | /* | ||
254 | * Log macro that allows you to pass in a varargs ("args" is a va_list). | ||
255 | */ | ||
256 | #ifndef MPL_LOG_PRI_VA | ||
257 | /* not allowed in the Kernel because there is no dev_dbg that takes a va_list */ | ||
258 | #endif | ||
259 | |||
260 | /* --------------------------------------------------------------------- */ | ||
261 | |||
262 | /* | ||
263 | * =========================================================================== | ||
264 | * | ||
265 | * The stuff in the rest of this file should not be used directly. | ||
266 | */ | ||
267 | |||
268 | int _MLPrintLog(int priority, const char *tag, const char *fmt, ...); | ||
269 | int _MLPrintVaLog(int priority, const char *tag, const char *fmt, va_list args); | ||
270 | /* Final implementation of actual writing to a character device */ | ||
271 | int _MLWriteLog(const char *buf, int buflen); | ||
272 | |||
273 | static inline void __print_result_location(int result, | ||
274 | const char *file, | ||
275 | const char *func, int line) | ||
276 | { | ||
277 | MPL_LOGE("%s|%s|%d returning %d\n", file, func, line, result); | ||
278 | } | ||
279 | |||
280 | #define LOG_RESULT_LOCATION(condition) \ | ||
281 | do { \ | ||
282 | __print_result_location((int)(condition), __FILE__, \ | ||
283 | __func__, __LINE__); \ | ||
284 | } while (0) | ||
285 | |||
286 | |||
287 | #endif /* _LIBS_CUTILS_MPL_LOG_H */ | ||
diff --git a/drivers/misc/inv_mpu/mldl_cfg.h b/drivers/misc/inv_mpu/mldl_cfg.h new file mode 100644 index 00000000000..2b81b89179d --- /dev/null +++ b/drivers/misc/inv_mpu/mldl_cfg.h | |||
@@ -0,0 +1,384 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup MLDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mldl_cfg.h | ||
25 | * @brief The Motion Library Driver Layer Configuration header file. | ||
26 | */ | ||
27 | |||
28 | #ifndef __MLDL_CFG_H__ | ||
29 | #define __MLDL_CFG_H__ | ||
30 | |||
31 | #include "mltypes.h" | ||
32 | #include "mlsl.h" | ||
33 | #include <linux/mpu.h> | ||
34 | #ifdef MPU_CURRENT_BUILD_MPU6050B1 | ||
35 | #include "mpu6050b1.h" | ||
36 | #elif defined(MPU_CURRENT_BUILD_MPU3050) | ||
37 | #include "mpu3050.h" | ||
38 | #endif | ||
39 | |||
40 | #include "log.h" | ||
41 | |||
42 | /************************************************************************* | ||
43 | * Sensors Bit definitions | ||
44 | *************************************************************************/ | ||
45 | |||
46 | #define INV_X_GYRO (0x0001) | ||
47 | #define INV_Y_GYRO (0x0002) | ||
48 | #define INV_Z_GYRO (0x0004) | ||
49 | #define INV_DMP_PROCESSOR (0x0008) | ||
50 | |||
51 | #define INV_X_ACCEL (0x0010) | ||
52 | #define INV_Y_ACCEL (0x0020) | ||
53 | #define INV_Z_ACCEL (0x0040) | ||
54 | |||
55 | #define INV_X_COMPASS (0x0080) | ||
56 | #define INV_Y_COMPASS (0x0100) | ||
57 | #define INV_Z_COMPASS (0x0200) | ||
58 | |||
59 | #define INV_X_PRESSURE (0x0300) | ||
60 | #define INV_Y_PRESSURE (0x0800) | ||
61 | #define INV_Z_PRESSURE (0x1000) | ||
62 | |||
63 | #define INV_TEMPERATURE (0x2000) | ||
64 | #define INV_TIME (0x4000) | ||
65 | |||
66 | #define INV_THREE_AXIS_GYRO (0x000F) | ||
67 | #define INV_THREE_AXIS_ACCEL (0x0070) | ||
68 | #define INV_THREE_AXIS_COMPASS (0x0380) | ||
69 | #define INV_THREE_AXIS_PRESSURE (0x1C00) | ||
70 | |||
71 | #define INV_FIVE_AXIS (0x007B) | ||
72 | #define INV_SIX_AXIS_GYRO_ACCEL (0x007F) | ||
73 | #define INV_SIX_AXIS_ACCEL_COMPASS (0x03F0) | ||
74 | #define INV_NINE_AXIS (0x03FF) | ||
75 | #define INV_ALL_SENSORS (0x7FFF) | ||
76 | |||
77 | #define MPL_PROD_KEY(ver, rev) (ver * 100 + rev) | ||
78 | |||
79 | /* -------------------------------------------------------------------------- */ | ||
80 | struct mpu_ram { | ||
81 | __u16 length; | ||
82 | __u8 *ram; | ||
83 | }; | ||
84 | |||
85 | struct mpu_gyro_cfg { | ||
86 | __u8 int_config; | ||
87 | __u8 ext_sync; | ||
88 | __u8 full_scale; | ||
89 | __u8 lpf; | ||
90 | __u8 clk_src; | ||
91 | __u8 divider; | ||
92 | __u8 dmp_enable; | ||
93 | __u8 fifo_enable; | ||
94 | __u8 dmp_cfg1; | ||
95 | __u8 dmp_cfg2; | ||
96 | }; | ||
97 | |||
98 | /* Offset registers that can be calibrated */ | ||
99 | struct mpu_offsets { | ||
100 | __u8 tc[GYRO_NUM_AXES]; | ||
101 | __u16 gyro[GYRO_NUM_AXES]; | ||
102 | }; | ||
103 | |||
104 | /* Chip related information that can be read and verified */ | ||
105 | struct mpu_chip_info { | ||
106 | __u8 addr; | ||
107 | __u8 product_revision; | ||
108 | __u8 silicon_revision; | ||
109 | __u8 product_id; | ||
110 | __u16 gyro_sens_trim; | ||
111 | /* Only used for MPU6050 */ | ||
112 | __u16 accel_sens_trim; | ||
113 | }; | ||
114 | |||
115 | |||
116 | struct inv_mpu_cfg { | ||
117 | __u32 requested_sensors; | ||
118 | __u8 ignore_system_suspend; | ||
119 | }; | ||
120 | |||
121 | /* Driver related state information */ | ||
122 | struct inv_mpu_state { | ||
123 | #define MPU_GYRO_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_GYROSCOPE) | ||
124 | #define MPU_ACCEL_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_ACCEL) | ||
125 | #define MPU_COMPASS_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_COMPASS) | ||
126 | #define MPU_PRESSURE_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_PRESSURE) | ||
127 | #define MPU_GYRO_IS_BYPASSED (0x10) | ||
128 | #define MPU_DMP_IS_SUSPENDED (0x20) | ||
129 | #define MPU_GYRO_NEEDS_CONFIG (0x40) | ||
130 | #define MPU_DEVICE_IS_SUSPENDED (0x80) | ||
131 | __u8 status; | ||
132 | /* 0-1 for 3050, bitfield of BIT_SLVx_DLY_EN, x = [0..4] */ | ||
133 | __u8 i2c_slaves_enabled; | ||
134 | }; | ||
135 | |||
136 | /* Platform data for the MPU */ | ||
137 | struct mldl_cfg { | ||
138 | struct mpu_ram *mpu_ram; | ||
139 | struct mpu_gyro_cfg *mpu_gyro_cfg; | ||
140 | struct mpu_offsets *mpu_offsets; | ||
141 | struct mpu_chip_info *mpu_chip_info; | ||
142 | |||
143 | /* MPU Related stored status and info */ | ||
144 | struct inv_mpu_cfg *inv_mpu_cfg; | ||
145 | struct inv_mpu_state *inv_mpu_state; | ||
146 | |||
147 | /* Slave related information */ | ||
148 | struct ext_slave_descr *slave[EXT_SLAVE_NUM_TYPES]; | ||
149 | /* Platform Data */ | ||
150 | struct mpu_platform_data *pdata; | ||
151 | struct ext_slave_platform_data *pdata_slave[EXT_SLAVE_NUM_TYPES]; | ||
152 | }; | ||
153 | |||
154 | /* -------------------------------------------------------------------------- */ | ||
155 | |||
156 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
157 | void *mlsl_handle, | ||
158 | void *accel_handle, | ||
159 | void *compass_handle, | ||
160 | void *pressure_handle); | ||
161 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
162 | void *mlsl_handle, | ||
163 | void *accel_handle, | ||
164 | void *compass_handle, | ||
165 | void *pressure_handle); | ||
166 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
167 | void *gyro_handle, | ||
168 | void *accel_handle, | ||
169 | void *compass_handle, | ||
170 | void *pressure_handle, | ||
171 | unsigned long sensors); | ||
172 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
173 | void *gyro_handle, | ||
174 | void *accel_handle, | ||
175 | void *compass_handle, | ||
176 | void *pressure_handle, | ||
177 | unsigned long sensors); | ||
178 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, | ||
179 | void *mlsl_handle, | ||
180 | const unsigned char *data, | ||
181 | int size); | ||
182 | |||
183 | /* -------------------------------------------------------------------------- */ | ||
184 | /* Slave Read functions */ | ||
185 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
186 | void *gyro_handle, | ||
187 | void *slave_handle, | ||
188 | struct ext_slave_descr *slave, | ||
189 | struct ext_slave_platform_data *pdata, | ||
190 | unsigned char *data); | ||
191 | static inline int inv_mpu_read_accel(struct mldl_cfg *mldl_cfg, | ||
192 | void *gyro_handle, | ||
193 | void *accel_handle, unsigned char *data) | ||
194 | { | ||
195 | if (!mldl_cfg) { | ||
196 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
197 | return INV_ERROR_INVALID_PARAMETER; | ||
198 | } | ||
199 | |||
200 | return inv_mpu_slave_read( | ||
201 | mldl_cfg, gyro_handle, accel_handle, | ||
202 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
203 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
204 | data); | ||
205 | } | ||
206 | |||
207 | static inline int inv_mpu_read_compass(struct mldl_cfg *mldl_cfg, | ||
208 | void *gyro_handle, | ||
209 | void *compass_handle, | ||
210 | unsigned char *data) | ||
211 | { | ||
212 | if (!mldl_cfg) { | ||
213 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
214 | return INV_ERROR_INVALID_PARAMETER; | ||
215 | } | ||
216 | |||
217 | return inv_mpu_slave_read( | ||
218 | mldl_cfg, gyro_handle, compass_handle, | ||
219 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
220 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
221 | data); | ||
222 | } | ||
223 | |||
224 | static inline int inv_mpu_read_pressure(struct mldl_cfg *mldl_cfg, | ||
225 | void *gyro_handle, | ||
226 | void *pressure_handle, | ||
227 | unsigned char *data) | ||
228 | { | ||
229 | if (!mldl_cfg) { | ||
230 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
231 | return INV_ERROR_INVALID_PARAMETER; | ||
232 | } | ||
233 | |||
234 | return inv_mpu_slave_read( | ||
235 | mldl_cfg, gyro_handle, pressure_handle, | ||
236 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
237 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
238 | data); | ||
239 | } | ||
240 | |||
241 | int gyro_config(void *mlsl_handle, | ||
242 | struct mldl_cfg *mldl_cfg, | ||
243 | struct ext_slave_config *data); | ||
244 | |||
245 | /* Slave Config functions */ | ||
246 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
247 | void *gyro_handle, | ||
248 | void *slave_handle, | ||
249 | struct ext_slave_config *data, | ||
250 | struct ext_slave_descr *slave, | ||
251 | struct ext_slave_platform_data *pdata); | ||
252 | static inline int inv_mpu_config_accel(struct mldl_cfg *mldl_cfg, | ||
253 | void *gyro_handle, | ||
254 | void *accel_handle, | ||
255 | struct ext_slave_config *data) | ||
256 | { | ||
257 | if (!mldl_cfg) { | ||
258 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
259 | return INV_ERROR_INVALID_PARAMETER; | ||
260 | } | ||
261 | |||
262 | return inv_mpu_slave_config( | ||
263 | mldl_cfg, gyro_handle, accel_handle, data, | ||
264 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
265 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]); | ||
266 | } | ||
267 | |||
268 | static inline int inv_mpu_config_compass(struct mldl_cfg *mldl_cfg, | ||
269 | void *gyro_handle, | ||
270 | void *compass_handle, | ||
271 | struct ext_slave_config *data) | ||
272 | { | ||
273 | if (!mldl_cfg) { | ||
274 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
275 | return INV_ERROR_INVALID_PARAMETER; | ||
276 | } | ||
277 | |||
278 | return inv_mpu_slave_config( | ||
279 | mldl_cfg, gyro_handle, compass_handle, data, | ||
280 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
281 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]); | ||
282 | } | ||
283 | |||
284 | static inline int inv_mpu_config_pressure(struct mldl_cfg *mldl_cfg, | ||
285 | void *gyro_handle, | ||
286 | void *pressure_handle, | ||
287 | struct ext_slave_config *data) | ||
288 | { | ||
289 | if (!mldl_cfg) { | ||
290 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
291 | return INV_ERROR_INVALID_PARAMETER; | ||
292 | } | ||
293 | |||
294 | return inv_mpu_slave_config( | ||
295 | mldl_cfg, gyro_handle, pressure_handle, data, | ||
296 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
297 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]); | ||
298 | } | ||
299 | |||
300 | int gyro_get_config(void *mlsl_handle, | ||
301 | struct mldl_cfg *mldl_cfg, | ||
302 | struct ext_slave_config *data); | ||
303 | |||
304 | /* Slave get config functions */ | ||
305 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
306 | void *gyro_handle, | ||
307 | void *slave_handle, | ||
308 | struct ext_slave_config *data, | ||
309 | struct ext_slave_descr *slave, | ||
310 | struct ext_slave_platform_data *pdata); | ||
311 | |||
312 | static inline int inv_mpu_get_accel_config(struct mldl_cfg *mldl_cfg, | ||
313 | void *gyro_handle, | ||
314 | void *accel_handle, | ||
315 | struct ext_slave_config *data) | ||
316 | { | ||
317 | if (!mldl_cfg) { | ||
318 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
319 | return INV_ERROR_INVALID_PARAMETER; | ||
320 | } | ||
321 | |||
322 | return inv_mpu_get_slave_config( | ||
323 | mldl_cfg, gyro_handle, accel_handle, data, | ||
324 | mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL], | ||
325 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]); | ||
326 | } | ||
327 | |||
328 | static inline int inv_mpu_get_compass_config(struct mldl_cfg *mldl_cfg, | ||
329 | void *gyro_handle, | ||
330 | void *compass_handle, | ||
331 | struct ext_slave_config *data) | ||
332 | { | ||
333 | if (!mldl_cfg || !(mldl_cfg->pdata)) { | ||
334 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
335 | return INV_ERROR_INVALID_PARAMETER; | ||
336 | } | ||
337 | |||
338 | return inv_mpu_get_slave_config( | ||
339 | mldl_cfg, gyro_handle, compass_handle, data, | ||
340 | mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS], | ||
341 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]); | ||
342 | } | ||
343 | |||
344 | static inline int inv_mpu_get_pressure_config(struct mldl_cfg *mldl_cfg, | ||
345 | void *gyro_handle, | ||
346 | void *pressure_handle, | ||
347 | struct ext_slave_config *data) | ||
348 | { | ||
349 | if (!mldl_cfg || !(mldl_cfg->pdata)) { | ||
350 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
351 | return INV_ERROR_INVALID_PARAMETER; | ||
352 | } | ||
353 | |||
354 | return inv_mpu_get_slave_config( | ||
355 | mldl_cfg, gyro_handle, pressure_handle, data, | ||
356 | mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE], | ||
357 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]); | ||
358 | } | ||
359 | |||
360 | /* -------------------------------------------------------------------------- */ | ||
361 | |||
362 | static inline | ||
363 | long inv_mpu_get_sampling_rate_hz(struct mpu_gyro_cfg *gyro_cfg) | ||
364 | { | ||
365 | if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7)) | ||
366 | return 8000L / (gyro_cfg->divider + 1); | ||
367 | else | ||
368 | return 1000L / (gyro_cfg->divider + 1); | ||
369 | } | ||
370 | |||
371 | static inline | ||
372 | long inv_mpu_get_sampling_period_us(struct mpu_gyro_cfg *gyro_cfg) | ||
373 | { | ||
374 | if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7)) | ||
375 | return (long) (1000000L * (gyro_cfg->divider + 1)) / 8000L; | ||
376 | else | ||
377 | return (long) (1000000L * (gyro_cfg->divider + 1)) / 1000L; | ||
378 | } | ||
379 | |||
380 | #endif /* __MLDL_CFG_H__ */ | ||
381 | |||
382 | /** | ||
383 | * @} | ||
384 | */ | ||
diff --git a/drivers/misc/inv_mpu/mldl_print_cfg.h b/drivers/misc/inv_mpu/mldl_print_cfg.h new file mode 100644 index 00000000000..2e1911440cc --- /dev/null +++ b/drivers/misc/inv_mpu/mldl_print_cfg.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @defgroup | ||
22 | * @brief | ||
23 | * | ||
24 | * @{ | ||
25 | * @file mldl_print_cfg.h | ||
26 | * @brief | ||
27 | * | ||
28 | * | ||
29 | */ | ||
30 | #ifndef __MLDL_PRINT_CFG__ | ||
31 | #define __MLDL_PRINT_CFG__ | ||
32 | |||
33 | #include "mldl_cfg.h" | ||
34 | |||
35 | |||
36 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg); | ||
37 | |||
38 | #endif /* __MLDL_PRINT_CFG__ */ | ||
diff --git a/drivers/misc/inv_mpu/mlsl.h b/drivers/misc/inv_mpu/mlsl.h new file mode 100644 index 00000000000..204baedc1e2 --- /dev/null +++ b/drivers/misc/inv_mpu/mlsl.h | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #ifndef __MLSL_H__ | ||
21 | #define __MLSL_H__ | ||
22 | |||
23 | /** | ||
24 | * @defgroup MLSL | ||
25 | * @brief Motion Library - Serial Layer. | ||
26 | * The Motion Library System Layer provides the Motion Library | ||
27 | * with the communication interface to the hardware. | ||
28 | * | ||
29 | * The communication interface is assumed to support serial | ||
30 | * transfers in burst of variable length up to | ||
31 | * SERIAL_MAX_TRANSFER_SIZE. | ||
32 | * The default value for SERIAL_MAX_TRANSFER_SIZE is 128 bytes. | ||
33 | * Transfers of length greater than SERIAL_MAX_TRANSFER_SIZE, will | ||
34 | * be subdivided in smaller transfers of length <= | ||
35 | * SERIAL_MAX_TRANSFER_SIZE. | ||
36 | * The SERIAL_MAX_TRANSFER_SIZE definition can be modified to | ||
37 | * overcome any host processor transfer size limitation down to | ||
38 | * 1 B, the minimum. | ||
39 | * An higher value for SERIAL_MAX_TRANSFER_SIZE will favor | ||
40 | * performance and efficiency while requiring higher resource usage | ||
41 | * (mostly buffering). A smaller value will increase overhead and | ||
42 | * decrease efficiency but allows to operate with more resource | ||
43 | * constrained processor and master serial controllers. | ||
44 | * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the | ||
45 | * mlsl.h header file and master serial controllers. | ||
46 | * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the | ||
47 | * mlsl.h header file. | ||
48 | * | ||
49 | * @{ | ||
50 | * @file mlsl.h | ||
51 | * @brief The Motion Library System Layer. | ||
52 | * | ||
53 | */ | ||
54 | |||
55 | #include "mltypes.h" | ||
56 | #include <linux/mpu.h> | ||
57 | |||
58 | |||
59 | /* | ||
60 | * NOTE : to properly support Yamaha compass reads, | ||
61 | * the max transfer size should be at least 9 B. | ||
62 | * Length in bytes, typically a power of 2 >= 2 | ||
63 | */ | ||
64 | #define SERIAL_MAX_TRANSFER_SIZE 128 | ||
65 | |||
66 | |||
67 | /** | ||
68 | * inv_serial_single_write() - used to write a single byte of data. | ||
69 | * @sl_handle pointer to the serial device used for the communication. | ||
70 | * @slave_addr I2C slave address of device. | ||
71 | * @register_addr Register address to write. | ||
72 | * @data Single byte of data to write. | ||
73 | * | ||
74 | * It is called by the MPL to write a single byte of data to the MPU. | ||
75 | * | ||
76 | * returns INV_SUCCESS if successful, a non-zero error code otherwise. | ||
77 | */ | ||
78 | int inv_serial_single_write( | ||
79 | void *sl_handle, | ||
80 | unsigned char slave_addr, | ||
81 | unsigned char register_addr, | ||
82 | unsigned char data); | ||
83 | |||
84 | /** | ||
85 | * inv_serial_write() - used to write multiple bytes of data to registers. | ||
86 | * @sl_handle a file handle to the serial device used for the communication. | ||
87 | * @slave_addr I2C slave address of device. | ||
88 | * @register_addr Register address to write. | ||
89 | * @length Length of burst of data. | ||
90 | * @data Pointer to block of data. | ||
91 | * | ||
92 | * returns INV_SUCCESS if successful, a non-zero error code otherwise. | ||
93 | */ | ||
94 | int inv_serial_write( | ||
95 | void *sl_handle, | ||
96 | unsigned char slave_addr, | ||
97 | unsigned short length, | ||
98 | unsigned char const *data); | ||
99 | |||
100 | /** | ||
101 | * inv_serial_read() - used to read multiple bytes of data from registers. | ||
102 | * @sl_handle a file handle to the serial device used for the communication. | ||
103 | * @slave_addr I2C slave address of device. | ||
104 | * @register_addr Register address to read. | ||
105 | * @length Length of burst of data. | ||
106 | * @data Pointer to block of data. | ||
107 | * | ||
108 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
109 | */ | ||
110 | int inv_serial_read( | ||
111 | void *sl_handle, | ||
112 | unsigned char slave_addr, | ||
113 | unsigned char register_addr, | ||
114 | unsigned short length, | ||
115 | unsigned char *data); | ||
116 | |||
117 | /** | ||
118 | * inv_serial_read_mem() - used to read multiple bytes of data from the memory. | ||
119 | * This should be sent by I2C or SPI. | ||
120 | * | ||
121 | * @sl_handle a file handle to the serial device used for the communication. | ||
122 | * @slave_addr I2C slave address of device. | ||
123 | * @mem_addr The location in the memory to read from. | ||
124 | * @length Length of burst data. | ||
125 | * @data Pointer to block of data. | ||
126 | * | ||
127 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
128 | */ | ||
129 | int inv_serial_read_mem( | ||
130 | void *sl_handle, | ||
131 | unsigned char slave_addr, | ||
132 | unsigned short mem_addr, | ||
133 | unsigned short length, | ||
134 | unsigned char *data); | ||
135 | |||
136 | /** | ||
137 | * inv_serial_write_mem() - used to write multiple bytes of data to the memory. | ||
138 | * @sl_handle a file handle to the serial device used for the communication. | ||
139 | * @slave_addr I2C slave address of device. | ||
140 | * @mem_addr The location in the memory to write to. | ||
141 | * @length Length of burst data. | ||
142 | * @data Pointer to block of data. | ||
143 | * | ||
144 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
145 | */ | ||
146 | int inv_serial_write_mem( | ||
147 | void *sl_handle, | ||
148 | unsigned char slave_addr, | ||
149 | unsigned short mem_addr, | ||
150 | unsigned short length, | ||
151 | unsigned char const *data); | ||
152 | |||
153 | /** | ||
154 | * inv_serial_read_fifo() - used to read multiple bytes of data from the fifo. | ||
155 | * @sl_handle a file handle to the serial device used for the communication. | ||
156 | * @slave_addr I2C slave address of device. | ||
157 | * @length Length of burst of data. | ||
158 | * @data Pointer to block of data. | ||
159 | * | ||
160 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
161 | */ | ||
162 | int inv_serial_read_fifo( | ||
163 | void *sl_handle, | ||
164 | unsigned char slave_addr, | ||
165 | unsigned short length, | ||
166 | unsigned char *data); | ||
167 | |||
168 | /** | ||
169 | * inv_serial_write_fifo() - used to write multiple bytes of data to the fifo. | ||
170 | * @sl_handle a file handle to the serial device used for the communication. | ||
171 | * @slave_addr I2C slave address of device. | ||
172 | * @length Length of burst of data. | ||
173 | * @data Pointer to block of data. | ||
174 | * | ||
175 | * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise. | ||
176 | */ | ||
177 | int inv_serial_write_fifo( | ||
178 | void *sl_handle, | ||
179 | unsigned char slave_addr, | ||
180 | unsigned short length, | ||
181 | unsigned char const *data); | ||
182 | |||
183 | /** | ||
184 | * @} | ||
185 | */ | ||
186 | #endif /* __MLSL_H__ */ | ||
diff --git a/drivers/misc/inv_mpu/mltypes.h b/drivers/misc/inv_mpu/mltypes.h new file mode 100644 index 00000000000..a249f93be3e --- /dev/null +++ b/drivers/misc/inv_mpu/mltypes.h | |||
@@ -0,0 +1,234 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @defgroup MLERROR | ||
22 | * @brief Definition of the error codes used within the MPL and | ||
23 | * returned to the user. | ||
24 | * Every function tries to return a meaningful error code basing | ||
25 | * on the occuring error condition. The error code is numeric. | ||
26 | * | ||
27 | * The available error codes and their associated values are: | ||
28 | * - (0) INV_SUCCESS | ||
29 | * - (32) INV_ERROR | ||
30 | * - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER | ||
31 | * - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED | ||
32 | * - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED | ||
33 | * - (38) INV_ERROR_DMP_NOT_STARTED | ||
34 | * - (39) INV_ERROR_DMP_STARTED | ||
35 | * - (40) INV_ERROR_NOT_OPENED | ||
36 | * - (41) INV_ERROR_OPENED | ||
37 | * - (19 / ENODEV) INV_ERROR_INVALID_MODULE | ||
38 | * - (12 / ENOMEM) INV_ERROR_MEMORY_EXAUSTED | ||
39 | * - (44) INV_ERROR_DIVIDE_BY_ZERO | ||
40 | * - (45) INV_ERROR_ASSERTION_FAILURE | ||
41 | * - (46) INV_ERROR_FILE_OPEN | ||
42 | * - (47) INV_ERROR_FILE_READ | ||
43 | * - (48) INV_ERROR_FILE_WRITE | ||
44 | * - (49) INV_ERROR_INVALID_CONFIGURATION | ||
45 | * - (52) INV_ERROR_SERIAL_CLOSED | ||
46 | * - (53) INV_ERROR_SERIAL_OPEN_ERROR | ||
47 | * - (54) INV_ERROR_SERIAL_READ | ||
48 | * - (55) INV_ERROR_SERIAL_WRITE | ||
49 | * - (56) INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
50 | * - (57) INV_ERROR_SM_TRANSITION | ||
51 | * - (58) INV_ERROR_SM_IMPROPER_STATE | ||
52 | * - (62) INV_ERROR_FIFO_OVERFLOW | ||
53 | * - (63) INV_ERROR_FIFO_FOOTER | ||
54 | * - (64) INV_ERROR_FIFO_READ_COUNT | ||
55 | * - (65) INV_ERROR_FIFO_READ_DATA | ||
56 | * - (72) INV_ERROR_MEMORY_SET | ||
57 | * - (82) INV_ERROR_LOG_MEMORY_ERROR | ||
58 | * - (83) INV_ERROR_LOG_OUTPUT_ERROR | ||
59 | * - (92) INV_ERROR_OS_BAD_PTR | ||
60 | * - (93) INV_ERROR_OS_BAD_HANDLE | ||
61 | * - (94) INV_ERROR_OS_CREATE_FAILED | ||
62 | * - (95) INV_ERROR_OS_LOCK_FAILED | ||
63 | * - (102) INV_ERROR_COMPASS_DATA_OVERFLOW | ||
64 | * - (103) INV_ERROR_COMPASS_DATA_UNDERFLOW | ||
65 | * - (104) INV_ERROR_COMPASS_DATA_NOT_READY | ||
66 | * - (105) INV_ERROR_COMPASS_DATA_ERROR | ||
67 | * - (107) INV_ERROR_CALIBRATION_LOAD | ||
68 | * - (108) INV_ERROR_CALIBRATION_STORE | ||
69 | * - (109) INV_ERROR_CALIBRATION_LEN | ||
70 | * - (110) INV_ERROR_CALIBRATION_CHECKSUM | ||
71 | * - (111) INV_ERROR_ACCEL_DATA_OVERFLOW | ||
72 | * - (112) INV_ERROR_ACCEL_DATA_UNDERFLOW | ||
73 | * - (113) INV_ERROR_ACCEL_DATA_NOT_READY | ||
74 | * - (114) INV_ERROR_ACCEL_DATA_ERROR | ||
75 | * | ||
76 | * The available warning codes and their associated values are: | ||
77 | * - (115) INV_WARNING_MOTION_RACE | ||
78 | * - (116) INV_WARNING_QUAT_TRASHED | ||
79 | * | ||
80 | * @{ | ||
81 | * @file mltypes.h | ||
82 | * @} | ||
83 | */ | ||
84 | |||
85 | #ifndef MLTYPES_H | ||
86 | #define MLTYPES_H | ||
87 | |||
88 | #include <linux/types.h> | ||
89 | #include <asm-generic/errno-base.h> | ||
90 | |||
91 | |||
92 | |||
93 | |||
94 | /*--------------------------- | ||
95 | * ML Defines | ||
96 | *--------------------------*/ | ||
97 | |||
98 | #ifndef NULL | ||
99 | #define NULL 0 | ||
100 | #endif | ||
101 | |||
102 | /* - ML Errors. - */ | ||
103 | #define ERROR_NAME(x) (#x) | ||
104 | #define ERROR_CHECK_FIRST(first, x) \ | ||
105 | { if (INV_SUCCESS == first) first = x; } | ||
106 | |||
107 | #define INV_SUCCESS (0) | ||
108 | /* Generic Error code. Proprietary Error Codes only */ | ||
109 | #define INV_ERROR_BASE (0x20) | ||
110 | #define INV_ERROR (INV_ERROR_BASE) | ||
111 | |||
112 | /* Compatibility and other generic error codes */ | ||
113 | #define INV_ERROR_INVALID_PARAMETER (EINVAL) | ||
114 | #define INV_ERROR_FEATURE_NOT_ENABLED (EPERM) | ||
115 | #define INV_ERROR_FEATURE_NOT_IMPLEMENTED (INV_ERROR_BASE + 4) | ||
116 | #define INV_ERROR_DMP_NOT_STARTED (INV_ERROR_BASE + 6) | ||
117 | #define INV_ERROR_DMP_STARTED (INV_ERROR_BASE + 7) | ||
118 | #define INV_ERROR_NOT_OPENED (INV_ERROR_BASE + 8) | ||
119 | #define INV_ERROR_OPENED (INV_ERROR_BASE + 9) | ||
120 | #define INV_ERROR_INVALID_MODULE (ENODEV) | ||
121 | #define INV_ERROR_MEMORY_EXAUSTED (ENOMEM) | ||
122 | #define INV_ERROR_DIVIDE_BY_ZERO (INV_ERROR_BASE + 12) | ||
123 | #define INV_ERROR_ASSERTION_FAILURE (INV_ERROR_BASE + 13) | ||
124 | #define INV_ERROR_FILE_OPEN (INV_ERROR_BASE + 14) | ||
125 | #define INV_ERROR_FILE_READ (INV_ERROR_BASE + 15) | ||
126 | #define INV_ERROR_FILE_WRITE (INV_ERROR_BASE + 16) | ||
127 | #define INV_ERROR_INVALID_CONFIGURATION (INV_ERROR_BASE + 17) | ||
128 | |||
129 | /* Serial Communication */ | ||
130 | #define INV_ERROR_SERIAL_CLOSED (INV_ERROR_BASE + 20) | ||
131 | #define INV_ERROR_SERIAL_OPEN_ERROR (INV_ERROR_BASE + 21) | ||
132 | #define INV_ERROR_SERIAL_READ (INV_ERROR_BASE + 22) | ||
133 | #define INV_ERROR_SERIAL_WRITE (INV_ERROR_BASE + 23) | ||
134 | #define INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (INV_ERROR_BASE + 24) | ||
135 | |||
136 | /* SM = State Machine */ | ||
137 | #define INV_ERROR_SM_TRANSITION (INV_ERROR_BASE + 25) | ||
138 | #define INV_ERROR_SM_IMPROPER_STATE (INV_ERROR_BASE + 26) | ||
139 | |||
140 | /* Fifo */ | ||
141 | #define INV_ERROR_FIFO_OVERFLOW (INV_ERROR_BASE + 30) | ||
142 | #define INV_ERROR_FIFO_FOOTER (INV_ERROR_BASE + 31) | ||
143 | #define INV_ERROR_FIFO_READ_COUNT (INV_ERROR_BASE + 32) | ||
144 | #define INV_ERROR_FIFO_READ_DATA (INV_ERROR_BASE + 33) | ||
145 | |||
146 | /* Memory & Registers, Set & Get */ | ||
147 | #define INV_ERROR_MEMORY_SET (INV_ERROR_BASE + 40) | ||
148 | |||
149 | #define INV_ERROR_LOG_MEMORY_ERROR (INV_ERROR_BASE + 50) | ||
150 | #define INV_ERROR_LOG_OUTPUT_ERROR (INV_ERROR_BASE + 51) | ||
151 | |||
152 | /* OS interface errors */ | ||
153 | #define INV_ERROR_OS_BAD_PTR (INV_ERROR_BASE + 60) | ||
154 | #define INV_ERROR_OS_BAD_HANDLE (INV_ERROR_BASE + 61) | ||
155 | #define INV_ERROR_OS_CREATE_FAILED (INV_ERROR_BASE + 62) | ||
156 | #define INV_ERROR_OS_LOCK_FAILED (INV_ERROR_BASE + 63) | ||
157 | |||
158 | /* Compass errors */ | ||
159 | #define INV_ERROR_COMPASS_DATA_OVERFLOW (INV_ERROR_BASE + 70) | ||
160 | #define INV_ERROR_COMPASS_DATA_UNDERFLOW (INV_ERROR_BASE + 71) | ||
161 | #define INV_ERROR_COMPASS_DATA_NOT_READY (INV_ERROR_BASE + 72) | ||
162 | #define INV_ERROR_COMPASS_DATA_ERROR (INV_ERROR_BASE + 73) | ||
163 | |||
164 | /* Load/Store calibration */ | ||
165 | #define INV_ERROR_CALIBRATION_LOAD (INV_ERROR_BASE + 75) | ||
166 | #define INV_ERROR_CALIBRATION_STORE (INV_ERROR_BASE + 76) | ||
167 | #define INV_ERROR_CALIBRATION_LEN (INV_ERROR_BASE + 77) | ||
168 | #define INV_ERROR_CALIBRATION_CHECKSUM (INV_ERROR_BASE + 78) | ||
169 | |||
170 | /* Accel errors */ | ||
171 | #define INV_ERROR_ACCEL_DATA_OVERFLOW (INV_ERROR_BASE + 79) | ||
172 | #define INV_ERROR_ACCEL_DATA_UNDERFLOW (INV_ERROR_BASE + 80) | ||
173 | #define INV_ERROR_ACCEL_DATA_NOT_READY (INV_ERROR_BASE + 81) | ||
174 | #define INV_ERROR_ACCEL_DATA_ERROR (INV_ERROR_BASE + 82) | ||
175 | |||
176 | /* No Motion Warning States */ | ||
177 | #define INV_WARNING_MOTION_RACE (INV_ERROR_BASE + 83) | ||
178 | #define INV_WARNING_QUAT_TRASHED (INV_ERROR_BASE + 84) | ||
179 | #define INV_WARNING_GYRO_MAG (INV_ERROR_BASE + 85) | ||
180 | |||
181 | #ifdef INV_USE_LEGACY_NAMES | ||
182 | #define ML_SUCCESS INV_SUCCESS | ||
183 | #define ML_ERROR INV_ERROR | ||
184 | #define ML_ERROR_INVALID_PARAMETER INV_ERROR_INVALID_PARAMETER | ||
185 | #define ML_ERROR_FEATURE_NOT_ENABLED INV_ERROR_FEATURE_NOT_ENABLED | ||
186 | #define ML_ERROR_FEATURE_NOT_IMPLEMENTED INV_ERROR_FEATURE_NOT_IMPLEMENTED | ||
187 | #define ML_ERROR_DMP_NOT_STARTED INV_ERROR_DMP_NOT_STARTED | ||
188 | #define ML_ERROR_DMP_STARTED INV_ERROR_DMP_STARTED | ||
189 | #define ML_ERROR_NOT_OPENED INV_ERROR_NOT_OPENED | ||
190 | #define ML_ERROR_OPENED INV_ERROR_OPENED | ||
191 | #define ML_ERROR_INVALID_MODULE INV_ERROR_INVALID_MODULE | ||
192 | #define ML_ERROR_MEMORY_EXAUSTED INV_ERROR_MEMORY_EXAUSTED | ||
193 | #define ML_ERROR_DIVIDE_BY_ZERO INV_ERROR_DIVIDE_BY_ZERO | ||
194 | #define ML_ERROR_ASSERTION_FAILURE INV_ERROR_ASSERTION_FAILURE | ||
195 | #define ML_ERROR_FILE_OPEN INV_ERROR_FILE_OPEN | ||
196 | #define ML_ERROR_FILE_READ INV_ERROR_FILE_READ | ||
197 | #define ML_ERROR_FILE_WRITE INV_ERROR_FILE_WRITE | ||
198 | #define ML_ERROR_INVALID_CONFIGURATION INV_ERROR_INVALID_CONFIGURATION | ||
199 | #define ML_ERROR_SERIAL_CLOSED INV_ERROR_SERIAL_CLOSED | ||
200 | #define ML_ERROR_SERIAL_OPEN_ERROR INV_ERROR_SERIAL_OPEN_ERROR | ||
201 | #define ML_ERROR_SERIAL_READ INV_ERROR_SERIAL_READ | ||
202 | #define ML_ERROR_SERIAL_WRITE INV_ERROR_SERIAL_WRITE | ||
203 | #define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED \ | ||
204 | INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
205 | #define ML_ERROR_SM_TRANSITION INV_ERROR_SM_TRANSITION | ||
206 | #define ML_ERROR_SM_IMPROPER_STATE INV_ERROR_SM_IMPROPER_STATE | ||
207 | #define ML_ERROR_FIFO_OVERFLOW INV_ERROR_FIFO_OVERFLOW | ||
208 | #define ML_ERROR_FIFO_FOOTER INV_ERROR_FIFO_FOOTER | ||
209 | #define ML_ERROR_FIFO_READ_COUNT INV_ERROR_FIFO_READ_COUNT | ||
210 | #define ML_ERROR_FIFO_READ_DATA INV_ERROR_FIFO_READ_DATA | ||
211 | #define ML_ERROR_MEMORY_SET INV_ERROR_MEMORY_SET | ||
212 | #define ML_ERROR_LOG_MEMORY_ERROR INV_ERROR_LOG_MEMORY_ERROR | ||
213 | #define ML_ERROR_LOG_OUTPUT_ERROR INV_ERROR_LOG_OUTPUT_ERROR | ||
214 | #define ML_ERROR_OS_BAD_PTR INV_ERROR_OS_BAD_PTR | ||
215 | #define ML_ERROR_OS_BAD_HANDLE INV_ERROR_OS_BAD_HANDLE | ||
216 | #define ML_ERROR_OS_CREATE_FAILED INV_ERROR_OS_CREATE_FAILED | ||
217 | #define ML_ERROR_OS_LOCK_FAILED INV_ERROR_OS_LOCK_FAILED | ||
218 | #define ML_ERROR_COMPASS_DATA_OVERFLOW INV_ERROR_COMPASS_DATA_OVERFLOW | ||
219 | #define ML_ERROR_COMPASS_DATA_UNDERFLOW INV_ERROR_COMPASS_DATA_UNDERFLOW | ||
220 | #define ML_ERROR_COMPASS_DATA_NOT_READY INV_ERROR_COMPASS_DATA_NOT_READY | ||
221 | #define ML_ERROR_COMPASS_DATA_ERROR INV_ERROR_COMPASS_DATA_ERROR | ||
222 | #define ML_ERROR_CALIBRATION_LOAD INV_ERROR_CALIBRATION_LOAD | ||
223 | #define ML_ERROR_CALIBRATION_STORE INV_ERROR_CALIBRATION_STORE | ||
224 | #define ML_ERROR_CALIBRATION_LEN INV_ERROR_CALIBRATION_LEN | ||
225 | #define ML_ERROR_CALIBRATION_CHECKSUM INV_ERROR_CALIBRATION_CHECKSUM | ||
226 | #define ML_ERROR_ACCEL_DATA_OVERFLOW INV_ERROR_ACCEL_DATA_OVERFLOW | ||
227 | #define ML_ERROR_ACCEL_DATA_UNDERFLOW INV_ERROR_ACCEL_DATA_UNDERFLOW | ||
228 | #define ML_ERROR_ACCEL_DATA_NOT_READY INV_ERROR_ACCEL_DATA_NOT_READY | ||
229 | #define ML_ERROR_ACCEL_DATA_ERROR INV_ERROR_ACCEL_DATA_ERROR | ||
230 | #endif | ||
231 | |||
232 | /* For Linux coding compliance */ | ||
233 | |||
234 | #endif /* MLTYPES_H */ | ||
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h new file mode 100644 index 00000000000..b6a4fcfac58 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu-dev.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | |||
21 | #ifndef __MPU_DEV_H__ | ||
22 | #define __MPU_DEV_H__ | ||
23 | |||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/mpu.h> | ||
27 | |||
28 | int inv_mpu_register_slave(struct module *slave_module, | ||
29 | struct i2c_client *client, | ||
30 | struct ext_slave_platform_data *pdata, | ||
31 | struct ext_slave_descr *(*slave_descr)(void)); | ||
32 | |||
33 | void inv_mpu_unregister_slave(struct i2c_client *client, | ||
34 | struct ext_slave_platform_data *pdata, | ||
35 | struct ext_slave_descr *(*slave_descr)(void)); | ||
36 | #endif | ||
diff --git a/drivers/misc/inv_mpu/mpu3050.h b/drivers/misc/inv_mpu/mpu3050.h new file mode 100644 index 00000000000..02af16ed121 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050.h | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #ifndef __MPU_H_ | ||
21 | #error Do not include this file directly. Include mpu.h instead. | ||
22 | #endif | ||
23 | |||
24 | #ifndef __MPU3050_H_ | ||
25 | #define __MPU3050_H_ | ||
26 | |||
27 | #include <linux/types.h> | ||
28 | |||
29 | |||
30 | #define MPU_NAME "mpu3050" | ||
31 | #define DEFAULT_MPU_SLAVEADDR 0x68 | ||
32 | |||
33 | /*==== MPU REGISTER SET ====*/ | ||
34 | enum mpu_register { | ||
35 | MPUREG_WHO_AM_I = 0, /* 00 0x00 */ | ||
36 | MPUREG_PRODUCT_ID, /* 01 0x01 */ | ||
37 | MPUREG_02_RSVD, /* 02 0x02 */ | ||
38 | MPUREG_03_RSVD, /* 03 0x03 */ | ||
39 | MPUREG_04_RSVD, /* 04 0x04 */ | ||
40 | MPUREG_XG_OFFS_TC, /* 05 0x05 */ | ||
41 | MPUREG_06_RSVD, /* 06 0x06 */ | ||
42 | MPUREG_07_RSVD, /* 07 0x07 */ | ||
43 | MPUREG_YG_OFFS_TC, /* 08 0x08 */ | ||
44 | MPUREG_09_RSVD, /* 09 0x09 */ | ||
45 | MPUREG_0A_RSVD, /* 10 0x0a */ | ||
46 | MPUREG_ZG_OFFS_TC, /* 11 0x0b */ | ||
47 | MPUREG_X_OFFS_USRH, /* 12 0x0c */ | ||
48 | MPUREG_X_OFFS_USRL, /* 13 0x0d */ | ||
49 | MPUREG_Y_OFFS_USRH, /* 14 0x0e */ | ||
50 | MPUREG_Y_OFFS_USRL, /* 15 0x0f */ | ||
51 | MPUREG_Z_OFFS_USRH, /* 16 0x10 */ | ||
52 | MPUREG_Z_OFFS_USRL, /* 17 0x11 */ | ||
53 | MPUREG_FIFO_EN1, /* 18 0x12 */ | ||
54 | MPUREG_FIFO_EN2, /* 19 0x13 */ | ||
55 | MPUREG_AUX_SLV_ADDR, /* 20 0x14 */ | ||
56 | MPUREG_SMPLRT_DIV, /* 21 0x15 */ | ||
57 | MPUREG_DLPF_FS_SYNC, /* 22 0x16 */ | ||
58 | MPUREG_INT_CFG, /* 23 0x17 */ | ||
59 | MPUREG_ACCEL_BURST_ADDR,/* 24 0x18 */ | ||
60 | MPUREG_19_RSVD, /* 25 0x19 */ | ||
61 | MPUREG_INT_STATUS, /* 26 0x1a */ | ||
62 | MPUREG_TEMP_OUT_H, /* 27 0x1b */ | ||
63 | MPUREG_TEMP_OUT_L, /* 28 0x1c */ | ||
64 | MPUREG_GYRO_XOUT_H, /* 29 0x1d */ | ||
65 | MPUREG_GYRO_XOUT_L, /* 30 0x1e */ | ||
66 | MPUREG_GYRO_YOUT_H, /* 31 0x1f */ | ||
67 | MPUREG_GYRO_YOUT_L, /* 32 0x20 */ | ||
68 | MPUREG_GYRO_ZOUT_H, /* 33 0x21 */ | ||
69 | MPUREG_GYRO_ZOUT_L, /* 34 0x22 */ | ||
70 | MPUREG_23_RSVD, /* 35 0x23 */ | ||
71 | MPUREG_24_RSVD, /* 36 0x24 */ | ||
72 | MPUREG_25_RSVD, /* 37 0x25 */ | ||
73 | MPUREG_26_RSVD, /* 38 0x26 */ | ||
74 | MPUREG_27_RSVD, /* 39 0x27 */ | ||
75 | MPUREG_28_RSVD, /* 40 0x28 */ | ||
76 | MPUREG_29_RSVD, /* 41 0x29 */ | ||
77 | MPUREG_2A_RSVD, /* 42 0x2a */ | ||
78 | MPUREG_2B_RSVD, /* 43 0x2b */ | ||
79 | MPUREG_2C_RSVD, /* 44 0x2c */ | ||
80 | MPUREG_2D_RSVD, /* 45 0x2d */ | ||
81 | MPUREG_2E_RSVD, /* 46 0x2e */ | ||
82 | MPUREG_2F_RSVD, /* 47 0x2f */ | ||
83 | MPUREG_30_RSVD, /* 48 0x30 */ | ||
84 | MPUREG_31_RSVD, /* 49 0x31 */ | ||
85 | MPUREG_32_RSVD, /* 50 0x32 */ | ||
86 | MPUREG_33_RSVD, /* 51 0x33 */ | ||
87 | MPUREG_34_RSVD, /* 52 0x34 */ | ||
88 | MPUREG_DMP_CFG_1, /* 53 0x35 */ | ||
89 | MPUREG_DMP_CFG_2, /* 54 0x36 */ | ||
90 | MPUREG_BANK_SEL, /* 55 0x37 */ | ||
91 | MPUREG_MEM_START_ADDR, /* 56 0x38 */ | ||
92 | MPUREG_MEM_R_W, /* 57 0x39 */ | ||
93 | MPUREG_FIFO_COUNTH, /* 58 0x3a */ | ||
94 | MPUREG_FIFO_COUNTL, /* 59 0x3b */ | ||
95 | MPUREG_FIFO_R_W, /* 60 0x3c */ | ||
96 | MPUREG_USER_CTRL, /* 61 0x3d */ | ||
97 | MPUREG_PWR_MGM, /* 62 0x3e */ | ||
98 | MPUREG_3F_RSVD, /* 63 0x3f */ | ||
99 | NUM_OF_MPU_REGISTERS /* 64 0x40 */ | ||
100 | }; | ||
101 | |||
102 | /*==== BITS FOR MPU ====*/ | ||
103 | |||
104 | /*---- MPU 'FIFO_EN1' register (12) ----*/ | ||
105 | #define BIT_TEMP_OUT 0x80 | ||
106 | #define BIT_GYRO_XOUT 0x40 | ||
107 | #define BIT_GYRO_YOUT 0x20 | ||
108 | #define BIT_GYRO_ZOUT 0x10 | ||
109 | #define BIT_ACCEL_XOUT 0x08 | ||
110 | #define BIT_ACCEL_YOUT 0x04 | ||
111 | #define BIT_ACCEL_ZOUT 0x02 | ||
112 | #define BIT_AUX_1OUT 0x01 | ||
113 | /*---- MPU 'FIFO_EN2' register (13) ----*/ | ||
114 | #define BIT_AUX_2OUT 0x02 | ||
115 | #define BIT_AUX_3OUT 0x01 | ||
116 | /*---- MPU 'DLPF_FS_SYNC' register (16) ----*/ | ||
117 | #define BITS_EXT_SYNC_NONE 0x00 | ||
118 | #define BITS_EXT_SYNC_TEMP 0x20 | ||
119 | #define BITS_EXT_SYNC_GYROX 0x40 | ||
120 | #define BITS_EXT_SYNC_GYROY 0x60 | ||
121 | #define BITS_EXT_SYNC_GYROZ 0x80 | ||
122 | #define BITS_EXT_SYNC_ACCELX 0xA0 | ||
123 | #define BITS_EXT_SYNC_ACCELY 0xC0 | ||
124 | #define BITS_EXT_SYNC_ACCELZ 0xE0 | ||
125 | #define BITS_EXT_SYNC_MASK 0xE0 | ||
126 | #define BITS_FS_250DPS 0x00 | ||
127 | #define BITS_FS_500DPS 0x08 | ||
128 | #define BITS_FS_1000DPS 0x10 | ||
129 | #define BITS_FS_2000DPS 0x18 | ||
130 | #define BITS_FS_MASK 0x18 | ||
131 | #define BITS_DLPF_CFG_256HZ_NOLPF2 0x00 | ||
132 | #define BITS_DLPF_CFG_188HZ 0x01 | ||
133 | #define BITS_DLPF_CFG_98HZ 0x02 | ||
134 | #define BITS_DLPF_CFG_42HZ 0x03 | ||
135 | #define BITS_DLPF_CFG_20HZ 0x04 | ||
136 | #define BITS_DLPF_CFG_10HZ 0x05 | ||
137 | #define BITS_DLPF_CFG_5HZ 0x06 | ||
138 | #define BITS_DLPF_CFG_2100HZ_NOLPF 0x07 | ||
139 | #define BITS_DLPF_CFG_MASK 0x07 | ||
140 | /*---- MPU 'INT_CFG' register (17) ----*/ | ||
141 | #define BIT_ACTL 0x80 | ||
142 | #define BIT_ACTL_LOW 0x80 | ||
143 | #define BIT_ACTL_HIGH 0x00 | ||
144 | #define BIT_OPEN 0x40 | ||
145 | #define BIT_OPEN_DRAIN 0x40 | ||
146 | #define BIT_PUSH_PULL 0x00 | ||
147 | #define BIT_LATCH_INT_EN 0x20 | ||
148 | #define BIT_INT_PULSE_WIDTH_50US 0x00 | ||
149 | #define BIT_INT_ANYRD_2CLEAR 0x10 | ||
150 | #define BIT_INT_STAT_READ_2CLEAR 0x00 | ||
151 | #define BIT_MPU_RDY_EN 0x04 | ||
152 | #define BIT_DMP_INT_EN 0x02 | ||
153 | #define BIT_RAW_RDY_EN 0x01 | ||
154 | /*---- MPU 'INT_STATUS' register (1A) ----*/ | ||
155 | #define BIT_INT_STATUS_FIFO_OVERLOW 0x80 | ||
156 | #define BIT_MPU_RDY 0x04 | ||
157 | #define BIT_DMP_INT 0x02 | ||
158 | #define BIT_RAW_RDY 0x01 | ||
159 | /*---- MPU 'BANK_SEL' register (37) ----*/ | ||
160 | #define BIT_PRFTCH_EN 0x20 | ||
161 | #define BIT_CFG_USER_BANK 0x10 | ||
162 | #define BITS_MEM_SEL 0x0f | ||
163 | /*---- MPU 'USER_CTRL' register (3D) ----*/ | ||
164 | #define BIT_DMP_EN 0x80 | ||
165 | #define BIT_FIFO_EN 0x40 | ||
166 | #define BIT_AUX_IF_EN 0x20 | ||
167 | #define BIT_AUX_RD_LENG 0x10 | ||
168 | #define BIT_AUX_IF_RST 0x08 | ||
169 | #define BIT_DMP_RST 0x04 | ||
170 | #define BIT_FIFO_RST 0x02 | ||
171 | #define BIT_GYRO_RST 0x01 | ||
172 | /*---- MPU 'PWR_MGM' register (3E) ----*/ | ||
173 | #define BIT_H_RESET 0x80 | ||
174 | #define BIT_SLEEP 0x40 | ||
175 | #define BIT_STBY_XG 0x20 | ||
176 | #define BIT_STBY_YG 0x10 | ||
177 | #define BIT_STBY_ZG 0x08 | ||
178 | #define BITS_CLKSEL 0x07 | ||
179 | |||
180 | /*---- MPU Silicon Revision ----*/ | ||
181 | #define MPU_SILICON_REV_A4 1 /* MPU A4 Device */ | ||
182 | #define MPU_SILICON_REV_B1 2 /* MPU B1 Device */ | ||
183 | #define MPU_SILICON_REV_B4 3 /* MPU B4 Device */ | ||
184 | #define MPU_SILICON_REV_B6 4 /* MPU B6 Device */ | ||
185 | |||
186 | /*---- MPU Memory ----*/ | ||
187 | #define MPU_MEM_BANK_SIZE (256) | ||
188 | #define FIFO_HW_SIZE (512) | ||
189 | |||
190 | enum MPU_MEMORY_BANKS { | ||
191 | MPU_MEM_RAM_BANK_0 = 0, | ||
192 | MPU_MEM_RAM_BANK_1, | ||
193 | MPU_MEM_RAM_BANK_2, | ||
194 | MPU_MEM_RAM_BANK_3, | ||
195 | MPU_MEM_NUM_RAM_BANKS, | ||
196 | MPU_MEM_OTP_BANK_0 = MPU_MEM_NUM_RAM_BANKS, | ||
197 | /* This one is always last */ | ||
198 | MPU_MEM_NUM_BANKS | ||
199 | }; | ||
200 | |||
201 | /*---- structure containing control variables used by MLDL ----*/ | ||
202 | /*---- MPU clock source settings ----*/ | ||
203 | /*---- MPU filter selections ----*/ | ||
204 | enum mpu_filter { | ||
205 | MPU_FILTER_256HZ_NOLPF2 = 0, | ||
206 | MPU_FILTER_188HZ, | ||
207 | MPU_FILTER_98HZ, | ||
208 | MPU_FILTER_42HZ, | ||
209 | MPU_FILTER_20HZ, | ||
210 | MPU_FILTER_10HZ, | ||
211 | MPU_FILTER_5HZ, | ||
212 | MPU_FILTER_2100HZ_NOLPF, | ||
213 | NUM_MPU_FILTER | ||
214 | }; | ||
215 | |||
216 | enum mpu_fullscale { | ||
217 | MPU_FS_250DPS = 0, | ||
218 | MPU_FS_500DPS, | ||
219 | MPU_FS_1000DPS, | ||
220 | MPU_FS_2000DPS, | ||
221 | NUM_MPU_FS | ||
222 | }; | ||
223 | |||
224 | enum mpu_clock_sel { | ||
225 | MPU_CLK_SEL_INTERNAL = 0, | ||
226 | MPU_CLK_SEL_PLLGYROX, | ||
227 | MPU_CLK_SEL_PLLGYROY, | ||
228 | MPU_CLK_SEL_PLLGYROZ, | ||
229 | MPU_CLK_SEL_PLLEXT32K, | ||
230 | MPU_CLK_SEL_PLLEXT19M, | ||
231 | MPU_CLK_SEL_RESERVED, | ||
232 | MPU_CLK_SEL_STOP, | ||
233 | NUM_CLK_SEL | ||
234 | }; | ||
235 | |||
236 | enum mpu_ext_sync { | ||
237 | MPU_EXT_SYNC_NONE = 0, | ||
238 | MPU_EXT_SYNC_TEMP, | ||
239 | MPU_EXT_SYNC_GYROX, | ||
240 | MPU_EXT_SYNC_GYROY, | ||
241 | MPU_EXT_SYNC_GYROZ, | ||
242 | MPU_EXT_SYNC_ACCELX, | ||
243 | MPU_EXT_SYNC_ACCELY, | ||
244 | MPU_EXT_SYNC_ACCELZ, | ||
245 | NUM_MPU_EXT_SYNC | ||
246 | }; | ||
247 | |||
248 | #define DLPF_FS_SYNC_VALUE(ext_sync, full_scale, lpf) \ | ||
249 | ((ext_sync << 5) | (full_scale << 3) | lpf) | ||
250 | |||
251 | #endif /* __MPU3050_H_ */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/Makefile b/drivers/misc/inv_mpu/mpu3050/Makefile new file mode 100644 index 00000000000..9e573930238 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | |||
2 | # Kernel makefile for motions sensors | ||
3 | # | ||
4 | # | ||
5 | |||
6 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o | ||
7 | |||
8 | ccflags-y := -DMPU_CURRENT_BUILD_MPU3050 | ||
9 | |||
10 | mpu3050-objs += mldl_cfg.o | ||
11 | mpu3050-objs += mldl_print_cfg.o | ||
12 | mpu3050-objs += mlsl-kernel.o | ||
13 | mpu3050-objs += mpu-dev.o | ||
14 | |||
15 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
16 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
17 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c new file mode 100644 index 00000000000..ccacc8ec0b5 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c | |||
@@ -0,0 +1,1765 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup MLDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mldl_cfg.c | ||
25 | * @brief The Motion Library Driver Layer. | ||
26 | */ | ||
27 | |||
28 | /* -------------------------------------------------------------------------- */ | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | #include <stddef.h> | ||
33 | |||
34 | #include "mldl_cfg.h" | ||
35 | #include <linux/mpu.h> | ||
36 | #include "mpu3050.h" | ||
37 | |||
38 | #include "mlsl.h" | ||
39 | #include "mldl_print_cfg.h" | ||
40 | #include "log.h" | ||
41 | #undef MPL_LOG_TAG | ||
42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
43 | |||
44 | /* -------------------------------------------------------------------------- */ | ||
45 | |||
46 | #define SLEEP 1 | ||
47 | #define WAKE_UP 0 | ||
48 | #define RESET 1 | ||
49 | #define STANDBY 1 | ||
50 | |||
51 | /* -------------------------------------------------------------------------- */ | ||
52 | |||
53 | /** | ||
54 | * @brief Stop the DMP running | ||
55 | * | ||
56 | * @return INV_SUCCESS or non-zero error code | ||
57 | */ | ||
58 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
59 | { | ||
60 | unsigned char user_ctrl_reg; | ||
61 | int result; | ||
62 | |||
63 | if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) | ||
64 | return INV_SUCCESS; | ||
65 | |||
66 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
67 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
68 | if (result) { | ||
69 | LOG_RESULT_LOCATION(result); | ||
70 | return result; | ||
71 | } | ||
72 | user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
73 | user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
74 | |||
75 | result = inv_serial_single_write(gyro_handle, | ||
76 | mldl_cfg->mpu_chip_info->addr, | ||
77 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
78 | if (result) { | ||
79 | LOG_RESULT_LOCATION(result); | ||
80 | return result; | ||
81 | } | ||
82 | mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED; | ||
83 | |||
84 | return result; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * @brief Starts the DMP running | ||
89 | * | ||
90 | * @return INV_SUCCESS or non-zero error code | ||
91 | */ | ||
92 | static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
93 | { | ||
94 | unsigned char user_ctrl_reg; | ||
95 | int result; | ||
96 | |||
97 | if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
98 | mldl_cfg->mpu_gyro_cfg->dmp_enable) | ||
99 | || | ||
100 | ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
101 | !mldl_cfg->mpu_gyro_cfg->dmp_enable)) | ||
102 | return INV_SUCCESS; | ||
103 | |||
104 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
105 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
106 | if (result) { | ||
107 | LOG_RESULT_LOCATION(result); | ||
108 | return result; | ||
109 | } | ||
110 | |||
111 | result = inv_serial_single_write( | ||
112 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
113 | MPUREG_USER_CTRL, | ||
114 | ((user_ctrl_reg & (~BIT_FIFO_EN)) | ||
115 | | BIT_FIFO_RST)); | ||
116 | if (result) { | ||
117 | LOG_RESULT_LOCATION(result); | ||
118 | return result; | ||
119 | } | ||
120 | |||
121 | result = inv_serial_single_write( | ||
122 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
123 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
124 | if (result) { | ||
125 | LOG_RESULT_LOCATION(result); | ||
126 | return result; | ||
127 | } | ||
128 | |||
129 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
130 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
131 | if (result) { | ||
132 | LOG_RESULT_LOCATION(result); | ||
133 | return result; | ||
134 | } | ||
135 | |||
136 | user_ctrl_reg |= BIT_DMP_EN; | ||
137 | |||
138 | if (mldl_cfg->mpu_gyro_cfg->fifo_enable) | ||
139 | user_ctrl_reg |= BIT_FIFO_EN; | ||
140 | else | ||
141 | user_ctrl_reg &= ~BIT_FIFO_EN; | ||
142 | |||
143 | user_ctrl_reg |= BIT_DMP_RST; | ||
144 | |||
145 | result = inv_serial_single_write( | ||
146 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
147 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
148 | if (result) { | ||
149 | LOG_RESULT_LOCATION(result); | ||
150 | return result; | ||
151 | } | ||
152 | mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED; | ||
153 | |||
154 | return result; | ||
155 | } | ||
156 | |||
157 | |||
158 | |||
159 | static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg, | ||
160 | void *mlsl_handle, unsigned char enable) | ||
161 | { | ||
162 | unsigned char b; | ||
163 | int result; | ||
164 | unsigned char status = mldl_cfg->inv_mpu_state->status; | ||
165 | |||
166 | if ((status & MPU_GYRO_IS_BYPASSED && enable) || | ||
167 | (!(status & MPU_GYRO_IS_BYPASSED) && !enable)) | ||
168 | return INV_SUCCESS; | ||
169 | |||
170 | /*---- get current 'USER_CTRL' into b ----*/ | ||
171 | result = inv_serial_read( | ||
172 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
173 | MPUREG_USER_CTRL, 1, &b); | ||
174 | if (result) { | ||
175 | LOG_RESULT_LOCATION(result); | ||
176 | return result; | ||
177 | } | ||
178 | |||
179 | b &= ~BIT_AUX_IF_EN; | ||
180 | |||
181 | if (!enable) { | ||
182 | result = inv_serial_single_write( | ||
183 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
184 | MPUREG_USER_CTRL, | ||
185 | (b | BIT_AUX_IF_EN)); | ||
186 | if (result) { | ||
187 | LOG_RESULT_LOCATION(result); | ||
188 | return result; | ||
189 | } | ||
190 | } else { | ||
191 | /* Coming out of I2C is tricky due to several erratta. Do not | ||
192 | * modify this algorithm | ||
193 | */ | ||
194 | /* | ||
195 | * 1) wait for the right time and send the command to change | ||
196 | * the aux i2c slave address to an invalid address that will | ||
197 | * get nack'ed | ||
198 | * | ||
199 | * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. | ||
200 | */ | ||
201 | result = inv_serial_single_write( | ||
202 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
203 | MPUREG_AUX_SLV_ADDR, 0x7F); | ||
204 | if (result) { | ||
205 | LOG_RESULT_LOCATION(result); | ||
206 | return result; | ||
207 | } | ||
208 | /* | ||
209 | * 2) wait enough time for a nack to occur, then go into | ||
210 | * bypass mode: | ||
211 | */ | ||
212 | msleep(2); | ||
213 | result = inv_serial_single_write( | ||
214 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
215 | MPUREG_USER_CTRL, (b)); | ||
216 | if (result) { | ||
217 | LOG_RESULT_LOCATION(result); | ||
218 | return result; | ||
219 | } | ||
220 | /* | ||
221 | * 3) wait for up to one MPU cycle then restore the slave | ||
222 | * address | ||
223 | */ | ||
224 | msleep(inv_mpu_get_sampling_period_us(mldl_cfg->mpu_gyro_cfg) | ||
225 | / 1000); | ||
226 | result = inv_serial_single_write( | ||
227 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
228 | MPUREG_AUX_SLV_ADDR, | ||
229 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL] | ||
230 | ->address); | ||
231 | if (result) { | ||
232 | LOG_RESULT_LOCATION(result); | ||
233 | return result; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * 4) reset the ime interface | ||
238 | */ | ||
239 | |||
240 | result = inv_serial_single_write( | ||
241 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
242 | MPUREG_USER_CTRL, | ||
243 | (b | BIT_AUX_IF_RST)); | ||
244 | if (result) { | ||
245 | LOG_RESULT_LOCATION(result); | ||
246 | return result; | ||
247 | } | ||
248 | msleep(2); | ||
249 | } | ||
250 | if (enable) | ||
251 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
252 | else | ||
253 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
254 | |||
255 | return result; | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * @brief enables/disables the I2C bypass to an external device | ||
261 | * connected to MPU's secondary I2C bus. | ||
262 | * @param enable | ||
263 | * Non-zero to enable pass through. | ||
264 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
265 | */ | ||
266 | static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
267 | unsigned char enable) | ||
268 | { | ||
269 | return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable); | ||
270 | } | ||
271 | |||
272 | |||
273 | #define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) | ||
274 | |||
275 | /* NOTE : when not indicated, product revision | ||
276 | is considered an 'npp'; non production part */ | ||
277 | |||
278 | struct prod_rev_map_t { | ||
279 | unsigned char silicon_rev; | ||
280 | unsigned short gyro_trim; | ||
281 | }; | ||
282 | |||
283 | #define OLDEST_PROD_REV_SUPPORTED 11 | ||
284 | static struct prod_rev_map_t prod_rev_map[] = { | ||
285 | {0, 0}, | ||
286 | {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */ | ||
287 | {MPU_SILICON_REV_A4, 131}, /* 2 | */ | ||
288 | {MPU_SILICON_REV_A4, 131}, /* 3 | */ | ||
289 | {MPU_SILICON_REV_A4, 131}, /* 4 | */ | ||
290 | {MPU_SILICON_REV_A4, 131}, /* 5 | */ | ||
291 | {MPU_SILICON_REV_A4, 131}, /* 6 | */ | ||
292 | {MPU_SILICON_REV_A4, 131}, /* 7 | */ | ||
293 | {MPU_SILICON_REV_A4, 131}, /* 8 | */ | ||
294 | {MPU_SILICON_REV_A4, 131}, /* 9 | */ | ||
295 | {MPU_SILICON_REV_A4, 131}, /* 10 V */ | ||
296 | {MPU_SILICON_REV_B1, 131}, /* 11 B1 */ | ||
297 | {MPU_SILICON_REV_B1, 131}, /* 12 | */ | ||
298 | {MPU_SILICON_REV_B1, 131}, /* 13 | */ | ||
299 | {MPU_SILICON_REV_B1, 131}, /* 14 V */ | ||
300 | {MPU_SILICON_REV_B4, 131}, /* 15 B4 */ | ||
301 | {MPU_SILICON_REV_B4, 131}, /* 16 | */ | ||
302 | {MPU_SILICON_REV_B4, 131}, /* 17 | */ | ||
303 | {MPU_SILICON_REV_B4, 131}, /* 18 | */ | ||
304 | {MPU_SILICON_REV_B4, 115}, /* 19 | */ | ||
305 | {MPU_SILICON_REV_B4, 115}, /* 20 V */ | ||
306 | {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */ | ||
307 | {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */ | ||
308 | {MPU_SILICON_REV_B6, 0}, /* 23 B6 */ | ||
309 | {MPU_SILICON_REV_B6, 0}, /* 24 | */ | ||
310 | {MPU_SILICON_REV_B6, 0}, /* 25 | */ | ||
311 | {MPU_SILICON_REV_B6, 131}, /* 26 V (B6/A11) */ | ||
312 | }; | ||
313 | |||
314 | /** | ||
315 | * @internal | ||
316 | * @brief Get the silicon revision ID from OTP for MPU3050. | ||
317 | * The silicon revision number is in read from OTP bank 0, | ||
318 | * ADDR6[7:2]. The corresponding ID is retrieved by lookup | ||
319 | * in a map. | ||
320 | * | ||
321 | * @param mldl_cfg | ||
322 | * a pointer to the mldl config data structure. | ||
323 | * @param mlsl_handle | ||
324 | * an file handle to the serial communication device the | ||
325 | * device is connected to. | ||
326 | * | ||
327 | * @return 0 on success, a non-zero error code otherwise. | ||
328 | */ | ||
329 | static int inv_get_silicon_rev_mpu3050( | ||
330 | struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
331 | { | ||
332 | int result; | ||
333 | unsigned char index = 0x00; | ||
334 | unsigned char bank = | ||
335 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
336 | unsigned short mem_addr = ((bank << 8) | 0x06); | ||
337 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
338 | |||
339 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
340 | MPUREG_PRODUCT_ID, 1, | ||
341 | &mpu_chip_info->product_id); | ||
342 | if (result) { | ||
343 | LOG_RESULT_LOCATION(result); | ||
344 | return result; | ||
345 | } | ||
346 | |||
347 | result = inv_serial_read_mem( | ||
348 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
349 | mem_addr, 1, &index); | ||
350 | if (result) { | ||
351 | LOG_RESULT_LOCATION(result); | ||
352 | return result; | ||
353 | } | ||
354 | index >>= 2; | ||
355 | |||
356 | /* clean the prefetch and cfg user bank bits */ | ||
357 | result = inv_serial_single_write( | ||
358 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
359 | MPUREG_BANK_SEL, 0); | ||
360 | if (result) { | ||
361 | LOG_RESULT_LOCATION(result); | ||
362 | return result; | ||
363 | } | ||
364 | |||
365 | if (index < OLDEST_PROD_REV_SUPPORTED || index >= NUM_OF_PROD_REVS) { | ||
366 | mpu_chip_info->silicon_revision = 0; | ||
367 | mpu_chip_info->gyro_sens_trim = 0; | ||
368 | MPL_LOGE("Unsupported Product Revision Detected : %d\n", index); | ||
369 | return INV_ERROR_INVALID_MODULE; | ||
370 | } | ||
371 | |||
372 | mpu_chip_info->product_revision = index; | ||
373 | mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev; | ||
374 | mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim; | ||
375 | if (mpu_chip_info->gyro_sens_trim == 0) { | ||
376 | MPL_LOGE("gyro sensitivity trim is 0" | ||
377 | " - unsupported non production part.\n"); | ||
378 | return INV_ERROR_INVALID_MODULE; | ||
379 | } | ||
380 | |||
381 | return result; | ||
382 | } | ||
383 | #define inv_get_silicon_rev inv_get_silicon_rev_mpu3050 | ||
384 | |||
385 | |||
386 | /** | ||
387 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
388 | * shifters. | ||
389 | * When enabled the secondary I2C interface to which the external | ||
390 | * device is connected runs at VDD voltage (main supply). | ||
391 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
392 | * See the device specification for more details. | ||
393 | * | ||
394 | * @note using this API may produce unpredictable results, depending on how | ||
395 | * the MPU and slave device are setup on the target platform. | ||
396 | * Use of this API should entirely be restricted to system | ||
397 | * integrators. Once the correct value is found, there should be no | ||
398 | * need to change the level shifter at runtime. | ||
399 | * | ||
400 | * @pre Must be called after inv_serial_start(). | ||
401 | * @note Typically called before inv_dmp_open(). | ||
402 | * | ||
403 | * @param[in] enable: | ||
404 | * 0 to run at VDDIO (default), | ||
405 | * 1 to run at VDD. | ||
406 | * | ||
407 | * @return INV_SUCCESS if successfull, a non-zero error code otherwise. | ||
408 | */ | ||
409 | static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg, | ||
410 | void *mlsl_handle, unsigned char enable) | ||
411 | { | ||
412 | int result; | ||
413 | unsigned char regval; | ||
414 | |||
415 | unsigned char reg; | ||
416 | unsigned char mask; | ||
417 | |||
418 | if (0 == mldl_cfg->mpu_chip_info->silicon_revision) | ||
419 | return INV_ERROR_INVALID_PARAMETER; | ||
420 | |||
421 | /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR -- | ||
422 | NOTE: this is incompatible with ST accelerometers where the VDDIO | ||
423 | bit MUST be set to enable ST's internal logic to autoincrement | ||
424 | the register address on burst reads --*/ | ||
425 | if ((mldl_cfg->mpu_chip_info->silicon_revision & 0xf) | ||
426 | < MPU_SILICON_REV_B6) { | ||
427 | reg = MPUREG_ACCEL_BURST_ADDR; | ||
428 | mask = 0x80; | ||
429 | } else { | ||
430 | /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 => | ||
431 | the mask is always 0x04 --*/ | ||
432 | reg = MPUREG_FIFO_EN2; | ||
433 | mask = 0x04; | ||
434 | } | ||
435 | |||
436 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
437 | reg, 1, ®val); | ||
438 | if (result) { | ||
439 | LOG_RESULT_LOCATION(result); | ||
440 | return result; | ||
441 | } | ||
442 | |||
443 | if (enable) | ||
444 | regval |= mask; | ||
445 | else | ||
446 | regval &= ~mask; | ||
447 | |||
448 | result = inv_serial_single_write( | ||
449 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, reg, regval); | ||
450 | if (result) { | ||
451 | LOG_RESULT_LOCATION(result); | ||
452 | return result; | ||
453 | } | ||
454 | return result; | ||
455 | return INV_SUCCESS; | ||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * @internal | ||
461 | * @brief This function controls the power management on the MPU device. | ||
462 | * The entire chip can be put to low power sleep mode, or individual | ||
463 | * gyros can be turned on/off. | ||
464 | * | ||
465 | * Putting the device into sleep mode depending upon the changing needs | ||
466 | * of the associated applications is a recommended method for reducing | ||
467 | * power consuption. It is a safe opearation in that sleep/wake up of | ||
468 | * gyros while running will not result in any interruption of data. | ||
469 | * | ||
470 | * Although it is entirely allowed to put the device into full sleep | ||
471 | * while running the DMP, it is not recomended because it will disrupt | ||
472 | * the ongoing calculations carried on inside the DMP and consequently | ||
473 | * the sensor fusion algorithm. Furthermore, while in sleep mode | ||
474 | * read & write operation from the app processor on both registers and | ||
475 | * memory are disabled and can only regained by restoring the MPU in | ||
476 | * normal power mode. | ||
477 | * Disabling any of the gyro axis will reduce the associated power | ||
478 | * consuption from the PLL but will not stop the DMP from running | ||
479 | * state. | ||
480 | * | ||
481 | * @param reset | ||
482 | * Non-zero to reset the device. Note that this setting | ||
483 | * is volatile and the corresponding register bit will | ||
484 | * clear itself right after being applied. | ||
485 | * @param sleep | ||
486 | * Non-zero to put device into full sleep. | ||
487 | * @param disable_gx | ||
488 | * Non-zero to disable gyro X. | ||
489 | * @param disable_gy | ||
490 | * Non-zero to disable gyro Y. | ||
491 | * @param disable_gz | ||
492 | * Non-zero to disable gyro Z. | ||
493 | * | ||
494 | * @return INV_SUCCESS if successfull; a non-zero error code otherwise. | ||
495 | */ | ||
496 | static int mpu3050_pwr_mgmt(struct mldl_cfg *mldl_cfg, | ||
497 | void *mlsl_handle, | ||
498 | unsigned char reset, | ||
499 | unsigned char sleep, | ||
500 | unsigned char disable_gx, | ||
501 | unsigned char disable_gy, | ||
502 | unsigned char disable_gz) | ||
503 | { | ||
504 | unsigned char b; | ||
505 | int result; | ||
506 | |||
507 | result = | ||
508 | inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
509 | MPUREG_PWR_MGM, 1, &b); | ||
510 | if (result) { | ||
511 | LOG_RESULT_LOCATION(result); | ||
512 | return result; | ||
513 | } | ||
514 | |||
515 | /* If we are awake, we need to put it in bypass before resetting */ | ||
516 | if ((!(b & BIT_SLEEP)) && reset) | ||
517 | result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1); | ||
518 | |||
519 | /* Reset if requested */ | ||
520 | if (reset) { | ||
521 | MPL_LOGV("Reset MPU3050\n"); | ||
522 | result = inv_serial_single_write( | ||
523 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
524 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
525 | if (result) { | ||
526 | LOG_RESULT_LOCATION(result); | ||
527 | return result; | ||
528 | } | ||
529 | msleep(5); | ||
530 | /* Some chips are awake after reset and some are asleep, | ||
531 | * check the status */ | ||
532 | result = inv_serial_read( | ||
533 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
534 | MPUREG_PWR_MGM, 1, &b); | ||
535 | if (result) { | ||
536 | LOG_RESULT_LOCATION(result); | ||
537 | return result; | ||
538 | } | ||
539 | } | ||
540 | |||
541 | /* Update the suspended state just in case we return early */ | ||
542 | if (b & BIT_SLEEP) { | ||
543 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED; | ||
544 | mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED; | ||
545 | } else { | ||
546 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED; | ||
547 | mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED; | ||
548 | } | ||
549 | |||
550 | /* if power status match requested, nothing else's left to do */ | ||
551 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
552 | (((sleep != 0) * BIT_SLEEP) | | ||
553 | ((disable_gx != 0) * BIT_STBY_XG) | | ||
554 | ((disable_gy != 0) * BIT_STBY_YG) | | ||
555 | ((disable_gz != 0) * BIT_STBY_ZG))) { | ||
556 | return INV_SUCCESS; | ||
557 | } | ||
558 | |||
559 | /* | ||
560 | * This specific transition between states needs to be reinterpreted: | ||
561 | * (1,1,1,1) -> (0,1,1,1) has to become | ||
562 | * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1) | ||
563 | * where | ||
564 | * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1) | ||
565 | */ | ||
566 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
567 | (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) | ||
568 | && ((!sleep) && disable_gx && disable_gy && disable_gz)) { | ||
569 | result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0); | ||
570 | if (result) | ||
571 | return result; | ||
572 | b |= BIT_SLEEP; | ||
573 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
574 | } | ||
575 | |||
576 | if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) { | ||
577 | if (sleep) { | ||
578 | result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1); | ||
579 | if (result) { | ||
580 | LOG_RESULT_LOCATION(result); | ||
581 | return result; | ||
582 | } | ||
583 | b |= BIT_SLEEP; | ||
584 | result = | ||
585 | inv_serial_single_write( | ||
586 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
587 | MPUREG_PWR_MGM, b); | ||
588 | if (result) { | ||
589 | LOG_RESULT_LOCATION(result); | ||
590 | return result; | ||
591 | } | ||
592 | mldl_cfg->inv_mpu_state->status |= | ||
593 | MPU_GYRO_IS_SUSPENDED; | ||
594 | mldl_cfg->inv_mpu_state->status |= | ||
595 | MPU_DEVICE_IS_SUSPENDED; | ||
596 | } else { | ||
597 | b &= ~BIT_SLEEP; | ||
598 | result = | ||
599 | inv_serial_single_write( | ||
600 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
601 | MPUREG_PWR_MGM, b); | ||
602 | if (result) { | ||
603 | LOG_RESULT_LOCATION(result); | ||
604 | return result; | ||
605 | } | ||
606 | mldl_cfg->inv_mpu_state->status &= | ||
607 | ~MPU_GYRO_IS_SUSPENDED; | ||
608 | mldl_cfg->inv_mpu_state->status &= | ||
609 | ~MPU_DEVICE_IS_SUSPENDED; | ||
610 | msleep(5); | ||
611 | } | ||
612 | } | ||
613 | /*--- | ||
614 | WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE | ||
615 | 1) put one axis at a time in stand-by | ||
616 | ---*/ | ||
617 | if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) { | ||
618 | b ^= BIT_STBY_XG; | ||
619 | result = inv_serial_single_write( | ||
620 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
621 | MPUREG_PWR_MGM, b); | ||
622 | if (result) { | ||
623 | LOG_RESULT_LOCATION(result); | ||
624 | return result; | ||
625 | } | ||
626 | } | ||
627 | if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) { | ||
628 | b ^= BIT_STBY_YG; | ||
629 | result = inv_serial_single_write( | ||
630 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
631 | MPUREG_PWR_MGM, b); | ||
632 | if (result) { | ||
633 | LOG_RESULT_LOCATION(result); | ||
634 | return result; | ||
635 | } | ||
636 | } | ||
637 | if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) { | ||
638 | b ^= BIT_STBY_ZG; | ||
639 | result = inv_serial_single_write( | ||
640 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
641 | MPUREG_PWR_MGM, b); | ||
642 | if (result) { | ||
643 | LOG_RESULT_LOCATION(result); | ||
644 | return result; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | return INV_SUCCESS; | ||
649 | } | ||
650 | |||
651 | |||
652 | /** | ||
653 | * @brief sets the clock source for the gyros. | ||
654 | * @param mldl_cfg | ||
655 | * a pointer to the struct mldl_cfg data structure. | ||
656 | * @param gyro_handle | ||
657 | * an handle to the serial device the gyro is assigned to. | ||
658 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
659 | */ | ||
660 | static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg) | ||
661 | { | ||
662 | int result; | ||
663 | unsigned char cur_clk_src; | ||
664 | unsigned char reg; | ||
665 | |||
666 | /* clock source selection */ | ||
667 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
668 | MPUREG_PWR_MGM, 1, ®); | ||
669 | if (result) { | ||
670 | LOG_RESULT_LOCATION(result); | ||
671 | return result; | ||
672 | } | ||
673 | cur_clk_src = reg & BITS_CLKSEL; | ||
674 | reg &= ~BITS_CLKSEL; | ||
675 | |||
676 | |||
677 | result = inv_serial_single_write( | ||
678 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
679 | MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg); | ||
680 | if (result) { | ||
681 | LOG_RESULT_LOCATION(result); | ||
682 | return result; | ||
683 | } | ||
684 | /* TODO : workarounds to be determined and implemented */ | ||
685 | |||
686 | return result; | ||
687 | } | ||
688 | |||
689 | /** | ||
690 | * Configures the MPU I2C Master | ||
691 | * | ||
692 | * @mldl_cfg Handle to the configuration data | ||
693 | * @gyro_handle handle to the gyro communictation interface | ||
694 | * @slave Can be Null if turning off the slave | ||
695 | * @slave_pdata Can be null if turning off the slave | ||
696 | * @slave_id enum ext_slave_type to determine which index to use | ||
697 | * | ||
698 | * | ||
699 | * This fucntion configures the slaves by: | ||
700 | * 1) Setting up the read | ||
701 | * a) Read Register | ||
702 | * b) Read Length | ||
703 | * 2) Set up the data trigger (MPU6050 only) | ||
704 | * a) Set trigger write register | ||
705 | * b) Set Trigger write value | ||
706 | * 3) Set up the divider (MPU6050 only) | ||
707 | * 4) Set the slave bypass mode depending on slave | ||
708 | * | ||
709 | * returns INV_SUCCESS or non-zero error code | ||
710 | */ | ||
711 | static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg, | ||
712 | void *gyro_handle, | ||
713 | struct ext_slave_descr *slave, | ||
714 | struct ext_slave_platform_data *slave_pdata, | ||
715 | int slave_id) | ||
716 | { | ||
717 | int result; | ||
718 | unsigned char reg; | ||
719 | unsigned char slave_reg; | ||
720 | unsigned char slave_len; | ||
721 | unsigned char slave_endian; | ||
722 | unsigned char slave_address; | ||
723 | |||
724 | if (slave_id != EXT_SLAVE_TYPE_ACCEL) | ||
725 | return 0; | ||
726 | |||
727 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
728 | |||
729 | if (NULL == slave || NULL == slave_pdata) { | ||
730 | slave_reg = 0; | ||
731 | slave_len = 0; | ||
732 | slave_endian = 0; | ||
733 | slave_address = 0; | ||
734 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
735 | } else { | ||
736 | slave_reg = slave->read_reg; | ||
737 | slave_len = slave->read_len; | ||
738 | slave_endian = slave->endian; | ||
739 | slave_address = slave_pdata->address; | ||
740 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 1; | ||
741 | } | ||
742 | |||
743 | /* Address */ | ||
744 | result = inv_serial_single_write(gyro_handle, | ||
745 | mldl_cfg->mpu_chip_info->addr, | ||
746 | MPUREG_AUX_SLV_ADDR, slave_address); | ||
747 | if (result) { | ||
748 | LOG_RESULT_LOCATION(result); | ||
749 | return result; | ||
750 | } | ||
751 | /* Register */ | ||
752 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
753 | MPUREG_ACCEL_BURST_ADDR, 1, ®); | ||
754 | if (result) { | ||
755 | LOG_RESULT_LOCATION(result); | ||
756 | return result; | ||
757 | } | ||
758 | reg = ((reg & 0x80) | slave_reg); | ||
759 | result = inv_serial_single_write(gyro_handle, | ||
760 | mldl_cfg->mpu_chip_info->addr, | ||
761 | MPUREG_ACCEL_BURST_ADDR, reg); | ||
762 | if (result) { | ||
763 | LOG_RESULT_LOCATION(result); | ||
764 | return result; | ||
765 | } | ||
766 | |||
767 | /* Length */ | ||
768 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
769 | MPUREG_USER_CTRL, 1, ®); | ||
770 | if (result) { | ||
771 | LOG_RESULT_LOCATION(result); | ||
772 | return result; | ||
773 | } | ||
774 | reg = (reg & ~BIT_AUX_RD_LENG); | ||
775 | result = inv_serial_single_write( | ||
776 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
777 | MPUREG_USER_CTRL, reg); | ||
778 | if (result) { | ||
779 | LOG_RESULT_LOCATION(result); | ||
780 | return result; | ||
781 | } | ||
782 | |||
783 | return result; | ||
784 | } | ||
785 | |||
786 | |||
787 | static int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
788 | void *gyro_handle, | ||
789 | struct ext_slave_descr *slave, | ||
790 | struct ext_slave_platform_data *slave_pdata, | ||
791 | int slave_id) | ||
792 | { | ||
793 | return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave, | ||
794 | slave_pdata, slave_id); | ||
795 | } | ||
796 | /** | ||
797 | * Check to see if the gyro was reset by testing a couple of registers known | ||
798 | * to change on reset. | ||
799 | * | ||
800 | * @mldl_cfg mldl configuration structure | ||
801 | * @gyro_handle handle used to communicate with the gyro | ||
802 | * | ||
803 | * @return INV_SUCCESS or non-zero error code | ||
804 | */ | ||
805 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
806 | { | ||
807 | int result = INV_SUCCESS; | ||
808 | unsigned char reg; | ||
809 | |||
810 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
811 | MPUREG_DMP_CFG_2, 1, ®); | ||
812 | if (result) { | ||
813 | LOG_RESULT_LOCATION(result); | ||
814 | return result; | ||
815 | } | ||
816 | |||
817 | if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg) | ||
818 | return true; | ||
819 | |||
820 | if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1) | ||
821 | return false; | ||
822 | |||
823 | /* Inconclusive assume it was reset */ | ||
824 | return true; | ||
825 | } | ||
826 | |||
827 | |||
828 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
829 | const unsigned char *data, int size) | ||
830 | { | ||
831 | int bank, offset, write_size; | ||
832 | int result; | ||
833 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
834 | |||
835 | if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) { | ||
836 | #if INV_CACHE_DMP == 1 | ||
837 | memcpy(mldl_cfg->mpu_ram->ram, data, size); | ||
838 | return INV_SUCCESS; | ||
839 | #else | ||
840 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
841 | return INV_ERROR_MEMORY_SET; | ||
842 | #endif | ||
843 | } | ||
844 | |||
845 | if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) { | ||
846 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
847 | return INV_ERROR_MEMORY_SET; | ||
848 | } | ||
849 | /* Write and verify memory */ | ||
850 | for (bank = 0; size > 0; bank++, | ||
851 | size -= write_size, | ||
852 | data += write_size) { | ||
853 | if (size > MPU_MEM_BANK_SIZE) | ||
854 | write_size = MPU_MEM_BANK_SIZE; | ||
855 | else | ||
856 | write_size = size; | ||
857 | |||
858 | result = inv_serial_write_mem(mlsl_handle, | ||
859 | mldl_cfg->mpu_chip_info->addr, | ||
860 | ((bank << 8) | 0x00), | ||
861 | write_size, | ||
862 | data); | ||
863 | if (result) { | ||
864 | LOG_RESULT_LOCATION(result); | ||
865 | MPL_LOGE("Write mem error in bank %d\n", bank); | ||
866 | return result; | ||
867 | } | ||
868 | result = inv_serial_read_mem(mlsl_handle, | ||
869 | mldl_cfg->mpu_chip_info->addr, | ||
870 | ((bank << 8) | 0x00), | ||
871 | write_size, | ||
872 | read); | ||
873 | if (result) { | ||
874 | LOG_RESULT_LOCATION(result); | ||
875 | MPL_LOGE("Read mem error in bank %d\n", bank); | ||
876 | return result; | ||
877 | } | ||
878 | |||
879 | #define ML_SKIP_CHECK 20 | ||
880 | for (offset = 0; offset < write_size; offset++) { | ||
881 | /* skip the register memory locations */ | ||
882 | if (bank == 0 && offset < ML_SKIP_CHECK) | ||
883 | continue; | ||
884 | if (data[offset] != read[offset]) { | ||
885 | result = INV_ERROR_SERIAL_WRITE; | ||
886 | break; | ||
887 | } | ||
888 | } | ||
889 | if (result != INV_SUCCESS) { | ||
890 | LOG_RESULT_LOCATION(result); | ||
891 | MPL_LOGE("Read data mismatch at bank %d, offset %d\n", | ||
892 | bank, offset); | ||
893 | return result; | ||
894 | } | ||
895 | } | ||
896 | return INV_SUCCESS; | ||
897 | } | ||
898 | |||
899 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle, | ||
900 | unsigned long sensors) | ||
901 | { | ||
902 | int result; | ||
903 | int ii; | ||
904 | unsigned char reg; | ||
905 | unsigned char regs[7]; | ||
906 | |||
907 | /* Wake up the part */ | ||
908 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, false, false, | ||
909 | !(sensors & INV_X_GYRO), | ||
910 | !(sensors & INV_Y_GYRO), | ||
911 | !(sensors & INV_Z_GYRO)); | ||
912 | |||
913 | if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) && | ||
914 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
915 | return INV_SUCCESS; | ||
916 | } | ||
917 | |||
918 | if (result) { | ||
919 | LOG_RESULT_LOCATION(result); | ||
920 | return result; | ||
921 | } | ||
922 | result = inv_serial_single_write( | ||
923 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
924 | MPUREG_INT_CFG, | ||
925 | (mldl_cfg->mpu_gyro_cfg->int_config | | ||
926 | mldl_cfg->pdata->int_config)); | ||
927 | if (result) { | ||
928 | LOG_RESULT_LOCATION(result); | ||
929 | return result; | ||
930 | } | ||
931 | result = inv_serial_single_write( | ||
932 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
933 | MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider); | ||
934 | if (result) { | ||
935 | LOG_RESULT_LOCATION(result); | ||
936 | return result; | ||
937 | } | ||
938 | result = mpu_set_clock_source(gyro_handle, mldl_cfg); | ||
939 | if (result) { | ||
940 | LOG_RESULT_LOCATION(result); | ||
941 | return result; | ||
942 | } | ||
943 | |||
944 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync, | ||
945 | mldl_cfg->mpu_gyro_cfg->full_scale, | ||
946 | mldl_cfg->mpu_gyro_cfg->lpf); | ||
947 | result = inv_serial_single_write( | ||
948 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
949 | MPUREG_DLPF_FS_SYNC, reg); | ||
950 | if (result) { | ||
951 | LOG_RESULT_LOCATION(result); | ||
952 | return result; | ||
953 | } | ||
954 | result = inv_serial_single_write( | ||
955 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
956 | MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1); | ||
957 | if (result) { | ||
958 | LOG_RESULT_LOCATION(result); | ||
959 | return result; | ||
960 | } | ||
961 | result = inv_serial_single_write( | ||
962 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
963 | MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2); | ||
964 | if (result) { | ||
965 | LOG_RESULT_LOCATION(result); | ||
966 | return result; | ||
967 | } | ||
968 | |||
969 | /* Write and verify memory */ | ||
970 | #if INV_CACHE_DMP != 0 | ||
971 | inv_mpu_set_firmware(mldl_cfg, gyro_handle, | ||
972 | mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length); | ||
973 | #endif | ||
974 | |||
975 | result = inv_serial_single_write( | ||
976 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
977 | MPUREG_XG_OFFS_TC, mldl_cfg->mpu_offsets->tc[0]); | ||
978 | if (result) { | ||
979 | LOG_RESULT_LOCATION(result); | ||
980 | return result; | ||
981 | } | ||
982 | result = inv_serial_single_write( | ||
983 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
984 | MPUREG_YG_OFFS_TC, mldl_cfg->mpu_offsets->tc[1]); | ||
985 | if (result) { | ||
986 | LOG_RESULT_LOCATION(result); | ||
987 | return result; | ||
988 | } | ||
989 | result = inv_serial_single_write( | ||
990 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
991 | MPUREG_ZG_OFFS_TC, mldl_cfg->mpu_offsets->tc[2]); | ||
992 | if (result) { | ||
993 | LOG_RESULT_LOCATION(result); | ||
994 | return result; | ||
995 | } | ||
996 | regs[0] = MPUREG_X_OFFS_USRH; | ||
997 | for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) { | ||
998 | regs[1 + ii * 2] = | ||
999 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8) | ||
1000 | & 0xff; | ||
1001 | regs[1 + ii * 2 + 1] = | ||
1002 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff); | ||
1003 | } | ||
1004 | result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1005 | 7, regs); | ||
1006 | if (result) { | ||
1007 | LOG_RESULT_LOCATION(result); | ||
1008 | return result; | ||
1009 | } | ||
1010 | |||
1011 | /* Configure slaves */ | ||
1012 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
1013 | mldl_cfg->pdata->level_shifter); | ||
1014 | if (result) { | ||
1015 | LOG_RESULT_LOCATION(result); | ||
1016 | return result; | ||
1017 | } | ||
1018 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG; | ||
1019 | |||
1020 | return result; | ||
1021 | } | ||
1022 | |||
1023 | int gyro_config(void *mlsl_handle, | ||
1024 | struct mldl_cfg *mldl_cfg, | ||
1025 | struct ext_slave_config *data) | ||
1026 | { | ||
1027 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
1028 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
1029 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
1030 | int ii; | ||
1031 | |||
1032 | if (!data->data) | ||
1033 | return INV_ERROR_INVALID_PARAMETER; | ||
1034 | |||
1035 | switch (data->key) { | ||
1036 | case MPU_SLAVE_INT_CONFIG: | ||
1037 | mpu_gyro_cfg->int_config = *((__u8 *)data->data); | ||
1038 | break; | ||
1039 | case MPU_SLAVE_EXT_SYNC: | ||
1040 | mpu_gyro_cfg->ext_sync = *((__u8 *)data->data); | ||
1041 | break; | ||
1042 | case MPU_SLAVE_FULL_SCALE: | ||
1043 | mpu_gyro_cfg->full_scale = *((__u8 *)data->data); | ||
1044 | break; | ||
1045 | case MPU_SLAVE_LPF: | ||
1046 | mpu_gyro_cfg->lpf = *((__u8 *)data->data); | ||
1047 | break; | ||
1048 | case MPU_SLAVE_CLK_SRC: | ||
1049 | mpu_gyro_cfg->clk_src = *((__u8 *)data->data); | ||
1050 | break; | ||
1051 | case MPU_SLAVE_DIVIDER: | ||
1052 | mpu_gyro_cfg->divider = *((__u8 *)data->data); | ||
1053 | break; | ||
1054 | case MPU_SLAVE_DMP_ENABLE: | ||
1055 | mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data); | ||
1056 | break; | ||
1057 | case MPU_SLAVE_FIFO_ENABLE: | ||
1058 | mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data); | ||
1059 | break; | ||
1060 | case MPU_SLAVE_DMP_CFG1: | ||
1061 | mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data); | ||
1062 | break; | ||
1063 | case MPU_SLAVE_DMP_CFG2: | ||
1064 | mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data); | ||
1065 | break; | ||
1066 | case MPU_SLAVE_TC: | ||
1067 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1068 | mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii]; | ||
1069 | break; | ||
1070 | case MPU_SLAVE_GYRO: | ||
1071 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1072 | mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii]; | ||
1073 | break; | ||
1074 | case MPU_SLAVE_ADDR: | ||
1075 | mpu_chip_info->addr = *((__u8 *)data->data); | ||
1076 | break; | ||
1077 | case MPU_SLAVE_PRODUCT_REVISION: | ||
1078 | mpu_chip_info->product_revision = *((__u8 *)data->data); | ||
1079 | break; | ||
1080 | case MPU_SLAVE_SILICON_REVISION: | ||
1081 | mpu_chip_info->silicon_revision = *((__u8 *)data->data); | ||
1082 | break; | ||
1083 | case MPU_SLAVE_PRODUCT_ID: | ||
1084 | mpu_chip_info->product_id = *((__u8 *)data->data); | ||
1085 | break; | ||
1086 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
1087 | mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data); | ||
1088 | break; | ||
1089 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
1090 | mpu_chip_info->accel_sens_trim = *((__u16 *)data->data); | ||
1091 | break; | ||
1092 | case MPU_SLAVE_RAM: | ||
1093 | if (data->len != mldl_cfg->mpu_ram->length) | ||
1094 | return INV_ERROR_INVALID_PARAMETER; | ||
1095 | |||
1096 | memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len); | ||
1097 | break; | ||
1098 | default: | ||
1099 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
1100 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1101 | }; | ||
1102 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG; | ||
1103 | return INV_SUCCESS; | ||
1104 | } | ||
1105 | |||
1106 | int gyro_get_config(void *mlsl_handle, | ||
1107 | struct mldl_cfg *mldl_cfg, | ||
1108 | struct ext_slave_config *data) | ||
1109 | { | ||
1110 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
1111 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
1112 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
1113 | int ii; | ||
1114 | |||
1115 | if (!data->data) | ||
1116 | return INV_ERROR_INVALID_PARAMETER; | ||
1117 | |||
1118 | switch (data->key) { | ||
1119 | case MPU_SLAVE_INT_CONFIG: | ||
1120 | *((__u8 *)data->data) = mpu_gyro_cfg->int_config; | ||
1121 | break; | ||
1122 | case MPU_SLAVE_EXT_SYNC: | ||
1123 | *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync; | ||
1124 | break; | ||
1125 | case MPU_SLAVE_FULL_SCALE: | ||
1126 | *((__u8 *)data->data) = mpu_gyro_cfg->full_scale; | ||
1127 | break; | ||
1128 | case MPU_SLAVE_LPF: | ||
1129 | *((__u8 *)data->data) = mpu_gyro_cfg->lpf; | ||
1130 | break; | ||
1131 | case MPU_SLAVE_CLK_SRC: | ||
1132 | *((__u8 *)data->data) = mpu_gyro_cfg->clk_src; | ||
1133 | break; | ||
1134 | case MPU_SLAVE_DIVIDER: | ||
1135 | *((__u8 *)data->data) = mpu_gyro_cfg->divider; | ||
1136 | break; | ||
1137 | case MPU_SLAVE_DMP_ENABLE: | ||
1138 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable; | ||
1139 | break; | ||
1140 | case MPU_SLAVE_FIFO_ENABLE: | ||
1141 | *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable; | ||
1142 | break; | ||
1143 | case MPU_SLAVE_DMP_CFG1: | ||
1144 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1; | ||
1145 | break; | ||
1146 | case MPU_SLAVE_DMP_CFG2: | ||
1147 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2; | ||
1148 | break; | ||
1149 | case MPU_SLAVE_TC: | ||
1150 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1151 | ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii]; | ||
1152 | break; | ||
1153 | case MPU_SLAVE_GYRO: | ||
1154 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1155 | ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii]; | ||
1156 | break; | ||
1157 | case MPU_SLAVE_ADDR: | ||
1158 | *((__u8 *)data->data) = mpu_chip_info->addr; | ||
1159 | break; | ||
1160 | case MPU_SLAVE_PRODUCT_REVISION: | ||
1161 | *((__u8 *)data->data) = mpu_chip_info->product_revision; | ||
1162 | break; | ||
1163 | case MPU_SLAVE_SILICON_REVISION: | ||
1164 | *((__u8 *)data->data) = mpu_chip_info->silicon_revision; | ||
1165 | break; | ||
1166 | case MPU_SLAVE_PRODUCT_ID: | ||
1167 | *((__u8 *)data->data) = mpu_chip_info->product_id; | ||
1168 | break; | ||
1169 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
1170 | *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim; | ||
1171 | break; | ||
1172 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
1173 | *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim; | ||
1174 | break; | ||
1175 | case MPU_SLAVE_RAM: | ||
1176 | if (data->len != mldl_cfg->mpu_ram->length) | ||
1177 | return INV_ERROR_INVALID_PARAMETER; | ||
1178 | |||
1179 | memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len); | ||
1180 | break; | ||
1181 | default: | ||
1182 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
1183 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1184 | }; | ||
1185 | |||
1186 | return INV_SUCCESS; | ||
1187 | } | ||
1188 | |||
1189 | |||
1190 | /******************************************************************************* | ||
1191 | ******************************************************************************* | ||
1192 | * Exported functions | ||
1193 | ******************************************************************************* | ||
1194 | ******************************************************************************/ | ||
1195 | |||
1196 | /** | ||
1197 | * Initializes the pdata structure to defaults. | ||
1198 | * | ||
1199 | * Opens the device to read silicon revision, product id and whoami. | ||
1200 | * | ||
1201 | * @mldl_cfg | ||
1202 | * The internal device configuration data structure. | ||
1203 | * @mlsl_handle | ||
1204 | * The serial communication handle. | ||
1205 | * | ||
1206 | * @return INV_SUCCESS if silicon revision, product id and woami are supported | ||
1207 | * by this software. | ||
1208 | */ | ||
1209 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
1210 | void *gyro_handle, | ||
1211 | void *accel_handle, | ||
1212 | void *compass_handle, void *pressure_handle) | ||
1213 | { | ||
1214 | int result; | ||
1215 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1216 | int ii; | ||
1217 | |||
1218 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
1219 | ii = 0; | ||
1220 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false; | ||
1221 | mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN; | ||
1222 | mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
1223 | mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ; | ||
1224 | mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS; | ||
1225 | mldl_cfg->mpu_gyro_cfg->divider = 4; | ||
1226 | mldl_cfg->mpu_gyro_cfg->dmp_enable = 1; | ||
1227 | mldl_cfg->mpu_gyro_cfg->fifo_enable = 1; | ||
1228 | mldl_cfg->mpu_gyro_cfg->ext_sync = 0; | ||
1229 | mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0; | ||
1230 | mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0; | ||
1231 | mldl_cfg->inv_mpu_state->status = | ||
1232 | MPU_DMP_IS_SUSPENDED | | ||
1233 | MPU_GYRO_IS_SUSPENDED | | ||
1234 | MPU_ACCEL_IS_SUSPENDED | | ||
1235 | MPU_COMPASS_IS_SUSPENDED | | ||
1236 | MPU_PRESSURE_IS_SUSPENDED | | ||
1237 | MPU_DEVICE_IS_SUSPENDED; | ||
1238 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
1239 | |||
1240 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1241 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1242 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1243 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1244 | |||
1245 | if (mldl_cfg->mpu_chip_info->addr == 0) { | ||
1246 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
1247 | return INV_ERROR_INVALID_PARAMETER; | ||
1248 | } | ||
1249 | |||
1250 | /* | ||
1251 | * Reset, | ||
1252 | * Take the DMP out of sleep, and | ||
1253 | * read the product_id, sillicon rev and whoami | ||
1254 | */ | ||
1255 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
1256 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, RESET, 0, 0, 0, 0); | ||
1257 | if (result) { | ||
1258 | LOG_RESULT_LOCATION(result); | ||
1259 | return result; | ||
1260 | } | ||
1261 | |||
1262 | result = inv_get_silicon_rev(mldl_cfg, gyro_handle); | ||
1263 | if (result) { | ||
1264 | LOG_RESULT_LOCATION(result); | ||
1265 | return result; | ||
1266 | } | ||
1267 | |||
1268 | /* Get the factory temperature compensation offsets */ | ||
1269 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1270 | MPUREG_XG_OFFS_TC, 1, | ||
1271 | &mldl_cfg->mpu_offsets->tc[0]); | ||
1272 | if (result) { | ||
1273 | LOG_RESULT_LOCATION(result); | ||
1274 | return result; | ||
1275 | } | ||
1276 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1277 | MPUREG_YG_OFFS_TC, 1, | ||
1278 | &mldl_cfg->mpu_offsets->tc[1]); | ||
1279 | if (result) { | ||
1280 | LOG_RESULT_LOCATION(result); | ||
1281 | return result; | ||
1282 | } | ||
1283 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1284 | MPUREG_ZG_OFFS_TC, 1, | ||
1285 | &mldl_cfg->mpu_offsets->tc[2]); | ||
1286 | if (result) { | ||
1287 | LOG_RESULT_LOCATION(result); | ||
1288 | return result; | ||
1289 | } | ||
1290 | |||
1291 | /* Into bypass mode before sleeping and calling the slaves init */ | ||
1292 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
1293 | if (result) { | ||
1294 | LOG_RESULT_LOCATION(result); | ||
1295 | return result; | ||
1296 | } | ||
1297 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
1298 | mldl_cfg->pdata->level_shifter); | ||
1299 | if (result) { | ||
1300 | LOG_RESULT_LOCATION(result); | ||
1301 | return result; | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | #if INV_CACHE_DMP != 0 | ||
1306 | result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP, 0, 0, 0); | ||
1307 | #endif | ||
1308 | if (result) { | ||
1309 | LOG_RESULT_LOCATION(result); | ||
1310 | return result; | ||
1311 | } | ||
1312 | |||
1313 | |||
1314 | return result; | ||
1315 | |||
1316 | } | ||
1317 | |||
1318 | /** | ||
1319 | * Close the mpu interface | ||
1320 | * | ||
1321 | * @mldl_cfg pointer to the configuration structure | ||
1322 | * @mlsl_handle pointer to the serial layer handle | ||
1323 | * | ||
1324 | * @return INV_SUCCESS or non-zero error code | ||
1325 | */ | ||
1326 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
1327 | void *gyro_handle, | ||
1328 | void *accel_handle, | ||
1329 | void *compass_handle, | ||
1330 | void *pressure_handle) | ||
1331 | { | ||
1332 | return 0; | ||
1333 | } | ||
1334 | |||
1335 | /** | ||
1336 | * @brief resume the MPU device and all the other sensor | ||
1337 | * devices from their low power state. | ||
1338 | * | ||
1339 | * @mldl_cfg | ||
1340 | * pointer to the configuration structure | ||
1341 | * @gyro_handle | ||
1342 | * the main file handle to the MPU device. | ||
1343 | * @accel_handle | ||
1344 | * an handle to the accelerometer device, if sitting | ||
1345 | * onto a separate bus. Can match mlsl_handle if | ||
1346 | * the accelerometer device operates on the same | ||
1347 | * primary bus of MPU. | ||
1348 | * @compass_handle | ||
1349 | * an handle to the compass device, if sitting | ||
1350 | * onto a separate bus. Can match mlsl_handle if | ||
1351 | * the compass device operates on the same | ||
1352 | * primary bus of MPU. | ||
1353 | * @pressure_handle | ||
1354 | * an handle to the pressure sensor device, if sitting | ||
1355 | * onto a separate bus. Can match mlsl_handle if | ||
1356 | * the pressure sensor device operates on the same | ||
1357 | * primary bus of MPU. | ||
1358 | * @resume_gyro | ||
1359 | * whether resuming the gyroscope device is | ||
1360 | * actually needed (if the device supports low power | ||
1361 | * mode of some sort). | ||
1362 | * @resume_accel | ||
1363 | * whether resuming the accelerometer device is | ||
1364 | * actually needed (if the device supports low power | ||
1365 | * mode of some sort). | ||
1366 | * @resume_compass | ||
1367 | * whether resuming the compass device is | ||
1368 | * actually needed (if the device supports low power | ||
1369 | * mode of some sort). | ||
1370 | * @resume_pressure | ||
1371 | * whether resuming the pressure sensor device is | ||
1372 | * actually needed (if the device supports low power | ||
1373 | * mode of some sort). | ||
1374 | * @return INV_SUCCESS or a non-zero error code. | ||
1375 | */ | ||
1376 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
1377 | void *gyro_handle, | ||
1378 | void *accel_handle, | ||
1379 | void *compass_handle, | ||
1380 | void *pressure_handle, | ||
1381 | unsigned long sensors) | ||
1382 | { | ||
1383 | int result = INV_SUCCESS; | ||
1384 | int ii; | ||
1385 | bool resume_slave[EXT_SLAVE_NUM_TYPES]; | ||
1386 | bool resume_dmp = sensors & INV_DMP_PROCESSOR; | ||
1387 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1388 | resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
1389 | (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
1390 | resume_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
1391 | sensors & INV_THREE_AXIS_ACCEL; | ||
1392 | resume_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
1393 | sensors & INV_THREE_AXIS_COMPASS; | ||
1394 | resume_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
1395 | sensors & INV_THREE_AXIS_PRESSURE; | ||
1396 | |||
1397 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1398 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1399 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1400 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1401 | |||
1402 | |||
1403 | mldl_print_cfg(mldl_cfg); | ||
1404 | |||
1405 | /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */ | ||
1406 | for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1407 | if (resume_slave[ii] && | ||
1408 | ((!mldl_cfg->slave[ii]) || | ||
1409 | (!mldl_cfg->slave[ii]->resume))) { | ||
1410 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
1411 | return INV_ERROR_INVALID_PARAMETER; | ||
1412 | } | ||
1413 | } | ||
1414 | |||
1415 | if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp) | ||
1416 | && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) || | ||
1417 | (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) { | ||
1418 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1419 | if (result) { | ||
1420 | LOG_RESULT_LOCATION(result); | ||
1421 | return result; | ||
1422 | } | ||
1423 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
1424 | if (result) { | ||
1425 | LOG_RESULT_LOCATION(result); | ||
1426 | return result; | ||
1427 | } | ||
1428 | result = gyro_resume(mldl_cfg, gyro_handle, sensors); | ||
1429 | if (result) { | ||
1430 | LOG_RESULT_LOCATION(result); | ||
1431 | return result; | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1436 | if (!mldl_cfg->slave[ii] || | ||
1437 | !mldl_cfg->pdata_slave[ii] || | ||
1438 | !resume_slave[ii] || | ||
1439 | !(mldl_cfg->inv_mpu_state->status & (1 << ii))) | ||
1440 | continue; | ||
1441 | |||
1442 | if (EXT_SLAVE_BUS_SECONDARY == | ||
1443 | mldl_cfg->pdata_slave[ii]->bus) { | ||
1444 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, | ||
1445 | true); | ||
1446 | if (result) { | ||
1447 | LOG_RESULT_LOCATION(result); | ||
1448 | return result; | ||
1449 | } | ||
1450 | } | ||
1451 | result = mldl_cfg->slave[ii]->resume(slave_handle[ii], | ||
1452 | mldl_cfg->slave[ii], | ||
1453 | mldl_cfg->pdata_slave[ii]); | ||
1454 | if (result) { | ||
1455 | LOG_RESULT_LOCATION(result); | ||
1456 | return result; | ||
1457 | } | ||
1458 | mldl_cfg->inv_mpu_state->status &= ~(1 << ii); | ||
1459 | } | ||
1460 | |||
1461 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1462 | if (resume_dmp && | ||
1463 | !(mldl_cfg->inv_mpu_state->status & (1 << ii)) && | ||
1464 | mldl_cfg->pdata_slave[ii] && | ||
1465 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) { | ||
1466 | result = mpu_set_slave(mldl_cfg, | ||
1467 | gyro_handle, | ||
1468 | mldl_cfg->slave[ii], | ||
1469 | mldl_cfg->pdata_slave[ii], | ||
1470 | mldl_cfg->slave[ii]->type); | ||
1471 | if (result) { | ||
1472 | LOG_RESULT_LOCATION(result); | ||
1473 | return result; | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | |||
1478 | /* Turn on the master i2c iterface if necessary */ | ||
1479 | if (resume_dmp) { | ||
1480 | result = mpu_set_i2c_bypass( | ||
1481 | mldl_cfg, gyro_handle, | ||
1482 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
1483 | if (result) { | ||
1484 | LOG_RESULT_LOCATION(result); | ||
1485 | return result; | ||
1486 | } | ||
1487 | |||
1488 | /* Now start */ | ||
1489 | result = dmp_start(mldl_cfg, gyro_handle); | ||
1490 | if (result) { | ||
1491 | LOG_RESULT_LOCATION(result); | ||
1492 | return result; | ||
1493 | } | ||
1494 | } | ||
1495 | mldl_cfg->inv_mpu_cfg->requested_sensors = sensors; | ||
1496 | |||
1497 | return result; | ||
1498 | } | ||
1499 | |||
1500 | /** | ||
1501 | * @brief suspend the MPU device and all the other sensor | ||
1502 | * devices into their low power state. | ||
1503 | * @mldl_cfg | ||
1504 | * a pointer to the struct mldl_cfg internal data | ||
1505 | * structure. | ||
1506 | * @gyro_handle | ||
1507 | * the main file handle to the MPU device. | ||
1508 | * @accel_handle | ||
1509 | * an handle to the accelerometer device, if sitting | ||
1510 | * onto a separate bus. Can match gyro_handle if | ||
1511 | * the accelerometer device operates on the same | ||
1512 | * primary bus of MPU. | ||
1513 | * @compass_handle | ||
1514 | * an handle to the compass device, if sitting | ||
1515 | * onto a separate bus. Can match gyro_handle if | ||
1516 | * the compass device operates on the same | ||
1517 | * primary bus of MPU. | ||
1518 | * @pressure_handle | ||
1519 | * an handle to the pressure sensor device, if sitting | ||
1520 | * onto a separate bus. Can match gyro_handle if | ||
1521 | * the pressure sensor device operates on the same | ||
1522 | * primary bus of MPU. | ||
1523 | * @accel | ||
1524 | * whether suspending the accelerometer device is | ||
1525 | * actually needed (if the device supports low power | ||
1526 | * mode of some sort). | ||
1527 | * @compass | ||
1528 | * whether suspending the compass device is | ||
1529 | * actually needed (if the device supports low power | ||
1530 | * mode of some sort). | ||
1531 | * @pressure | ||
1532 | * whether suspending the pressure sensor device is | ||
1533 | * actually needed (if the device supports low power | ||
1534 | * mode of some sort). | ||
1535 | * @return INV_SUCCESS or a non-zero error code. | ||
1536 | */ | ||
1537 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
1538 | void *gyro_handle, | ||
1539 | void *accel_handle, | ||
1540 | void *compass_handle, | ||
1541 | void *pressure_handle, | ||
1542 | unsigned long sensors) | ||
1543 | { | ||
1544 | int result = INV_SUCCESS; | ||
1545 | int ii; | ||
1546 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
1547 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
1548 | bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR); | ||
1549 | bool suspend_slave[EXT_SLAVE_NUM_TYPES]; | ||
1550 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1551 | |||
1552 | suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
1553 | ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)) | ||
1554 | == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
1555 | suspend_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
1556 | ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL); | ||
1557 | suspend_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
1558 | ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS); | ||
1559 | suspend_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
1560 | ((sensors & INV_THREE_AXIS_PRESSURE) == | ||
1561 | INV_THREE_AXIS_PRESSURE); | ||
1562 | |||
1563 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1564 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1565 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1566 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1567 | |||
1568 | if (suspend_dmp) { | ||
1569 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1570 | if (result) { | ||
1571 | LOG_RESULT_LOCATION(result); | ||
1572 | return result; | ||
1573 | } | ||
1574 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
1575 | if (result) { | ||
1576 | LOG_RESULT_LOCATION(result); | ||
1577 | return result; | ||
1578 | } | ||
1579 | } | ||
1580 | |||
1581 | /* Gyro */ | ||
1582 | if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] && | ||
1583 | !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) { | ||
1584 | result = mpu3050_pwr_mgmt( | ||
1585 | mldl_cfg, gyro_handle, 0, | ||
1586 | suspend_dmp && suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE], | ||
1587 | (unsigned char)(sensors & INV_X_GYRO), | ||
1588 | (unsigned char)(sensors & INV_Y_GYRO), | ||
1589 | (unsigned char)(sensors & INV_Z_GYRO)); | ||
1590 | if (result) { | ||
1591 | LOG_RESULT_LOCATION(result); | ||
1592 | return result; | ||
1593 | } | ||
1594 | } | ||
1595 | |||
1596 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1597 | bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii); | ||
1598 | if (!slave[ii] || !pdata_slave[ii] || | ||
1599 | is_suspended || !suspend_slave[ii]) | ||
1600 | continue; | ||
1601 | |||
1602 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
1603 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1604 | if (result) { | ||
1605 | LOG_RESULT_LOCATION(result); | ||
1606 | return result; | ||
1607 | } | ||
1608 | } | ||
1609 | result = slave[ii]->suspend(slave_handle[ii], | ||
1610 | slave[ii], | ||
1611 | pdata_slave[ii]); | ||
1612 | if (result) { | ||
1613 | LOG_RESULT_LOCATION(result); | ||
1614 | return result; | ||
1615 | } | ||
1616 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
1617 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
1618 | NULL, NULL, | ||
1619 | slave[ii]->type); | ||
1620 | if (result) { | ||
1621 | LOG_RESULT_LOCATION(result); | ||
1622 | return result; | ||
1623 | } | ||
1624 | } | ||
1625 | mldl_cfg->inv_mpu_state->status |= (1 << ii); | ||
1626 | } | ||
1627 | |||
1628 | /* Re-enable the i2c master if there are configured slaves and DMP */ | ||
1629 | if (!suspend_dmp) { | ||
1630 | result = mpu_set_i2c_bypass( | ||
1631 | mldl_cfg, gyro_handle, | ||
1632 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
1633 | if (result) { | ||
1634 | LOG_RESULT_LOCATION(result); | ||
1635 | return result; | ||
1636 | } | ||
1637 | } | ||
1638 | mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS; | ||
1639 | |||
1640 | return result; | ||
1641 | } | ||
1642 | |||
1643 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
1644 | void *gyro_handle, | ||
1645 | void *slave_handle, | ||
1646 | struct ext_slave_descr *slave, | ||
1647 | struct ext_slave_platform_data *pdata, | ||
1648 | unsigned char *data) | ||
1649 | { | ||
1650 | int result; | ||
1651 | int bypass_result; | ||
1652 | int remain_bypassed = true; | ||
1653 | |||
1654 | if (NULL == slave || NULL == slave->read) { | ||
1655 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1656 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1657 | } | ||
1658 | |||
1659 | if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1660 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1661 | remain_bypassed = false; | ||
1662 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1663 | if (result) { | ||
1664 | LOG_RESULT_LOCATION(result); | ||
1665 | return result; | ||
1666 | } | ||
1667 | } | ||
1668 | |||
1669 | result = slave->read(slave_handle, slave, pdata, data); | ||
1670 | |||
1671 | if (!remain_bypassed) { | ||
1672 | bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1673 | if (bypass_result) { | ||
1674 | LOG_RESULT_LOCATION(bypass_result); | ||
1675 | return bypass_result; | ||
1676 | } | ||
1677 | } | ||
1678 | return result; | ||
1679 | } | ||
1680 | |||
1681 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
1682 | void *gyro_handle, | ||
1683 | void *slave_handle, | ||
1684 | struct ext_slave_config *data, | ||
1685 | struct ext_slave_descr *slave, | ||
1686 | struct ext_slave_platform_data *pdata) | ||
1687 | { | ||
1688 | int result; | ||
1689 | int remain_bypassed = true; | ||
1690 | |||
1691 | if (NULL == slave || NULL == slave->config) { | ||
1692 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1693 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1694 | } | ||
1695 | |||
1696 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1697 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1698 | remain_bypassed = false; | ||
1699 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1700 | if (result) { | ||
1701 | LOG_RESULT_LOCATION(result); | ||
1702 | return result; | ||
1703 | } | ||
1704 | } | ||
1705 | |||
1706 | result = slave->config(slave_handle, slave, pdata, data); | ||
1707 | if (result) { | ||
1708 | LOG_RESULT_LOCATION(result); | ||
1709 | return result; | ||
1710 | } | ||
1711 | |||
1712 | if (!remain_bypassed) { | ||
1713 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1714 | if (result) { | ||
1715 | LOG_RESULT_LOCATION(result); | ||
1716 | return result; | ||
1717 | } | ||
1718 | } | ||
1719 | return result; | ||
1720 | } | ||
1721 | |||
1722 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
1723 | void *gyro_handle, | ||
1724 | void *slave_handle, | ||
1725 | struct ext_slave_config *data, | ||
1726 | struct ext_slave_descr *slave, | ||
1727 | struct ext_slave_platform_data *pdata) | ||
1728 | { | ||
1729 | int result; | ||
1730 | int remain_bypassed = true; | ||
1731 | |||
1732 | if (NULL == slave || NULL == slave->get_config) { | ||
1733 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1734 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1735 | } | ||
1736 | |||
1737 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1738 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1739 | remain_bypassed = false; | ||
1740 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1741 | if (result) { | ||
1742 | LOG_RESULT_LOCATION(result); | ||
1743 | return result; | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1747 | result = slave->get_config(slave_handle, slave, pdata, data); | ||
1748 | if (result) { | ||
1749 | LOG_RESULT_LOCATION(result); | ||
1750 | return result; | ||
1751 | } | ||
1752 | |||
1753 | if (!remain_bypassed) { | ||
1754 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1755 | if (result) { | ||
1756 | LOG_RESULT_LOCATION(result); | ||
1757 | return result; | ||
1758 | } | ||
1759 | } | ||
1760 | return result; | ||
1761 | } | ||
1762 | |||
1763 | /** | ||
1764 | * @} | ||
1765 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c new file mode 100644 index 00000000000..e2b8d30ceba --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup MLDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mldl_print_cfg.c | ||
25 | * @brief The Motion Library Driver Layer. | ||
26 | */ | ||
27 | |||
28 | #include <stddef.h> | ||
29 | #include "mldl_cfg.h" | ||
30 | #include "mlsl.h" | ||
31 | #include "linux/mpu.h" | ||
32 | |||
33 | #include "log.h" | ||
34 | #undef MPL_LOG_TAG | ||
35 | #define MPL_LOG_TAG "mldl_print_cfg:" | ||
36 | |||
37 | #undef MPL_LOG_NDEBUG | ||
38 | #define MPL_LOG_NDEBUG 1 | ||
39 | |||
40 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg) | ||
41 | { | ||
42 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
43 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
44 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
45 | struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg; | ||
46 | struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state; | ||
47 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
48 | struct mpu_platform_data *pdata = mldl_cfg->pdata; | ||
49 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
50 | int ii; | ||
51 | |||
52 | /* mpu_gyro_cfg */ | ||
53 | MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config); | ||
54 | MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync); | ||
55 | MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale); | ||
56 | MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf); | ||
57 | MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src); | ||
58 | MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider); | ||
59 | MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable); | ||
60 | MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable); | ||
61 | MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1); | ||
62 | MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2); | ||
63 | /* mpu_offsets */ | ||
64 | MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]); | ||
65 | MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]); | ||
66 | MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]); | ||
67 | MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]); | ||
68 | MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]); | ||
69 | MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]); | ||
70 | |||
71 | /* mpu_chip_info */ | ||
72 | MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr); | ||
73 | |||
74 | MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision); | ||
75 | MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision); | ||
76 | MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id); | ||
77 | MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim); | ||
78 | |||
79 | MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors); | ||
80 | MPL_LOGV("ignore_system_suspend= %04x\n", | ||
81 | inv_mpu_cfg->ignore_system_suspend); | ||
82 | MPL_LOGV("status = %04x\n", inv_mpu_state->status); | ||
83 | MPL_LOGV("i2c_slaves_enabled= %04x\n", | ||
84 | inv_mpu_state->i2c_slaves_enabled); | ||
85 | |||
86 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
87 | if (!slave[ii]) | ||
88 | continue; | ||
89 | MPL_LOGV("SLAVE %d:\n", ii); | ||
90 | MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend); | ||
91 | MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume); | ||
92 | MPL_LOGV(" read = %02x\n", (int)slave[ii]->read); | ||
93 | MPL_LOGV(" type = %02x\n", slave[ii]->type); | ||
94 | MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg); | ||
95 | MPL_LOGV(" len = %02x\n", slave[ii]->read_len); | ||
96 | MPL_LOGV(" endian = %02x\n", slave[ii]->endian); | ||
97 | MPL_LOGV(" range.mantissa= %02x\n", | ||
98 | slave[ii]->range.mantissa); | ||
99 | MPL_LOGV(" range.fraction= %02x\n", | ||
100 | slave[ii]->range.fraction); | ||
101 | } | ||
102 | |||
103 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
104 | if (!pdata_slave[ii]) | ||
105 | continue; | ||
106 | MPL_LOGV("PDATA_SLAVE[%d]\n", ii); | ||
107 | MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq); | ||
108 | MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num); | ||
109 | MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus); | ||
110 | MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address); | ||
111 | MPL_LOGV(" orientation=\n" | ||
112 | " %2d %2d %2d\n" | ||
113 | " %2d %2d %2d\n" | ||
114 | " %2d %2d %2d\n", | ||
115 | pdata_slave[ii]->orientation[0], | ||
116 | pdata_slave[ii]->orientation[1], | ||
117 | pdata_slave[ii]->orientation[2], | ||
118 | pdata_slave[ii]->orientation[3], | ||
119 | pdata_slave[ii]->orientation[4], | ||
120 | pdata_slave[ii]->orientation[5], | ||
121 | pdata_slave[ii]->orientation[6], | ||
122 | pdata_slave[ii]->orientation[7], | ||
123 | pdata_slave[ii]->orientation[8]); | ||
124 | } | ||
125 | |||
126 | MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config); | ||
127 | MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter); | ||
128 | MPL_LOGV("pdata->orientation =\n" | ||
129 | " %2d %2d %2d\n" | ||
130 | " %2d %2d %2d\n" | ||
131 | " %2d %2d %2d\n", | ||
132 | pdata->orientation[0], pdata->orientation[1], | ||
133 | pdata->orientation[2], pdata->orientation[3], | ||
134 | pdata->orientation[4], pdata->orientation[5], | ||
135 | pdata->orientation[6], pdata->orientation[7], | ||
136 | pdata->orientation[8]); | ||
137 | } | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c new file mode 100644 index 00000000000..19adf5182c0 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #include "mlsl.h" | ||
21 | #include <linux/i2c.h> | ||
22 | #include "log.h" | ||
23 | #include "mpu3050.h" | ||
24 | |||
25 | static int inv_i2c_write(struct i2c_adapter *i2c_adap, | ||
26 | unsigned char address, | ||
27 | unsigned int len, unsigned char const *data) | ||
28 | { | ||
29 | struct i2c_msg msgs[1]; | ||
30 | int res; | ||
31 | |||
32 | if (!data || !i2c_adap) { | ||
33 | LOG_RESULT_LOCATION(-EINVAL); | ||
34 | return -EINVAL; | ||
35 | } | ||
36 | |||
37 | msgs[0].addr = address; | ||
38 | msgs[0].flags = 0; /* write */ | ||
39 | msgs[0].buf = (unsigned char *)data; | ||
40 | msgs[0].len = len; | ||
41 | |||
42 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
43 | if (res < 1) { | ||
44 | if (res == 0) | ||
45 | res = -EIO; | ||
46 | LOG_RESULT_LOCATION(res); | ||
47 | return res; | ||
48 | } else | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int inv_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
53 | unsigned char address, | ||
54 | unsigned char reg, unsigned char value) | ||
55 | { | ||
56 | unsigned char data[2]; | ||
57 | |||
58 | data[0] = reg; | ||
59 | data[1] = value; | ||
60 | return inv_i2c_write(i2c_adap, address, 2, data); | ||
61 | } | ||
62 | |||
63 | static int inv_i2c_read(struct i2c_adapter *i2c_adap, | ||
64 | unsigned char address, unsigned char reg, | ||
65 | unsigned int len, unsigned char *data) | ||
66 | { | ||
67 | struct i2c_msg msgs[2]; | ||
68 | int res; | ||
69 | |||
70 | if (!data || !i2c_adap) { | ||
71 | LOG_RESULT_LOCATION(-EINVAL); | ||
72 | return -EINVAL; | ||
73 | } | ||
74 | |||
75 | msgs[0].addr = address; | ||
76 | msgs[0].flags = 0; /* write */ | ||
77 | msgs[0].buf = ® | ||
78 | msgs[0].len = 1; | ||
79 | |||
80 | msgs[1].addr = address; | ||
81 | msgs[1].flags = I2C_M_RD; | ||
82 | msgs[1].buf = data; | ||
83 | msgs[1].len = len; | ||
84 | |||
85 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
86 | if (res < 2) { | ||
87 | if (res >= 0) | ||
88 | res = -EIO; | ||
89 | LOG_RESULT_LOCATION(res); | ||
90 | return res; | ||
91 | } else | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
96 | unsigned char mpu_addr, | ||
97 | unsigned short mem_addr, | ||
98 | unsigned int len, unsigned char *data) | ||
99 | { | ||
100 | unsigned char bank[2]; | ||
101 | unsigned char addr[2]; | ||
102 | unsigned char buf; | ||
103 | |||
104 | struct i2c_msg msgs[4]; | ||
105 | int res; | ||
106 | |||
107 | if (!data || !i2c_adap) { | ||
108 | LOG_RESULT_LOCATION(-EINVAL); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | |||
112 | bank[0] = MPUREG_BANK_SEL; | ||
113 | bank[1] = mem_addr >> 8; | ||
114 | |||
115 | addr[0] = MPUREG_MEM_START_ADDR; | ||
116 | addr[1] = mem_addr & 0xFF; | ||
117 | |||
118 | buf = MPUREG_MEM_R_W; | ||
119 | |||
120 | /* write message */ | ||
121 | msgs[0].addr = mpu_addr; | ||
122 | msgs[0].flags = 0; | ||
123 | msgs[0].buf = bank; | ||
124 | msgs[0].len = sizeof(bank); | ||
125 | |||
126 | msgs[1].addr = mpu_addr; | ||
127 | msgs[1].flags = 0; | ||
128 | msgs[1].buf = addr; | ||
129 | msgs[1].len = sizeof(addr); | ||
130 | |||
131 | msgs[2].addr = mpu_addr; | ||
132 | msgs[2].flags = 0; | ||
133 | msgs[2].buf = &buf; | ||
134 | msgs[2].len = 1; | ||
135 | |||
136 | msgs[3].addr = mpu_addr; | ||
137 | msgs[3].flags = I2C_M_RD; | ||
138 | msgs[3].buf = data; | ||
139 | msgs[3].len = len; | ||
140 | |||
141 | res = i2c_transfer(i2c_adap, msgs, 4); | ||
142 | if (res != 4) { | ||
143 | if (res >= 0) | ||
144 | res = -EIO; | ||
145 | LOG_RESULT_LOCATION(res); | ||
146 | return res; | ||
147 | } else | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
152 | unsigned char mpu_addr, | ||
153 | unsigned short mem_addr, | ||
154 | unsigned int len, unsigned char const *data) | ||
155 | { | ||
156 | unsigned char bank[2]; | ||
157 | unsigned char addr[2]; | ||
158 | unsigned char buf[513]; | ||
159 | |||
160 | struct i2c_msg msgs[3]; | ||
161 | int res; | ||
162 | |||
163 | if (!data || !i2c_adap) { | ||
164 | LOG_RESULT_LOCATION(-EINVAL); | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | if (len >= (sizeof(buf) - 1)) { | ||
168 | LOG_RESULT_LOCATION(-ENOMEM); | ||
169 | return -ENOMEM; | ||
170 | } | ||
171 | |||
172 | bank[0] = MPUREG_BANK_SEL; | ||
173 | bank[1] = mem_addr >> 8; | ||
174 | |||
175 | addr[0] = MPUREG_MEM_START_ADDR; | ||
176 | addr[1] = mem_addr & 0xFF; | ||
177 | |||
178 | buf[0] = MPUREG_MEM_R_W; | ||
179 | memcpy(buf + 1, data, len); | ||
180 | |||
181 | /* write message */ | ||
182 | msgs[0].addr = mpu_addr; | ||
183 | msgs[0].flags = 0; | ||
184 | msgs[0].buf = bank; | ||
185 | msgs[0].len = sizeof(bank); | ||
186 | |||
187 | msgs[1].addr = mpu_addr; | ||
188 | msgs[1].flags = 0; | ||
189 | msgs[1].buf = addr; | ||
190 | msgs[1].len = sizeof(addr); | ||
191 | |||
192 | msgs[2].addr = mpu_addr; | ||
193 | msgs[2].flags = 0; | ||
194 | msgs[2].buf = (unsigned char *)buf; | ||
195 | msgs[2].len = len + 1; | ||
196 | |||
197 | res = i2c_transfer(i2c_adap, msgs, 3); | ||
198 | if (res != 3) { | ||
199 | if (res >= 0) | ||
200 | res = -EIO; | ||
201 | LOG_RESULT_LOCATION(res); | ||
202 | return res; | ||
203 | } else | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | int inv_serial_single_write( | ||
208 | void *sl_handle, | ||
209 | unsigned char slave_addr, | ||
210 | unsigned char register_addr, | ||
211 | unsigned char data) | ||
212 | { | ||
213 | return inv_i2c_write_register((struct i2c_adapter *)sl_handle, | ||
214 | slave_addr, register_addr, data); | ||
215 | } | ||
216 | EXPORT_SYMBOL(inv_serial_single_write); | ||
217 | |||
218 | int inv_serial_write( | ||
219 | void *sl_handle, | ||
220 | unsigned char slave_addr, | ||
221 | unsigned short length, | ||
222 | unsigned char const *data) | ||
223 | { | ||
224 | int result; | ||
225 | const unsigned short data_length = length - 1; | ||
226 | const unsigned char start_reg_addr = data[0]; | ||
227 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
228 | unsigned short bytes_written = 0; | ||
229 | |||
230 | while (bytes_written < data_length) { | ||
231 | unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE, | ||
232 | data_length - bytes_written); | ||
233 | if (bytes_written == 0) { | ||
234 | result = inv_i2c_write((struct i2c_adapter *) | ||
235 | sl_handle, slave_addr, | ||
236 | 1 + this_len, data); | ||
237 | } else { | ||
238 | /* manually increment register addr between chunks */ | ||
239 | i2c_write[0] = start_reg_addr + bytes_written; | ||
240 | memcpy(&i2c_write[1], &data[1 + bytes_written], | ||
241 | this_len); | ||
242 | result = inv_i2c_write((struct i2c_adapter *) | ||
243 | sl_handle, slave_addr, | ||
244 | 1 + this_len, i2c_write); | ||
245 | } | ||
246 | if (result) { | ||
247 | LOG_RESULT_LOCATION(result); | ||
248 | return result; | ||
249 | } | ||
250 | bytes_written += this_len; | ||
251 | } | ||
252 | return 0; | ||
253 | } | ||
254 | EXPORT_SYMBOL(inv_serial_write); | ||
255 | |||
256 | int inv_serial_read( | ||
257 | void *sl_handle, | ||
258 | unsigned char slave_addr, | ||
259 | unsigned char register_addr, | ||
260 | unsigned short length, | ||
261 | unsigned char *data) | ||
262 | { | ||
263 | int result; | ||
264 | unsigned short bytes_read = 0; | ||
265 | |||
266 | if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR | ||
267 | && (register_addr == MPUREG_FIFO_R_W || | ||
268 | register_addr == MPUREG_MEM_R_W)) { | ||
269 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
270 | return INV_ERROR_INVALID_PARAMETER; | ||
271 | } | ||
272 | |||
273 | while (bytes_read < length) { | ||
274 | unsigned short this_len = | ||
275 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
276 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
277 | slave_addr, register_addr + bytes_read, | ||
278 | this_len, &data[bytes_read]); | ||
279 | if (result) { | ||
280 | LOG_RESULT_LOCATION(result); | ||
281 | return result; | ||
282 | } | ||
283 | bytes_read += this_len; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | EXPORT_SYMBOL(inv_serial_read); | ||
288 | |||
289 | int inv_serial_write_mem( | ||
290 | void *sl_handle, | ||
291 | unsigned char slave_addr, | ||
292 | unsigned short mem_addr, | ||
293 | unsigned short length, | ||
294 | unsigned char const *data) | ||
295 | { | ||
296 | int result; | ||
297 | unsigned short bytes_written = 0; | ||
298 | |||
299 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
300 | pr_err("memory read length (%d B) extends beyond its" | ||
301 | " limits (%d) if started at location %d\n", length, | ||
302 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
303 | return INV_ERROR_INVALID_PARAMETER; | ||
304 | } | ||
305 | while (bytes_written < length) { | ||
306 | unsigned short this_len = | ||
307 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
308 | result = mpu_memory_write((struct i2c_adapter *)sl_handle, | ||
309 | slave_addr, mem_addr + bytes_written, | ||
310 | this_len, &data[bytes_written]); | ||
311 | if (result) { | ||
312 | LOG_RESULT_LOCATION(result); | ||
313 | return result; | ||
314 | } | ||
315 | bytes_written += this_len; | ||
316 | } | ||
317 | return 0; | ||
318 | } | ||
319 | EXPORT_SYMBOL(inv_serial_write_mem); | ||
320 | |||
321 | int inv_serial_read_mem( | ||
322 | void *sl_handle, | ||
323 | unsigned char slave_addr, | ||
324 | unsigned short mem_addr, | ||
325 | unsigned short length, | ||
326 | unsigned char *data) | ||
327 | { | ||
328 | int result; | ||
329 | unsigned short bytes_read = 0; | ||
330 | |||
331 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
332 | printk | ||
333 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
334 | "if started at location %d\n", length, | ||
335 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
336 | return INV_ERROR_INVALID_PARAMETER; | ||
337 | } | ||
338 | while (bytes_read < length) { | ||
339 | unsigned short this_len = | ||
340 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
341 | result = | ||
342 | mpu_memory_read((struct i2c_adapter *)sl_handle, | ||
343 | slave_addr, mem_addr + bytes_read, | ||
344 | this_len, &data[bytes_read]); | ||
345 | if (result) { | ||
346 | LOG_RESULT_LOCATION(result); | ||
347 | return result; | ||
348 | } | ||
349 | bytes_read += this_len; | ||
350 | } | ||
351 | return 0; | ||
352 | } | ||
353 | EXPORT_SYMBOL(inv_serial_read_mem); | ||
354 | |||
355 | int inv_serial_write_fifo( | ||
356 | void *sl_handle, | ||
357 | unsigned char slave_addr, | ||
358 | unsigned short length, | ||
359 | unsigned char const *data) | ||
360 | { | ||
361 | int result; | ||
362 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
363 | unsigned short bytes_written = 0; | ||
364 | |||
365 | if (length > FIFO_HW_SIZE) { | ||
366 | printk(KERN_ERR | ||
367 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
368 | return INV_ERROR_INVALID_PARAMETER; | ||
369 | } | ||
370 | while (bytes_written < length) { | ||
371 | unsigned short this_len = | ||
372 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
373 | i2c_write[0] = MPUREG_FIFO_R_W; | ||
374 | memcpy(&i2c_write[1], &data[bytes_written], this_len); | ||
375 | result = inv_i2c_write((struct i2c_adapter *)sl_handle, | ||
376 | slave_addr, this_len + 1, i2c_write); | ||
377 | if (result) { | ||
378 | LOG_RESULT_LOCATION(result); | ||
379 | return result; | ||
380 | } | ||
381 | bytes_written += this_len; | ||
382 | } | ||
383 | return 0; | ||
384 | } | ||
385 | EXPORT_SYMBOL(inv_serial_write_fifo); | ||
386 | |||
387 | int inv_serial_read_fifo( | ||
388 | void *sl_handle, | ||
389 | unsigned char slave_addr, | ||
390 | unsigned short length, | ||
391 | unsigned char *data) | ||
392 | { | ||
393 | int result; | ||
394 | unsigned short bytes_read = 0; | ||
395 | |||
396 | if (length > FIFO_HW_SIZE) { | ||
397 | printk(KERN_ERR | ||
398 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
399 | return INV_ERROR_INVALID_PARAMETER; | ||
400 | } | ||
401 | while (bytes_read < length) { | ||
402 | unsigned short this_len = | ||
403 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
404 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
405 | slave_addr, MPUREG_FIFO_R_W, this_len, | ||
406 | &data[bytes_read]); | ||
407 | if (result) { | ||
408 | LOG_RESULT_LOCATION(result); | ||
409 | return result; | ||
410 | } | ||
411 | bytes_read += this_len; | ||
412 | } | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | EXPORT_SYMBOL(inv_serial_read_fifo); | ||
417 | |||
418 | /** | ||
419 | * @} | ||
420 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu3050/mpu-dev.c b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c new file mode 100644 index 00000000000..c98ed3b4a80 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c | |||
@@ -0,0 +1,1250 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/i2c-dev.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/stat.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/signal.h> | ||
30 | #include <linux/miscdevice.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/version.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/suspend.h> | ||
36 | #include <linux/poll.h> | ||
37 | |||
38 | #include <linux/errno.h> | ||
39 | #include <linux/fs.h> | ||
40 | #include <linux/mm.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/wait.h> | ||
43 | #include <linux/uaccess.h> | ||
44 | #include <linux/io.h> | ||
45 | |||
46 | #include "mpuirq.h" | ||
47 | #include "slaveirq.h" | ||
48 | #include "mlsl.h" | ||
49 | #include "mldl_cfg.h" | ||
50 | #include <linux/mpu.h> | ||
51 | |||
52 | |||
53 | /* Platform data for the MPU */ | ||
54 | struct mpu_private_data { | ||
55 | struct miscdevice dev; | ||
56 | struct i2c_client *client; | ||
57 | |||
58 | /* mldl_cfg data */ | ||
59 | struct mldl_cfg mldl_cfg; | ||
60 | struct mpu_ram mpu_ram; | ||
61 | struct mpu_gyro_cfg mpu_gyro_cfg; | ||
62 | struct mpu_offsets mpu_offsets; | ||
63 | struct mpu_chip_info mpu_chip_info; | ||
64 | struct inv_mpu_cfg inv_mpu_cfg; | ||
65 | struct inv_mpu_state inv_mpu_state; | ||
66 | |||
67 | struct mutex mutex; | ||
68 | wait_queue_head_t mpu_event_wait; | ||
69 | struct completion completion; | ||
70 | struct timer_list timeout; | ||
71 | struct notifier_block nb; | ||
72 | struct mpuirq_data mpu_pm_event; | ||
73 | int response_timeout; /* In seconds */ | ||
74 | unsigned long event; | ||
75 | int pid; | ||
76 | struct module *slave_modules[EXT_SLAVE_NUM_TYPES]; | ||
77 | }; | ||
78 | |||
79 | struct mpu_private_data *mpu_private_data; | ||
80 | |||
81 | static void mpu_pm_timeout(u_long data) | ||
82 | { | ||
83 | struct mpu_private_data *mpu = (struct mpu_private_data *)data; | ||
84 | struct i2c_client *client = mpu->client; | ||
85 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
86 | complete(&mpu->completion); | ||
87 | } | ||
88 | |||
89 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
90 | unsigned long event, void *unused) | ||
91 | { | ||
92 | struct mpu_private_data *mpu = | ||
93 | container_of(nb, struct mpu_private_data, nb); | ||
94 | struct i2c_client *client = mpu->client; | ||
95 | struct timeval event_time; | ||
96 | dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event); | ||
97 | |||
98 | /* Prevent the file handle from being closed before we initialize | ||
99 | the completion event */ | ||
100 | mutex_lock(&mpu->mutex); | ||
101 | if (!(mpu->pid) || | ||
102 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
103 | mutex_unlock(&mpu->mutex); | ||
104 | return NOTIFY_OK; | ||
105 | } | ||
106 | |||
107 | if (event == PM_SUSPEND_PREPARE) | ||
108 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
109 | if (event == PM_POST_SUSPEND) | ||
110 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
111 | |||
112 | do_gettimeofday(&event_time); | ||
113 | mpu->mpu_pm_event.interruptcount++; | ||
114 | mpu->mpu_pm_event.irqtime = | ||
115 | (((long long)event_time.tv_sec) << 32) + event_time.tv_usec; | ||
116 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
117 | mpu->mpu_pm_event.data = mpu->event; | ||
118 | |||
119 | if (mpu->response_timeout > 0) { | ||
120 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
121 | add_timer(&mpu->timeout); | ||
122 | } | ||
123 | INIT_COMPLETION(mpu->completion); | ||
124 | mutex_unlock(&mpu->mutex); | ||
125 | |||
126 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
127 | wait_for_completion(&mpu->completion); | ||
128 | del_timer_sync(&mpu->timeout); | ||
129 | dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
130 | return NOTIFY_OK; | ||
131 | } | ||
132 | |||
133 | static int mpu_dev_open(struct inode *inode, struct file *file) | ||
134 | { | ||
135 | struct mpu_private_data *mpu = | ||
136 | container_of(file->private_data, struct mpu_private_data, dev); | ||
137 | struct i2c_client *client = mpu->client; | ||
138 | int result; | ||
139 | int ii; | ||
140 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
141 | dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid); | ||
142 | |||
143 | result = mutex_lock_interruptible(&mpu->mutex); | ||
144 | if (mpu->pid) { | ||
145 | mutex_unlock(&mpu->mutex); | ||
146 | return -EBUSY; | ||
147 | } | ||
148 | mpu->pid = current->pid; | ||
149 | |||
150 | /* Reset the sensors to the default */ | ||
151 | if (result) { | ||
152 | dev_err(&client->adapter->dev, | ||
153 | "%s: mutex_lock_interruptible returned %d\n", | ||
154 | __func__, result); | ||
155 | return result; | ||
156 | } | ||
157 | |||
158 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
159 | __module_get(mpu->slave_modules[ii]); | ||
160 | |||
161 | mutex_unlock(&mpu->mutex); | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
166 | static int mpu_release(struct inode *inode, struct file *file) | ||
167 | { | ||
168 | struct mpu_private_data *mpu = | ||
169 | container_of(file->private_data, struct mpu_private_data, dev); | ||
170 | struct i2c_client *client = mpu->client; | ||
171 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
172 | int result = 0; | ||
173 | int ii; | ||
174 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
175 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
176 | |||
177 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
178 | if (!pdata_slave[ii]) | ||
179 | slave_adapter[ii] = NULL; | ||
180 | else | ||
181 | slave_adapter[ii] = | ||
182 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
183 | } | ||
184 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
185 | |||
186 | mutex_lock(&mpu->mutex); | ||
187 | mldl_cfg->inv_mpu_cfg->requested_sensors = 0; | ||
188 | result = inv_mpu_suspend(mldl_cfg, | ||
189 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
190 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
191 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
192 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
193 | INV_ALL_SENSORS); | ||
194 | mpu->pid = 0; | ||
195 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
196 | module_put(mpu->slave_modules[ii]); | ||
197 | |||
198 | mutex_unlock(&mpu->mutex); | ||
199 | complete(&mpu->completion); | ||
200 | dev_dbg(&client->adapter->dev, "mpu_release\n"); | ||
201 | |||
202 | return result; | ||
203 | } | ||
204 | |||
205 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
206 | static ssize_t mpu_read(struct file *file, | ||
207 | char __user *buf, size_t count, loff_t *offset) | ||
208 | { | ||
209 | struct mpu_private_data *mpu = | ||
210 | container_of(file->private_data, struct mpu_private_data, dev); | ||
211 | struct i2c_client *client = mpu->client; | ||
212 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
213 | int err; | ||
214 | |||
215 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
216 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
217 | |||
218 | if (!mpu->event || !buf | ||
219 | || count < sizeof(mpu->mpu_pm_event)) | ||
220 | return 0; | ||
221 | |||
222 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
223 | if (err) { | ||
224 | dev_err(&client->adapter->dev, | ||
225 | "Copy to user returned %d\n", err); | ||
226 | return -EFAULT; | ||
227 | } | ||
228 | mpu->event = 0; | ||
229 | return len; | ||
230 | } | ||
231 | |||
232 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
233 | { | ||
234 | struct mpu_private_data *mpu = | ||
235 | container_of(file->private_data, struct mpu_private_data, dev); | ||
236 | int mask = 0; | ||
237 | |||
238 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
239 | if (mpu->event) | ||
240 | mask |= POLLIN | POLLRDNORM; | ||
241 | return mask; | ||
242 | } | ||
243 | |||
244 | static int mpu_dev_ioctl_get_ext_slave_platform_data( | ||
245 | struct i2c_client *client, | ||
246 | struct ext_slave_platform_data __user *arg) | ||
247 | { | ||
248 | struct mpu_private_data *mpu = | ||
249 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
250 | struct ext_slave_platform_data *pdata_slave; | ||
251 | struct ext_slave_platform_data local_pdata_slave; | ||
252 | |||
253 | if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave))) | ||
254 | return -EFAULT; | ||
255 | |||
256 | if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
257 | return -EINVAL; | ||
258 | |||
259 | pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type]; | ||
260 | /* All but private data and irq_data */ | ||
261 | if (!pdata_slave) | ||
262 | return -ENODEV; | ||
263 | if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave))) | ||
264 | return -EFAULT; | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int mpu_dev_ioctl_get_mpu_platform_data( | ||
269 | struct i2c_client *client, | ||
270 | struct mpu_platform_data __user *arg) | ||
271 | { | ||
272 | struct mpu_private_data *mpu = | ||
273 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
274 | struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
275 | |||
276 | if (copy_to_user(arg, pdata, sizeof(*pdata))) | ||
277 | return -EFAULT; | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int mpu_dev_ioctl_get_ext_slave_descr( | ||
282 | struct i2c_client *client, | ||
283 | struct ext_slave_descr __user *arg) | ||
284 | { | ||
285 | struct mpu_private_data *mpu = | ||
286 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
287 | struct ext_slave_descr *slave; | ||
288 | struct ext_slave_descr local_slave; | ||
289 | |||
290 | if (copy_from_user(&local_slave, arg, sizeof(local_slave))) | ||
291 | return -EFAULT; | ||
292 | |||
293 | if (local_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
294 | return -EINVAL; | ||
295 | |||
296 | slave = mpu->mldl_cfg.slave[local_slave.type]; | ||
297 | /* All but private data and irq_data */ | ||
298 | if (!slave) | ||
299 | return -ENODEV; | ||
300 | if (copy_to_user(arg, slave, sizeof(*slave))) | ||
301 | return -EFAULT; | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | |||
306 | /** | ||
307 | * slave_config() - Pass a requested slave configuration to the slave sensor | ||
308 | * | ||
309 | * @adapter the adaptor to use to communicate with the slave | ||
310 | * @mldl_cfg the mldl configuration structuer | ||
311 | * @slave pointer to the slave descriptor | ||
312 | * @usr_config The configuration to pass to the slave sensor | ||
313 | * | ||
314 | * returns 0 or non-zero error code | ||
315 | */ | ||
316 | static int inv_mpu_config(struct mldl_cfg *mldl_cfg, | ||
317 | void *gyro_adapter, | ||
318 | struct ext_slave_config __user *usr_config) | ||
319 | { | ||
320 | int retval = 0; | ||
321 | struct ext_slave_config config; | ||
322 | |||
323 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
324 | if (retval) | ||
325 | return -EFAULT; | ||
326 | |||
327 | if (config.len && config.data) { | ||
328 | void *data; | ||
329 | data = kmalloc(config.len, GFP_KERNEL); | ||
330 | if (!data) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | retval = copy_from_user(data, | ||
334 | (void __user *)config.data, config.len); | ||
335 | if (retval) { | ||
336 | retval = -EFAULT; | ||
337 | kfree(data); | ||
338 | return retval; | ||
339 | } | ||
340 | config.data = data; | ||
341 | } | ||
342 | retval = gyro_config(gyro_adapter, mldl_cfg, &config); | ||
343 | kfree(config.data); | ||
344 | return retval; | ||
345 | } | ||
346 | |||
347 | static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg, | ||
348 | void *gyro_adapter, | ||
349 | struct ext_slave_config __user *usr_config) | ||
350 | { | ||
351 | int retval = 0; | ||
352 | struct ext_slave_config config; | ||
353 | void *user_data; | ||
354 | |||
355 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
356 | if (retval) | ||
357 | return -EFAULT; | ||
358 | |||
359 | user_data = config.data; | ||
360 | if (config.len && config.data) { | ||
361 | void *data; | ||
362 | data = kmalloc(config.len, GFP_KERNEL); | ||
363 | if (!data) | ||
364 | return -ENOMEM; | ||
365 | |||
366 | retval = copy_from_user(data, | ||
367 | (void __user *)config.data, config.len); | ||
368 | if (retval) { | ||
369 | retval = -EFAULT; | ||
370 | kfree(data); | ||
371 | return retval; | ||
372 | } | ||
373 | config.data = data; | ||
374 | } | ||
375 | retval = gyro_get_config(gyro_adapter, mldl_cfg, &config); | ||
376 | if (!retval) | ||
377 | retval = copy_to_user((unsigned char __user *)user_data, | ||
378 | config.data, config.len); | ||
379 | kfree(config.data); | ||
380 | return retval; | ||
381 | } | ||
382 | |||
383 | static int slave_config(struct mldl_cfg *mldl_cfg, | ||
384 | void *gyro_adapter, | ||
385 | void *slave_adapter, | ||
386 | struct ext_slave_descr *slave, | ||
387 | struct ext_slave_platform_data *pdata, | ||
388 | struct ext_slave_config __user *usr_config) | ||
389 | { | ||
390 | int retval = 0; | ||
391 | struct ext_slave_config config; | ||
392 | if ((!slave) || (!slave->config)) | ||
393 | return -ENODEV; | ||
394 | |||
395 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
396 | if (retval) | ||
397 | return -EFAULT; | ||
398 | |||
399 | if (config.len && config.data) { | ||
400 | void *data; | ||
401 | data = kmalloc(config.len, GFP_KERNEL); | ||
402 | if (!data) | ||
403 | return -ENOMEM; | ||
404 | |||
405 | retval = copy_from_user(data, | ||
406 | (void __user *)config.data, config.len); | ||
407 | if (retval) { | ||
408 | retval = -EFAULT; | ||
409 | kfree(data); | ||
410 | return retval; | ||
411 | } | ||
412 | config.data = data; | ||
413 | } | ||
414 | retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter, | ||
415 | &config, slave, pdata); | ||
416 | kfree(config.data); | ||
417 | return retval; | ||
418 | } | ||
419 | |||
420 | static int slave_get_config(struct mldl_cfg *mldl_cfg, | ||
421 | void *gyro_adapter, | ||
422 | void *slave_adapter, | ||
423 | struct ext_slave_descr *slave, | ||
424 | struct ext_slave_platform_data *pdata, | ||
425 | struct ext_slave_config __user *usr_config) | ||
426 | { | ||
427 | int retval = 0; | ||
428 | struct ext_slave_config config; | ||
429 | void *user_data; | ||
430 | if (!(slave) || !(slave->get_config)) | ||
431 | return -ENODEV; | ||
432 | |||
433 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
434 | if (retval) | ||
435 | return -EFAULT; | ||
436 | |||
437 | user_data = config.data; | ||
438 | if (config.len && config.data) { | ||
439 | void *data; | ||
440 | data = kmalloc(config.len, GFP_KERNEL); | ||
441 | if (!data) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | retval = copy_from_user(data, | ||
445 | (void __user *)config.data, config.len); | ||
446 | if (retval) { | ||
447 | retval = -EFAULT; | ||
448 | kfree(data); | ||
449 | return retval; | ||
450 | } | ||
451 | config.data = data; | ||
452 | } | ||
453 | retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter, | ||
454 | slave_adapter, &config, slave, pdata); | ||
455 | if (retval) { | ||
456 | kfree(config.data); | ||
457 | return retval; | ||
458 | } | ||
459 | retval = copy_to_user((unsigned char __user *)user_data, | ||
460 | config.data, config.len); | ||
461 | kfree(config.data); | ||
462 | return retval; | ||
463 | } | ||
464 | |||
465 | static int inv_slave_read(struct mldl_cfg *mldl_cfg, | ||
466 | void *gyro_adapter, | ||
467 | void *slave_adapter, | ||
468 | struct ext_slave_descr *slave, | ||
469 | struct ext_slave_platform_data *pdata, | ||
470 | void __user *usr_data) | ||
471 | { | ||
472 | int retval; | ||
473 | unsigned char *data; | ||
474 | data = kzalloc(slave->read_len, GFP_KERNEL); | ||
475 | if (!data) | ||
476 | return -EFAULT; | ||
477 | |||
478 | retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter, | ||
479 | slave, pdata, data); | ||
480 | |||
481 | if ((!retval) && | ||
482 | (copy_to_user((unsigned char __user *)usr_data, | ||
483 | data, slave->read_len))) | ||
484 | retval = -EFAULT; | ||
485 | |||
486 | kfree(data); | ||
487 | return retval; | ||
488 | } | ||
489 | |||
490 | static int mpu_handle_mlsl(void *sl_handle, | ||
491 | unsigned char addr, | ||
492 | unsigned int cmd, | ||
493 | struct mpu_read_write __user *usr_msg) | ||
494 | { | ||
495 | int retval = 0; | ||
496 | struct mpu_read_write msg; | ||
497 | unsigned char *user_data; | ||
498 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
499 | if (retval) | ||
500 | return -EFAULT; | ||
501 | |||
502 | user_data = msg.data; | ||
503 | if (msg.length && msg.data) { | ||
504 | unsigned char *data; | ||
505 | data = kmalloc(msg.length, GFP_KERNEL); | ||
506 | if (!data) | ||
507 | return -ENOMEM; | ||
508 | |||
509 | retval = copy_from_user(data, | ||
510 | (void __user *)msg.data, msg.length); | ||
511 | if (retval) { | ||
512 | retval = -EFAULT; | ||
513 | kfree(data); | ||
514 | return retval; | ||
515 | } | ||
516 | msg.data = data; | ||
517 | } else { | ||
518 | return -EPERM; | ||
519 | } | ||
520 | |||
521 | switch (cmd) { | ||
522 | case MPU_READ: | ||
523 | retval = inv_serial_read(sl_handle, addr, | ||
524 | msg.address, msg.length, msg.data); | ||
525 | break; | ||
526 | case MPU_WRITE: | ||
527 | retval = inv_serial_write(sl_handle, addr, | ||
528 | msg.length, msg.data); | ||
529 | break; | ||
530 | case MPU_READ_MEM: | ||
531 | retval = inv_serial_read_mem(sl_handle, addr, | ||
532 | msg.address, msg.length, msg.data); | ||
533 | break; | ||
534 | case MPU_WRITE_MEM: | ||
535 | retval = inv_serial_write_mem(sl_handle, addr, | ||
536 | msg.address, msg.length, | ||
537 | msg.data); | ||
538 | break; | ||
539 | case MPU_READ_FIFO: | ||
540 | retval = inv_serial_read_fifo(sl_handle, addr, | ||
541 | msg.length, msg.data); | ||
542 | break; | ||
543 | case MPU_WRITE_FIFO: | ||
544 | retval = inv_serial_write_fifo(sl_handle, addr, | ||
545 | msg.length, msg.data); | ||
546 | break; | ||
547 | |||
548 | }; | ||
549 | if (retval) { | ||
550 | dev_err(&((struct i2c_adapter *)sl_handle)->dev, | ||
551 | "%s: i2c %d error %d\n", | ||
552 | __func__, cmd, retval); | ||
553 | kfree(msg.data); | ||
554 | return retval; | ||
555 | } | ||
556 | retval = copy_to_user((unsigned char __user *)user_data, | ||
557 | msg.data, msg.length); | ||
558 | kfree(msg.data); | ||
559 | return retval; | ||
560 | } | ||
561 | |||
562 | /* ioctl - I/O control */ | ||
563 | static long mpu_dev_ioctl(struct file *file, | ||
564 | unsigned int cmd, unsigned long arg) | ||
565 | { | ||
566 | struct mpu_private_data *mpu = | ||
567 | container_of(file->private_data, struct mpu_private_data, dev); | ||
568 | struct i2c_client *client = mpu->client; | ||
569 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
570 | int retval = 0; | ||
571 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
572 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
573 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
574 | int ii; | ||
575 | |||
576 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
577 | if (!pdata_slave[ii]) | ||
578 | slave_adapter[ii] = NULL; | ||
579 | else | ||
580 | slave_adapter[ii] = | ||
581 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
582 | } | ||
583 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
584 | |||
585 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
586 | if (retval) { | ||
587 | dev_err(&client->adapter->dev, | ||
588 | "%s: mutex_lock_interruptible returned %d\n", | ||
589 | __func__, retval); | ||
590 | return retval; | ||
591 | } | ||
592 | |||
593 | switch (cmd) { | ||
594 | case MPU_GET_EXT_SLAVE_PLATFORM_DATA: | ||
595 | retval = mpu_dev_ioctl_get_ext_slave_platform_data( | ||
596 | client, | ||
597 | (struct ext_slave_platform_data __user *)arg); | ||
598 | break; | ||
599 | case MPU_GET_MPU_PLATFORM_DATA: | ||
600 | retval = mpu_dev_ioctl_get_mpu_platform_data( | ||
601 | client, | ||
602 | (struct mpu_platform_data __user *)arg); | ||
603 | break; | ||
604 | case MPU_GET_EXT_SLAVE_DESCR: | ||
605 | retval = mpu_dev_ioctl_get_ext_slave_descr( | ||
606 | client, | ||
607 | (struct ext_slave_descr __user *)arg); | ||
608 | break; | ||
609 | case MPU_READ: | ||
610 | case MPU_WRITE: | ||
611 | case MPU_READ_MEM: | ||
612 | case MPU_WRITE_MEM: | ||
613 | case MPU_READ_FIFO: | ||
614 | case MPU_WRITE_FIFO: | ||
615 | retval = mpu_handle_mlsl( | ||
616 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
617 | mldl_cfg->mpu_chip_info->addr, cmd, | ||
618 | (struct mpu_read_write __user *)arg); | ||
619 | break; | ||
620 | case MPU_CONFIG_GYRO: | ||
621 | retval = inv_mpu_config( | ||
622 | mldl_cfg, | ||
623 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
624 | (struct ext_slave_config __user *)arg); | ||
625 | break; | ||
626 | case MPU_CONFIG_ACCEL: | ||
627 | retval = slave_config( | ||
628 | mldl_cfg, | ||
629 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
630 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
631 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
632 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
633 | (struct ext_slave_config __user *)arg); | ||
634 | break; | ||
635 | case MPU_CONFIG_COMPASS: | ||
636 | retval = slave_config( | ||
637 | mldl_cfg, | ||
638 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
639 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
640 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
641 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
642 | (struct ext_slave_config __user *)arg); | ||
643 | break; | ||
644 | case MPU_CONFIG_PRESSURE: | ||
645 | retval = slave_config( | ||
646 | mldl_cfg, | ||
647 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
648 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
649 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
650 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
651 | (struct ext_slave_config __user *)arg); | ||
652 | break; | ||
653 | case MPU_GET_CONFIG_GYRO: | ||
654 | retval = inv_mpu_get_config( | ||
655 | mldl_cfg, | ||
656 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
657 | (struct ext_slave_config __user *)arg); | ||
658 | break; | ||
659 | case MPU_GET_CONFIG_ACCEL: | ||
660 | retval = slave_get_config( | ||
661 | mldl_cfg, | ||
662 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
663 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
664 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
665 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
666 | (struct ext_slave_config __user *)arg); | ||
667 | break; | ||
668 | case MPU_GET_CONFIG_COMPASS: | ||
669 | retval = slave_get_config( | ||
670 | mldl_cfg, | ||
671 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
672 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
673 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
674 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
675 | (struct ext_slave_config __user *)arg); | ||
676 | break; | ||
677 | case MPU_GET_CONFIG_PRESSURE: | ||
678 | retval = slave_get_config( | ||
679 | mldl_cfg, | ||
680 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
681 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
682 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
683 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
684 | (struct ext_slave_config __user *)arg); | ||
685 | break; | ||
686 | case MPU_SUSPEND: | ||
687 | retval = inv_mpu_suspend( | ||
688 | mldl_cfg, | ||
689 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
690 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
691 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
692 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
693 | arg); | ||
694 | break; | ||
695 | case MPU_RESUME: | ||
696 | retval = inv_mpu_resume( | ||
697 | mldl_cfg, | ||
698 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
699 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
700 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
701 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
702 | arg); | ||
703 | break; | ||
704 | case MPU_PM_EVENT_HANDLED: | ||
705 | dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd); | ||
706 | complete(&mpu->completion); | ||
707 | break; | ||
708 | case MPU_READ_ACCEL: | ||
709 | retval = inv_slave_read( | ||
710 | mldl_cfg, | ||
711 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
712 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
713 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
714 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
715 | (unsigned char __user *)arg); | ||
716 | break; | ||
717 | case MPU_READ_COMPASS: | ||
718 | retval = inv_slave_read( | ||
719 | mldl_cfg, | ||
720 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
721 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
722 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
723 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
724 | (unsigned char __user *)arg); | ||
725 | break; | ||
726 | case MPU_READ_PRESSURE: | ||
727 | retval = inv_slave_read( | ||
728 | mldl_cfg, | ||
729 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
730 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
731 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
732 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
733 | (unsigned char __user *)arg); | ||
734 | break; | ||
735 | case MPU_GET_REQUESTED_SENSORS: | ||
736 | if (copy_to_user( | ||
737 | (__u32 __user *)arg, | ||
738 | &mldl_cfg->inv_mpu_cfg->requested_sensors, | ||
739 | sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors))) | ||
740 | retval = -EFAULT; | ||
741 | break; | ||
742 | case MPU_SET_REQUESTED_SENSORS: | ||
743 | mldl_cfg->inv_mpu_cfg->requested_sensors = arg; | ||
744 | break; | ||
745 | case MPU_GET_IGNORE_SYSTEM_SUSPEND: | ||
746 | if (copy_to_user( | ||
747 | (unsigned char __user *)arg, | ||
748 | &mldl_cfg->inv_mpu_cfg->ignore_system_suspend, | ||
749 | sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend))) | ||
750 | retval = -EFAULT; | ||
751 | break; | ||
752 | case MPU_SET_IGNORE_SYSTEM_SUSPEND: | ||
753 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg; | ||
754 | break; | ||
755 | case MPU_GET_MLDL_STATUS: | ||
756 | if (copy_to_user( | ||
757 | (unsigned char __user *)arg, | ||
758 | &mldl_cfg->inv_mpu_state->status, | ||
759 | sizeof(mldl_cfg->inv_mpu_state->status))) | ||
760 | retval = -EFAULT; | ||
761 | break; | ||
762 | case MPU_GET_I2C_SLAVES_ENABLED: | ||
763 | if (copy_to_user( | ||
764 | (unsigned char __user *)arg, | ||
765 | &mldl_cfg->inv_mpu_state->i2c_slaves_enabled, | ||
766 | sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled))) | ||
767 | retval = -EFAULT; | ||
768 | break; | ||
769 | default: | ||
770 | dev_err(&client->adapter->dev, | ||
771 | "%s: Unknown cmd %x, arg %lu\n", | ||
772 | __func__, cmd, arg); | ||
773 | retval = -EINVAL; | ||
774 | }; | ||
775 | |||
776 | mutex_unlock(&mpu->mutex); | ||
777 | dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n", | ||
778 | __func__, cmd, arg, retval); | ||
779 | |||
780 | if (retval > 0) | ||
781 | retval = -retval; | ||
782 | |||
783 | return retval; | ||
784 | } | ||
785 | |||
786 | void mpu_shutdown(struct i2c_client *client) | ||
787 | { | ||
788 | struct mpu_private_data *mpu = | ||
789 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
790 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
791 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
792 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
793 | int ii; | ||
794 | |||
795 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
796 | if (!pdata_slave[ii]) | ||
797 | slave_adapter[ii] = NULL; | ||
798 | else | ||
799 | slave_adapter[ii] = | ||
800 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
801 | } | ||
802 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
803 | |||
804 | mutex_lock(&mpu->mutex); | ||
805 | (void)inv_mpu_suspend(mldl_cfg, | ||
806 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
807 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
808 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
809 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
810 | INV_ALL_SENSORS); | ||
811 | mutex_unlock(&mpu->mutex); | ||
812 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
813 | } | ||
814 | |||
815 | int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg) | ||
816 | { | ||
817 | struct mpu_private_data *mpu = | ||
818 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
819 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
820 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
821 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
822 | int ii; | ||
823 | |||
824 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
825 | if (!pdata_slave[ii]) | ||
826 | slave_adapter[ii] = NULL; | ||
827 | else | ||
828 | slave_adapter[ii] = | ||
829 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
830 | } | ||
831 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
832 | |||
833 | mutex_lock(&mpu->mutex); | ||
834 | if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
835 | dev_dbg(&client->adapter->dev, | ||
836 | "%s: suspending on event %d\n", __func__, mesg.event); | ||
837 | (void)inv_mpu_suspend(mldl_cfg, | ||
838 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
839 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
840 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
841 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
842 | INV_ALL_SENSORS); | ||
843 | } else { | ||
844 | dev_dbg(&client->adapter->dev, | ||
845 | "%s: Already suspended %d\n", __func__, mesg.event); | ||
846 | } | ||
847 | mutex_unlock(&mpu->mutex); | ||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | int mpu_dev_resume(struct i2c_client *client) | ||
852 | { | ||
853 | struct mpu_private_data *mpu = | ||
854 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
855 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
856 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
857 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
858 | int ii; | ||
859 | |||
860 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
861 | if (!pdata_slave[ii]) | ||
862 | slave_adapter[ii] = NULL; | ||
863 | else | ||
864 | slave_adapter[ii] = | ||
865 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
866 | } | ||
867 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
868 | |||
869 | mutex_lock(&mpu->mutex); | ||
870 | if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
871 | (void)inv_mpu_resume(mldl_cfg, | ||
872 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
873 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
874 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
875 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
876 | mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
877 | dev_dbg(&client->adapter->dev, | ||
878 | "%s for pid %d\n", __func__, mpu->pid); | ||
879 | } | ||
880 | mutex_unlock(&mpu->mutex); | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | /* define which file operations are supported */ | ||
885 | static const struct file_operations mpu_fops = { | ||
886 | .owner = THIS_MODULE, | ||
887 | .read = mpu_read, | ||
888 | .poll = mpu_poll, | ||
889 | .unlocked_ioctl = mpu_dev_ioctl, | ||
890 | .open = mpu_dev_open, | ||
891 | .release = mpu_release, | ||
892 | }; | ||
893 | |||
894 | int inv_mpu_register_slave(struct module *slave_module, | ||
895 | struct i2c_client *slave_client, | ||
896 | struct ext_slave_platform_data *slave_pdata, | ||
897 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
898 | { | ||
899 | struct mpu_private_data *mpu = mpu_private_data; | ||
900 | struct mldl_cfg *mldl_cfg; | ||
901 | struct ext_slave_descr *slave_descr; | ||
902 | struct ext_slave_platform_data **pdata_slave; | ||
903 | char *irq_name = NULL; | ||
904 | int result = 0; | ||
905 | |||
906 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
907 | return -EINVAL; | ||
908 | |||
909 | if (!mpu) { | ||
910 | dev_err(&slave_client->adapter->dev, | ||
911 | "%s: Null mpu_private_data\n", __func__); | ||
912 | return -EINVAL; | ||
913 | } | ||
914 | mldl_cfg = &mpu->mldl_cfg; | ||
915 | pdata_slave = mldl_cfg->pdata_slave; | ||
916 | slave_descr = get_slave_descr(); | ||
917 | |||
918 | if (!slave_descr) { | ||
919 | dev_err(&slave_client->adapter->dev, | ||
920 | "%s: Null ext_slave_descr\n", __func__); | ||
921 | return -EINVAL; | ||
922 | } | ||
923 | |||
924 | mutex_lock(&mpu->mutex); | ||
925 | if (mpu->pid) { | ||
926 | mutex_unlock(&mpu->mutex); | ||
927 | return -EBUSY; | ||
928 | } | ||
929 | |||
930 | if (pdata_slave[slave_descr->type]) { | ||
931 | result = -EBUSY; | ||
932 | goto out_unlock_mutex; | ||
933 | } | ||
934 | |||
935 | slave_pdata->address = slave_client->addr; | ||
936 | slave_pdata->irq = slave_client->irq; | ||
937 | slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter); | ||
938 | |||
939 | dev_info(&slave_client->adapter->dev, | ||
940 | "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n", | ||
941 | __func__, | ||
942 | slave_descr->name, | ||
943 | slave_descr->type, | ||
944 | slave_pdata->address, | ||
945 | slave_pdata->irq, | ||
946 | slave_pdata->adapt_num); | ||
947 | |||
948 | switch (slave_descr->type) { | ||
949 | case EXT_SLAVE_TYPE_ACCEL: | ||
950 | irq_name = "accelirq"; | ||
951 | break; | ||
952 | case EXT_SLAVE_TYPE_COMPASS: | ||
953 | irq_name = "compassirq"; | ||
954 | break; | ||
955 | case EXT_SLAVE_TYPE_PRESSURE: | ||
956 | irq_name = "pressureirq"; | ||
957 | break; | ||
958 | default: | ||
959 | irq_name = "none"; | ||
960 | }; | ||
961 | if (slave_descr->init) { | ||
962 | result = slave_descr->init(slave_client->adapter, | ||
963 | slave_descr, | ||
964 | slave_pdata); | ||
965 | if (result) { | ||
966 | dev_err(&slave_client->adapter->dev, | ||
967 | "%s init failed %d\n", | ||
968 | slave_descr->name, result); | ||
969 | goto out_unlock_mutex; | ||
970 | } | ||
971 | } | ||
972 | |||
973 | pdata_slave[slave_descr->type] = slave_pdata; | ||
974 | mpu->slave_modules[slave_descr->type] = slave_module; | ||
975 | mldl_cfg->slave[slave_descr->type] = slave_descr; | ||
976 | |||
977 | goto out_unlock_mutex; | ||
978 | |||
979 | out_unlock_mutex: | ||
980 | mutex_unlock(&mpu->mutex); | ||
981 | |||
982 | if (!result && irq_name && (slave_pdata->irq > 0)) { | ||
983 | int warn_result; | ||
984 | dev_info(&slave_client->adapter->dev, | ||
985 | "Installing %s irq using %d\n", | ||
986 | irq_name, | ||
987 | slave_pdata->irq); | ||
988 | warn_result = slaveirq_init(slave_client->adapter, | ||
989 | slave_pdata, irq_name); | ||
990 | if (result) | ||
991 | dev_WARN(&slave_client->adapter->dev, | ||
992 | "%s irq assigned error: %d\n", | ||
993 | slave_descr->name, warn_result); | ||
994 | } else { | ||
995 | dev_info(&slave_client->adapter->dev, | ||
996 | "%s irq not assigned: %d %d %d\n", | ||
997 | slave_descr->name, | ||
998 | result, (int)irq_name, slave_pdata->irq); | ||
999 | } | ||
1000 | |||
1001 | return result; | ||
1002 | } | ||
1003 | EXPORT_SYMBOL(inv_mpu_register_slave); | ||
1004 | |||
1005 | void inv_mpu_unregister_slave(struct i2c_client *slave_client, | ||
1006 | struct ext_slave_platform_data *slave_pdata, | ||
1007 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
1008 | { | ||
1009 | struct mpu_private_data *mpu = mpu_private_data; | ||
1010 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1011 | struct ext_slave_descr *slave_descr; | ||
1012 | int result; | ||
1013 | |||
1014 | dev_info(&slave_client->adapter->dev, "%s\n", __func__); | ||
1015 | |||
1016 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
1017 | return; | ||
1018 | |||
1019 | if (slave_pdata->irq) | ||
1020 | slaveirq_exit(slave_pdata); | ||
1021 | |||
1022 | slave_descr = get_slave_descr(); | ||
1023 | if (!slave_descr) | ||
1024 | return; | ||
1025 | |||
1026 | mutex_lock(&mpu->mutex); | ||
1027 | |||
1028 | if (slave_descr->exit) { | ||
1029 | result = slave_descr->exit(slave_client->adapter, | ||
1030 | slave_descr, | ||
1031 | slave_pdata); | ||
1032 | if (result) | ||
1033 | dev_err(&slave_client->adapter->dev, | ||
1034 | "Accel exit failed %d\n", result); | ||
1035 | } | ||
1036 | mldl_cfg->slave[slave_descr->type] = NULL; | ||
1037 | mldl_cfg->pdata_slave[slave_descr->type] = NULL; | ||
1038 | mpu->slave_modules[slave_descr->type] = NULL; | ||
1039 | |||
1040 | mutex_unlock(&mpu->mutex); | ||
1041 | |||
1042 | } | ||
1043 | EXPORT_SYMBOL(inv_mpu_unregister_slave); | ||
1044 | |||
1045 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
1046 | |||
1047 | static const struct i2c_device_id mpu_id[] = { | ||
1048 | {"mpu3050", 0}, | ||
1049 | {"mpu6050", 0}, | ||
1050 | {"mpu6050_no_accel", 0}, | ||
1051 | {} | ||
1052 | }; | ||
1053 | MODULE_DEVICE_TABLE(i2c, mpu_id); | ||
1054 | |||
1055 | int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid) | ||
1056 | { | ||
1057 | struct mpu_platform_data *pdata; | ||
1058 | struct mpu_private_data *mpu; | ||
1059 | struct mldl_cfg *mldl_cfg; | ||
1060 | int res = 0; | ||
1061 | int ii = 0; | ||
1062 | unsigned long irq_flags; | ||
1063 | |||
1064 | dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++); | ||
1065 | |||
1066 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
1067 | res = -ENODEV; | ||
1068 | goto out_check_functionality_failed; | ||
1069 | } | ||
1070 | |||
1071 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
1072 | if (!mpu) { | ||
1073 | res = -ENOMEM; | ||
1074 | goto out_alloc_data_failed; | ||
1075 | } | ||
1076 | mldl_cfg = &mpu->mldl_cfg; | ||
1077 | mldl_cfg->mpu_ram = &mpu->mpu_ram; | ||
1078 | mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg; | ||
1079 | mldl_cfg->mpu_offsets = &mpu->mpu_offsets; | ||
1080 | mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info; | ||
1081 | mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg; | ||
1082 | mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state; | ||
1083 | |||
1084 | mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE; | ||
1085 | mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL); | ||
1086 | if (!mldl_cfg->mpu_ram->ram) { | ||
1087 | res = -ENOMEM; | ||
1088 | goto out_alloc_ram_failed; | ||
1089 | } | ||
1090 | mpu_private_data = mpu; | ||
1091 | i2c_set_clientdata(client, mpu); | ||
1092 | mpu->client = client; | ||
1093 | |||
1094 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
1095 | mutex_init(&mpu->mutex); | ||
1096 | init_completion(&mpu->completion); | ||
1097 | |||
1098 | mpu->response_timeout = 60; /* Seconds */ | ||
1099 | mpu->timeout.function = mpu_pm_timeout; | ||
1100 | mpu->timeout.data = (u_long) mpu; | ||
1101 | init_timer(&mpu->timeout); | ||
1102 | |||
1103 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
1104 | mpu->nb.priority = 0; | ||
1105 | res = register_pm_notifier(&mpu->nb); | ||
1106 | if (res) { | ||
1107 | dev_err(&client->adapter->dev, | ||
1108 | "Unable to register pm_notifier %d\n", res); | ||
1109 | goto out_register_pm_notifier_failed; | ||
1110 | } | ||
1111 | |||
1112 | pdata = (struct mpu_platform_data *)client->dev.platform_data; | ||
1113 | if (!pdata) { | ||
1114 | dev_WARN(&client->adapter->dev, | ||
1115 | "Missing platform data for mpu\n"); | ||
1116 | } | ||
1117 | mldl_cfg->pdata = pdata; | ||
1118 | |||
1119 | mldl_cfg->mpu_chip_info->addr = client->addr; | ||
1120 | res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1121 | |||
1122 | if (res) { | ||
1123 | dev_err(&client->adapter->dev, | ||
1124 | "Unable to open %s %d\n", MPU_NAME, res); | ||
1125 | res = -ENODEV; | ||
1126 | goto out_whoami_failed; | ||
1127 | } | ||
1128 | |||
1129 | mpu->dev.minor = MISC_DYNAMIC_MINOR; | ||
1130 | mpu->dev.name = "mpu"; | ||
1131 | mpu->dev.fops = &mpu_fops; | ||
1132 | res = misc_register(&mpu->dev); | ||
1133 | if (res < 0) { | ||
1134 | dev_err(&client->adapter->dev, | ||
1135 | "ERROR: misc_register returned %d\n", res); | ||
1136 | goto out_misc_register_failed; | ||
1137 | } | ||
1138 | |||
1139 | if (client->irq) { | ||
1140 | dev_info(&client->adapter->dev, | ||
1141 | "Installing irq using %d\n", client->irq); | ||
1142 | if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
1143 | irq_flags = IRQF_TRIGGER_FALLING; | ||
1144 | else | ||
1145 | irq_flags = IRQF_TRIGGER_RISING; | ||
1146 | res = mpuirq_init(client, mldl_cfg, irq_flags); | ||
1147 | |||
1148 | if (res) | ||
1149 | goto out_mpuirq_failed; | ||
1150 | } else { | ||
1151 | dev_WARN(&client->adapter->dev, | ||
1152 | "Missing %s IRQ\n", MPU_NAME); | ||
1153 | } | ||
1154 | return res; | ||
1155 | |||
1156 | out_mpuirq_failed: | ||
1157 | misc_deregister(&mpu->dev); | ||
1158 | out_misc_register_failed: | ||
1159 | inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1160 | out_whoami_failed: | ||
1161 | unregister_pm_notifier(&mpu->nb); | ||
1162 | out_register_pm_notifier_failed: | ||
1163 | kfree(mldl_cfg->mpu_ram->ram); | ||
1164 | mpu_private_data = NULL; | ||
1165 | out_alloc_ram_failed: | ||
1166 | kfree(mpu); | ||
1167 | out_alloc_data_failed: | ||
1168 | out_check_functionality_failed: | ||
1169 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res); | ||
1170 | return res; | ||
1171 | |||
1172 | } | ||
1173 | |||
1174 | static int mpu_remove(struct i2c_client *client) | ||
1175 | { | ||
1176 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
1177 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
1178 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1179 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
1180 | int ii; | ||
1181 | |||
1182 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1183 | if (!pdata_slave[ii]) | ||
1184 | slave_adapter[ii] = NULL; | ||
1185 | else | ||
1186 | slave_adapter[ii] = | ||
1187 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
1188 | } | ||
1189 | |||
1190 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
1191 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
1192 | |||
1193 | inv_mpu_close(mldl_cfg, | ||
1194 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
1195 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
1196 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
1197 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE]); | ||
1198 | |||
1199 | |||
1200 | if (client->irq) | ||
1201 | mpuirq_exit(); | ||
1202 | |||
1203 | misc_deregister(&mpu->dev); | ||
1204 | |||
1205 | unregister_pm_notifier(&mpu->nb); | ||
1206 | |||
1207 | kfree(mpu->mldl_cfg.mpu_ram->ram); | ||
1208 | kfree(mpu); | ||
1209 | |||
1210 | return 0; | ||
1211 | } | ||
1212 | |||
1213 | static struct i2c_driver mpu_driver = { | ||
1214 | .class = I2C_CLASS_HWMON, | ||
1215 | .probe = mpu_probe, | ||
1216 | .remove = mpu_remove, | ||
1217 | .id_table = mpu_id, | ||
1218 | .driver = { | ||
1219 | .owner = THIS_MODULE, | ||
1220 | .name = MPU_NAME, | ||
1221 | }, | ||
1222 | .address_list = normal_i2c, | ||
1223 | .shutdown = mpu_shutdown, /* optional */ | ||
1224 | .suspend = mpu_dev_suspend, /* optional */ | ||
1225 | .resume = mpu_dev_resume, /* optional */ | ||
1226 | |||
1227 | }; | ||
1228 | |||
1229 | static int __init mpu_init(void) | ||
1230 | { | ||
1231 | int res = i2c_add_driver(&mpu_driver); | ||
1232 | pr_info("%s: Probe name %s\n", __func__, MPU_NAME); | ||
1233 | if (res) | ||
1234 | pr_err("%s failed\n", __func__); | ||
1235 | return res; | ||
1236 | } | ||
1237 | |||
1238 | static void __exit mpu_exit(void) | ||
1239 | { | ||
1240 | pr_info("%s\n", __func__); | ||
1241 | i2c_del_driver(&mpu_driver); | ||
1242 | } | ||
1243 | |||
1244 | module_init(mpu_init); | ||
1245 | module_exit(mpu_exit); | ||
1246 | |||
1247 | MODULE_AUTHOR("Invensense Corporation"); | ||
1248 | MODULE_DESCRIPTION("User space character device interface for MPU"); | ||
1249 | MODULE_LICENSE("GPL"); | ||
1250 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/Makefile b/drivers/misc/inv_mpu/mpu6050/Makefile new file mode 100644 index 00000000000..a93aa97a699 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | |||
2 | # Kernel makefile for motions sensors | ||
3 | # | ||
4 | # | ||
5 | |||
6 | obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050b1.o | ||
7 | |||
8 | ccflags-y := -DMPU_CURRENT_BUILD_MPU6050B1 | ||
9 | |||
10 | mpu6050b1-objs += mldl_cfg.o | ||
11 | mpu6050b1-objs += mldl_print_cfg.o | ||
12 | mpu6050b1-objs += mlsl-kernel.o | ||
13 | mpu6050b1-objs += mpu-dev.o | ||
14 | mpu6050b1-objs += ../accel/mpu6050.o | ||
15 | |||
16 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
17 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
18 | EXTRA_CFLAGS += -DINV_CACHE_DMP=1 | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c new file mode 100644 index 00000000000..22af0c20098 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c | |||
@@ -0,0 +1,1916 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup MLDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mldl_cfg.c | ||
25 | * @brief The Motion Library Driver Layer. | ||
26 | */ | ||
27 | |||
28 | /* -------------------------------------------------------------------------- */ | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | #include <stddef.h> | ||
33 | |||
34 | #include "mldl_cfg.h" | ||
35 | #include <linux/mpu.h> | ||
36 | #include "mpu6050b1.h" | ||
37 | |||
38 | #include "mlsl.h" | ||
39 | #include "mldl_print_cfg.h" | ||
40 | #include "log.h" | ||
41 | #undef MPL_LOG_TAG | ||
42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
43 | |||
44 | /* -------------------------------------------------------------------------- */ | ||
45 | |||
46 | #define SLEEP 0 | ||
47 | #define WAKE_UP 7 | ||
48 | #define RESET 1 | ||
49 | #define STANDBY 1 | ||
50 | |||
51 | /* -------------------------------------------------------------------------- */ | ||
52 | |||
53 | /** | ||
54 | * @brief Stop the DMP running | ||
55 | * | ||
56 | * @return INV_SUCCESS or non-zero error code | ||
57 | */ | ||
58 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
59 | { | ||
60 | unsigned char user_ctrl_reg; | ||
61 | int result; | ||
62 | |||
63 | if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) | ||
64 | return INV_SUCCESS; | ||
65 | |||
66 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
67 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
68 | if (result) { | ||
69 | LOG_RESULT_LOCATION(result); | ||
70 | return result; | ||
71 | } | ||
72 | user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
73 | user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
74 | |||
75 | result = inv_serial_single_write(gyro_handle, | ||
76 | mldl_cfg->mpu_chip_info->addr, | ||
77 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
78 | if (result) { | ||
79 | LOG_RESULT_LOCATION(result); | ||
80 | return result; | ||
81 | } | ||
82 | mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED; | ||
83 | |||
84 | return result; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * @brief Starts the DMP running | ||
89 | * | ||
90 | * @return INV_SUCCESS or non-zero error code | ||
91 | */ | ||
92 | static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
93 | { | ||
94 | unsigned char user_ctrl_reg; | ||
95 | int result; | ||
96 | |||
97 | if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
98 | mldl_cfg->mpu_gyro_cfg->dmp_enable) | ||
99 | || | ||
100 | ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) && | ||
101 | !mldl_cfg->mpu_gyro_cfg->dmp_enable)) | ||
102 | return INV_SUCCESS; | ||
103 | |||
104 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
105 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
106 | if (result) { | ||
107 | LOG_RESULT_LOCATION(result); | ||
108 | return result; | ||
109 | } | ||
110 | |||
111 | result = inv_serial_single_write( | ||
112 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
113 | MPUREG_USER_CTRL, | ||
114 | ((user_ctrl_reg & (~BIT_FIFO_EN)) | ||
115 | | BIT_FIFO_RST)); | ||
116 | if (result) { | ||
117 | LOG_RESULT_LOCATION(result); | ||
118 | return result; | ||
119 | } | ||
120 | |||
121 | result = inv_serial_single_write( | ||
122 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
123 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
124 | if (result) { | ||
125 | LOG_RESULT_LOCATION(result); | ||
126 | return result; | ||
127 | } | ||
128 | |||
129 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
130 | MPUREG_USER_CTRL, 1, &user_ctrl_reg); | ||
131 | if (result) { | ||
132 | LOG_RESULT_LOCATION(result); | ||
133 | return result; | ||
134 | } | ||
135 | |||
136 | user_ctrl_reg |= BIT_DMP_EN; | ||
137 | |||
138 | if (mldl_cfg->mpu_gyro_cfg->fifo_enable) | ||
139 | user_ctrl_reg |= BIT_FIFO_EN; | ||
140 | else | ||
141 | user_ctrl_reg &= ~BIT_FIFO_EN; | ||
142 | |||
143 | user_ctrl_reg |= BIT_DMP_RST; | ||
144 | |||
145 | result = inv_serial_single_write( | ||
146 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
147 | MPUREG_USER_CTRL, user_ctrl_reg); | ||
148 | if (result) { | ||
149 | LOG_RESULT_LOCATION(result); | ||
150 | return result; | ||
151 | } | ||
152 | mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED; | ||
153 | |||
154 | return result; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * @brief enables/disables the I2C bypass to an external device | ||
159 | * connected to MPU's secondary I2C bus. | ||
160 | * @param enable | ||
161 | * Non-zero to enable pass through. | ||
162 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
163 | */ | ||
164 | static int mpu6050b1_set_i2c_bypass(struct mldl_cfg *mldl_cfg, | ||
165 | void *mlsl_handle, unsigned char enable) | ||
166 | { | ||
167 | unsigned char reg; | ||
168 | int result; | ||
169 | unsigned char status = mldl_cfg->inv_mpu_state->status; | ||
170 | if ((status & MPU_GYRO_IS_BYPASSED && enable) || | ||
171 | (!(status & MPU_GYRO_IS_BYPASSED) && !enable)) | ||
172 | return INV_SUCCESS; | ||
173 | |||
174 | /*---- get current 'USER_CTRL' into b ----*/ | ||
175 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
176 | MPUREG_USER_CTRL, 1, ®); | ||
177 | if (result) { | ||
178 | LOG_RESULT_LOCATION(result); | ||
179 | return result; | ||
180 | } | ||
181 | |||
182 | if (!enable) { | ||
183 | /* setting int_config with the property flag BIT_BYPASS_EN | ||
184 | should be done by the setup functions */ | ||
185 | result = inv_serial_single_write( | ||
186 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
187 | MPUREG_INT_PIN_CFG, | ||
188 | (mldl_cfg->pdata->int_config & ~(BIT_BYPASS_EN))); | ||
189 | if (!(reg & BIT_I2C_MST_EN)) { | ||
190 | result = | ||
191 | inv_serial_single_write( | ||
192 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
193 | MPUREG_USER_CTRL, | ||
194 | (reg | BIT_I2C_MST_EN)); | ||
195 | if (result) { | ||
196 | LOG_RESULT_LOCATION(result); | ||
197 | return result; | ||
198 | } | ||
199 | } | ||
200 | } else if (enable) { | ||
201 | if (reg & BIT_AUX_IF_EN) { | ||
202 | result = | ||
203 | inv_serial_single_write( | ||
204 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
205 | MPUREG_USER_CTRL, | ||
206 | (reg & (~BIT_I2C_MST_EN))); | ||
207 | if (result) { | ||
208 | LOG_RESULT_LOCATION(result); | ||
209 | return result; | ||
210 | } | ||
211 | /***************************************** | ||
212 | * To avoid hanging the bus we must sleep until all | ||
213 | * slave transactions have been completed. | ||
214 | * 24 bytes max slave reads | ||
215 | * +1 byte possible extra write | ||
216 | * +4 max slave address | ||
217 | * --- | ||
218 | * 33 Maximum bytes | ||
219 | * x9 Approximate bits per byte | ||
220 | * --- | ||
221 | * 297 bits. | ||
222 | * 2.97 ms minimum @ 100kbps | ||
223 | * 0.75 ms minimum @ 400kbps. | ||
224 | *****************************************/ | ||
225 | msleep(3); | ||
226 | } | ||
227 | result = inv_serial_single_write( | ||
228 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
229 | MPUREG_INT_PIN_CFG, | ||
230 | (mldl_cfg->pdata->int_config | BIT_BYPASS_EN)); | ||
231 | if (result) { | ||
232 | LOG_RESULT_LOCATION(result); | ||
233 | return result; | ||
234 | } | ||
235 | } | ||
236 | if (enable) | ||
237 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED; | ||
238 | else | ||
239 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
240 | |||
241 | return result; | ||
242 | } | ||
243 | |||
244 | |||
245 | |||
246 | |||
247 | /** | ||
248 | * @brief enables/disables the I2C bypass to an external device | ||
249 | * connected to MPU's secondary I2C bus. | ||
250 | * @param enable | ||
251 | * Non-zero to enable pass through. | ||
252 | * @return INV_SUCCESS if successful, a non-zero error code otherwise. | ||
253 | */ | ||
254 | static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
255 | unsigned char enable) | ||
256 | { | ||
257 | return mpu6050b1_set_i2c_bypass(mldl_cfg, mlsl_handle, enable); | ||
258 | } | ||
259 | |||
260 | |||
261 | #define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) | ||
262 | |||
263 | /* NOTE : when not indicated, product revision | ||
264 | is considered an 'npp'; non production part */ | ||
265 | |||
266 | /* produces an unique identifier for each device based on the | ||
267 | combination of product version and product revision */ | ||
268 | struct prod_rev_map_t { | ||
269 | unsigned short mpl_product_key; | ||
270 | unsigned char silicon_rev; | ||
271 | unsigned short gyro_trim; | ||
272 | unsigned short accel_trim; | ||
273 | }; | ||
274 | |||
275 | /* NOTE: product entries are in chronological order */ | ||
276 | static struct prod_rev_map_t prod_rev_map[] = { | ||
277 | /* prod_ver = 0 */ | ||
278 | {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384}, | ||
279 | {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384}, | ||
280 | {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384}, | ||
281 | {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384}, | ||
282 | {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384}, | ||
283 | {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/C2-1) */ | ||
284 | /* prod_ver = 1, forced to 0 for MPU6050 A2 */ | ||
285 | {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384}, | ||
286 | {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384}, | ||
287 | {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384}, | ||
288 | {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384}, | ||
289 | {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D2-1) */ | ||
290 | {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384}, | ||
291 | {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384}, | ||
292 | {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384}, | ||
293 | {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384}, | ||
294 | {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D4) */ | ||
295 | /* prod_ver = 1 */ | ||
296 | {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-1) */ | ||
297 | {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-2) */ | ||
298 | {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-3) */ | ||
299 | {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-4) */ | ||
300 | {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-5) */ | ||
301 | {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D4) */ | ||
302 | {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-1) */ | ||
303 | {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-2) */ | ||
304 | {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-3) */ | ||
305 | {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-4) */ | ||
306 | {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-5) */ | ||
307 | {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-6) */ | ||
308 | /* prod_ver = 2 */ | ||
309 | {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-1) */ | ||
310 | {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-2) */ | ||
311 | {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-3) */ | ||
312 | {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-4) */ | ||
313 | {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-5) */ | ||
314 | {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-6) */ | ||
315 | {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/D4) */ | ||
316 | /* prod_ver = 3 */ | ||
317 | {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E2) */ | ||
318 | /* prod_ver = 4 */ | ||
319 | {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192}, /* (B2/F1) */ | ||
320 | {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192}, /* (B3/F1) */ | ||
321 | {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192}, /* (B4/F1) */ | ||
322 | /* prod_ver = 5 */ | ||
323 | {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
324 | /* prod_ver = 7 */ | ||
325 | {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
326 | /* prod_ver = 8 */ | ||
327 | {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
328 | /* prod_ver = 9 */ | ||
329 | {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */ | ||
330 | /* prod_ver = 10 */ | ||
331 | {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384} /* (B5/E2) */ | ||
332 | }; | ||
333 | |||
334 | /** | ||
335 | * @internal | ||
336 | * @brief Inverse lookup of the index of an MPL product key . | ||
337 | * @param key | ||
338 | * the MPL product indentifier also referred to as 'key'. | ||
339 | * @return the index position of the key in the array, -1 if not found. | ||
340 | */ | ||
341 | short index_of_key(unsigned short key) | ||
342 | { | ||
343 | int i; | ||
344 | for (i = 0; i < NUM_OF_PROD_REVS; i++) | ||
345 | if (prod_rev_map[i].mpl_product_key == key) | ||
346 | return (short)i; | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * @internal | ||
352 | * @brief Get the product revision and version for MPU6050 and | ||
353 | * extract all per-part specific information. | ||
354 | * The product version number is read from the PRODUCT_ID register in | ||
355 | * user space register map. | ||
356 | * The product revision number is in read from OTP bank 0, ADDR6[7:2]. | ||
357 | * These 2 numbers, combined, provide an unique key to be used to | ||
358 | * retrieve some per-device information such as the silicon revision | ||
359 | * and the gyro and accel sensitivity trim values. | ||
360 | * | ||
361 | * @param mldl_cfg | ||
362 | * a pointer to the mldl config data structure. | ||
363 | * @param mlsl_handle | ||
364 | * an file handle to the serial communication device the | ||
365 | * device is connected to. | ||
366 | * | ||
367 | * @return 0 on success, a non-zero error code otherwise. | ||
368 | */ | ||
369 | static int inv_get_silicon_rev_mpu6050( | ||
370 | struct mldl_cfg *mldl_cfg, void *mlsl_handle) | ||
371 | { | ||
372 | int result; | ||
373 | unsigned char prod_ver = 0x00, prod_rev = 0x00; | ||
374 | unsigned char bank = | ||
375 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
376 | unsigned short memAddr = ((bank << 8) | 0x06); | ||
377 | unsigned short key; | ||
378 | short index; | ||
379 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
380 | |||
381 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
382 | MPUREG_PRODUCT_ID, 1, &prod_ver); | ||
383 | if (result) { | ||
384 | LOG_RESULT_LOCATION(result); | ||
385 | return result; | ||
386 | } | ||
387 | prod_ver &= 0xF; | ||
388 | |||
389 | result = inv_serial_read_mem(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
390 | memAddr, 1, &prod_rev); | ||
391 | if (result) { | ||
392 | LOG_RESULT_LOCATION(result); | ||
393 | return result; | ||
394 | } | ||
395 | prod_rev >>= 2; | ||
396 | |||
397 | /* clean the prefetch and cfg user bank bits */ | ||
398 | result = inv_serial_single_write( | ||
399 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
400 | MPUREG_BANK_SEL, 0); | ||
401 | if (result) { | ||
402 | LOG_RESULT_LOCATION(result); | ||
403 | return result; | ||
404 | } | ||
405 | |||
406 | key = MPL_PROD_KEY(prod_ver, prod_rev); | ||
407 | if (key == 0) { | ||
408 | MPL_LOGE("Product id read as 0 " | ||
409 | "indicates device is either " | ||
410 | "incompatible or an MPU3050\n"); | ||
411 | return INV_ERROR_INVALID_MODULE; | ||
412 | } | ||
413 | index = index_of_key(key); | ||
414 | if (index == -1 || index >= NUM_OF_PROD_REVS) { | ||
415 | MPL_LOGE("Unsupported product key %d in MPL\n", key); | ||
416 | return INV_ERROR_INVALID_MODULE; | ||
417 | } | ||
418 | /* check MPL is compiled for this device */ | ||
419 | if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) { | ||
420 | MPL_LOGE("MPL compiled for MPU6050B1 support " | ||
421 | "but device is not MPU6050B1 (%d)\n", key); | ||
422 | return INV_ERROR_INVALID_MODULE; | ||
423 | } | ||
424 | |||
425 | mpu_chip_info->product_id = prod_ver; | ||
426 | mpu_chip_info->product_revision = prod_rev; | ||
427 | mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev; | ||
428 | mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim; | ||
429 | mpu_chip_info->accel_sens_trim = prod_rev_map[index].accel_trim; | ||
430 | |||
431 | return result; | ||
432 | } | ||
433 | #define inv_get_silicon_rev inv_get_silicon_rev_mpu6050 | ||
434 | |||
435 | |||
436 | /** | ||
437 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
438 | * shifters. | ||
439 | * When enabled the secondary I2C interface to which the external | ||
440 | * device is connected runs at VDD voltage (main supply). | ||
441 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
442 | * See the device specification for more details. | ||
443 | * | ||
444 | * @note using this API may produce unpredictable results, depending on how | ||
445 | * the MPU and slave device are setup on the target platform. | ||
446 | * Use of this API should entirely be restricted to system | ||
447 | * integrators. Once the correct value is found, there should be no | ||
448 | * need to change the level shifter at runtime. | ||
449 | * | ||
450 | * @pre Must be called after inv_serial_start(). | ||
451 | * @note Typically called before inv_dmp_open(). | ||
452 | * | ||
453 | * @param[in] enable: | ||
454 | * 0 to run at VDDIO (default), | ||
455 | * 1 to run at VDD. | ||
456 | * | ||
457 | * @return INV_SUCCESS if successfull, a non-zero error code otherwise. | ||
458 | */ | ||
459 | static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg, | ||
460 | void *mlsl_handle, unsigned char enable) | ||
461 | { | ||
462 | int result; | ||
463 | unsigned char regval; | ||
464 | |||
465 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
466 | MPUREG_YG_OFFS_TC, 1, ®val); | ||
467 | if (result) { | ||
468 | LOG_RESULT_LOCATION(result); | ||
469 | return result; | ||
470 | } | ||
471 | if (enable) | ||
472 | regval |= BIT_I2C_MST_VDDIO; | ||
473 | |||
474 | result = inv_serial_single_write( | ||
475 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
476 | MPUREG_YG_OFFS_TC, regval); | ||
477 | if (result) { | ||
478 | LOG_RESULT_LOCATION(result); | ||
479 | return result; | ||
480 | } | ||
481 | return INV_SUCCESS; | ||
482 | } | ||
483 | |||
484 | |||
485 | /** | ||
486 | * @internal | ||
487 | * @brief MPU6050 B1 power management functions. | ||
488 | * @param mldl_cfg | ||
489 | * a pointer to the internal mldl_cfg data structure. | ||
490 | * @param mlsl_handle | ||
491 | * a file handle to the serial device used to communicate | ||
492 | * with the MPU6050 B1 device. | ||
493 | * @param reset | ||
494 | * 1 to reset hardware. | ||
495 | * @param sensors | ||
496 | * Bitfield of sensors to leave on | ||
497 | * | ||
498 | * @return 0 on success, a non-zero error code on error. | ||
499 | */ | ||
500 | static int mpu60xx_pwr_mgmt(struct mldl_cfg *mldl_cfg, | ||
501 | void *mlsl_handle, | ||
502 | unsigned int reset, unsigned long sensors) | ||
503 | { | ||
504 | unsigned char pwr_mgmt[2]; | ||
505 | unsigned char pwr_mgmt_prev[2]; | ||
506 | int result; | ||
507 | int sleep = !(sensors & (INV_THREE_AXIS_GYRO | INV_THREE_AXIS_ACCEL | ||
508 | | INV_DMP_PROCESSOR)); | ||
509 | |||
510 | if (reset) { | ||
511 | MPL_LOGI("Reset MPU6050 B1\n"); | ||
512 | result = inv_serial_single_write( | ||
513 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
514 | MPUREG_PWR_MGMT_1, BIT_H_RESET); | ||
515 | if (result) { | ||
516 | LOG_RESULT_LOCATION(result); | ||
517 | return result; | ||
518 | } | ||
519 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
520 | msleep(15); | ||
521 | } | ||
522 | |||
523 | /* NOTE : reading both PWR_MGMT_1 and PWR_MGMT_2 for efficiency because | ||
524 | they are accessible even when the device is powered off */ | ||
525 | result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
526 | MPUREG_PWR_MGMT_1, 2, pwr_mgmt_prev); | ||
527 | if (result) { | ||
528 | LOG_RESULT_LOCATION(result); | ||
529 | return result; | ||
530 | } | ||
531 | |||
532 | pwr_mgmt[0] = pwr_mgmt_prev[0]; | ||
533 | pwr_mgmt[1] = pwr_mgmt_prev[1]; | ||
534 | |||
535 | if (sleep) { | ||
536 | mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED; | ||
537 | pwr_mgmt[0] |= BIT_SLEEP; | ||
538 | } else { | ||
539 | mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED; | ||
540 | pwr_mgmt[0] &= ~BIT_SLEEP; | ||
541 | } | ||
542 | if (pwr_mgmt[0] != pwr_mgmt_prev[0]) { | ||
543 | result = inv_serial_single_write( | ||
544 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
545 | MPUREG_PWR_MGMT_1, pwr_mgmt[0]); | ||
546 | if (result) { | ||
547 | LOG_RESULT_LOCATION(result); | ||
548 | return result; | ||
549 | } | ||
550 | } | ||
551 | |||
552 | pwr_mgmt[1] &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
553 | if (!(sensors & INV_X_GYRO)) | ||
554 | pwr_mgmt[1] |= BIT_STBY_XG; | ||
555 | if (!(sensors & INV_Y_GYRO)) | ||
556 | pwr_mgmt[1] |= BIT_STBY_YG; | ||
557 | if (!(sensors & INV_Z_GYRO)) | ||
558 | pwr_mgmt[1] |= BIT_STBY_ZG; | ||
559 | |||
560 | if (pwr_mgmt[1] != pwr_mgmt_prev[1]) { | ||
561 | result = inv_serial_single_write( | ||
562 | mlsl_handle, mldl_cfg->mpu_chip_info->addr, | ||
563 | MPUREG_PWR_MGMT_2, pwr_mgmt[1]); | ||
564 | if (result) { | ||
565 | LOG_RESULT_LOCATION(result); | ||
566 | return result; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | if ((pwr_mgmt[1] & (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
571 | (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) { | ||
572 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED; | ||
573 | } else { | ||
574 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED; | ||
575 | } | ||
576 | |||
577 | return INV_SUCCESS; | ||
578 | } | ||
579 | |||
580 | |||
581 | /** | ||
582 | * @brief sets the clock source for the gyros. | ||
583 | * @param mldl_cfg | ||
584 | * a pointer to the struct mldl_cfg data structure. | ||
585 | * @param gyro_handle | ||
586 | * an handle to the serial device the gyro is assigned to. | ||
587 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
588 | */ | ||
589 | static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg) | ||
590 | { | ||
591 | int result; | ||
592 | unsigned char cur_clk_src; | ||
593 | unsigned char reg; | ||
594 | |||
595 | /* clock source selection */ | ||
596 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
597 | MPUREG_PWR_MGM, 1, ®); | ||
598 | if (result) { | ||
599 | LOG_RESULT_LOCATION(result); | ||
600 | return result; | ||
601 | } | ||
602 | cur_clk_src = reg & BITS_CLKSEL; | ||
603 | reg &= ~BITS_CLKSEL; | ||
604 | |||
605 | |||
606 | result = inv_serial_single_write( | ||
607 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
608 | MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg); | ||
609 | if (result) { | ||
610 | LOG_RESULT_LOCATION(result); | ||
611 | return result; | ||
612 | } | ||
613 | |||
614 | /* ERRATA: | ||
615 | workaroud to switch from any MPU_CLK_SEL_PLLGYROx to | ||
616 | MPU_CLK_SEL_INTERNAL and XGyro is powered up: | ||
617 | 1) Select INT_OSC | ||
618 | 2) PD XGyro | ||
619 | 3) PU XGyro | ||
620 | */ | ||
621 | if ((cur_clk_src == MPU_CLK_SEL_PLLGYROX | ||
622 | || cur_clk_src == MPU_CLK_SEL_PLLGYROY | ||
623 | || cur_clk_src == MPU_CLK_SEL_PLLGYROZ) | ||
624 | && mldl_cfg->mpu_gyro_cfg->clk_src == MPU_CLK_SEL_INTERNAL | ||
625 | && mldl_cfg->inv_mpu_cfg->requested_sensors & INV_X_GYRO) { | ||
626 | unsigned char first_result = INV_SUCCESS; | ||
627 | mldl_cfg->inv_mpu_cfg->requested_sensors &= ~INV_X_GYRO; | ||
628 | result = mpu60xx_pwr_mgmt( | ||
629 | mldl_cfg, gyro_handle, | ||
630 | false, mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
631 | ERROR_CHECK_FIRST(first_result, result); | ||
632 | mldl_cfg->inv_mpu_cfg->requested_sensors |= INV_X_GYRO; | ||
633 | result = mpu60xx_pwr_mgmt( | ||
634 | mldl_cfg, gyro_handle, | ||
635 | false, mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
636 | ERROR_CHECK_FIRST(first_result, result); | ||
637 | result = first_result; | ||
638 | } | ||
639 | return result; | ||
640 | } | ||
641 | |||
642 | /** | ||
643 | * Configures the MPU I2C Master | ||
644 | * | ||
645 | * @mldl_cfg Handle to the configuration data | ||
646 | * @gyro_handle handle to the gyro communictation interface | ||
647 | * @slave Can be Null if turning off the slave | ||
648 | * @slave_pdata Can be null if turning off the slave | ||
649 | * @slave_id enum ext_slave_type to determine which index to use | ||
650 | * | ||
651 | * | ||
652 | * This fucntion configures the slaves by: | ||
653 | * 1) Setting up the read | ||
654 | * a) Read Register | ||
655 | * b) Read Length | ||
656 | * 2) Set up the data trigger (MPU6050 only) | ||
657 | * a) Set trigger write register | ||
658 | * b) Set Trigger write value | ||
659 | * 3) Set up the divider (MPU6050 only) | ||
660 | * 4) Set the slave bypass mode depending on slave | ||
661 | * | ||
662 | * returns INV_SUCCESS or non-zero error code | ||
663 | */ | ||
664 | |||
665 | static int mpu_set_slave_mpu60xx(struct mldl_cfg *mldl_cfg, | ||
666 | void *gyro_handle, | ||
667 | struct ext_slave_descr *slave, | ||
668 | struct ext_slave_platform_data *slave_pdata, | ||
669 | int slave_id) | ||
670 | { | ||
671 | int result; | ||
672 | unsigned char reg; | ||
673 | /* Slave values */ | ||
674 | unsigned char slave_reg; | ||
675 | unsigned char slave_len; | ||
676 | unsigned char slave_endian; | ||
677 | unsigned char slave_address; | ||
678 | /* Which MPU6050 registers to use */ | ||
679 | unsigned char addr_reg; | ||
680 | unsigned char reg_reg; | ||
681 | unsigned char ctrl_reg; | ||
682 | /* Which MPU6050 registers to use for the trigger */ | ||
683 | unsigned char addr_trig_reg; | ||
684 | unsigned char reg_trig_reg; | ||
685 | unsigned char ctrl_trig_reg; | ||
686 | |||
687 | unsigned char bits_slave_delay = 0; | ||
688 | /* Divide down rate for the Slave, from the mpu rate */ | ||
689 | unsigned char d0_trig_reg; | ||
690 | unsigned char delay_ctrl_orig; | ||
691 | unsigned char delay_ctrl; | ||
692 | long divider; | ||
693 | |||
694 | if (NULL == slave || NULL == slave_pdata) { | ||
695 | slave_reg = 0; | ||
696 | slave_len = 0; | ||
697 | slave_endian = 0; | ||
698 | slave_address = 0; | ||
699 | } else { | ||
700 | slave_reg = slave->read_reg; | ||
701 | slave_len = slave->read_len; | ||
702 | slave_endian = slave->endian; | ||
703 | slave_address = slave_pdata->address; | ||
704 | slave_address |= BIT_I2C_READ; | ||
705 | } | ||
706 | |||
707 | switch (slave_id) { | ||
708 | case EXT_SLAVE_TYPE_ACCEL: | ||
709 | addr_reg = MPUREG_I2C_SLV1_ADDR; | ||
710 | reg_reg = MPUREG_I2C_SLV1_REG; | ||
711 | ctrl_reg = MPUREG_I2C_SLV1_CTRL; | ||
712 | addr_trig_reg = 0; | ||
713 | reg_trig_reg = 0; | ||
714 | ctrl_trig_reg = 0; | ||
715 | bits_slave_delay = BIT_SLV1_DLY_EN; | ||
716 | break; | ||
717 | case EXT_SLAVE_TYPE_COMPASS: | ||
718 | addr_reg = MPUREG_I2C_SLV0_ADDR; | ||
719 | reg_reg = MPUREG_I2C_SLV0_REG; | ||
720 | ctrl_reg = MPUREG_I2C_SLV0_CTRL; | ||
721 | addr_trig_reg = MPUREG_I2C_SLV2_ADDR; | ||
722 | reg_trig_reg = MPUREG_I2C_SLV2_REG; | ||
723 | ctrl_trig_reg = MPUREG_I2C_SLV2_CTRL; | ||
724 | d0_trig_reg = MPUREG_I2C_SLV2_DO; | ||
725 | bits_slave_delay = BIT_SLV2_DLY_EN | BIT_SLV0_DLY_EN; | ||
726 | break; | ||
727 | case EXT_SLAVE_TYPE_PRESSURE: | ||
728 | addr_reg = MPUREG_I2C_SLV3_ADDR; | ||
729 | reg_reg = MPUREG_I2C_SLV3_REG; | ||
730 | ctrl_reg = MPUREG_I2C_SLV3_CTRL; | ||
731 | addr_trig_reg = MPUREG_I2C_SLV4_ADDR; | ||
732 | reg_trig_reg = MPUREG_I2C_SLV4_REG; | ||
733 | ctrl_trig_reg = MPUREG_I2C_SLV4_CTRL; | ||
734 | bits_slave_delay = BIT_SLV4_DLY_EN | BIT_SLV3_DLY_EN; | ||
735 | break; | ||
736 | default: | ||
737 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
738 | return INV_ERROR_INVALID_PARAMETER; | ||
739 | }; | ||
740 | |||
741 | /* return if this slave has already been set */ | ||
742 | if ((slave_address && | ||
743 | ((mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) | ||
744 | == bits_slave_delay)) || | ||
745 | (!slave_address && | ||
746 | (mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) == | ||
747 | 0)) | ||
748 | return 0; | ||
749 | |||
750 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
751 | |||
752 | /* Address */ | ||
753 | result = inv_serial_single_write(gyro_handle, | ||
754 | mldl_cfg->mpu_chip_info->addr, | ||
755 | addr_reg, slave_address); | ||
756 | if (result) { | ||
757 | LOG_RESULT_LOCATION(result); | ||
758 | return result; | ||
759 | } | ||
760 | /* Register */ | ||
761 | result = inv_serial_single_write(gyro_handle, | ||
762 | mldl_cfg->mpu_chip_info->addr, | ||
763 | reg_reg, slave_reg); | ||
764 | if (result) { | ||
765 | LOG_RESULT_LOCATION(result); | ||
766 | return result; | ||
767 | } | ||
768 | |||
769 | /* Length, byte swapping, grouping & enable */ | ||
770 | if (slave_len > BITS_SLV_LENG) { | ||
771 | MPL_LOGW("Limiting slave burst read length to " | ||
772 | "the allowed maximum (15B, req. %d)\n", slave_len); | ||
773 | slave_len = BITS_SLV_LENG; | ||
774 | return INV_ERROR_INVALID_CONFIGURATION; | ||
775 | } | ||
776 | reg = slave_len; | ||
777 | if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) { | ||
778 | reg |= BIT_SLV_BYTE_SW; | ||
779 | if (slave_reg & 1) | ||
780 | reg |= BIT_SLV_GRP; | ||
781 | } | ||
782 | if (slave_address) | ||
783 | reg |= BIT_SLV_ENABLE; | ||
784 | |||
785 | result = inv_serial_single_write(gyro_handle, | ||
786 | mldl_cfg->mpu_chip_info->addr, | ||
787 | ctrl_reg, reg); | ||
788 | if (result) { | ||
789 | LOG_RESULT_LOCATION(result); | ||
790 | return result; | ||
791 | } | ||
792 | |||
793 | /* Trigger */ | ||
794 | if (addr_trig_reg) { | ||
795 | /* If slave address is 0 this clears the trigger */ | ||
796 | result = inv_serial_single_write(gyro_handle, | ||
797 | mldl_cfg->mpu_chip_info->addr, | ||
798 | addr_trig_reg, | ||
799 | slave_address & ~BIT_I2C_READ); | ||
800 | if (result) { | ||
801 | LOG_RESULT_LOCATION(result); | ||
802 | return result; | ||
803 | } | ||
804 | } | ||
805 | |||
806 | if (slave && slave->trigger && reg_trig_reg) { | ||
807 | result = inv_serial_single_write(gyro_handle, | ||
808 | mldl_cfg->mpu_chip_info->addr, | ||
809 | reg_trig_reg, | ||
810 | slave->trigger->reg); | ||
811 | if (result) { | ||
812 | LOG_RESULT_LOCATION(result); | ||
813 | return result; | ||
814 | } | ||
815 | result = inv_serial_single_write(gyro_handle, | ||
816 | mldl_cfg->mpu_chip_info->addr, | ||
817 | ctrl_trig_reg, | ||
818 | BIT_SLV_ENABLE | 0x01); | ||
819 | if (result) { | ||
820 | LOG_RESULT_LOCATION(result); | ||
821 | return result; | ||
822 | } | ||
823 | result = inv_serial_single_write(gyro_handle, | ||
824 | mldl_cfg->mpu_chip_info->addr, | ||
825 | d0_trig_reg, | ||
826 | slave->trigger->value); | ||
827 | if (result) { | ||
828 | LOG_RESULT_LOCATION(result); | ||
829 | return result; | ||
830 | } | ||
831 | } else if (ctrl_trig_reg) { | ||
832 | result = inv_serial_single_write(gyro_handle, | ||
833 | mldl_cfg->mpu_chip_info->addr, | ||
834 | ctrl_trig_reg, 0x00); | ||
835 | if (result) { | ||
836 | LOG_RESULT_LOCATION(result); | ||
837 | return result; | ||
838 | } | ||
839 | } | ||
840 | |||
841 | /* Data rate */ | ||
842 | if (slave) { | ||
843 | struct ext_slave_config config; | ||
844 | long data; | ||
845 | config.key = MPU_SLAVE_CONFIG_ODR_RESUME; | ||
846 | config.len = sizeof(long); | ||
847 | config.apply = false; | ||
848 | config.data = &data; | ||
849 | if (!(slave->get_config)) | ||
850 | return INV_ERROR_INVALID_CONFIGURATION; | ||
851 | |||
852 | result = slave->get_config(NULL, slave, slave_pdata, &config); | ||
853 | if (result) { | ||
854 | LOG_RESULT_LOCATION(result); | ||
855 | return result; | ||
856 | } | ||
857 | MPL_LOGI("Slave %d ODR: %ld Hz\n", slave_id, data / 1000); | ||
858 | divider = ((1000 * inv_mpu_get_sampling_rate_hz( | ||
859 | mldl_cfg->mpu_gyro_cfg)) | ||
860 | / data) - 1; | ||
861 | } else { | ||
862 | divider = 0; | ||
863 | } | ||
864 | |||
865 | result = inv_serial_read(gyro_handle, | ||
866 | mldl_cfg->mpu_chip_info->addr, | ||
867 | MPUREG_I2C_MST_DELAY_CTRL, | ||
868 | 1, &delay_ctrl_orig); | ||
869 | delay_ctrl = delay_ctrl_orig; | ||
870 | if (result) { | ||
871 | LOG_RESULT_LOCATION(result); | ||
872 | return result; | ||
873 | } | ||
874 | |||
875 | if (divider > 0 && divider <= MASK_I2C_MST_DLY) { | ||
876 | result = inv_serial_read(gyro_handle, | ||
877 | mldl_cfg->mpu_chip_info->addr, | ||
878 | MPUREG_I2C_SLV4_CTRL, 1, ®); | ||
879 | if (result) { | ||
880 | LOG_RESULT_LOCATION(result); | ||
881 | return result; | ||
882 | } | ||
883 | if ((reg & MASK_I2C_MST_DLY) && | ||
884 | ((long)(reg & MASK_I2C_MST_DLY) != | ||
885 | (divider & MASK_I2C_MST_DLY))) { | ||
886 | MPL_LOGW("Changing slave divider: %ld to %ld\n", | ||
887 | (long)(reg & MASK_I2C_MST_DLY), | ||
888 | (divider & MASK_I2C_MST_DLY)); | ||
889 | |||
890 | } | ||
891 | reg |= (unsigned char)(divider & MASK_I2C_MST_DLY); | ||
892 | result = inv_serial_single_write(gyro_handle, | ||
893 | mldl_cfg->mpu_chip_info->addr, | ||
894 | MPUREG_I2C_SLV4_CTRL, | ||
895 | reg); | ||
896 | if (result) { | ||
897 | LOG_RESULT_LOCATION(result); | ||
898 | return result; | ||
899 | } | ||
900 | |||
901 | delay_ctrl |= bits_slave_delay; | ||
902 | } else { | ||
903 | delay_ctrl &= ~(bits_slave_delay); | ||
904 | } | ||
905 | if (delay_ctrl != delay_ctrl_orig) { | ||
906 | result = inv_serial_single_write( | ||
907 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
908 | MPUREG_I2C_MST_DELAY_CTRL, | ||
909 | delay_ctrl); | ||
910 | if (result) { | ||
911 | LOG_RESULT_LOCATION(result); | ||
912 | return result; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | if (slave_address) | ||
917 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled |= | ||
918 | bits_slave_delay; | ||
919 | else | ||
920 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled &= | ||
921 | ~bits_slave_delay; | ||
922 | |||
923 | return result; | ||
924 | } | ||
925 | |||
926 | static int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
927 | void *gyro_handle, | ||
928 | struct ext_slave_descr *slave, | ||
929 | struct ext_slave_platform_data *slave_pdata, | ||
930 | int slave_id) | ||
931 | { | ||
932 | return mpu_set_slave_mpu60xx(mldl_cfg, gyro_handle, slave, | ||
933 | slave_pdata, slave_id); | ||
934 | } | ||
935 | /** | ||
936 | * Check to see if the gyro was reset by testing a couple of registers known | ||
937 | * to change on reset. | ||
938 | * | ||
939 | * @mldl_cfg mldl configuration structure | ||
940 | * @gyro_handle handle used to communicate with the gyro | ||
941 | * | ||
942 | * @return INV_SUCCESS or non-zero error code | ||
943 | */ | ||
944 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
945 | { | ||
946 | int result = INV_SUCCESS; | ||
947 | unsigned char reg; | ||
948 | |||
949 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
950 | MPUREG_DMP_CFG_2, 1, ®); | ||
951 | if (result) { | ||
952 | LOG_RESULT_LOCATION(result); | ||
953 | return result; | ||
954 | } | ||
955 | |||
956 | if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg) | ||
957 | return true; | ||
958 | |||
959 | if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1) | ||
960 | return false; | ||
961 | |||
962 | /* Inconclusive assume it was reset */ | ||
963 | return true; | ||
964 | } | ||
965 | |||
966 | |||
967 | int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
968 | const unsigned char *data, int size) | ||
969 | { | ||
970 | int bank, offset, write_size; | ||
971 | int result; | ||
972 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
973 | |||
974 | if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) { | ||
975 | #if INV_CACHE_DMP == 1 | ||
976 | memcpy(mldl_cfg->mpu_ram->ram, data, size); | ||
977 | return INV_SUCCESS; | ||
978 | #else | ||
979 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
980 | return INV_ERROR_MEMORY_SET; | ||
981 | #endif | ||
982 | } | ||
983 | |||
984 | if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) { | ||
985 | LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET); | ||
986 | return INV_ERROR_MEMORY_SET; | ||
987 | } | ||
988 | /* Write and verify memory */ | ||
989 | for (bank = 0; size > 0; bank++, | ||
990 | size -= write_size, | ||
991 | data += write_size) { | ||
992 | if (size > MPU_MEM_BANK_SIZE) | ||
993 | write_size = MPU_MEM_BANK_SIZE; | ||
994 | else | ||
995 | write_size = size; | ||
996 | |||
997 | result = inv_serial_write_mem(mlsl_handle, | ||
998 | mldl_cfg->mpu_chip_info->addr, | ||
999 | ((bank << 8) | 0x00), | ||
1000 | write_size, | ||
1001 | data); | ||
1002 | if (result) { | ||
1003 | LOG_RESULT_LOCATION(result); | ||
1004 | MPL_LOGE("Write mem error in bank %d\n", bank); | ||
1005 | return result; | ||
1006 | } | ||
1007 | result = inv_serial_read_mem(mlsl_handle, | ||
1008 | mldl_cfg->mpu_chip_info->addr, | ||
1009 | ((bank << 8) | 0x00), | ||
1010 | write_size, | ||
1011 | read); | ||
1012 | if (result) { | ||
1013 | LOG_RESULT_LOCATION(result); | ||
1014 | MPL_LOGE("Read mem error in bank %d\n", bank); | ||
1015 | return result; | ||
1016 | } | ||
1017 | |||
1018 | #define ML_SKIP_CHECK 38 | ||
1019 | for (offset = 0; offset < write_size; offset++) { | ||
1020 | /* skip the register memory locations */ | ||
1021 | if (bank == 0 && offset < ML_SKIP_CHECK) | ||
1022 | continue; | ||
1023 | if (data[offset] != read[offset]) { | ||
1024 | result = INV_ERROR_SERIAL_WRITE; | ||
1025 | break; | ||
1026 | } | ||
1027 | } | ||
1028 | if (result != INV_SUCCESS) { | ||
1029 | LOG_RESULT_LOCATION(result); | ||
1030 | MPL_LOGE("Read data mismatch at bank %d, offset %d\n", | ||
1031 | bank, offset); | ||
1032 | return result; | ||
1033 | } | ||
1034 | } | ||
1035 | return INV_SUCCESS; | ||
1036 | } | ||
1037 | |||
1038 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle, | ||
1039 | unsigned long sensors) | ||
1040 | { | ||
1041 | int result; | ||
1042 | int ii; | ||
1043 | unsigned char reg; | ||
1044 | unsigned char regs[7]; | ||
1045 | |||
1046 | /* Wake up the part */ | ||
1047 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, sensors); | ||
1048 | if (result) { | ||
1049 | LOG_RESULT_LOCATION(result); | ||
1050 | return result; | ||
1051 | } | ||
1052 | |||
1053 | /* Always set the INT_ENABLE and DIVIDER as the Accel Only mode for 6050 | ||
1054 | can set these too */ | ||
1055 | result = inv_serial_single_write( | ||
1056 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1057 | MPUREG_INT_ENABLE, (mldl_cfg->mpu_gyro_cfg->int_config)); | ||
1058 | if (result) { | ||
1059 | LOG_RESULT_LOCATION(result); | ||
1060 | return result; | ||
1061 | } | ||
1062 | result = inv_serial_single_write( | ||
1063 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1064 | MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider); | ||
1065 | if (result) { | ||
1066 | LOG_RESULT_LOCATION(result); | ||
1067 | return result; | ||
1068 | } | ||
1069 | |||
1070 | if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) && | ||
1071 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
1072 | return INV_SUCCESS; | ||
1073 | } | ||
1074 | |||
1075 | /* Configure the MPU */ | ||
1076 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1077 | if (result) { | ||
1078 | LOG_RESULT_LOCATION(result); | ||
1079 | return result; | ||
1080 | } | ||
1081 | result = mpu_set_clock_source(gyro_handle, mldl_cfg); | ||
1082 | if (result) { | ||
1083 | LOG_RESULT_LOCATION(result); | ||
1084 | return result; | ||
1085 | } | ||
1086 | |||
1087 | reg = MPUREG_GYRO_CONFIG_VALUE(0, 0, 0, | ||
1088 | mldl_cfg->mpu_gyro_cfg->full_scale); | ||
1089 | result = inv_serial_single_write( | ||
1090 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1091 | MPUREG_GYRO_CONFIG, reg); | ||
1092 | reg = MPUREG_CONFIG_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync, | ||
1093 | mldl_cfg->mpu_gyro_cfg->lpf); | ||
1094 | result = inv_serial_single_write( | ||
1095 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1096 | MPUREG_CONFIG, reg); | ||
1097 | if (result) { | ||
1098 | LOG_RESULT_LOCATION(result); | ||
1099 | return result; | ||
1100 | } | ||
1101 | result = inv_serial_single_write( | ||
1102 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1103 | MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1); | ||
1104 | if (result) { | ||
1105 | LOG_RESULT_LOCATION(result); | ||
1106 | return result; | ||
1107 | } | ||
1108 | result = inv_serial_single_write( | ||
1109 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1110 | MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2); | ||
1111 | if (result) { | ||
1112 | LOG_RESULT_LOCATION(result); | ||
1113 | return result; | ||
1114 | } | ||
1115 | |||
1116 | /* Write and verify memory */ | ||
1117 | #if INV_CACHE_DMP != 0 | ||
1118 | inv_mpu_set_firmware(mldl_cfg, gyro_handle, | ||
1119 | mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length); | ||
1120 | #endif | ||
1121 | |||
1122 | result = inv_serial_single_write( | ||
1123 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1124 | MPUREG_XG_OFFS_TC, | ||
1125 | ((mldl_cfg->mpu_offsets->tc[0] << 1) & BITS_XG_OFFS_TC)); | ||
1126 | if (result) { | ||
1127 | LOG_RESULT_LOCATION(result); | ||
1128 | return result; | ||
1129 | } | ||
1130 | regs[0] = ((mldl_cfg->mpu_offsets->tc[1] << 1) & BITS_YG_OFFS_TC); | ||
1131 | result = inv_serial_single_write( | ||
1132 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1133 | MPUREG_YG_OFFS_TC, regs[0]); | ||
1134 | if (result) { | ||
1135 | LOG_RESULT_LOCATION(result); | ||
1136 | return result; | ||
1137 | } | ||
1138 | result = inv_serial_single_write( | ||
1139 | gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1140 | MPUREG_ZG_OFFS_TC, | ||
1141 | ((mldl_cfg->mpu_offsets->tc[2] << 1) & BITS_ZG_OFFS_TC)); | ||
1142 | if (result) { | ||
1143 | LOG_RESULT_LOCATION(result); | ||
1144 | return result; | ||
1145 | } | ||
1146 | regs[0] = MPUREG_X_OFFS_USRH; | ||
1147 | for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) { | ||
1148 | regs[1 + ii * 2] = | ||
1149 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8) | ||
1150 | & 0xff; | ||
1151 | regs[1 + ii * 2 + 1] = | ||
1152 | (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff); | ||
1153 | } | ||
1154 | result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1155 | 7, regs); | ||
1156 | if (result) { | ||
1157 | LOG_RESULT_LOCATION(result); | ||
1158 | return result; | ||
1159 | } | ||
1160 | |||
1161 | /* Configure slaves */ | ||
1162 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
1163 | mldl_cfg->pdata->level_shifter); | ||
1164 | if (result) { | ||
1165 | LOG_RESULT_LOCATION(result); | ||
1166 | return result; | ||
1167 | } | ||
1168 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG; | ||
1169 | |||
1170 | return result; | ||
1171 | } | ||
1172 | |||
1173 | int gyro_config(void *mlsl_handle, | ||
1174 | struct mldl_cfg *mldl_cfg, | ||
1175 | struct ext_slave_config *data) | ||
1176 | { | ||
1177 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
1178 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
1179 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
1180 | int ii; | ||
1181 | |||
1182 | if (!data->data) | ||
1183 | return INV_ERROR_INVALID_PARAMETER; | ||
1184 | |||
1185 | switch (data->key) { | ||
1186 | case MPU_SLAVE_INT_CONFIG: | ||
1187 | mpu_gyro_cfg->int_config = *((__u8 *)data->data); | ||
1188 | break; | ||
1189 | case MPU_SLAVE_EXT_SYNC: | ||
1190 | mpu_gyro_cfg->ext_sync = *((__u8 *)data->data); | ||
1191 | break; | ||
1192 | case MPU_SLAVE_FULL_SCALE: | ||
1193 | mpu_gyro_cfg->full_scale = *((__u8 *)data->data); | ||
1194 | break; | ||
1195 | case MPU_SLAVE_LPF: | ||
1196 | mpu_gyro_cfg->lpf = *((__u8 *)data->data); | ||
1197 | break; | ||
1198 | case MPU_SLAVE_CLK_SRC: | ||
1199 | mpu_gyro_cfg->clk_src = *((__u8 *)data->data); | ||
1200 | break; | ||
1201 | case MPU_SLAVE_DIVIDER: | ||
1202 | mpu_gyro_cfg->divider = *((__u8 *)data->data); | ||
1203 | break; | ||
1204 | case MPU_SLAVE_DMP_ENABLE: | ||
1205 | mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data); | ||
1206 | break; | ||
1207 | case MPU_SLAVE_FIFO_ENABLE: | ||
1208 | mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data); | ||
1209 | break; | ||
1210 | case MPU_SLAVE_DMP_CFG1: | ||
1211 | mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data); | ||
1212 | break; | ||
1213 | case MPU_SLAVE_DMP_CFG2: | ||
1214 | mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data); | ||
1215 | break; | ||
1216 | case MPU_SLAVE_TC: | ||
1217 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1218 | mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii]; | ||
1219 | break; | ||
1220 | case MPU_SLAVE_GYRO: | ||
1221 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1222 | mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii]; | ||
1223 | break; | ||
1224 | case MPU_SLAVE_ADDR: | ||
1225 | mpu_chip_info->addr = *((__u8 *)data->data); | ||
1226 | break; | ||
1227 | case MPU_SLAVE_PRODUCT_REVISION: | ||
1228 | mpu_chip_info->product_revision = *((__u8 *)data->data); | ||
1229 | break; | ||
1230 | case MPU_SLAVE_SILICON_REVISION: | ||
1231 | mpu_chip_info->silicon_revision = *((__u8 *)data->data); | ||
1232 | break; | ||
1233 | case MPU_SLAVE_PRODUCT_ID: | ||
1234 | mpu_chip_info->product_id = *((__u8 *)data->data); | ||
1235 | break; | ||
1236 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
1237 | mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data); | ||
1238 | break; | ||
1239 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
1240 | mpu_chip_info->accel_sens_trim = *((__u16 *)data->data); | ||
1241 | break; | ||
1242 | case MPU_SLAVE_RAM: | ||
1243 | if (data->len != mldl_cfg->mpu_ram->length) | ||
1244 | return INV_ERROR_INVALID_PARAMETER; | ||
1245 | |||
1246 | memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len); | ||
1247 | break; | ||
1248 | default: | ||
1249 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
1250 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1251 | }; | ||
1252 | mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG; | ||
1253 | return INV_SUCCESS; | ||
1254 | } | ||
1255 | |||
1256 | int gyro_get_config(void *mlsl_handle, | ||
1257 | struct mldl_cfg *mldl_cfg, | ||
1258 | struct ext_slave_config *data) | ||
1259 | { | ||
1260 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
1261 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
1262 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
1263 | int ii; | ||
1264 | |||
1265 | if (!data->data) | ||
1266 | return INV_ERROR_INVALID_PARAMETER; | ||
1267 | |||
1268 | switch (data->key) { | ||
1269 | case MPU_SLAVE_INT_CONFIG: | ||
1270 | *((__u8 *)data->data) = mpu_gyro_cfg->int_config; | ||
1271 | break; | ||
1272 | case MPU_SLAVE_EXT_SYNC: | ||
1273 | *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync; | ||
1274 | break; | ||
1275 | case MPU_SLAVE_FULL_SCALE: | ||
1276 | *((__u8 *)data->data) = mpu_gyro_cfg->full_scale; | ||
1277 | break; | ||
1278 | case MPU_SLAVE_LPF: | ||
1279 | *((__u8 *)data->data) = mpu_gyro_cfg->lpf; | ||
1280 | break; | ||
1281 | case MPU_SLAVE_CLK_SRC: | ||
1282 | *((__u8 *)data->data) = mpu_gyro_cfg->clk_src; | ||
1283 | break; | ||
1284 | case MPU_SLAVE_DIVIDER: | ||
1285 | *((__u8 *)data->data) = mpu_gyro_cfg->divider; | ||
1286 | break; | ||
1287 | case MPU_SLAVE_DMP_ENABLE: | ||
1288 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable; | ||
1289 | break; | ||
1290 | case MPU_SLAVE_FIFO_ENABLE: | ||
1291 | *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable; | ||
1292 | break; | ||
1293 | case MPU_SLAVE_DMP_CFG1: | ||
1294 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1; | ||
1295 | break; | ||
1296 | case MPU_SLAVE_DMP_CFG2: | ||
1297 | *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2; | ||
1298 | break; | ||
1299 | case MPU_SLAVE_TC: | ||
1300 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1301 | ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii]; | ||
1302 | break; | ||
1303 | case MPU_SLAVE_GYRO: | ||
1304 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) | ||
1305 | ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii]; | ||
1306 | break; | ||
1307 | case MPU_SLAVE_ADDR: | ||
1308 | *((__u8 *)data->data) = mpu_chip_info->addr; | ||
1309 | break; | ||
1310 | case MPU_SLAVE_PRODUCT_REVISION: | ||
1311 | *((__u8 *)data->data) = mpu_chip_info->product_revision; | ||
1312 | break; | ||
1313 | case MPU_SLAVE_SILICON_REVISION: | ||
1314 | *((__u8 *)data->data) = mpu_chip_info->silicon_revision; | ||
1315 | break; | ||
1316 | case MPU_SLAVE_PRODUCT_ID: | ||
1317 | *((__u8 *)data->data) = mpu_chip_info->product_id; | ||
1318 | break; | ||
1319 | case MPU_SLAVE_GYRO_SENS_TRIM: | ||
1320 | *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim; | ||
1321 | break; | ||
1322 | case MPU_SLAVE_ACCEL_SENS_TRIM: | ||
1323 | *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim; | ||
1324 | break; | ||
1325 | case MPU_SLAVE_RAM: | ||
1326 | if (data->len != mldl_cfg->mpu_ram->length) | ||
1327 | return INV_ERROR_INVALID_PARAMETER; | ||
1328 | |||
1329 | memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len); | ||
1330 | break; | ||
1331 | default: | ||
1332 | LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED); | ||
1333 | return INV_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1334 | }; | ||
1335 | |||
1336 | return INV_SUCCESS; | ||
1337 | } | ||
1338 | |||
1339 | |||
1340 | /******************************************************************************* | ||
1341 | ******************************************************************************* | ||
1342 | * Exported functions | ||
1343 | ******************************************************************************* | ||
1344 | ******************************************************************************/ | ||
1345 | |||
1346 | /** | ||
1347 | * Initializes the pdata structure to defaults. | ||
1348 | * | ||
1349 | * Opens the device to read silicon revision, product id and whoami. | ||
1350 | * | ||
1351 | * @mldl_cfg | ||
1352 | * The internal device configuration data structure. | ||
1353 | * @mlsl_handle | ||
1354 | * The serial communication handle. | ||
1355 | * | ||
1356 | * @return INV_SUCCESS if silicon revision, product id and woami are supported | ||
1357 | * by this software. | ||
1358 | */ | ||
1359 | int inv_mpu_open(struct mldl_cfg *mldl_cfg, | ||
1360 | void *gyro_handle, | ||
1361 | void *accel_handle, | ||
1362 | void *compass_handle, void *pressure_handle) | ||
1363 | { | ||
1364 | int result; | ||
1365 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1366 | int ii; | ||
1367 | |||
1368 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
1369 | ii = 0; | ||
1370 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false; | ||
1371 | mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN; | ||
1372 | mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
1373 | mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ; | ||
1374 | mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS; | ||
1375 | mldl_cfg->mpu_gyro_cfg->divider = 4; | ||
1376 | mldl_cfg->mpu_gyro_cfg->dmp_enable = 1; | ||
1377 | mldl_cfg->mpu_gyro_cfg->fifo_enable = 1; | ||
1378 | mldl_cfg->mpu_gyro_cfg->ext_sync = 0; | ||
1379 | mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0; | ||
1380 | mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0; | ||
1381 | mldl_cfg->inv_mpu_state->status = | ||
1382 | MPU_DMP_IS_SUSPENDED | | ||
1383 | MPU_GYRO_IS_SUSPENDED | | ||
1384 | MPU_ACCEL_IS_SUSPENDED | | ||
1385 | MPU_COMPASS_IS_SUSPENDED | | ||
1386 | MPU_PRESSURE_IS_SUSPENDED | | ||
1387 | MPU_DEVICE_IS_SUSPENDED; | ||
1388 | mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0; | ||
1389 | |||
1390 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1391 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1392 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1393 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1394 | |||
1395 | if (mldl_cfg->mpu_chip_info->addr == 0) { | ||
1396 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
1397 | return INV_ERROR_INVALID_PARAMETER; | ||
1398 | } | ||
1399 | |||
1400 | /* | ||
1401 | * Reset, | ||
1402 | * Take the DMP out of sleep, and | ||
1403 | * read the product_id, sillicon rev and whoami | ||
1404 | */ | ||
1405 | mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED; | ||
1406 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, true, | ||
1407 | INV_THREE_AXIS_GYRO); | ||
1408 | if (result) { | ||
1409 | LOG_RESULT_LOCATION(result); | ||
1410 | return result; | ||
1411 | } | ||
1412 | |||
1413 | result = inv_get_silicon_rev(mldl_cfg, gyro_handle); | ||
1414 | if (result) { | ||
1415 | LOG_RESULT_LOCATION(result); | ||
1416 | return result; | ||
1417 | } | ||
1418 | |||
1419 | /* Get the factory temperature compensation offsets */ | ||
1420 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1421 | MPUREG_XG_OFFS_TC, 1, | ||
1422 | &mldl_cfg->mpu_offsets->tc[0]); | ||
1423 | if (result) { | ||
1424 | LOG_RESULT_LOCATION(result); | ||
1425 | return result; | ||
1426 | } | ||
1427 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1428 | MPUREG_YG_OFFS_TC, 1, | ||
1429 | &mldl_cfg->mpu_offsets->tc[1]); | ||
1430 | if (result) { | ||
1431 | LOG_RESULT_LOCATION(result); | ||
1432 | return result; | ||
1433 | } | ||
1434 | result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr, | ||
1435 | MPUREG_ZG_OFFS_TC, 1, | ||
1436 | &mldl_cfg->mpu_offsets->tc[2]); | ||
1437 | if (result) { | ||
1438 | LOG_RESULT_LOCATION(result); | ||
1439 | return result; | ||
1440 | } | ||
1441 | |||
1442 | /* Into bypass mode before sleeping and calling the slaves init */ | ||
1443 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true); | ||
1444 | if (result) { | ||
1445 | LOG_RESULT_LOCATION(result); | ||
1446 | return result; | ||
1447 | } | ||
1448 | result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle, | ||
1449 | mldl_cfg->pdata->level_shifter); | ||
1450 | if (result) { | ||
1451 | LOG_RESULT_LOCATION(result); | ||
1452 | return result; | ||
1453 | } | ||
1454 | |||
1455 | for (ii = 0; ii < GYRO_NUM_AXES; ii++) { | ||
1456 | mldl_cfg->mpu_offsets->tc[ii] = | ||
1457 | (mldl_cfg->mpu_offsets->tc[ii] & BITS_XG_OFFS_TC) >> 1; | ||
1458 | } | ||
1459 | |||
1460 | #if INV_CACHE_DMP != 0 | ||
1461 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, 0); | ||
1462 | #endif | ||
1463 | if (result) { | ||
1464 | LOG_RESULT_LOCATION(result); | ||
1465 | return result; | ||
1466 | } | ||
1467 | |||
1468 | |||
1469 | return result; | ||
1470 | |||
1471 | } | ||
1472 | |||
1473 | /** | ||
1474 | * Close the mpu interface | ||
1475 | * | ||
1476 | * @mldl_cfg pointer to the configuration structure | ||
1477 | * @mlsl_handle pointer to the serial layer handle | ||
1478 | * | ||
1479 | * @return INV_SUCCESS or non-zero error code | ||
1480 | */ | ||
1481 | int inv_mpu_close(struct mldl_cfg *mldl_cfg, | ||
1482 | void *gyro_handle, | ||
1483 | void *accel_handle, | ||
1484 | void *compass_handle, | ||
1485 | void *pressure_handle) | ||
1486 | { | ||
1487 | return 0; | ||
1488 | } | ||
1489 | |||
1490 | /** | ||
1491 | * @brief resume the MPU device and all the other sensor | ||
1492 | * devices from their low power state. | ||
1493 | * | ||
1494 | * @mldl_cfg | ||
1495 | * pointer to the configuration structure | ||
1496 | * @gyro_handle | ||
1497 | * the main file handle to the MPU device. | ||
1498 | * @accel_handle | ||
1499 | * an handle to the accelerometer device, if sitting | ||
1500 | * onto a separate bus. Can match mlsl_handle if | ||
1501 | * the accelerometer device operates on the same | ||
1502 | * primary bus of MPU. | ||
1503 | * @compass_handle | ||
1504 | * an handle to the compass device, if sitting | ||
1505 | * onto a separate bus. Can match mlsl_handle if | ||
1506 | * the compass device operates on the same | ||
1507 | * primary bus of MPU. | ||
1508 | * @pressure_handle | ||
1509 | * an handle to the pressure sensor device, if sitting | ||
1510 | * onto a separate bus. Can match mlsl_handle if | ||
1511 | * the pressure sensor device operates on the same | ||
1512 | * primary bus of MPU. | ||
1513 | * @resume_gyro | ||
1514 | * whether resuming the gyroscope device is | ||
1515 | * actually needed (if the device supports low power | ||
1516 | * mode of some sort). | ||
1517 | * @resume_accel | ||
1518 | * whether resuming the accelerometer device is | ||
1519 | * actually needed (if the device supports low power | ||
1520 | * mode of some sort). | ||
1521 | * @resume_compass | ||
1522 | * whether resuming the compass device is | ||
1523 | * actually needed (if the device supports low power | ||
1524 | * mode of some sort). | ||
1525 | * @resume_pressure | ||
1526 | * whether resuming the pressure sensor device is | ||
1527 | * actually needed (if the device supports low power | ||
1528 | * mode of some sort). | ||
1529 | * @return INV_SUCCESS or a non-zero error code. | ||
1530 | */ | ||
1531 | int inv_mpu_resume(struct mldl_cfg *mldl_cfg, | ||
1532 | void *gyro_handle, | ||
1533 | void *accel_handle, | ||
1534 | void *compass_handle, | ||
1535 | void *pressure_handle, | ||
1536 | unsigned long sensors) | ||
1537 | { | ||
1538 | int result = INV_SUCCESS; | ||
1539 | int ii; | ||
1540 | bool resume_slave[EXT_SLAVE_NUM_TYPES]; | ||
1541 | bool resume_dmp = sensors & INV_DMP_PROCESSOR; | ||
1542 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1543 | resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
1544 | (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
1545 | resume_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
1546 | sensors & INV_THREE_AXIS_ACCEL; | ||
1547 | resume_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
1548 | sensors & INV_THREE_AXIS_COMPASS; | ||
1549 | resume_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
1550 | sensors & INV_THREE_AXIS_PRESSURE; | ||
1551 | |||
1552 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1553 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1554 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1555 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1556 | |||
1557 | |||
1558 | mldl_print_cfg(mldl_cfg); | ||
1559 | |||
1560 | /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */ | ||
1561 | for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1562 | if (resume_slave[ii] && | ||
1563 | ((!mldl_cfg->slave[ii]) || | ||
1564 | (!mldl_cfg->slave[ii]->resume))) { | ||
1565 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
1566 | return INV_ERROR_INVALID_PARAMETER; | ||
1567 | } | ||
1568 | } | ||
1569 | |||
1570 | if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp) | ||
1571 | && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) || | ||
1572 | (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) { | ||
1573 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1574 | if (result) { | ||
1575 | LOG_RESULT_LOCATION(result); | ||
1576 | return result; | ||
1577 | } | ||
1578 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
1579 | if (result) { | ||
1580 | LOG_RESULT_LOCATION(result); | ||
1581 | return result; | ||
1582 | } | ||
1583 | result = gyro_resume(mldl_cfg, gyro_handle, sensors); | ||
1584 | if (result) { | ||
1585 | LOG_RESULT_LOCATION(result); | ||
1586 | return result; | ||
1587 | } | ||
1588 | } | ||
1589 | |||
1590 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1591 | if (!mldl_cfg->slave[ii] || | ||
1592 | !mldl_cfg->pdata_slave[ii] || | ||
1593 | !resume_slave[ii] || | ||
1594 | !(mldl_cfg->inv_mpu_state->status & (1 << ii))) | ||
1595 | continue; | ||
1596 | |||
1597 | if (EXT_SLAVE_BUS_SECONDARY == | ||
1598 | mldl_cfg->pdata_slave[ii]->bus) { | ||
1599 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, | ||
1600 | true); | ||
1601 | if (result) { | ||
1602 | LOG_RESULT_LOCATION(result); | ||
1603 | return result; | ||
1604 | } | ||
1605 | } | ||
1606 | result = mldl_cfg->slave[ii]->resume(slave_handle[ii], | ||
1607 | mldl_cfg->slave[ii], | ||
1608 | mldl_cfg->pdata_slave[ii]); | ||
1609 | if (result) { | ||
1610 | LOG_RESULT_LOCATION(result); | ||
1611 | return result; | ||
1612 | } | ||
1613 | mldl_cfg->inv_mpu_state->status &= ~(1 << ii); | ||
1614 | } | ||
1615 | |||
1616 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1617 | if (resume_dmp && | ||
1618 | !(mldl_cfg->inv_mpu_state->status & (1 << ii)) && | ||
1619 | mldl_cfg->pdata_slave[ii] && | ||
1620 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) { | ||
1621 | result = mpu_set_slave(mldl_cfg, | ||
1622 | gyro_handle, | ||
1623 | mldl_cfg->slave[ii], | ||
1624 | mldl_cfg->pdata_slave[ii], | ||
1625 | mldl_cfg->slave[ii]->type); | ||
1626 | if (result) { | ||
1627 | LOG_RESULT_LOCATION(result); | ||
1628 | return result; | ||
1629 | } | ||
1630 | } | ||
1631 | } | ||
1632 | |||
1633 | /* Turn on the master i2c iterface if necessary */ | ||
1634 | if (resume_dmp) { | ||
1635 | result = mpu_set_i2c_bypass( | ||
1636 | mldl_cfg, gyro_handle, | ||
1637 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
1638 | if (result) { | ||
1639 | LOG_RESULT_LOCATION(result); | ||
1640 | return result; | ||
1641 | } | ||
1642 | |||
1643 | /* Now start */ | ||
1644 | result = dmp_start(mldl_cfg, gyro_handle); | ||
1645 | if (result) { | ||
1646 | LOG_RESULT_LOCATION(result); | ||
1647 | return result; | ||
1648 | } | ||
1649 | } | ||
1650 | mldl_cfg->inv_mpu_cfg->requested_sensors = sensors; | ||
1651 | |||
1652 | return result; | ||
1653 | } | ||
1654 | |||
1655 | /** | ||
1656 | * @brief suspend the MPU device and all the other sensor | ||
1657 | * devices into their low power state. | ||
1658 | * @mldl_cfg | ||
1659 | * a pointer to the struct mldl_cfg internal data | ||
1660 | * structure. | ||
1661 | * @gyro_handle | ||
1662 | * the main file handle to the MPU device. | ||
1663 | * @accel_handle | ||
1664 | * an handle to the accelerometer device, if sitting | ||
1665 | * onto a separate bus. Can match gyro_handle if | ||
1666 | * the accelerometer device operates on the same | ||
1667 | * primary bus of MPU. | ||
1668 | * @compass_handle | ||
1669 | * an handle to the compass device, if sitting | ||
1670 | * onto a separate bus. Can match gyro_handle if | ||
1671 | * the compass device operates on the same | ||
1672 | * primary bus of MPU. | ||
1673 | * @pressure_handle | ||
1674 | * an handle to the pressure sensor device, if sitting | ||
1675 | * onto a separate bus. Can match gyro_handle if | ||
1676 | * the pressure sensor device operates on the same | ||
1677 | * primary bus of MPU. | ||
1678 | * @accel | ||
1679 | * whether suspending the accelerometer device is | ||
1680 | * actually needed (if the device supports low power | ||
1681 | * mode of some sort). | ||
1682 | * @compass | ||
1683 | * whether suspending the compass device is | ||
1684 | * actually needed (if the device supports low power | ||
1685 | * mode of some sort). | ||
1686 | * @pressure | ||
1687 | * whether suspending the pressure sensor device is | ||
1688 | * actually needed (if the device supports low power | ||
1689 | * mode of some sort). | ||
1690 | * @return INV_SUCCESS or a non-zero error code. | ||
1691 | */ | ||
1692 | int inv_mpu_suspend(struct mldl_cfg *mldl_cfg, | ||
1693 | void *gyro_handle, | ||
1694 | void *accel_handle, | ||
1695 | void *compass_handle, | ||
1696 | void *pressure_handle, | ||
1697 | unsigned long sensors) | ||
1698 | { | ||
1699 | int result = INV_SUCCESS; | ||
1700 | int ii; | ||
1701 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
1702 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
1703 | bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR); | ||
1704 | bool suspend_slave[EXT_SLAVE_NUM_TYPES]; | ||
1705 | void *slave_handle[EXT_SLAVE_NUM_TYPES]; | ||
1706 | |||
1707 | suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] = | ||
1708 | ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)) | ||
1709 | == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)); | ||
1710 | suspend_slave[EXT_SLAVE_TYPE_ACCEL] = | ||
1711 | ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL); | ||
1712 | suspend_slave[EXT_SLAVE_TYPE_COMPASS] = | ||
1713 | ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS); | ||
1714 | suspend_slave[EXT_SLAVE_TYPE_PRESSURE] = | ||
1715 | ((sensors & INV_THREE_AXIS_PRESSURE) == | ||
1716 | INV_THREE_AXIS_PRESSURE); | ||
1717 | |||
1718 | slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle; | ||
1719 | slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle; | ||
1720 | slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle; | ||
1721 | slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle; | ||
1722 | |||
1723 | if (suspend_dmp) { | ||
1724 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1725 | if (result) { | ||
1726 | LOG_RESULT_LOCATION(result); | ||
1727 | return result; | ||
1728 | } | ||
1729 | result = dmp_stop(mldl_cfg, gyro_handle); | ||
1730 | if (result) { | ||
1731 | LOG_RESULT_LOCATION(result); | ||
1732 | return result; | ||
1733 | } | ||
1734 | } | ||
1735 | |||
1736 | /* Gyro */ | ||
1737 | if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] && | ||
1738 | !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) { | ||
1739 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, | ||
1740 | ((~sensors) & INV_ALL_SENSORS)); | ||
1741 | if (result) { | ||
1742 | LOG_RESULT_LOCATION(result); | ||
1743 | return result; | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1747 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1748 | bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii); | ||
1749 | if (!slave[ii] || !pdata_slave[ii] || | ||
1750 | is_suspended || !suspend_slave[ii]) | ||
1751 | continue; | ||
1752 | |||
1753 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
1754 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1755 | if (result) { | ||
1756 | LOG_RESULT_LOCATION(result); | ||
1757 | return result; | ||
1758 | } | ||
1759 | } | ||
1760 | result = slave[ii]->suspend(slave_handle[ii], | ||
1761 | slave[ii], | ||
1762 | pdata_slave[ii]); | ||
1763 | if (result) { | ||
1764 | LOG_RESULT_LOCATION(result); | ||
1765 | return result; | ||
1766 | } | ||
1767 | if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) { | ||
1768 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
1769 | NULL, NULL, | ||
1770 | slave[ii]->type); | ||
1771 | if (result) { | ||
1772 | LOG_RESULT_LOCATION(result); | ||
1773 | return result; | ||
1774 | } | ||
1775 | } | ||
1776 | mldl_cfg->inv_mpu_state->status |= (1 << ii); | ||
1777 | } | ||
1778 | |||
1779 | /* Re-enable the i2c master if there are configured slaves and DMP */ | ||
1780 | if (!suspend_dmp) { | ||
1781 | result = mpu_set_i2c_bypass( | ||
1782 | mldl_cfg, gyro_handle, | ||
1783 | !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)); | ||
1784 | if (result) { | ||
1785 | LOG_RESULT_LOCATION(result); | ||
1786 | return result; | ||
1787 | } | ||
1788 | } | ||
1789 | mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS; | ||
1790 | |||
1791 | return result; | ||
1792 | } | ||
1793 | |||
1794 | int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg, | ||
1795 | void *gyro_handle, | ||
1796 | void *slave_handle, | ||
1797 | struct ext_slave_descr *slave, | ||
1798 | struct ext_slave_platform_data *pdata, | ||
1799 | unsigned char *data) | ||
1800 | { | ||
1801 | int result; | ||
1802 | int bypass_result; | ||
1803 | int remain_bypassed = true; | ||
1804 | |||
1805 | if (NULL == slave || NULL == slave->read) { | ||
1806 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1807 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1808 | } | ||
1809 | |||
1810 | if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1811 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1812 | remain_bypassed = false; | ||
1813 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1814 | if (result) { | ||
1815 | LOG_RESULT_LOCATION(result); | ||
1816 | return result; | ||
1817 | } | ||
1818 | } | ||
1819 | |||
1820 | result = slave->read(slave_handle, slave, pdata, data); | ||
1821 | |||
1822 | if (!remain_bypassed) { | ||
1823 | bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1824 | if (bypass_result) { | ||
1825 | LOG_RESULT_LOCATION(bypass_result); | ||
1826 | return bypass_result; | ||
1827 | } | ||
1828 | } | ||
1829 | return result; | ||
1830 | } | ||
1831 | |||
1832 | int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg, | ||
1833 | void *gyro_handle, | ||
1834 | void *slave_handle, | ||
1835 | struct ext_slave_config *data, | ||
1836 | struct ext_slave_descr *slave, | ||
1837 | struct ext_slave_platform_data *pdata) | ||
1838 | { | ||
1839 | int result; | ||
1840 | int remain_bypassed = true; | ||
1841 | |||
1842 | if (NULL == slave || NULL == slave->config) { | ||
1843 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1844 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1845 | } | ||
1846 | |||
1847 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1848 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1849 | remain_bypassed = false; | ||
1850 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1851 | if (result) { | ||
1852 | LOG_RESULT_LOCATION(result); | ||
1853 | return result; | ||
1854 | } | ||
1855 | } | ||
1856 | |||
1857 | result = slave->config(slave_handle, slave, pdata, data); | ||
1858 | if (result) { | ||
1859 | LOG_RESULT_LOCATION(result); | ||
1860 | return result; | ||
1861 | } | ||
1862 | |||
1863 | if (!remain_bypassed) { | ||
1864 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1865 | if (result) { | ||
1866 | LOG_RESULT_LOCATION(result); | ||
1867 | return result; | ||
1868 | } | ||
1869 | } | ||
1870 | return result; | ||
1871 | } | ||
1872 | |||
1873 | int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg, | ||
1874 | void *gyro_handle, | ||
1875 | void *slave_handle, | ||
1876 | struct ext_slave_config *data, | ||
1877 | struct ext_slave_descr *slave, | ||
1878 | struct ext_slave_platform_data *pdata) | ||
1879 | { | ||
1880 | int result; | ||
1881 | int remain_bypassed = true; | ||
1882 | |||
1883 | if (NULL == slave || NULL == slave->get_config) { | ||
1884 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION); | ||
1885 | return INV_ERROR_INVALID_CONFIGURATION; | ||
1886 | } | ||
1887 | |||
1888 | if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus) | ||
1889 | && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) { | ||
1890 | remain_bypassed = false; | ||
1891 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1); | ||
1892 | if (result) { | ||
1893 | LOG_RESULT_LOCATION(result); | ||
1894 | return result; | ||
1895 | } | ||
1896 | } | ||
1897 | |||
1898 | result = slave->get_config(slave_handle, slave, pdata, data); | ||
1899 | if (result) { | ||
1900 | LOG_RESULT_LOCATION(result); | ||
1901 | return result; | ||
1902 | } | ||
1903 | |||
1904 | if (!remain_bypassed) { | ||
1905 | result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0); | ||
1906 | if (result) { | ||
1907 | LOG_RESULT_LOCATION(result); | ||
1908 | return result; | ||
1909 | } | ||
1910 | } | ||
1911 | return result; | ||
1912 | } | ||
1913 | |||
1914 | /** | ||
1915 | * @} | ||
1916 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c new file mode 100644 index 00000000000..7379a170761 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @addtogroup MLDL | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mldl_print_cfg.c | ||
25 | * @brief The Motion Library Driver Layer. | ||
26 | */ | ||
27 | |||
28 | #include <stddef.h> | ||
29 | #include "mldl_cfg.h" | ||
30 | #include "mlsl.h" | ||
31 | #include "linux/mpu.h" | ||
32 | |||
33 | #include "log.h" | ||
34 | #undef MPL_LOG_TAG | ||
35 | #define MPL_LOG_TAG "mldl_print_cfg:" | ||
36 | |||
37 | #undef MPL_LOG_NDEBUG | ||
38 | #define MPL_LOG_NDEBUG 1 | ||
39 | |||
40 | void mldl_print_cfg(struct mldl_cfg *mldl_cfg) | ||
41 | { | ||
42 | struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg; | ||
43 | struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets; | ||
44 | struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info; | ||
45 | struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg; | ||
46 | struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state; | ||
47 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
48 | struct mpu_platform_data *pdata = mldl_cfg->pdata; | ||
49 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
50 | int ii; | ||
51 | |||
52 | /* mpu_gyro_cfg */ | ||
53 | MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config); | ||
54 | MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync); | ||
55 | MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale); | ||
56 | MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf); | ||
57 | MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src); | ||
58 | MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider); | ||
59 | MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable); | ||
60 | MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable); | ||
61 | MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1); | ||
62 | MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2); | ||
63 | /* mpu_offsets */ | ||
64 | MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]); | ||
65 | MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]); | ||
66 | MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]); | ||
67 | MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]); | ||
68 | MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]); | ||
69 | MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]); | ||
70 | |||
71 | /* mpu_chip_info */ | ||
72 | MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr); | ||
73 | |||
74 | MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision); | ||
75 | MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision); | ||
76 | MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id); | ||
77 | MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim); | ||
78 | MPL_LOGV("accel_sens_trim = %02x\n", mpu_chip_info->accel_sens_trim); | ||
79 | |||
80 | MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors); | ||
81 | MPL_LOGV("ignore_system_suspend= %04x\n", | ||
82 | inv_mpu_cfg->ignore_system_suspend); | ||
83 | MPL_LOGV("status = %04x\n", inv_mpu_state->status); | ||
84 | MPL_LOGV("i2c_slaves_enabled= %04x\n", | ||
85 | inv_mpu_state->i2c_slaves_enabled); | ||
86 | |||
87 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
88 | if (!slave[ii]) | ||
89 | continue; | ||
90 | MPL_LOGV("SLAVE %d:\n", ii); | ||
91 | MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend); | ||
92 | MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume); | ||
93 | MPL_LOGV(" read = %02x\n", (int)slave[ii]->read); | ||
94 | MPL_LOGV(" type = %02x\n", slave[ii]->type); | ||
95 | MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg); | ||
96 | MPL_LOGV(" len = %02x\n", slave[ii]->read_len); | ||
97 | MPL_LOGV(" endian = %02x\n", slave[ii]->endian); | ||
98 | MPL_LOGV(" range.mantissa= %02x\n", | ||
99 | slave[ii]->range.mantissa); | ||
100 | MPL_LOGV(" range.fraction= %02x\n", | ||
101 | slave[ii]->range.fraction); | ||
102 | } | ||
103 | |||
104 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
105 | if (!pdata_slave[ii]) | ||
106 | continue; | ||
107 | MPL_LOGV("PDATA_SLAVE[%d]\n", ii); | ||
108 | MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq); | ||
109 | MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num); | ||
110 | MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus); | ||
111 | MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address); | ||
112 | MPL_LOGV(" orientation=\n" | ||
113 | " %2d %2d %2d\n" | ||
114 | " %2d %2d %2d\n" | ||
115 | " %2d %2d %2d\n", | ||
116 | pdata_slave[ii]->orientation[0], | ||
117 | pdata_slave[ii]->orientation[1], | ||
118 | pdata_slave[ii]->orientation[2], | ||
119 | pdata_slave[ii]->orientation[3], | ||
120 | pdata_slave[ii]->orientation[4], | ||
121 | pdata_slave[ii]->orientation[5], | ||
122 | pdata_slave[ii]->orientation[6], | ||
123 | pdata_slave[ii]->orientation[7], | ||
124 | pdata_slave[ii]->orientation[8]); | ||
125 | } | ||
126 | |||
127 | MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config); | ||
128 | MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter); | ||
129 | MPL_LOGV("pdata->orientation =\n" | ||
130 | " %2d %2d %2d\n" | ||
131 | " %2d %2d %2d\n" | ||
132 | " %2d %2d %2d\n", | ||
133 | pdata->orientation[0], pdata->orientation[1], | ||
134 | pdata->orientation[2], pdata->orientation[3], | ||
135 | pdata->orientation[4], pdata->orientation[5], | ||
136 | pdata->orientation[6], pdata->orientation[7], | ||
137 | pdata->orientation[8]); | ||
138 | } | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c new file mode 100644 index 00000000000..f1c228f48fe --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #include "mlsl.h" | ||
21 | #include <linux/i2c.h> | ||
22 | #include "log.h" | ||
23 | #include "mpu6050b1.h" | ||
24 | |||
25 | static int inv_i2c_write(struct i2c_adapter *i2c_adap, | ||
26 | unsigned char address, | ||
27 | unsigned int len, unsigned char const *data) | ||
28 | { | ||
29 | struct i2c_msg msgs[1]; | ||
30 | int res; | ||
31 | |||
32 | if (!data || !i2c_adap) { | ||
33 | LOG_RESULT_LOCATION(-EINVAL); | ||
34 | return -EINVAL; | ||
35 | } | ||
36 | |||
37 | msgs[0].addr = address; | ||
38 | msgs[0].flags = 0; /* write */ | ||
39 | msgs[0].buf = (unsigned char *)data; | ||
40 | msgs[0].len = len; | ||
41 | |||
42 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
43 | if (res < 1) { | ||
44 | if (res == 0) | ||
45 | res = -EIO; | ||
46 | LOG_RESULT_LOCATION(res); | ||
47 | return res; | ||
48 | } else | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int inv_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
53 | unsigned char address, | ||
54 | unsigned char reg, unsigned char value) | ||
55 | { | ||
56 | unsigned char data[2]; | ||
57 | |||
58 | data[0] = reg; | ||
59 | data[1] = value; | ||
60 | return inv_i2c_write(i2c_adap, address, 2, data); | ||
61 | } | ||
62 | |||
63 | static int inv_i2c_read(struct i2c_adapter *i2c_adap, | ||
64 | unsigned char address, unsigned char reg, | ||
65 | unsigned int len, unsigned char *data) | ||
66 | { | ||
67 | struct i2c_msg msgs[2]; | ||
68 | int res; | ||
69 | |||
70 | if (!data || !i2c_adap) { | ||
71 | LOG_RESULT_LOCATION(-EINVAL); | ||
72 | return -EINVAL; | ||
73 | } | ||
74 | |||
75 | msgs[0].addr = address; | ||
76 | msgs[0].flags = 0; /* write */ | ||
77 | msgs[0].buf = ® | ||
78 | msgs[0].len = 1; | ||
79 | |||
80 | msgs[1].addr = address; | ||
81 | msgs[1].flags = I2C_M_RD; | ||
82 | msgs[1].buf = data; | ||
83 | msgs[1].len = len; | ||
84 | |||
85 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
86 | if (res < 2) { | ||
87 | if (res >= 0) | ||
88 | res = -EIO; | ||
89 | LOG_RESULT_LOCATION(res); | ||
90 | return res; | ||
91 | } else | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
96 | unsigned char mpu_addr, | ||
97 | unsigned short mem_addr, | ||
98 | unsigned int len, unsigned char *data) | ||
99 | { | ||
100 | unsigned char bank[2]; | ||
101 | unsigned char addr[2]; | ||
102 | unsigned char buf; | ||
103 | |||
104 | struct i2c_msg msgs[4]; | ||
105 | int res; | ||
106 | |||
107 | if (!data || !i2c_adap) { | ||
108 | LOG_RESULT_LOCATION(-EINVAL); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | |||
112 | bank[0] = MPUREG_BANK_SEL; | ||
113 | bank[1] = mem_addr >> 8; | ||
114 | |||
115 | addr[0] = MPUREG_MEM_START_ADDR; | ||
116 | addr[1] = mem_addr & 0xFF; | ||
117 | |||
118 | buf = MPUREG_MEM_R_W; | ||
119 | |||
120 | /* write message */ | ||
121 | msgs[0].addr = mpu_addr; | ||
122 | msgs[0].flags = 0; | ||
123 | msgs[0].buf = bank; | ||
124 | msgs[0].len = sizeof(bank); | ||
125 | |||
126 | msgs[1].addr = mpu_addr; | ||
127 | msgs[1].flags = 0; | ||
128 | msgs[1].buf = addr; | ||
129 | msgs[1].len = sizeof(addr); | ||
130 | |||
131 | msgs[2].addr = mpu_addr; | ||
132 | msgs[2].flags = 0; | ||
133 | msgs[2].buf = &buf; | ||
134 | msgs[2].len = 1; | ||
135 | |||
136 | msgs[3].addr = mpu_addr; | ||
137 | msgs[3].flags = I2C_M_RD; | ||
138 | msgs[3].buf = data; | ||
139 | msgs[3].len = len; | ||
140 | |||
141 | res = i2c_transfer(i2c_adap, msgs, 4); | ||
142 | if (res != 4) { | ||
143 | if (res >= 0) | ||
144 | res = -EIO; | ||
145 | LOG_RESULT_LOCATION(res); | ||
146 | return res; | ||
147 | } else | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
152 | unsigned char mpu_addr, | ||
153 | unsigned short mem_addr, | ||
154 | unsigned int len, unsigned char const *data) | ||
155 | { | ||
156 | unsigned char bank[2]; | ||
157 | unsigned char addr[2]; | ||
158 | unsigned char buf[513]; | ||
159 | |||
160 | struct i2c_msg msgs[3]; | ||
161 | int res; | ||
162 | |||
163 | if (!data || !i2c_adap) { | ||
164 | LOG_RESULT_LOCATION(-EINVAL); | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | if (len >= (sizeof(buf) - 1)) { | ||
168 | LOG_RESULT_LOCATION(-ENOMEM); | ||
169 | return -ENOMEM; | ||
170 | } | ||
171 | |||
172 | bank[0] = MPUREG_BANK_SEL; | ||
173 | bank[1] = mem_addr >> 8; | ||
174 | |||
175 | addr[0] = MPUREG_MEM_START_ADDR; | ||
176 | addr[1] = mem_addr & 0xFF; | ||
177 | |||
178 | buf[0] = MPUREG_MEM_R_W; | ||
179 | memcpy(buf + 1, data, len); | ||
180 | |||
181 | /* write message */ | ||
182 | msgs[0].addr = mpu_addr; | ||
183 | msgs[0].flags = 0; | ||
184 | msgs[0].buf = bank; | ||
185 | msgs[0].len = sizeof(bank); | ||
186 | |||
187 | msgs[1].addr = mpu_addr; | ||
188 | msgs[1].flags = 0; | ||
189 | msgs[1].buf = addr; | ||
190 | msgs[1].len = sizeof(addr); | ||
191 | |||
192 | msgs[2].addr = mpu_addr; | ||
193 | msgs[2].flags = 0; | ||
194 | msgs[2].buf = (unsigned char *)buf; | ||
195 | msgs[2].len = len + 1; | ||
196 | |||
197 | res = i2c_transfer(i2c_adap, msgs, 3); | ||
198 | if (res != 3) { | ||
199 | if (res >= 0) | ||
200 | res = -EIO; | ||
201 | LOG_RESULT_LOCATION(res); | ||
202 | return res; | ||
203 | } else | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | int inv_serial_single_write( | ||
208 | void *sl_handle, | ||
209 | unsigned char slave_addr, | ||
210 | unsigned char register_addr, | ||
211 | unsigned char data) | ||
212 | { | ||
213 | return inv_i2c_write_register((struct i2c_adapter *)sl_handle, | ||
214 | slave_addr, register_addr, data); | ||
215 | } | ||
216 | EXPORT_SYMBOL(inv_serial_single_write); | ||
217 | |||
218 | int inv_serial_write( | ||
219 | void *sl_handle, | ||
220 | unsigned char slave_addr, | ||
221 | unsigned short length, | ||
222 | unsigned char const *data) | ||
223 | { | ||
224 | int result; | ||
225 | const unsigned short data_length = length - 1; | ||
226 | const unsigned char start_reg_addr = data[0]; | ||
227 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
228 | unsigned short bytes_written = 0; | ||
229 | |||
230 | while (bytes_written < data_length) { | ||
231 | unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE, | ||
232 | data_length - bytes_written); | ||
233 | if (bytes_written == 0) { | ||
234 | result = inv_i2c_write((struct i2c_adapter *) | ||
235 | sl_handle, slave_addr, | ||
236 | 1 + this_len, data); | ||
237 | } else { | ||
238 | /* manually increment register addr between chunks */ | ||
239 | i2c_write[0] = start_reg_addr + bytes_written; | ||
240 | memcpy(&i2c_write[1], &data[1 + bytes_written], | ||
241 | this_len); | ||
242 | result = inv_i2c_write((struct i2c_adapter *) | ||
243 | sl_handle, slave_addr, | ||
244 | 1 + this_len, i2c_write); | ||
245 | } | ||
246 | if (result) { | ||
247 | LOG_RESULT_LOCATION(result); | ||
248 | return result; | ||
249 | } | ||
250 | bytes_written += this_len; | ||
251 | } | ||
252 | return 0; | ||
253 | } | ||
254 | EXPORT_SYMBOL(inv_serial_write); | ||
255 | |||
256 | int inv_serial_read( | ||
257 | void *sl_handle, | ||
258 | unsigned char slave_addr, | ||
259 | unsigned char register_addr, | ||
260 | unsigned short length, | ||
261 | unsigned char *data) | ||
262 | { | ||
263 | int result; | ||
264 | unsigned short bytes_read = 0; | ||
265 | |||
266 | if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR | ||
267 | && (register_addr == MPUREG_FIFO_R_W || | ||
268 | register_addr == MPUREG_MEM_R_W)) { | ||
269 | LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER); | ||
270 | return INV_ERROR_INVALID_PARAMETER; | ||
271 | } | ||
272 | |||
273 | while (bytes_read < length) { | ||
274 | unsigned short this_len = | ||
275 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
276 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
277 | slave_addr, register_addr + bytes_read, | ||
278 | this_len, &data[bytes_read]); | ||
279 | if (result) { | ||
280 | LOG_RESULT_LOCATION(result); | ||
281 | return result; | ||
282 | } | ||
283 | bytes_read += this_len; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | EXPORT_SYMBOL(inv_serial_read); | ||
288 | |||
289 | int inv_serial_write_mem( | ||
290 | void *sl_handle, | ||
291 | unsigned char slave_addr, | ||
292 | unsigned short mem_addr, | ||
293 | unsigned short length, | ||
294 | unsigned char const *data) | ||
295 | { | ||
296 | int result; | ||
297 | unsigned short bytes_written = 0; | ||
298 | |||
299 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
300 | pr_err("memory read length (%d B) extends beyond its" | ||
301 | " limits (%d) if started at location %d\n", length, | ||
302 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
303 | return INV_ERROR_INVALID_PARAMETER; | ||
304 | } | ||
305 | while (bytes_written < length) { | ||
306 | unsigned short this_len = | ||
307 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
308 | result = mpu_memory_write((struct i2c_adapter *)sl_handle, | ||
309 | slave_addr, mem_addr + bytes_written, | ||
310 | this_len, &data[bytes_written]); | ||
311 | if (result) { | ||
312 | LOG_RESULT_LOCATION(result); | ||
313 | return result; | ||
314 | } | ||
315 | bytes_written += this_len; | ||
316 | } | ||
317 | return 0; | ||
318 | } | ||
319 | EXPORT_SYMBOL(inv_serial_write_mem); | ||
320 | |||
321 | int inv_serial_read_mem( | ||
322 | void *sl_handle, | ||
323 | unsigned char slave_addr, | ||
324 | unsigned short mem_addr, | ||
325 | unsigned short length, | ||
326 | unsigned char *data) | ||
327 | { | ||
328 | int result; | ||
329 | unsigned short bytes_read = 0; | ||
330 | |||
331 | if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
332 | printk | ||
333 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
334 | "if started at location %d\n", length, | ||
335 | MPU_MEM_BANK_SIZE, mem_addr & 0xFF); | ||
336 | return INV_ERROR_INVALID_PARAMETER; | ||
337 | } | ||
338 | while (bytes_read < length) { | ||
339 | unsigned short this_len = | ||
340 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
341 | result = | ||
342 | mpu_memory_read((struct i2c_adapter *)sl_handle, | ||
343 | slave_addr, mem_addr + bytes_read, | ||
344 | this_len, &data[bytes_read]); | ||
345 | if (result) { | ||
346 | LOG_RESULT_LOCATION(result); | ||
347 | return result; | ||
348 | } | ||
349 | bytes_read += this_len; | ||
350 | } | ||
351 | return 0; | ||
352 | } | ||
353 | EXPORT_SYMBOL(inv_serial_read_mem); | ||
354 | |||
355 | int inv_serial_write_fifo( | ||
356 | void *sl_handle, | ||
357 | unsigned char slave_addr, | ||
358 | unsigned short length, | ||
359 | unsigned char const *data) | ||
360 | { | ||
361 | int result; | ||
362 | unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
363 | unsigned short bytes_written = 0; | ||
364 | |||
365 | if (length > FIFO_HW_SIZE) { | ||
366 | printk(KERN_ERR | ||
367 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
368 | return INV_ERROR_INVALID_PARAMETER; | ||
369 | } | ||
370 | while (bytes_written < length) { | ||
371 | unsigned short this_len = | ||
372 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written); | ||
373 | i2c_write[0] = MPUREG_FIFO_R_W; | ||
374 | memcpy(&i2c_write[1], &data[bytes_written], this_len); | ||
375 | result = inv_i2c_write((struct i2c_adapter *)sl_handle, | ||
376 | slave_addr, this_len + 1, i2c_write); | ||
377 | if (result) { | ||
378 | LOG_RESULT_LOCATION(result); | ||
379 | return result; | ||
380 | } | ||
381 | bytes_written += this_len; | ||
382 | } | ||
383 | return 0; | ||
384 | } | ||
385 | EXPORT_SYMBOL(inv_serial_write_fifo); | ||
386 | |||
387 | int inv_serial_read_fifo( | ||
388 | void *sl_handle, | ||
389 | unsigned char slave_addr, | ||
390 | unsigned short length, | ||
391 | unsigned char *data) | ||
392 | { | ||
393 | int result; | ||
394 | unsigned short bytes_read = 0; | ||
395 | |||
396 | if (length > FIFO_HW_SIZE) { | ||
397 | printk(KERN_ERR | ||
398 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
399 | return INV_ERROR_INVALID_PARAMETER; | ||
400 | } | ||
401 | while (bytes_read < length) { | ||
402 | unsigned short this_len = | ||
403 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read); | ||
404 | result = inv_i2c_read((struct i2c_adapter *)sl_handle, | ||
405 | slave_addr, MPUREG_FIFO_R_W, this_len, | ||
406 | &data[bytes_read]); | ||
407 | if (result) { | ||
408 | LOG_RESULT_LOCATION(result); | ||
409 | return result; | ||
410 | } | ||
411 | bytes_read += this_len; | ||
412 | } | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | EXPORT_SYMBOL(inv_serial_read_fifo); | ||
417 | |||
418 | /** | ||
419 | * @} | ||
420 | */ | ||
diff --git a/drivers/misc/inv_mpu/mpu6050/mpu-dev.c b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c new file mode 100644 index 00000000000..e171dc2a7ef --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c | |||
@@ -0,0 +1,1309 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/i2c-dev.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/stat.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/signal.h> | ||
30 | #include <linux/miscdevice.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/version.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/suspend.h> | ||
36 | #include <linux/poll.h> | ||
37 | |||
38 | #include <linux/errno.h> | ||
39 | #include <linux/fs.h> | ||
40 | #include <linux/mm.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/wait.h> | ||
43 | #include <linux/uaccess.h> | ||
44 | #include <linux/io.h> | ||
45 | |||
46 | #include "mpuirq.h" | ||
47 | #include "slaveirq.h" | ||
48 | #include "mlsl.h" | ||
49 | #include "mldl_cfg.h" | ||
50 | #include <linux/mpu.h> | ||
51 | |||
52 | #include "accel/mpu6050.h" | ||
53 | |||
54 | /* Platform data for the MPU */ | ||
55 | struct mpu_private_data { | ||
56 | struct miscdevice dev; | ||
57 | struct i2c_client *client; | ||
58 | |||
59 | /* mldl_cfg data */ | ||
60 | struct mldl_cfg mldl_cfg; | ||
61 | struct mpu_ram mpu_ram; | ||
62 | struct mpu_gyro_cfg mpu_gyro_cfg; | ||
63 | struct mpu_offsets mpu_offsets; | ||
64 | struct mpu_chip_info mpu_chip_info; | ||
65 | struct inv_mpu_cfg inv_mpu_cfg; | ||
66 | struct inv_mpu_state inv_mpu_state; | ||
67 | |||
68 | struct mutex mutex; | ||
69 | wait_queue_head_t mpu_event_wait; | ||
70 | struct completion completion; | ||
71 | struct timer_list timeout; | ||
72 | struct notifier_block nb; | ||
73 | struct mpuirq_data mpu_pm_event; | ||
74 | int response_timeout; /* In seconds */ | ||
75 | unsigned long event; | ||
76 | int pid; | ||
77 | struct module *slave_modules[EXT_SLAVE_NUM_TYPES]; | ||
78 | }; | ||
79 | |||
80 | struct mpu_private_data *mpu_private_data; | ||
81 | |||
82 | static void mpu_pm_timeout(u_long data) | ||
83 | { | ||
84 | struct mpu_private_data *mpu = (struct mpu_private_data *)data; | ||
85 | struct i2c_client *client = mpu->client; | ||
86 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
87 | complete(&mpu->completion); | ||
88 | } | ||
89 | |||
90 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
91 | unsigned long event, void *unused) | ||
92 | { | ||
93 | struct mpu_private_data *mpu = | ||
94 | container_of(nb, struct mpu_private_data, nb); | ||
95 | struct i2c_client *client = mpu->client; | ||
96 | struct timeval event_time; | ||
97 | dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event); | ||
98 | |||
99 | /* Prevent the file handle from being closed before we initialize | ||
100 | the completion event */ | ||
101 | mutex_lock(&mpu->mutex); | ||
102 | if (!(mpu->pid) || | ||
103 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
104 | mutex_unlock(&mpu->mutex); | ||
105 | return NOTIFY_OK; | ||
106 | } | ||
107 | |||
108 | if (event == PM_SUSPEND_PREPARE) | ||
109 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
110 | if (event == PM_POST_SUSPEND) | ||
111 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
112 | |||
113 | do_gettimeofday(&event_time); | ||
114 | mpu->mpu_pm_event.interruptcount++; | ||
115 | mpu->mpu_pm_event.irqtime = | ||
116 | (((long long)event_time.tv_sec) << 32) + event_time.tv_usec; | ||
117 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
118 | mpu->mpu_pm_event.data = mpu->event; | ||
119 | |||
120 | if (mpu->response_timeout > 0) { | ||
121 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
122 | add_timer(&mpu->timeout); | ||
123 | } | ||
124 | INIT_COMPLETION(mpu->completion); | ||
125 | mutex_unlock(&mpu->mutex); | ||
126 | |||
127 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
128 | wait_for_completion(&mpu->completion); | ||
129 | del_timer_sync(&mpu->timeout); | ||
130 | dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
131 | return NOTIFY_OK; | ||
132 | } | ||
133 | |||
134 | static int mpu_dev_open(struct inode *inode, struct file *file) | ||
135 | { | ||
136 | struct mpu_private_data *mpu = | ||
137 | container_of(file->private_data, struct mpu_private_data, dev); | ||
138 | struct i2c_client *client = mpu->client; | ||
139 | int result; | ||
140 | int ii; | ||
141 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
142 | dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid); | ||
143 | |||
144 | result = mutex_lock_interruptible(&mpu->mutex); | ||
145 | if (mpu->pid) { | ||
146 | mutex_unlock(&mpu->mutex); | ||
147 | return -EBUSY; | ||
148 | } | ||
149 | mpu->pid = current->pid; | ||
150 | |||
151 | /* Reset the sensors to the default */ | ||
152 | if (result) { | ||
153 | dev_err(&client->adapter->dev, | ||
154 | "%s: mutex_lock_interruptible returned %d\n", | ||
155 | __func__, result); | ||
156 | return result; | ||
157 | } | ||
158 | |||
159 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
160 | __module_get(mpu->slave_modules[ii]); | ||
161 | |||
162 | mutex_unlock(&mpu->mutex); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
167 | static int mpu_release(struct inode *inode, struct file *file) | ||
168 | { | ||
169 | struct mpu_private_data *mpu = | ||
170 | container_of(file->private_data, struct mpu_private_data, dev); | ||
171 | struct i2c_client *client = mpu->client; | ||
172 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
173 | int result = 0; | ||
174 | int ii; | ||
175 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
176 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
177 | |||
178 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
179 | if (!pdata_slave[ii]) | ||
180 | slave_adapter[ii] = NULL; | ||
181 | else | ||
182 | slave_adapter[ii] = | ||
183 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
184 | } | ||
185 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
186 | |||
187 | mutex_lock(&mpu->mutex); | ||
188 | mldl_cfg->inv_mpu_cfg->requested_sensors = 0; | ||
189 | result = inv_mpu_suspend(mldl_cfg, | ||
190 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
191 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
192 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
193 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
194 | INV_ALL_SENSORS); | ||
195 | mpu->pid = 0; | ||
196 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
197 | module_put(mpu->slave_modules[ii]); | ||
198 | |||
199 | mutex_unlock(&mpu->mutex); | ||
200 | complete(&mpu->completion); | ||
201 | dev_dbg(&client->adapter->dev, "mpu_release\n"); | ||
202 | |||
203 | return result; | ||
204 | } | ||
205 | |||
206 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
207 | static ssize_t mpu_read(struct file *file, | ||
208 | char __user *buf, size_t count, loff_t *offset) | ||
209 | { | ||
210 | struct mpu_private_data *mpu = | ||
211 | container_of(file->private_data, struct mpu_private_data, dev); | ||
212 | struct i2c_client *client = mpu->client; | ||
213 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
214 | int err; | ||
215 | |||
216 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
217 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
218 | |||
219 | if (!mpu->event || !buf | ||
220 | || count < sizeof(mpu->mpu_pm_event)) | ||
221 | return 0; | ||
222 | |||
223 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
224 | if (err) { | ||
225 | dev_err(&client->adapter->dev, | ||
226 | "Copy to user returned %d\n", err); | ||
227 | return -EFAULT; | ||
228 | } | ||
229 | mpu->event = 0; | ||
230 | return len; | ||
231 | } | ||
232 | |||
233 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
234 | { | ||
235 | struct mpu_private_data *mpu = | ||
236 | container_of(file->private_data, struct mpu_private_data, dev); | ||
237 | int mask = 0; | ||
238 | |||
239 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
240 | if (mpu->event) | ||
241 | mask |= POLLIN | POLLRDNORM; | ||
242 | return mask; | ||
243 | } | ||
244 | |||
245 | static int mpu_dev_ioctl_get_ext_slave_platform_data( | ||
246 | struct i2c_client *client, | ||
247 | struct ext_slave_platform_data __user *arg) | ||
248 | { | ||
249 | struct mpu_private_data *mpu = | ||
250 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
251 | struct ext_slave_platform_data *pdata_slave; | ||
252 | struct ext_slave_platform_data local_pdata_slave; | ||
253 | |||
254 | if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave))) | ||
255 | return -EFAULT; | ||
256 | |||
257 | if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
258 | return -EINVAL; | ||
259 | |||
260 | pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type]; | ||
261 | /* All but private data and irq_data */ | ||
262 | if (!pdata_slave) | ||
263 | return -ENODEV; | ||
264 | if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave))) | ||
265 | return -EFAULT; | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static int mpu_dev_ioctl_get_mpu_platform_data( | ||
270 | struct i2c_client *client, | ||
271 | struct mpu_platform_data __user *arg) | ||
272 | { | ||
273 | struct mpu_private_data *mpu = | ||
274 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
275 | struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
276 | |||
277 | if (copy_to_user(arg, pdata, sizeof(*pdata))) | ||
278 | return -EFAULT; | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int mpu_dev_ioctl_get_ext_slave_descr( | ||
283 | struct i2c_client *client, | ||
284 | struct ext_slave_descr __user *arg) | ||
285 | { | ||
286 | struct mpu_private_data *mpu = | ||
287 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
288 | struct ext_slave_descr *slave; | ||
289 | struct ext_slave_descr local_slave; | ||
290 | |||
291 | if (copy_from_user(&local_slave, arg, sizeof(local_slave))) | ||
292 | return -EFAULT; | ||
293 | |||
294 | if (local_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
295 | return -EINVAL; | ||
296 | |||
297 | slave = mpu->mldl_cfg.slave[local_slave.type]; | ||
298 | /* All but private data and irq_data */ | ||
299 | if (!slave) | ||
300 | return -ENODEV; | ||
301 | if (copy_to_user(arg, slave, sizeof(*slave))) | ||
302 | return -EFAULT; | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * slave_config() - Pass a requested slave configuration to the slave sensor | ||
309 | * | ||
310 | * @adapter the adaptor to use to communicate with the slave | ||
311 | * @mldl_cfg the mldl configuration structuer | ||
312 | * @slave pointer to the slave descriptor | ||
313 | * @usr_config The configuration to pass to the slave sensor | ||
314 | * | ||
315 | * returns 0 or non-zero error code | ||
316 | */ | ||
317 | static int inv_mpu_config(struct mldl_cfg *mldl_cfg, | ||
318 | void *gyro_adapter, | ||
319 | struct ext_slave_config __user *usr_config) | ||
320 | { | ||
321 | int retval = 0; | ||
322 | struct ext_slave_config config; | ||
323 | |||
324 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
325 | if (retval) | ||
326 | return -EFAULT; | ||
327 | |||
328 | if (config.len && config.data) { | ||
329 | void *data; | ||
330 | data = kmalloc(config.len, GFP_KERNEL); | ||
331 | if (!data) | ||
332 | return -ENOMEM; | ||
333 | |||
334 | retval = copy_from_user(data, | ||
335 | (void __user *)config.data, config.len); | ||
336 | if (retval) { | ||
337 | retval = -EFAULT; | ||
338 | kfree(data); | ||
339 | return retval; | ||
340 | } | ||
341 | config.data = data; | ||
342 | } | ||
343 | retval = gyro_config(gyro_adapter, mldl_cfg, &config); | ||
344 | kfree(config.data); | ||
345 | return retval; | ||
346 | } | ||
347 | |||
348 | static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg, | ||
349 | void *gyro_adapter, | ||
350 | struct ext_slave_config __user *usr_config) | ||
351 | { | ||
352 | int retval = 0; | ||
353 | struct ext_slave_config config; | ||
354 | void *user_data; | ||
355 | |||
356 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
357 | if (retval) | ||
358 | return -EFAULT; | ||
359 | |||
360 | user_data = config.data; | ||
361 | if (config.len && config.data) { | ||
362 | void *data; | ||
363 | data = kmalloc(config.len, GFP_KERNEL); | ||
364 | if (!data) | ||
365 | return -ENOMEM; | ||
366 | |||
367 | retval = copy_from_user(data, | ||
368 | (void __user *)config.data, config.len); | ||
369 | if (retval) { | ||
370 | retval = -EFAULT; | ||
371 | kfree(data); | ||
372 | return retval; | ||
373 | } | ||
374 | config.data = data; | ||
375 | } | ||
376 | retval = gyro_get_config(gyro_adapter, mldl_cfg, &config); | ||
377 | if (!retval) | ||
378 | retval = copy_to_user((unsigned char __user *)user_data, | ||
379 | config.data, config.len); | ||
380 | kfree(config.data); | ||
381 | return retval; | ||
382 | } | ||
383 | |||
384 | static int slave_config(struct mldl_cfg *mldl_cfg, | ||
385 | void *gyro_adapter, | ||
386 | void *slave_adapter, | ||
387 | struct ext_slave_descr *slave, | ||
388 | struct ext_slave_platform_data *pdata, | ||
389 | struct ext_slave_config __user *usr_config) | ||
390 | { | ||
391 | int retval = 0; | ||
392 | struct ext_slave_config config; | ||
393 | if ((!slave) || (!slave->config)) | ||
394 | return -ENODEV; | ||
395 | |||
396 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
397 | if (retval) | ||
398 | return -EFAULT; | ||
399 | |||
400 | if (config.len && config.data) { | ||
401 | void *data; | ||
402 | data = kmalloc(config.len, GFP_KERNEL); | ||
403 | if (!data) | ||
404 | return -ENOMEM; | ||
405 | |||
406 | retval = copy_from_user(data, | ||
407 | (void __user *)config.data, config.len); | ||
408 | if (retval) { | ||
409 | retval = -EFAULT; | ||
410 | kfree(data); | ||
411 | return retval; | ||
412 | } | ||
413 | config.data = data; | ||
414 | } | ||
415 | retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter, | ||
416 | &config, slave, pdata); | ||
417 | kfree(config.data); | ||
418 | return retval; | ||
419 | } | ||
420 | |||
421 | static int slave_get_config(struct mldl_cfg *mldl_cfg, | ||
422 | void *gyro_adapter, | ||
423 | void *slave_adapter, | ||
424 | struct ext_slave_descr *slave, | ||
425 | struct ext_slave_platform_data *pdata, | ||
426 | struct ext_slave_config __user *usr_config) | ||
427 | { | ||
428 | int retval = 0; | ||
429 | struct ext_slave_config config; | ||
430 | void *user_data; | ||
431 | if (!(slave) || !(slave->get_config)) | ||
432 | return -ENODEV; | ||
433 | |||
434 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
435 | if (retval) | ||
436 | return -EFAULT; | ||
437 | |||
438 | user_data = config.data; | ||
439 | if (config.len && config.data) { | ||
440 | void *data; | ||
441 | data = kmalloc(config.len, GFP_KERNEL); | ||
442 | if (!data) | ||
443 | return -ENOMEM; | ||
444 | |||
445 | retval = copy_from_user(data, | ||
446 | (void __user *)config.data, config.len); | ||
447 | if (retval) { | ||
448 | retval = -EFAULT; | ||
449 | kfree(data); | ||
450 | return retval; | ||
451 | } | ||
452 | config.data = data; | ||
453 | } | ||
454 | retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter, | ||
455 | slave_adapter, &config, slave, pdata); | ||
456 | if (retval) { | ||
457 | kfree(config.data); | ||
458 | return retval; | ||
459 | } | ||
460 | retval = copy_to_user((unsigned char __user *)user_data, | ||
461 | config.data, config.len); | ||
462 | kfree(config.data); | ||
463 | return retval; | ||
464 | } | ||
465 | |||
466 | static int inv_slave_read(struct mldl_cfg *mldl_cfg, | ||
467 | void *gyro_adapter, | ||
468 | void *slave_adapter, | ||
469 | struct ext_slave_descr *slave, | ||
470 | struct ext_slave_platform_data *pdata, | ||
471 | void __user *usr_data) | ||
472 | { | ||
473 | int retval; | ||
474 | unsigned char *data; | ||
475 | data = kzalloc(slave->read_len, GFP_KERNEL); | ||
476 | if (!data) | ||
477 | return -EFAULT; | ||
478 | |||
479 | retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter, | ||
480 | slave, pdata, data); | ||
481 | |||
482 | if ((!retval) && | ||
483 | (copy_to_user((unsigned char __user *)usr_data, | ||
484 | data, slave->read_len))) | ||
485 | retval = -EFAULT; | ||
486 | |||
487 | kfree(data); | ||
488 | return retval; | ||
489 | } | ||
490 | |||
491 | static int mpu_handle_mlsl(void *sl_handle, | ||
492 | unsigned char addr, | ||
493 | unsigned int cmd, | ||
494 | struct mpu_read_write __user *usr_msg) | ||
495 | { | ||
496 | int retval = 0; | ||
497 | struct mpu_read_write msg; | ||
498 | unsigned char *user_data; | ||
499 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
500 | if (retval) | ||
501 | return -EFAULT; | ||
502 | |||
503 | user_data = msg.data; | ||
504 | if (msg.length && msg.data) { | ||
505 | unsigned char *data; | ||
506 | data = kmalloc(msg.length, GFP_KERNEL); | ||
507 | if (!data) | ||
508 | return -ENOMEM; | ||
509 | |||
510 | retval = copy_from_user(data, | ||
511 | (void __user *)msg.data, msg.length); | ||
512 | if (retval) { | ||
513 | retval = -EFAULT; | ||
514 | kfree(data); | ||
515 | return retval; | ||
516 | } | ||
517 | msg.data = data; | ||
518 | } else { | ||
519 | return -EPERM; | ||
520 | } | ||
521 | |||
522 | switch (cmd) { | ||
523 | case MPU_READ: | ||
524 | retval = inv_serial_read(sl_handle, addr, | ||
525 | msg.address, msg.length, msg.data); | ||
526 | break; | ||
527 | case MPU_WRITE: | ||
528 | retval = inv_serial_write(sl_handle, addr, | ||
529 | msg.length, msg.data); | ||
530 | break; | ||
531 | case MPU_READ_MEM: | ||
532 | retval = inv_serial_read_mem(sl_handle, addr, | ||
533 | msg.address, msg.length, msg.data); | ||
534 | break; | ||
535 | case MPU_WRITE_MEM: | ||
536 | retval = inv_serial_write_mem(sl_handle, addr, | ||
537 | msg.address, msg.length, | ||
538 | msg.data); | ||
539 | break; | ||
540 | case MPU_READ_FIFO: | ||
541 | retval = inv_serial_read_fifo(sl_handle, addr, | ||
542 | msg.length, msg.data); | ||
543 | break; | ||
544 | case MPU_WRITE_FIFO: | ||
545 | retval = inv_serial_write_fifo(sl_handle, addr, | ||
546 | msg.length, msg.data); | ||
547 | break; | ||
548 | |||
549 | }; | ||
550 | if (retval) { | ||
551 | dev_err(&((struct i2c_adapter *)sl_handle)->dev, | ||
552 | "%s: i2c %d error %d\n", | ||
553 | __func__, cmd, retval); | ||
554 | kfree(msg.data); | ||
555 | return retval; | ||
556 | } | ||
557 | retval = copy_to_user((unsigned char __user *)user_data, | ||
558 | msg.data, msg.length); | ||
559 | kfree(msg.data); | ||
560 | return retval; | ||
561 | } | ||
562 | |||
563 | /* ioctl - I/O control */ | ||
564 | static long mpu_dev_ioctl(struct file *file, | ||
565 | unsigned int cmd, unsigned long arg) | ||
566 | { | ||
567 | struct mpu_private_data *mpu = | ||
568 | container_of(file->private_data, struct mpu_private_data, dev); | ||
569 | struct i2c_client *client = mpu->client; | ||
570 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
571 | int retval = 0; | ||
572 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
573 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
574 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
575 | int ii; | ||
576 | |||
577 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
578 | if (!pdata_slave[ii]) | ||
579 | slave_adapter[ii] = NULL; | ||
580 | else | ||
581 | slave_adapter[ii] = | ||
582 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
583 | } | ||
584 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
585 | |||
586 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
587 | if (retval) { | ||
588 | dev_err(&client->adapter->dev, | ||
589 | "%s: mutex_lock_interruptible returned %d\n", | ||
590 | __func__, retval); | ||
591 | return retval; | ||
592 | } | ||
593 | |||
594 | switch (cmd) { | ||
595 | case MPU_GET_EXT_SLAVE_PLATFORM_DATA: | ||
596 | retval = mpu_dev_ioctl_get_ext_slave_platform_data( | ||
597 | client, | ||
598 | (struct ext_slave_platform_data __user *)arg); | ||
599 | break; | ||
600 | case MPU_GET_MPU_PLATFORM_DATA: | ||
601 | retval = mpu_dev_ioctl_get_mpu_platform_data( | ||
602 | client, | ||
603 | (struct mpu_platform_data __user *)arg); | ||
604 | break; | ||
605 | case MPU_GET_EXT_SLAVE_DESCR: | ||
606 | retval = mpu_dev_ioctl_get_ext_slave_descr( | ||
607 | client, | ||
608 | (struct ext_slave_descr __user *)arg); | ||
609 | break; | ||
610 | case MPU_READ: | ||
611 | case MPU_WRITE: | ||
612 | case MPU_READ_MEM: | ||
613 | case MPU_WRITE_MEM: | ||
614 | case MPU_READ_FIFO: | ||
615 | case MPU_WRITE_FIFO: | ||
616 | retval = mpu_handle_mlsl( | ||
617 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
618 | mldl_cfg->mpu_chip_info->addr, cmd, | ||
619 | (struct mpu_read_write __user *)arg); | ||
620 | break; | ||
621 | case MPU_CONFIG_GYRO: | ||
622 | retval = inv_mpu_config( | ||
623 | mldl_cfg, | ||
624 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
625 | (struct ext_slave_config __user *)arg); | ||
626 | break; | ||
627 | case MPU_CONFIG_ACCEL: | ||
628 | retval = slave_config( | ||
629 | mldl_cfg, | ||
630 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
631 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
632 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
633 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
634 | (struct ext_slave_config __user *)arg); | ||
635 | break; | ||
636 | case MPU_CONFIG_COMPASS: | ||
637 | retval = slave_config( | ||
638 | mldl_cfg, | ||
639 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
640 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
641 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
642 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
643 | (struct ext_slave_config __user *)arg); | ||
644 | break; | ||
645 | case MPU_CONFIG_PRESSURE: | ||
646 | retval = slave_config( | ||
647 | mldl_cfg, | ||
648 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
649 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
650 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
651 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
652 | (struct ext_slave_config __user *)arg); | ||
653 | break; | ||
654 | case MPU_GET_CONFIG_GYRO: | ||
655 | retval = inv_mpu_get_config( | ||
656 | mldl_cfg, | ||
657 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
658 | (struct ext_slave_config __user *)arg); | ||
659 | break; | ||
660 | case MPU_GET_CONFIG_ACCEL: | ||
661 | retval = slave_get_config( | ||
662 | mldl_cfg, | ||
663 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
664 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
665 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
666 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
667 | (struct ext_slave_config __user *)arg); | ||
668 | break; | ||
669 | case MPU_GET_CONFIG_COMPASS: | ||
670 | retval = slave_get_config( | ||
671 | mldl_cfg, | ||
672 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
673 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
674 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
675 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
676 | (struct ext_slave_config __user *)arg); | ||
677 | break; | ||
678 | case MPU_GET_CONFIG_PRESSURE: | ||
679 | retval = slave_get_config( | ||
680 | mldl_cfg, | ||
681 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
682 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
683 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
684 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
685 | (struct ext_slave_config __user *)arg); | ||
686 | break; | ||
687 | case MPU_SUSPEND: | ||
688 | retval = inv_mpu_suspend( | ||
689 | mldl_cfg, | ||
690 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
691 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
692 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
693 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
694 | arg); | ||
695 | break; | ||
696 | case MPU_RESUME: | ||
697 | retval = inv_mpu_resume( | ||
698 | mldl_cfg, | ||
699 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
700 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
701 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
702 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
703 | arg); | ||
704 | break; | ||
705 | case MPU_PM_EVENT_HANDLED: | ||
706 | dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd); | ||
707 | complete(&mpu->completion); | ||
708 | break; | ||
709 | case MPU_READ_ACCEL: | ||
710 | retval = inv_slave_read( | ||
711 | mldl_cfg, | ||
712 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
713 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
714 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
715 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
716 | (unsigned char __user *)arg); | ||
717 | break; | ||
718 | case MPU_READ_COMPASS: | ||
719 | retval = inv_slave_read( | ||
720 | mldl_cfg, | ||
721 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
722 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
723 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
724 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
725 | (unsigned char __user *)arg); | ||
726 | break; | ||
727 | case MPU_READ_PRESSURE: | ||
728 | retval = inv_slave_read( | ||
729 | mldl_cfg, | ||
730 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
731 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
732 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
733 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
734 | (unsigned char __user *)arg); | ||
735 | break; | ||
736 | case MPU_GET_REQUESTED_SENSORS: | ||
737 | if (copy_to_user( | ||
738 | (__u32 __user *)arg, | ||
739 | &mldl_cfg->inv_mpu_cfg->requested_sensors, | ||
740 | sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors))) | ||
741 | retval = -EFAULT; | ||
742 | break; | ||
743 | case MPU_SET_REQUESTED_SENSORS: | ||
744 | mldl_cfg->inv_mpu_cfg->requested_sensors = arg; | ||
745 | break; | ||
746 | case MPU_GET_IGNORE_SYSTEM_SUSPEND: | ||
747 | if (copy_to_user( | ||
748 | (unsigned char __user *)arg, | ||
749 | &mldl_cfg->inv_mpu_cfg->ignore_system_suspend, | ||
750 | sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend))) | ||
751 | retval = -EFAULT; | ||
752 | break; | ||
753 | case MPU_SET_IGNORE_SYSTEM_SUSPEND: | ||
754 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg; | ||
755 | break; | ||
756 | case MPU_GET_MLDL_STATUS: | ||
757 | if (copy_to_user( | ||
758 | (unsigned char __user *)arg, | ||
759 | &mldl_cfg->inv_mpu_state->status, | ||
760 | sizeof(mldl_cfg->inv_mpu_state->status))) | ||
761 | retval = -EFAULT; | ||
762 | break; | ||
763 | case MPU_GET_I2C_SLAVES_ENABLED: | ||
764 | if (copy_to_user( | ||
765 | (unsigned char __user *)arg, | ||
766 | &mldl_cfg->inv_mpu_state->i2c_slaves_enabled, | ||
767 | sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled))) | ||
768 | retval = -EFAULT; | ||
769 | break; | ||
770 | default: | ||
771 | dev_err(&client->adapter->dev, | ||
772 | "%s: Unknown cmd %x, arg %lu\n", | ||
773 | __func__, cmd, arg); | ||
774 | retval = -EINVAL; | ||
775 | }; | ||
776 | |||
777 | mutex_unlock(&mpu->mutex); | ||
778 | dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n", | ||
779 | __func__, cmd, arg, retval); | ||
780 | |||
781 | if (retval > 0) | ||
782 | retval = -retval; | ||
783 | |||
784 | return retval; | ||
785 | } | ||
786 | |||
787 | void mpu_shutdown(struct i2c_client *client) | ||
788 | { | ||
789 | struct mpu_private_data *mpu = | ||
790 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
791 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
792 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
793 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
794 | int ii; | ||
795 | |||
796 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
797 | if (!pdata_slave[ii]) | ||
798 | slave_adapter[ii] = NULL; | ||
799 | else | ||
800 | slave_adapter[ii] = | ||
801 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
802 | } | ||
803 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
804 | |||
805 | mutex_lock(&mpu->mutex); | ||
806 | (void)inv_mpu_suspend(mldl_cfg, | ||
807 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
808 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
809 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
810 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
811 | INV_ALL_SENSORS); | ||
812 | mutex_unlock(&mpu->mutex); | ||
813 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
814 | } | ||
815 | |||
816 | int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg) | ||
817 | { | ||
818 | struct mpu_private_data *mpu = | ||
819 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
820 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
821 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
822 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
823 | int ii; | ||
824 | |||
825 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
826 | if (!pdata_slave[ii]) | ||
827 | slave_adapter[ii] = NULL; | ||
828 | else | ||
829 | slave_adapter[ii] = | ||
830 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
831 | } | ||
832 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
833 | |||
834 | mutex_lock(&mpu->mutex); | ||
835 | if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
836 | dev_dbg(&client->adapter->dev, | ||
837 | "%s: suspending on event %d\n", __func__, mesg.event); | ||
838 | (void)inv_mpu_suspend(mldl_cfg, | ||
839 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
840 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
841 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
842 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
843 | INV_ALL_SENSORS); | ||
844 | } else { | ||
845 | dev_dbg(&client->adapter->dev, | ||
846 | "%s: Already suspended %d\n", __func__, mesg.event); | ||
847 | } | ||
848 | mutex_unlock(&mpu->mutex); | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | int mpu_dev_resume(struct i2c_client *client) | ||
853 | { | ||
854 | struct mpu_private_data *mpu = | ||
855 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
856 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
857 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
858 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
859 | int ii; | ||
860 | |||
861 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
862 | if (!pdata_slave[ii]) | ||
863 | slave_adapter[ii] = NULL; | ||
864 | else | ||
865 | slave_adapter[ii] = | ||
866 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
867 | } | ||
868 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
869 | |||
870 | mutex_lock(&mpu->mutex); | ||
871 | if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
872 | (void)inv_mpu_resume(mldl_cfg, | ||
873 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
874 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
875 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
876 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
877 | mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
878 | dev_dbg(&client->adapter->dev, | ||
879 | "%s for pid %d\n", __func__, mpu->pid); | ||
880 | } | ||
881 | mutex_unlock(&mpu->mutex); | ||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | /* define which file operations are supported */ | ||
886 | static const struct file_operations mpu_fops = { | ||
887 | .owner = THIS_MODULE, | ||
888 | .read = mpu_read, | ||
889 | .poll = mpu_poll, | ||
890 | .unlocked_ioctl = mpu_dev_ioctl, | ||
891 | .open = mpu_dev_open, | ||
892 | .release = mpu_release, | ||
893 | }; | ||
894 | |||
895 | int inv_mpu_register_slave(struct module *slave_module, | ||
896 | struct i2c_client *slave_client, | ||
897 | struct ext_slave_platform_data *slave_pdata, | ||
898 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
899 | { | ||
900 | struct mpu_private_data *mpu = mpu_private_data; | ||
901 | struct mldl_cfg *mldl_cfg; | ||
902 | struct ext_slave_descr *slave_descr; | ||
903 | struct ext_slave_platform_data **pdata_slave; | ||
904 | char *irq_name = NULL; | ||
905 | int result = 0; | ||
906 | |||
907 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
908 | return -EINVAL; | ||
909 | |||
910 | if (!mpu) { | ||
911 | dev_err(&slave_client->adapter->dev, | ||
912 | "%s: Null mpu_private_data\n", __func__); | ||
913 | return -EINVAL; | ||
914 | } | ||
915 | mldl_cfg = &mpu->mldl_cfg; | ||
916 | pdata_slave = mldl_cfg->pdata_slave; | ||
917 | slave_descr = get_slave_descr(); | ||
918 | |||
919 | if (!slave_descr) { | ||
920 | dev_err(&slave_client->adapter->dev, | ||
921 | "%s: Null ext_slave_descr\n", __func__); | ||
922 | return -EINVAL; | ||
923 | } | ||
924 | |||
925 | mutex_lock(&mpu->mutex); | ||
926 | if (mpu->pid) { | ||
927 | mutex_unlock(&mpu->mutex); | ||
928 | return -EBUSY; | ||
929 | } | ||
930 | |||
931 | if (pdata_slave[slave_descr->type]) { | ||
932 | result = -EBUSY; | ||
933 | goto out_unlock_mutex; | ||
934 | } | ||
935 | |||
936 | slave_pdata->address = slave_client->addr; | ||
937 | slave_pdata->irq = slave_client->irq; | ||
938 | slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter); | ||
939 | |||
940 | dev_info(&slave_client->adapter->dev, | ||
941 | "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n", | ||
942 | __func__, | ||
943 | slave_descr->name, | ||
944 | slave_descr->type, | ||
945 | slave_pdata->address, | ||
946 | slave_pdata->irq, | ||
947 | slave_pdata->adapt_num); | ||
948 | |||
949 | switch (slave_descr->type) { | ||
950 | case EXT_SLAVE_TYPE_ACCEL: | ||
951 | irq_name = "accelirq"; | ||
952 | break; | ||
953 | case EXT_SLAVE_TYPE_COMPASS: | ||
954 | irq_name = "compassirq"; | ||
955 | break; | ||
956 | case EXT_SLAVE_TYPE_PRESSURE: | ||
957 | irq_name = "pressureirq"; | ||
958 | break; | ||
959 | default: | ||
960 | irq_name = "none"; | ||
961 | }; | ||
962 | if (slave_descr->init) { | ||
963 | result = slave_descr->init(slave_client->adapter, | ||
964 | slave_descr, | ||
965 | slave_pdata); | ||
966 | if (result) { | ||
967 | dev_err(&slave_client->adapter->dev, | ||
968 | "%s init failed %d\n", | ||
969 | slave_descr->name, result); | ||
970 | goto out_unlock_mutex; | ||
971 | } | ||
972 | } | ||
973 | |||
974 | if (slave_descr->type == EXT_SLAVE_TYPE_ACCEL && | ||
975 | slave_descr->id == ACCEL_ID_MPU6050 && | ||
976 | slave_descr->config) { | ||
977 | /* pass a reference to the mldl_cfg data | ||
978 | structure to the mpu6050 accel "class" */ | ||
979 | struct ext_slave_config config; | ||
980 | config.key = MPU_SLAVE_CONFIG_INTERNAL_REFERENCE; | ||
981 | config.len = sizeof(struct mldl_cfg *); | ||
982 | config.apply = true; | ||
983 | config.data = mldl_cfg; | ||
984 | result = slave_descr->config( | ||
985 | slave_client->adapter, slave_descr, | ||
986 | slave_pdata, &config); | ||
987 | if (result) { | ||
988 | LOG_RESULT_LOCATION(result); | ||
989 | goto out_slavedescr_exit; | ||
990 | } | ||
991 | } | ||
992 | pdata_slave[slave_descr->type] = slave_pdata; | ||
993 | mpu->slave_modules[slave_descr->type] = slave_module; | ||
994 | mldl_cfg->slave[slave_descr->type] = slave_descr; | ||
995 | |||
996 | goto out_unlock_mutex; | ||
997 | |||
998 | out_slavedescr_exit: | ||
999 | if (slave_descr->exit) | ||
1000 | slave_descr->exit(slave_client->adapter, | ||
1001 | slave_descr, slave_pdata); | ||
1002 | out_unlock_mutex: | ||
1003 | mutex_unlock(&mpu->mutex); | ||
1004 | |||
1005 | if (!result && irq_name && (slave_pdata->irq > 0)) { | ||
1006 | int warn_result; | ||
1007 | dev_info(&slave_client->adapter->dev, | ||
1008 | "Installing %s irq using %d\n", | ||
1009 | irq_name, | ||
1010 | slave_pdata->irq); | ||
1011 | warn_result = slaveirq_init(slave_client->adapter, | ||
1012 | slave_pdata, irq_name); | ||
1013 | if (result) | ||
1014 | dev_WARN(&slave_client->adapter->dev, | ||
1015 | "%s irq assigned error: %d\n", | ||
1016 | slave_descr->name, warn_result); | ||
1017 | } else { | ||
1018 | dev_WARN(&slave_client->adapter->dev, | ||
1019 | "%s irq not assigned: %d %d %d\n", | ||
1020 | slave_descr->name, | ||
1021 | result, (int)irq_name, slave_pdata->irq); | ||
1022 | } | ||
1023 | |||
1024 | return result; | ||
1025 | } | ||
1026 | EXPORT_SYMBOL(inv_mpu_register_slave); | ||
1027 | |||
1028 | void inv_mpu_unregister_slave(struct i2c_client *slave_client, | ||
1029 | struct ext_slave_platform_data *slave_pdata, | ||
1030 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
1031 | { | ||
1032 | struct mpu_private_data *mpu = mpu_private_data; | ||
1033 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1034 | struct ext_slave_descr *slave_descr; | ||
1035 | int result; | ||
1036 | |||
1037 | dev_info(&slave_client->adapter->dev, "%s\n", __func__); | ||
1038 | |||
1039 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
1040 | return; | ||
1041 | |||
1042 | if (slave_pdata->irq) | ||
1043 | slaveirq_exit(slave_pdata); | ||
1044 | |||
1045 | slave_descr = get_slave_descr(); | ||
1046 | if (!slave_descr) | ||
1047 | return; | ||
1048 | |||
1049 | mutex_lock(&mpu->mutex); | ||
1050 | |||
1051 | if (slave_descr->exit) { | ||
1052 | result = slave_descr->exit(slave_client->adapter, | ||
1053 | slave_descr, | ||
1054 | slave_pdata); | ||
1055 | if (result) | ||
1056 | dev_err(&slave_client->adapter->dev, | ||
1057 | "Accel exit failed %d\n", result); | ||
1058 | } | ||
1059 | mldl_cfg->slave[slave_descr->type] = NULL; | ||
1060 | mldl_cfg->pdata_slave[slave_descr->type] = NULL; | ||
1061 | mpu->slave_modules[slave_descr->type] = NULL; | ||
1062 | |||
1063 | mutex_unlock(&mpu->mutex); | ||
1064 | |||
1065 | } | ||
1066 | EXPORT_SYMBOL(inv_mpu_unregister_slave); | ||
1067 | |||
1068 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
1069 | |||
1070 | static const struct i2c_device_id mpu_id[] = { | ||
1071 | {"mpu3050", 0}, | ||
1072 | {"mpu6050", 0}, | ||
1073 | {"mpu6050_no_accel", 0}, | ||
1074 | {} | ||
1075 | }; | ||
1076 | MODULE_DEVICE_TABLE(i2c, mpu_id); | ||
1077 | |||
1078 | int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid) | ||
1079 | { | ||
1080 | struct mpu_platform_data *pdata; | ||
1081 | struct mpu_private_data *mpu; | ||
1082 | struct mldl_cfg *mldl_cfg; | ||
1083 | int res = 0; | ||
1084 | int ii = 0; | ||
1085 | unsigned long irq_flags; | ||
1086 | |||
1087 | dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++); | ||
1088 | |||
1089 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
1090 | res = -ENODEV; | ||
1091 | goto out_check_functionality_failed; | ||
1092 | } | ||
1093 | |||
1094 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
1095 | if (!mpu) { | ||
1096 | res = -ENOMEM; | ||
1097 | goto out_alloc_data_failed; | ||
1098 | } | ||
1099 | mldl_cfg = &mpu->mldl_cfg; | ||
1100 | mldl_cfg->mpu_ram = &mpu->mpu_ram; | ||
1101 | mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg; | ||
1102 | mldl_cfg->mpu_offsets = &mpu->mpu_offsets; | ||
1103 | mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info; | ||
1104 | mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg; | ||
1105 | mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state; | ||
1106 | |||
1107 | mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE; | ||
1108 | mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL); | ||
1109 | if (!mldl_cfg->mpu_ram->ram) { | ||
1110 | res = -ENOMEM; | ||
1111 | goto out_alloc_ram_failed; | ||
1112 | } | ||
1113 | mpu_private_data = mpu; | ||
1114 | i2c_set_clientdata(client, mpu); | ||
1115 | mpu->client = client; | ||
1116 | |||
1117 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
1118 | mutex_init(&mpu->mutex); | ||
1119 | init_completion(&mpu->completion); | ||
1120 | |||
1121 | mpu->response_timeout = 60; /* Seconds */ | ||
1122 | mpu->timeout.function = mpu_pm_timeout; | ||
1123 | mpu->timeout.data = (u_long) mpu; | ||
1124 | init_timer(&mpu->timeout); | ||
1125 | |||
1126 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
1127 | mpu->nb.priority = 0; | ||
1128 | res = register_pm_notifier(&mpu->nb); | ||
1129 | if (res) { | ||
1130 | dev_err(&client->adapter->dev, | ||
1131 | "Unable to register pm_notifier %d\n", res); | ||
1132 | goto out_register_pm_notifier_failed; | ||
1133 | } | ||
1134 | |||
1135 | pdata = (struct mpu_platform_data *)client->dev.platform_data; | ||
1136 | if (!pdata) { | ||
1137 | dev_WARN(&client->adapter->dev, | ||
1138 | "Missing platform data for mpu\n"); | ||
1139 | } | ||
1140 | mldl_cfg->pdata = pdata; | ||
1141 | |||
1142 | mldl_cfg->mpu_chip_info->addr = client->addr; | ||
1143 | res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1144 | |||
1145 | if (res) { | ||
1146 | dev_err(&client->adapter->dev, | ||
1147 | "Unable to open %s %d\n", MPU_NAME, res); | ||
1148 | res = -ENODEV; | ||
1149 | goto out_whoami_failed; | ||
1150 | } | ||
1151 | |||
1152 | mpu->dev.minor = MISC_DYNAMIC_MINOR; | ||
1153 | mpu->dev.name = "mpu"; | ||
1154 | mpu->dev.fops = &mpu_fops; | ||
1155 | res = misc_register(&mpu->dev); | ||
1156 | if (res < 0) { | ||
1157 | dev_err(&client->adapter->dev, | ||
1158 | "ERROR: misc_register returned %d\n", res); | ||
1159 | goto out_misc_register_failed; | ||
1160 | } | ||
1161 | |||
1162 | if (client->irq) { | ||
1163 | dev_info(&client->adapter->dev, | ||
1164 | "Installing irq using %d\n", client->irq); | ||
1165 | if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
1166 | irq_flags = IRQF_TRIGGER_FALLING; | ||
1167 | else | ||
1168 | irq_flags = IRQF_TRIGGER_RISING; | ||
1169 | res = mpuirq_init(client, mldl_cfg, irq_flags); | ||
1170 | |||
1171 | if (res) | ||
1172 | goto out_mpuirq_failed; | ||
1173 | } else { | ||
1174 | dev_WARN(&client->adapter->dev, | ||
1175 | "Missing %s IRQ\n", MPU_NAME); | ||
1176 | } | ||
1177 | if (!strcmp(mpu_id[1].name, devid->name)) { | ||
1178 | /* Special case to re-use the inv_mpu_register_slave */ | ||
1179 | struct ext_slave_platform_data *slave_pdata; | ||
1180 | slave_pdata = kzalloc(sizeof(*slave_pdata), GFP_KERNEL); | ||
1181 | if (!slave_pdata) { | ||
1182 | res = -ENOMEM; | ||
1183 | goto out_slave_pdata_kzalloc_failed; | ||
1184 | } | ||
1185 | slave_pdata->bus = EXT_SLAVE_BUS_PRIMARY; | ||
1186 | for (ii = 0; ii < 9; ii++) | ||
1187 | slave_pdata->orientation[ii] = pdata->orientation[ii]; | ||
1188 | res = inv_mpu_register_slave( | ||
1189 | NULL, client, | ||
1190 | slave_pdata, | ||
1191 | mpu6050_get_slave_descr); | ||
1192 | if (res) { | ||
1193 | /* if inv_mpu_register_slave fails there are no pointer | ||
1194 | references to the memory allocated to slave_pdata */ | ||
1195 | kfree(slave_pdata); | ||
1196 | goto out_slave_pdata_kzalloc_failed; | ||
1197 | } | ||
1198 | } | ||
1199 | return res; | ||
1200 | |||
1201 | out_slave_pdata_kzalloc_failed: | ||
1202 | if (client->irq) | ||
1203 | mpuirq_exit(); | ||
1204 | out_mpuirq_failed: | ||
1205 | misc_deregister(&mpu->dev); | ||
1206 | out_misc_register_failed: | ||
1207 | inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1208 | out_whoami_failed: | ||
1209 | unregister_pm_notifier(&mpu->nb); | ||
1210 | out_register_pm_notifier_failed: | ||
1211 | kfree(mldl_cfg->mpu_ram->ram); | ||
1212 | mpu_private_data = NULL; | ||
1213 | out_alloc_ram_failed: | ||
1214 | kfree(mpu); | ||
1215 | out_alloc_data_failed: | ||
1216 | out_check_functionality_failed: | ||
1217 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res); | ||
1218 | return res; | ||
1219 | |||
1220 | } | ||
1221 | |||
1222 | static int mpu_remove(struct i2c_client *client) | ||
1223 | { | ||
1224 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
1225 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
1226 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1227 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
1228 | int ii; | ||
1229 | |||
1230 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1231 | if (!pdata_slave[ii]) | ||
1232 | slave_adapter[ii] = NULL; | ||
1233 | else | ||
1234 | slave_adapter[ii] = | ||
1235 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
1236 | } | ||
1237 | |||
1238 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
1239 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
1240 | |||
1241 | inv_mpu_close(mldl_cfg, | ||
1242 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
1243 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
1244 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
1245 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE]); | ||
1246 | |||
1247 | if (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL] && | ||
1248 | (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL]->id == | ||
1249 | ACCEL_ID_MPU6050)) { | ||
1250 | struct ext_slave_platform_data *slave_pdata = | ||
1251 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]; | ||
1252 | inv_mpu_unregister_slave( | ||
1253 | client, | ||
1254 | mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
1255 | mpu6050_get_slave_descr); | ||
1256 | kfree(slave_pdata); | ||
1257 | } | ||
1258 | |||
1259 | if (client->irq) | ||
1260 | mpuirq_exit(); | ||
1261 | |||
1262 | misc_deregister(&mpu->dev); | ||
1263 | |||
1264 | unregister_pm_notifier(&mpu->nb); | ||
1265 | |||
1266 | kfree(mpu->mldl_cfg.mpu_ram->ram); | ||
1267 | kfree(mpu); | ||
1268 | |||
1269 | return 0; | ||
1270 | } | ||
1271 | |||
1272 | static struct i2c_driver mpu_driver = { | ||
1273 | .class = I2C_CLASS_HWMON, | ||
1274 | .probe = mpu_probe, | ||
1275 | .remove = mpu_remove, | ||
1276 | .id_table = mpu_id, | ||
1277 | .driver = { | ||
1278 | .owner = THIS_MODULE, | ||
1279 | .name = MPU_NAME, | ||
1280 | }, | ||
1281 | .address_list = normal_i2c, | ||
1282 | .shutdown = mpu_shutdown, /* optional */ | ||
1283 | .suspend = mpu_dev_suspend, /* optional */ | ||
1284 | .resume = mpu_dev_resume, /* optional */ | ||
1285 | |||
1286 | }; | ||
1287 | |||
1288 | static int __init mpu_init(void) | ||
1289 | { | ||
1290 | int res = i2c_add_driver(&mpu_driver); | ||
1291 | pr_info("%s: Probe name %s\n", __func__, MPU_NAME); | ||
1292 | if (res) | ||
1293 | pr_err("%s failed\n", __func__); | ||
1294 | return res; | ||
1295 | } | ||
1296 | |||
1297 | static void __exit mpu_exit(void) | ||
1298 | { | ||
1299 | pr_info("%s\n", __func__); | ||
1300 | i2c_del_driver(&mpu_driver); | ||
1301 | } | ||
1302 | |||
1303 | module_init(mpu_init); | ||
1304 | module_exit(mpu_exit); | ||
1305 | |||
1306 | MODULE_AUTHOR("Invensense Corporation"); | ||
1307 | MODULE_DESCRIPTION("User space character device interface for MPU"); | ||
1308 | MODULE_LICENSE("GPL"); | ||
1309 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h new file mode 100644 index 00000000000..686f6e5d86a --- /dev/null +++ b/drivers/misc/inv_mpu/mpu6050b1.h | |||
@@ -0,0 +1,435 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @defgroup | ||
22 | * @brief | ||
23 | * | ||
24 | * @{ | ||
25 | * @file mpu6050.h | ||
26 | * @brief | ||
27 | */ | ||
28 | |||
29 | #ifndef __MPU_H_ | ||
30 | #error Do not include this file directly. Include mpu.h instead. | ||
31 | #endif | ||
32 | |||
33 | #ifndef __MPU6050B1_H_ | ||
34 | #define __MPU6050B1_H_ | ||
35 | |||
36 | |||
37 | #define MPU_NAME "mpu6050B1" | ||
38 | #define DEFAULT_MPU_SLAVEADDR 0x68 | ||
39 | |||
40 | /*==== MPU6050B1 REGISTER SET ====*/ | ||
41 | enum { | ||
42 | MPUREG_XG_OFFS_TC = 0, /* 0x00, 0 */ | ||
43 | MPUREG_YG_OFFS_TC, /* 0x01, 1 */ | ||
44 | MPUREG_ZG_OFFS_TC, /* 0x02, 2 */ | ||
45 | MPUREG_X_FINE_GAIN, /* 0x03, 3 */ | ||
46 | MPUREG_Y_FINE_GAIN, /* 0x04, 4 */ | ||
47 | MPUREG_Z_FINE_GAIN, /* 0x05, 5 */ | ||
48 | MPUREG_XA_OFFS_H, /* 0x06, 6 */ | ||
49 | MPUREG_XA_OFFS_L, /* 0x07, 7 */ | ||
50 | MPUREG_YA_OFFS_H, /* 0x08, 8 */ | ||
51 | MPUREG_YA_OFFS_L, /* 0x09, 9 */ | ||
52 | MPUREG_ZA_OFFS_H, /* 0x0a, 10 */ | ||
53 | MPUREG_ZA_OFFS_L, /* 0x0B, 11 */ | ||
54 | MPUREG_PRODUCT_ID, /* 0x0c, 12 */ | ||
55 | MPUREG_0D_RSVD, /* 0x0d, 13 */ | ||
56 | MPUREG_0E_RSVD, /* 0x0e, 14 */ | ||
57 | MPUREG_0F_RSVD, /* 0x0f, 15 */ | ||
58 | MPUREG_10_RSVD, /* 0x00, 16 */ | ||
59 | MPUREG_11_RSVD, /* 0x11, 17 */ | ||
60 | MPUREG_12_RSVD, /* 0x12, 18 */ | ||
61 | MPUREG_XG_OFFS_USRH, /* 0x13, 19 */ | ||
62 | MPUREG_XG_OFFS_USRL, /* 0x14, 20 */ | ||
63 | MPUREG_YG_OFFS_USRH, /* 0x15, 21 */ | ||
64 | MPUREG_YG_OFFS_USRL, /* 0x16, 22 */ | ||
65 | MPUREG_ZG_OFFS_USRH, /* 0x17, 23 */ | ||
66 | MPUREG_ZG_OFFS_USRL, /* 0x18, 24 */ | ||
67 | MPUREG_SMPLRT_DIV, /* 0x19, 25 */ | ||
68 | MPUREG_CONFIG, /* 0x1A, 26 */ | ||
69 | MPUREG_GYRO_CONFIG, /* 0x1b, 27 */ | ||
70 | MPUREG_ACCEL_CONFIG, /* 0x1c, 28 */ | ||
71 | MPUREG_ACCEL_FF_THR, /* 0x1d, 29 */ | ||
72 | MPUREG_ACCEL_FF_DUR, /* 0x1e, 30 */ | ||
73 | MPUREG_ACCEL_MOT_THR, /* 0x1f, 31 */ | ||
74 | MPUREG_ACCEL_MOT_DUR, /* 0x20, 32 */ | ||
75 | MPUREG_ACCEL_ZRMOT_THR, /* 0x21, 33 */ | ||
76 | MPUREG_ACCEL_ZRMOT_DUR, /* 0x22, 34 */ | ||
77 | MPUREG_FIFO_EN, /* 0x23, 35 */ | ||
78 | MPUREG_I2C_MST_CTRL, /* 0x24, 36 */ | ||
79 | MPUREG_I2C_SLV0_ADDR, /* 0x25, 37 */ | ||
80 | MPUREG_I2C_SLV0_REG, /* 0x26, 38 */ | ||
81 | MPUREG_I2C_SLV0_CTRL, /* 0x27, 39 */ | ||
82 | MPUREG_I2C_SLV1_ADDR, /* 0x28, 40 */ | ||
83 | MPUREG_I2C_SLV1_REG, /* 0x29, 41 */ | ||
84 | MPUREG_I2C_SLV1_CTRL, /* 0x2a, 42 */ | ||
85 | MPUREG_I2C_SLV2_ADDR, /* 0x2B, 43 */ | ||
86 | MPUREG_I2C_SLV2_REG, /* 0x2c, 44 */ | ||
87 | MPUREG_I2C_SLV2_CTRL, /* 0x2d, 45 */ | ||
88 | MPUREG_I2C_SLV3_ADDR, /* 0x2E, 46 */ | ||
89 | MPUREG_I2C_SLV3_REG, /* 0x2f, 47 */ | ||
90 | MPUREG_I2C_SLV3_CTRL, /* 0x30, 48 */ | ||
91 | MPUREG_I2C_SLV4_ADDR, /* 0x31, 49 */ | ||
92 | MPUREG_I2C_SLV4_REG, /* 0x32, 50 */ | ||
93 | MPUREG_I2C_SLV4_DO, /* 0x33, 51 */ | ||
94 | MPUREG_I2C_SLV4_CTRL, /* 0x34, 52 */ | ||
95 | MPUREG_I2C_SLV4_DI, /* 0x35, 53 */ | ||
96 | MPUREG_I2C_MST_STATUS, /* 0x36, 54 */ | ||
97 | MPUREG_INT_PIN_CFG, /* 0x37, 55 */ | ||
98 | MPUREG_INT_ENABLE, /* 0x38, 56 */ | ||
99 | MPUREG_DMP_INT_STATUS, /* 0x39, 57 */ | ||
100 | MPUREG_INT_STATUS, /* 0x3A, 58 */ | ||
101 | MPUREG_ACCEL_XOUT_H, /* 0x3B, 59 */ | ||
102 | MPUREG_ACCEL_XOUT_L, /* 0x3c, 60 */ | ||
103 | MPUREG_ACCEL_YOUT_H, /* 0x3d, 61 */ | ||
104 | MPUREG_ACCEL_YOUT_L, /* 0x3e, 62 */ | ||
105 | MPUREG_ACCEL_ZOUT_H, /* 0x3f, 63 */ | ||
106 | MPUREG_ACCEL_ZOUT_L, /* 0x40, 64 */ | ||
107 | MPUREG_TEMP_OUT_H, /* 0x41, 65 */ | ||
108 | MPUREG_TEMP_OUT_L, /* 0x42, 66 */ | ||
109 | MPUREG_GYRO_XOUT_H, /* 0x43, 67 */ | ||
110 | MPUREG_GYRO_XOUT_L, /* 0x44, 68 */ | ||
111 | MPUREG_GYRO_YOUT_H, /* 0x45, 69 */ | ||
112 | MPUREG_GYRO_YOUT_L, /* 0x46, 70 */ | ||
113 | MPUREG_GYRO_ZOUT_H, /* 0x47, 71 */ | ||
114 | MPUREG_GYRO_ZOUT_L, /* 0x48, 72 */ | ||
115 | MPUREG_EXT_SLV_SENS_DATA_00, /* 0x49, 73 */ | ||
116 | MPUREG_EXT_SLV_SENS_DATA_01, /* 0x4a, 74 */ | ||
117 | MPUREG_EXT_SLV_SENS_DATA_02, /* 0x4b, 75 */ | ||
118 | MPUREG_EXT_SLV_SENS_DATA_03, /* 0x4c, 76 */ | ||
119 | MPUREG_EXT_SLV_SENS_DATA_04, /* 0x4d, 77 */ | ||
120 | MPUREG_EXT_SLV_SENS_DATA_05, /* 0x4e, 78 */ | ||
121 | MPUREG_EXT_SLV_SENS_DATA_06, /* 0x4F, 79 */ | ||
122 | MPUREG_EXT_SLV_SENS_DATA_07, /* 0x50, 80 */ | ||
123 | MPUREG_EXT_SLV_SENS_DATA_08, /* 0x51, 81 */ | ||
124 | MPUREG_EXT_SLV_SENS_DATA_09, /* 0x52, 82 */ | ||
125 | MPUREG_EXT_SLV_SENS_DATA_10, /* 0x53, 83 */ | ||
126 | MPUREG_EXT_SLV_SENS_DATA_11, /* 0x54, 84 */ | ||
127 | MPUREG_EXT_SLV_SENS_DATA_12, /* 0x55, 85 */ | ||
128 | MPUREG_EXT_SLV_SENS_DATA_13, /* 0x56, 86 */ | ||
129 | MPUREG_EXT_SLV_SENS_DATA_14, /* 0x57, 87 */ | ||
130 | MPUREG_EXT_SLV_SENS_DATA_15, /* 0x58, 88 */ | ||
131 | MPUREG_EXT_SLV_SENS_DATA_16, /* 0x59, 89 */ | ||
132 | MPUREG_EXT_SLV_SENS_DATA_17, /* 0x5a, 90 */ | ||
133 | MPUREG_EXT_SLV_SENS_DATA_18, /* 0x5B, 91 */ | ||
134 | MPUREG_EXT_SLV_SENS_DATA_19, /* 0x5c, 92 */ | ||
135 | MPUREG_EXT_SLV_SENS_DATA_20, /* 0x5d, 93 */ | ||
136 | MPUREG_EXT_SLV_SENS_DATA_21, /* 0x5e, 94 */ | ||
137 | MPUREG_EXT_SLV_SENS_DATA_22, /* 0x5f, 95 */ | ||
138 | MPUREG_EXT_SLV_SENS_DATA_23, /* 0x60, 96 */ | ||
139 | MPUREG_ACCEL_INTEL_STATUS, /* 0x61, 97 */ | ||
140 | MPUREG_62_RSVD, /* 0x62, 98 */ | ||
141 | MPUREG_I2C_SLV0_DO, /* 0x63, 99 */ | ||
142 | MPUREG_I2C_SLV1_DO, /* 0x64, 100 */ | ||
143 | MPUREG_I2C_SLV2_DO, /* 0x65, 101 */ | ||
144 | MPUREG_I2C_SLV3_DO, /* 0x66, 102 */ | ||
145 | MPUREG_I2C_MST_DELAY_CTRL, /* 0x67, 103 */ | ||
146 | MPUREG_SIGNAL_PATH_RESET, /* 0x68, 104 */ | ||
147 | MPUREG_ACCEL_INTEL_CTRL, /* 0x69, 105 */ | ||
148 | MPUREG_USER_CTRL, /* 0x6A, 106 */ | ||
149 | MPUREG_PWR_MGMT_1, /* 0x6B, 107 */ | ||
150 | MPUREG_PWR_MGMT_2, /* 0x6C, 108 */ | ||
151 | MPUREG_BANK_SEL, /* 0x6D, 109 */ | ||
152 | MPUREG_MEM_START_ADDR, /* 0x6E, 100 */ | ||
153 | MPUREG_MEM_R_W, /* 0x6F, 111 */ | ||
154 | MPUREG_DMP_CFG_1, /* 0x70, 112 */ | ||
155 | MPUREG_DMP_CFG_2, /* 0x71, 113 */ | ||
156 | MPUREG_FIFO_COUNTH, /* 0x72, 114 */ | ||
157 | MPUREG_FIFO_COUNTL, /* 0x73, 115 */ | ||
158 | MPUREG_FIFO_R_W, /* 0x74, 116 */ | ||
159 | MPUREG_WHOAMI, /* 0x75, 117 */ | ||
160 | |||
161 | NUM_OF_MPU_REGISTERS /* = 0x76, 118 */ | ||
162 | }; | ||
163 | |||
164 | /*==== MPU6050B1 MEMORY ====*/ | ||
165 | enum MPU_MEMORY_BANKS { | ||
166 | MEM_RAM_BANK_0 = 0, | ||
167 | MEM_RAM_BANK_1, | ||
168 | MEM_RAM_BANK_2, | ||
169 | MEM_RAM_BANK_3, | ||
170 | MEM_RAM_BANK_4, | ||
171 | MEM_RAM_BANK_5, | ||
172 | MEM_RAM_BANK_6, | ||
173 | MEM_RAM_BANK_7, | ||
174 | MEM_RAM_BANK_8, | ||
175 | MEM_RAM_BANK_9, | ||
176 | MEM_RAM_BANK_10, | ||
177 | MEM_RAM_BANK_11, | ||
178 | MPU_MEM_NUM_RAM_BANKS, | ||
179 | MPU_MEM_OTP_BANK_0 = 16 | ||
180 | }; | ||
181 | |||
182 | |||
183 | /*==== MPU6050B1 parameters ====*/ | ||
184 | |||
185 | #define NUM_REGS (NUM_OF_MPU_REGISTERS) | ||
186 | #define START_SENS_REGS (0x3B) | ||
187 | #define NUM_SENS_REGS (0x60 - START_SENS_REGS + 1) | ||
188 | |||
189 | /*---- MPU Memory ----*/ | ||
190 | #define NUM_BANKS (MPU_MEM_NUM_RAM_BANKS) | ||
191 | #define BANK_SIZE (256) | ||
192 | #define MEM_SIZE (NUM_BANKS * BANK_SIZE) | ||
193 | #define MPU_MEM_BANK_SIZE (BANK_SIZE) /*alternative name */ | ||
194 | |||
195 | #define FIFO_HW_SIZE (1024) | ||
196 | |||
197 | #define NUM_EXT_SLAVES (4) | ||
198 | |||
199 | |||
200 | /*==== BITS FOR MPU6050B1 ====*/ | ||
201 | /*---- MPU6050B1 'XG_OFFS_TC' register (0, 1, 2) ----*/ | ||
202 | #define BIT_PU_SLEEP_MODE 0x80 | ||
203 | #define BITS_XG_OFFS_TC 0x7E | ||
204 | #define BIT_OTP_BNK_VLD 0x01 | ||
205 | |||
206 | #define BIT_I2C_MST_VDDIO 0x80 | ||
207 | #define BITS_YG_OFFS_TC 0x7E | ||
208 | #define BITS_ZG_OFFS_TC 0x7E | ||
209 | /*---- MPU6050B1 'FIFO_EN' register (23) ----*/ | ||
210 | #define BIT_TEMP_OUT 0x80 | ||
211 | #define BIT_GYRO_XOUT 0x40 | ||
212 | #define BIT_GYRO_YOUT 0x20 | ||
213 | #define BIT_GYRO_ZOUT 0x10 | ||
214 | #define BIT_ACCEL 0x08 | ||
215 | #define BIT_SLV_2 0x04 | ||
216 | #define BIT_SLV_1 0x02 | ||
217 | #define BIT_SLV_0 0x01 | ||
218 | /*---- MPU6050B1 'CONFIG' register (1A) ----*/ | ||
219 | /*NONE 0xC0 */ | ||
220 | #define BITS_EXT_SYNC_SET 0x38 | ||
221 | #define BITS_DLPF_CFG 0x07 | ||
222 | /*---- MPU6050B1 'GYRO_CONFIG' register (1B) ----*/ | ||
223 | /* voluntarily modified label from BITS_FS_SEL to | ||
224 | * BITS_GYRO_FS_SEL to avoid confusion with MPU | ||
225 | */ | ||
226 | #define BITS_GYRO_FS_SEL 0x18 | ||
227 | /*NONE 0x07 */ | ||
228 | /*---- MPU6050B1 'ACCEL_CONFIG' register (1C) ----*/ | ||
229 | #define BITS_ACCEL_FS_SEL 0x18 | ||
230 | #define BITS_ACCEL_HPF 0x07 | ||
231 | /*---- MPU6050B1 'I2C_MST_CTRL' register (24) ----*/ | ||
232 | #define BIT_MULT_MST_EN 0x80 | ||
233 | #define BIT_WAIT_FOR_ES 0x40 | ||
234 | #define BIT_SLV_3_FIFO_EN 0x20 | ||
235 | #define BIT_I2C_MST_PSR 0x10 | ||
236 | #define BITS_I2C_MST_CLK 0x0F | ||
237 | /*---- MPU6050B1 'I2C_SLV?_ADDR' register (27,2A,2D,30) ----*/ | ||
238 | #define BIT_I2C_READ 0x80 | ||
239 | #define BIT_I2C_WRITE 0x00 | ||
240 | #define BITS_I2C_ADDR 0x7F | ||
241 | /*---- MPU6050B1 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/ | ||
242 | #define BIT_SLV_ENABLE 0x80 | ||
243 | #define BIT_SLV_BYTE_SW 0x40 | ||
244 | #define BIT_SLV_REG_DIS 0x20 | ||
245 | #define BIT_SLV_GRP 0x10 | ||
246 | #define BITS_SLV_LENG 0x0F | ||
247 | /*---- MPU6050B1 'I2C_SLV4_ADDR' register (31) ----*/ | ||
248 | #define BIT_I2C_SLV4_RNW 0x80 | ||
249 | /*---- MPU6050B1 'I2C_SLV4_CTRL' register (34) ----*/ | ||
250 | #define BIT_I2C_SLV4_EN 0x80 | ||
251 | #define BIT_SLV4_DONE_INT_EN 0x40 | ||
252 | #define BIT_SLV4_REG_DIS 0x20 | ||
253 | #define MASK_I2C_MST_DLY 0x1F | ||
254 | /*---- MPU6050B1 'I2C_MST_STATUS' register (36) ----*/ | ||
255 | #define BIT_PASS_THROUGH 0x80 | ||
256 | #define BIT_I2C_SLV4_DONE 0x40 | ||
257 | #define BIT_I2C_LOST_ARB 0x20 | ||
258 | #define BIT_I2C_SLV4_NACK 0x10 | ||
259 | #define BIT_I2C_SLV3_NACK 0x08 | ||
260 | #define BIT_I2C_SLV2_NACK 0x04 | ||
261 | #define BIT_I2C_SLV1_NACK 0x02 | ||
262 | #define BIT_I2C_SLV0_NACK 0x01 | ||
263 | /*---- MPU6050B1 'INT_PIN_CFG' register (37) ----*/ | ||
264 | #define BIT_ACTL 0x80 | ||
265 | #define BIT_ACTL_LOW 0x80 | ||
266 | #define BIT_ACTL_HIGH 0x00 | ||
267 | #define BIT_OPEN 0x40 | ||
268 | #define BIT_LATCH_INT_EN 0x20 | ||
269 | #define BIT_INT_ANYRD_2CLEAR 0x10 | ||
270 | #define BIT_ACTL_FSYNC 0x08 | ||
271 | #define BIT_FSYNC_INT_EN 0x04 | ||
272 | #define BIT_BYPASS_EN 0x02 | ||
273 | #define BIT_CLKOUT_EN 0x01 | ||
274 | /*---- MPU6050B1 'INT_ENABLE' register (38) ----*/ | ||
275 | #define BIT_FF_EN 0x80 | ||
276 | #define BIT_MOT_EN 0x40 | ||
277 | #define BIT_ZMOT_EN 0x20 | ||
278 | #define BIT_FIFO_OVERFLOW_EN 0x10 | ||
279 | #define BIT_I2C_MST_INT_EN 0x08 | ||
280 | #define BIT_PLL_RDY_EN 0x04 | ||
281 | #define BIT_DMP_INT_EN 0x02 | ||
282 | #define BIT_RAW_RDY_EN 0x01 | ||
283 | /*---- MPU6050B1 'DMP_INT_STATUS' register (39) ----*/ | ||
284 | /*NONE 0x80 */ | ||
285 | /*NONE 0x40 */ | ||
286 | #define BIT_DMP_INT_5 0x20 | ||
287 | #define BIT_DMP_INT_4 0x10 | ||
288 | #define BIT_DMP_INT_3 0x08 | ||
289 | #define BIT_DMP_INT_2 0x04 | ||
290 | #define BIT_DMP_INT_1 0x02 | ||
291 | #define BIT_DMP_INT_0 0x01 | ||
292 | /*---- MPU6050B1 'INT_STATUS' register (3A) ----*/ | ||
293 | #define BIT_FF_INT 0x80 | ||
294 | #define BIT_MOT_INT 0x40 | ||
295 | #define BIT_ZMOT_INT 0x20 | ||
296 | #define BIT_FIFO_OVERFLOW_INT 0x10 | ||
297 | #define BIT_I2C_MST_INT 0x08 | ||
298 | #define BIT_PLL_RDY_INT 0x04 | ||
299 | #define BIT_DMP_INT 0x02 | ||
300 | #define BIT_RAW_DATA_RDY_INT 0x01 | ||
301 | /*---- MPU6050B1 'MPUREG_I2C_MST_DELAY_CTRL' register (0x67) ----*/ | ||
302 | #define BIT_DELAY_ES_SHADOW 0x80 | ||
303 | #define BIT_SLV4_DLY_EN 0x10 | ||
304 | #define BIT_SLV3_DLY_EN 0x08 | ||
305 | #define BIT_SLV2_DLY_EN 0x04 | ||
306 | #define BIT_SLV1_DLY_EN 0x02 | ||
307 | #define BIT_SLV0_DLY_EN 0x01 | ||
308 | /*---- MPU6050B1 'BANK_SEL' register (6D) ----*/ | ||
309 | #define BIT_PRFTCH_EN 0x40 | ||
310 | #define BIT_CFG_USER_BANK 0x20 | ||
311 | #define BITS_MEM_SEL 0x1f | ||
312 | /*---- MPU6050B1 'USER_CTRL' register (6A) ----*/ | ||
313 | #define BIT_DMP_EN 0x80 | ||
314 | #define BIT_FIFO_EN 0x40 | ||
315 | #define BIT_I2C_MST_EN 0x20 | ||
316 | #define BIT_I2C_IF_DIS 0x10 | ||
317 | #define BIT_DMP_RST 0x08 | ||
318 | #define BIT_FIFO_RST 0x04 | ||
319 | #define BIT_I2C_MST_RST 0x02 | ||
320 | #define BIT_SIG_COND_RST 0x01 | ||
321 | /*---- MPU6050B1 'PWR_MGMT_1' register (6B) ----*/ | ||
322 | #define BIT_H_RESET 0x80 | ||
323 | #define BIT_SLEEP 0x40 | ||
324 | #define BIT_CYCLE 0x20 | ||
325 | #define BIT_PD_PTAT 0x08 | ||
326 | #define BITS_CLKSEL 0x07 | ||
327 | /*---- MPU6050B1 'PWR_MGMT_2' register (6C) ----*/ | ||
328 | #define BITS_LPA_WAKE_CTRL 0xC0 | ||
329 | #define BITS_LPA_WAKE_1HZ 0x00 | ||
330 | #define BITS_LPA_WAKE_2HZ 0x40 | ||
331 | #define BITS_LPA_WAKE_10HZ 0x80 | ||
332 | #define BITS_LPA_WAKE_40HZ 0xC0 | ||
333 | #define BIT_STBY_XA 0x20 | ||
334 | #define BIT_STBY_YA 0x10 | ||
335 | #define BIT_STBY_ZA 0x08 | ||
336 | #define BIT_STBY_XG 0x04 | ||
337 | #define BIT_STBY_YG 0x02 | ||
338 | #define BIT_STBY_ZG 0x01 | ||
339 | |||
340 | #define ACCEL_MOT_THR_LSB (32) /* mg */ | ||
341 | #define ACCEL_MOT_DUR_LSB (1) | ||
342 | #define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255) | ||
343 | #define ACCEL_ZRMOT_DUR_LSB (64) | ||
344 | |||
345 | /*----------------------------------------------------------------------------*/ | ||
346 | /*---- Alternative names to take care of conflicts with current mpu3050.h ----*/ | ||
347 | /*----------------------------------------------------------------------------*/ | ||
348 | |||
349 | /*-- registers --*/ | ||
350 | #define MPUREG_DLPF_FS_SYNC MPUREG_CONFIG /* 0x1A */ | ||
351 | |||
352 | #define MPUREG_PWR_MGM MPUREG_PWR_MGMT_1 /* 0x6B */ | ||
353 | #define MPUREG_FIFO_EN1 MPUREG_FIFO_EN /* 0x23 */ | ||
354 | #define MPUREG_INT_CFG MPUREG_INT_ENABLE /* 0x38 */ | ||
355 | #define MPUREG_X_OFFS_USRH MPUREG_XG_OFFS_USRH /* 0x13 */ | ||
356 | #define MPUREG_WHO_AM_I MPUREG_WHOAMI /* 0x75 */ | ||
357 | #define MPUREG_23_RSVD MPUREG_EXT_SLV_SENS_DATA_00 /* 0x49 */ | ||
358 | |||
359 | /*-- bits --*/ | ||
360 | /* 'USER_CTRL' register */ | ||
361 | #define BIT_AUX_IF_EN BIT_I2C_MST_EN | ||
362 | #define BIT_AUX_RD_LENG BIT_I2C_MST_EN | ||
363 | #define BIT_IME_IF_RST BIT_I2C_MST_RST | ||
364 | #define BIT_GYRO_RST BIT_SIG_COND_RST | ||
365 | /* 'INT_ENABLE' register */ | ||
366 | #define BIT_RAW_RDY BIT_RAW_DATA_RDY_INT | ||
367 | #define BIT_MPU_RDY_EN BIT_PLL_RDY_EN | ||
368 | /* 'INT_STATUS' register */ | ||
369 | #define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT | ||
370 | |||
371 | /*---- MPU6050 Silicon Revisions ----*/ | ||
372 | #define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */ | ||
373 | #define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */ | ||
374 | |||
375 | /*---- MPU6050 notable product revisions ----*/ | ||
376 | #define MPU_PRODUCT_KEY_B1_E1_5 105 | ||
377 | #define MPU_PRODUCT_KEY_B2_F1 431 | ||
378 | |||
379 | /*---- structure containing control variables used by MLDL ----*/ | ||
380 | /*---- MPU clock source settings ----*/ | ||
381 | /*---- MPU filter selections ----*/ | ||
382 | enum mpu_filter { | ||
383 | MPU_FILTER_256HZ_NOLPF2 = 0, | ||
384 | MPU_FILTER_188HZ, | ||
385 | MPU_FILTER_98HZ, | ||
386 | MPU_FILTER_42HZ, | ||
387 | MPU_FILTER_20HZ, | ||
388 | MPU_FILTER_10HZ, | ||
389 | MPU_FILTER_5HZ, | ||
390 | MPU_FILTER_2100HZ_NOLPF, | ||
391 | NUM_MPU_FILTER | ||
392 | }; | ||
393 | |||
394 | enum mpu_fullscale { | ||
395 | MPU_FS_250DPS = 0, | ||
396 | MPU_FS_500DPS, | ||
397 | MPU_FS_1000DPS, | ||
398 | MPU_FS_2000DPS, | ||
399 | NUM_MPU_FS | ||
400 | }; | ||
401 | |||
402 | enum mpu_clock_sel { | ||
403 | MPU_CLK_SEL_INTERNAL = 0, | ||
404 | MPU_CLK_SEL_PLLGYROX, | ||
405 | MPU_CLK_SEL_PLLGYROY, | ||
406 | MPU_CLK_SEL_PLLGYROZ, | ||
407 | MPU_CLK_SEL_PLLEXT32K, | ||
408 | MPU_CLK_SEL_PLLEXT19M, | ||
409 | MPU_CLK_SEL_RESERVED, | ||
410 | MPU_CLK_SEL_STOP, | ||
411 | NUM_CLK_SEL | ||
412 | }; | ||
413 | |||
414 | enum mpu_ext_sync { | ||
415 | MPU_EXT_SYNC_NONE = 0, | ||
416 | MPU_EXT_SYNC_TEMP, | ||
417 | MPU_EXT_SYNC_GYROX, | ||
418 | MPU_EXT_SYNC_GYROY, | ||
419 | MPU_EXT_SYNC_GYROZ, | ||
420 | MPU_EXT_SYNC_ACCELX, | ||
421 | MPU_EXT_SYNC_ACCELY, | ||
422 | MPU_EXT_SYNC_ACCELZ, | ||
423 | NUM_MPU_EXT_SYNC | ||
424 | }; | ||
425 | |||
426 | #define MPUREG_CONFIG_VALUE(ext_sync, lpf) \ | ||
427 | ((ext_sync << 3) | lpf) | ||
428 | |||
429 | #define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \ | ||
430 | ((x_st ? 0x80 : 0) | \ | ||
431 | (y_st ? 0x70 : 0) | \ | ||
432 | (z_st ? 0x60 : 0) | \ | ||
433 | (full_scale << 3)) | ||
434 | |||
435 | #endif /* __MPU6050_H_ */ | ||
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c new file mode 100644 index 00000000000..d8b721e4346 --- /dev/null +++ b/drivers/misc/inv_mpu/mpuirq.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/stat.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/signal.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/i2c-dev.h> | ||
30 | #include <linux/poll.h> | ||
31 | |||
32 | #include <linux/errno.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/mm.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/wait.h> | ||
37 | #include <linux/uaccess.h> | ||
38 | #include <linux/io.h> | ||
39 | |||
40 | #include <linux/mpu.h> | ||
41 | #include "mpuirq.h" | ||
42 | #include "mldl_cfg.h" | ||
43 | |||
44 | #define MPUIRQ_NAME "mpuirq" | ||
45 | |||
46 | /* function which gets accel data and sends it to MPU */ | ||
47 | |||
48 | DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait); | ||
49 | |||
50 | struct mpuirq_dev_data { | ||
51 | struct i2c_client *mpu_client; | ||
52 | struct miscdevice *dev; | ||
53 | int irq; | ||
54 | int pid; | ||
55 | int accel_divider; | ||
56 | int data_ready; | ||
57 | int timeout; | ||
58 | }; | ||
59 | |||
60 | static struct mpuirq_dev_data mpuirq_dev_data; | ||
61 | static struct mpuirq_data mpuirq_data; | ||
62 | static char *interface = MPUIRQ_NAME; | ||
63 | |||
64 | static int mpuirq_open(struct inode *inode, struct file *file) | ||
65 | { | ||
66 | dev_dbg(mpuirq_dev_data.dev->this_device, | ||
67 | "%s current->pid %d\n", __func__, current->pid); | ||
68 | mpuirq_dev_data.pid = current->pid; | ||
69 | file->private_data = &mpuirq_dev_data; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /* close function - called when the "file" /dev/mpuirq is closed in userspace */ | ||
74 | static int mpuirq_release(struct inode *inode, struct file *file) | ||
75 | { | ||
76 | dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n"); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | /* read function called when from /dev/mpuirq is read */ | ||
81 | static ssize_t mpuirq_read(struct file *file, | ||
82 | char *buf, size_t count, loff_t *ppos) | ||
83 | { | ||
84 | int len, err; | ||
85 | struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data; | ||
86 | |||
87 | if (!mpuirq_dev_data.data_ready && | ||
88 | mpuirq_dev_data.timeout && (!(file->f_flags & O_NONBLOCK))) { | ||
89 | wait_event_interruptible_timeout(mpuirq_wait, | ||
90 | mpuirq_dev_data.data_ready, | ||
91 | mpuirq_dev_data.timeout); | ||
92 | } | ||
93 | |||
94 | if (mpuirq_dev_data.data_ready && NULL != buf | ||
95 | && count >= sizeof(mpuirq_data)) { | ||
96 | err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data)); | ||
97 | mpuirq_data.data_type = 0; | ||
98 | } else { | ||
99 | return 0; | ||
100 | } | ||
101 | if (err != 0) { | ||
102 | dev_err(p_mpuirq_dev_data->dev->this_device, | ||
103 | "Copy to user returned %d\n", err); | ||
104 | return -EFAULT; | ||
105 | } | ||
106 | mpuirq_dev_data.data_ready = 0; | ||
107 | len = sizeof(mpuirq_data); | ||
108 | return len; | ||
109 | } | ||
110 | |||
111 | unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll) | ||
112 | { | ||
113 | int mask = 0; | ||
114 | |||
115 | poll_wait(file, &mpuirq_wait, poll); | ||
116 | if (mpuirq_dev_data.data_ready) | ||
117 | mask |= POLLIN | POLLRDNORM; | ||
118 | return mask; | ||
119 | } | ||
120 | |||
121 | /* ioctl - I/O control */ | ||
122 | static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
123 | { | ||
124 | int retval = 0; | ||
125 | int data; | ||
126 | |||
127 | switch (cmd) { | ||
128 | case MPUIRQ_SET_TIMEOUT: | ||
129 | mpuirq_dev_data.timeout = arg; | ||
130 | break; | ||
131 | |||
132 | case MPUIRQ_GET_INTERRUPT_CNT: | ||
133 | data = mpuirq_data.interruptcount - 1; | ||
134 | if (mpuirq_data.interruptcount > 1) | ||
135 | mpuirq_data.interruptcount = 1; | ||
136 | |||
137 | if (copy_to_user((int *)arg, &data, sizeof(int))) | ||
138 | return -EFAULT; | ||
139 | break; | ||
140 | case MPUIRQ_GET_IRQ_TIME: | ||
141 | if (copy_to_user((int *)arg, &mpuirq_data.irqtime, | ||
142 | sizeof(mpuirq_data.irqtime))) | ||
143 | return -EFAULT; | ||
144 | mpuirq_data.irqtime = 0; | ||
145 | break; | ||
146 | case MPUIRQ_SET_FREQUENCY_DIVIDER: | ||
147 | mpuirq_dev_data.accel_divider = arg; | ||
148 | break; | ||
149 | default: | ||
150 | retval = -EINVAL; | ||
151 | } | ||
152 | return retval; | ||
153 | } | ||
154 | |||
155 | static irqreturn_t mpuirq_handler(int irq, void *dev_id) | ||
156 | { | ||
157 | static int mycount; | ||
158 | struct timeval irqtime; | ||
159 | mycount++; | ||
160 | |||
161 | mpuirq_data.interruptcount++; | ||
162 | |||
163 | /* wake up (unblock) for reading data from userspace */ | ||
164 | /* and ignore first interrupt generated in module init */ | ||
165 | mpuirq_dev_data.data_ready = 1; | ||
166 | |||
167 | do_gettimeofday(&irqtime); | ||
168 | mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
169 | mpuirq_data.irqtime += irqtime.tv_usec; | ||
170 | mpuirq_data.data_type = MPUIRQ_DATA_TYPE_MPU_IRQ; | ||
171 | mpuirq_data.data = 0; | ||
172 | |||
173 | wake_up_interruptible(&mpuirq_wait); | ||
174 | |||
175 | return IRQ_HANDLED; | ||
176 | |||
177 | } | ||
178 | |||
179 | /* define which file operations are supported */ | ||
180 | const struct file_operations mpuirq_fops = { | ||
181 | .owner = THIS_MODULE, | ||
182 | .read = mpuirq_read, | ||
183 | .poll = mpuirq_poll, | ||
184 | |||
185 | .unlocked_ioctl = mpuirq_ioctl, | ||
186 | .open = mpuirq_open, | ||
187 | .release = mpuirq_release, | ||
188 | }; | ||
189 | |||
190 | static struct miscdevice mpuirq_device = { | ||
191 | .minor = MISC_DYNAMIC_MINOR, | ||
192 | .name = MPUIRQ_NAME, | ||
193 | .fops = &mpuirq_fops, | ||
194 | }; | ||
195 | |||
196 | int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg, | ||
197 | unsigned long irq_flags) | ||
198 | { | ||
199 | |||
200 | int res; | ||
201 | |||
202 | mpuirq_dev_data.mpu_client = mpu_client; | ||
203 | |||
204 | dev_info(&mpu_client->adapter->dev, | ||
205 | "Module Param interface = %s\n", interface); | ||
206 | |||
207 | mpuirq_dev_data.irq = mpu_client->irq; | ||
208 | mpuirq_dev_data.pid = 0; | ||
209 | mpuirq_dev_data.accel_divider = -1; | ||
210 | mpuirq_dev_data.data_ready = 0; | ||
211 | mpuirq_dev_data.timeout = 0; | ||
212 | mpuirq_dev_data.dev = &mpuirq_device; | ||
213 | |||
214 | if (mpuirq_dev_data.irq) { | ||
215 | irq_flags |= IRQF_SHARED; | ||
216 | res = | ||
217 | request_irq(mpuirq_dev_data.irq, mpuirq_handler, irq_flags, | ||
218 | interface, &mpuirq_dev_data.irq); | ||
219 | if (res) { | ||
220 | dev_err(&mpu_client->adapter->dev, | ||
221 | "myirqtest: cannot register IRQ %d\n", | ||
222 | mpuirq_dev_data.irq); | ||
223 | } else { | ||
224 | res = misc_register(&mpuirq_device); | ||
225 | if (res < 0) { | ||
226 | dev_err(&mpu_client->adapter->dev, | ||
227 | "misc_register returned %d\n", res); | ||
228 | free_irq(mpuirq_dev_data.irq, | ||
229 | &mpuirq_dev_data.irq); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | } else { | ||
234 | res = 0; | ||
235 | } | ||
236 | |||
237 | return res; | ||
238 | } | ||
239 | EXPORT_SYMBOL(mpuirq_init); | ||
240 | |||
241 | void mpuirq_exit(void) | ||
242 | { | ||
243 | if (mpuirq_dev_data.irq > 0) | ||
244 | free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq); | ||
245 | |||
246 | dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME); | ||
247 | misc_deregister(&mpuirq_device); | ||
248 | |||
249 | return; | ||
250 | } | ||
251 | EXPORT_SYMBOL(mpuirq_exit); | ||
252 | |||
253 | module_param(interface, charp, S_IRUGO | S_IWUSR); | ||
254 | MODULE_PARM_DESC(interface, "The Interface name"); | ||
diff --git a/drivers/misc/inv_mpu/mpuirq.h b/drivers/misc/inv_mpu/mpuirq.h new file mode 100644 index 00000000000..073867d830c --- /dev/null +++ b/drivers/misc/inv_mpu/mpuirq.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #ifndef __MPUIRQ__ | ||
21 | #define __MPUIRQ__ | ||
22 | |||
23 | #include <linux/i2c-dev.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <linux/ioctl.h> | ||
26 | #include "mldl_cfg.h" | ||
27 | |||
28 | #define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long) | ||
29 | #define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long) | ||
30 | #define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval) | ||
31 | #define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long) | ||
32 | |||
33 | void mpuirq_exit(void); | ||
34 | int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg, | ||
35 | unsigned long irq_flags); | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig new file mode 100644 index 00000000000..f1c021e8f12 --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | menuconfig: INV_SENSORS_PRESSURE | ||
2 | bool "Pressure Sensor Slaves" | ||
3 | depends on INV_SENSORS | ||
4 | default y | ||
5 | help | ||
6 | Select y to see a list of supported pressure sensors that can be | ||
7 | integrated with the MPUxxxx set of motion processors. | ||
8 | |||
9 | if INV_SENSORS_PRESSURE | ||
10 | |||
11 | config MPU_SENSORS_BMA085 | ||
12 | tristate "Bosch BMA085" | ||
13 | help | ||
14 | This enables support for the Bosch bma085 pressure sensor | ||
15 | This support is for integration with the MPU3050 or MPU6050 gyroscope | ||
16 | device driver. Only one accelerometer can be registered at a time. | ||
17 | Specifying more that one accelerometer in the board file will result | ||
18 | in runtime errors. | ||
19 | |||
20 | endif | ||
diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile new file mode 100644 index 00000000000..595923d809d --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Pressure Slaves to MPUxxxx | ||
3 | # | ||
4 | obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o | ||
5 | inv_mpu_bma085-objs += bma085.o | ||
6 | |||
7 | EXTRA_CFLAGS += -Idrivers/misc/inv_mpu | ||
8 | EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER | ||
diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c new file mode 100644 index 00000000000..696d2b6e183 --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/bma085.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @defgroup ACCELDL (Motion Library - Pressure Driver Layer) | ||
22 | * @brief Provides the interface to setup and handle a pressure | ||
23 | * connected to the secondary I2C interface of the gyroscope. | ||
24 | * | ||
25 | * @{ | ||
26 | * @file bma085.c | ||
27 | * @brief Pressure setup and handling methods. | ||
28 | */ | ||
29 | |||
30 | /* ------------------ */ | ||
31 | /* - Include Files. - */ | ||
32 | /* ------------------ */ | ||
33 | |||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include "mpu-dev.h" | ||
42 | |||
43 | #include <linux/mpu.h> | ||
44 | #include "mlsl.h" | ||
45 | #include "log.h" | ||
46 | |||
47 | /* | ||
48 | * this structure holds all device specific calibration parameters | ||
49 | */ | ||
50 | struct bmp085_calibration_param_t { | ||
51 | short ac1; | ||
52 | short ac2; | ||
53 | short ac3; | ||
54 | unsigned short ac4; | ||
55 | unsigned short ac5; | ||
56 | unsigned short ac6; | ||
57 | short b1; | ||
58 | short b2; | ||
59 | short mb; | ||
60 | short mc; | ||
61 | short md; | ||
62 | long param_b5; | ||
63 | }; | ||
64 | |||
65 | struct bmp085_calibration_param_t cal_param; | ||
66 | |||
67 | #define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */ | ||
68 | #define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */ | ||
69 | #define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */ | ||
70 | |||
71 | /********************************************* | ||
72 | * Pressure Initialization Functions | ||
73 | *********************************************/ | ||
74 | |||
75 | static int bma085_suspend(void *mlsl_handle, | ||
76 | struct ext_slave_descr *slave, | ||
77 | struct ext_slave_platform_data *pdata) | ||
78 | { | ||
79 | int result = INV_SUCCESS; | ||
80 | return result; | ||
81 | } | ||
82 | |||
83 | #define PRESSURE_BMA085_PROM_START_ADDR (0xAA) | ||
84 | #define PRESSURE_BMA085_PROM_DATA_LEN (22) | ||
85 | #define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4) | ||
86 | /* temperature measurent */ | ||
87 | #define PRESSURE_BMP085_T_MEAS (0x2E) | ||
88 | /* pressure measurement; oversampling_setting */ | ||
89 | #define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34) | ||
90 | #define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74) | ||
91 | #define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4) | ||
92 | #define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4) | ||
93 | #define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6) | ||
94 | #define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7) | ||
95 | |||
96 | static int bma085_resume(void *mlsl_handle, | ||
97 | struct ext_slave_descr *slave, | ||
98 | struct ext_slave_platform_data *pdata) | ||
99 | { | ||
100 | int result; | ||
101 | unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN]; | ||
102 | |||
103 | result = | ||
104 | inv_serial_read(mlsl_handle, pdata->address, | ||
105 | PRESSURE_BMA085_PROM_START_ADDR, | ||
106 | PRESSURE_BMA085_PROM_DATA_LEN, data); | ||
107 | if (result) { | ||
108 | LOG_RESULT_LOCATION(result); | ||
109 | return result; | ||
110 | } | ||
111 | |||
112 | /* parameters AC1-AC6 */ | ||
113 | cal_param.ac1 = (data[0] << 8) | data[1]; | ||
114 | cal_param.ac2 = (data[2] << 8) | data[3]; | ||
115 | cal_param.ac3 = (data[4] << 8) | data[5]; | ||
116 | cal_param.ac4 = (data[6] << 8) | data[7]; | ||
117 | cal_param.ac5 = (data[8] << 8) | data[9]; | ||
118 | cal_param.ac6 = (data[10] << 8) | data[11]; | ||
119 | |||
120 | /* parameters B1,B2 */ | ||
121 | cal_param.b1 = (data[12] << 8) | data[13]; | ||
122 | cal_param.b2 = (data[14] << 8) | data[15]; | ||
123 | |||
124 | /* parameters MB,MC,MD */ | ||
125 | cal_param.mb = (data[16] << 8) | data[17]; | ||
126 | cal_param.mc = (data[18] << 8) | data[19]; | ||
127 | cal_param.md = (data[20] << 8) | data[21]; | ||
128 | |||
129 | return result; | ||
130 | } | ||
131 | |||
132 | static int bma085_read(void *mlsl_handle, | ||
133 | struct ext_slave_descr *slave, | ||
134 | struct ext_slave_platform_data *pdata, | ||
135 | unsigned char *data) | ||
136 | { | ||
137 | int result; | ||
138 | long pressure, x1, x2, x3, b3, b6; | ||
139 | unsigned long b4, b7; | ||
140 | unsigned long up; | ||
141 | unsigned short ut; | ||
142 | short oversampling_setting = 0; | ||
143 | short temperature; | ||
144 | long divisor; | ||
145 | |||
146 | /* get temprature */ | ||
147 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
148 | PRESSURE_BMP085_CTRL_MEAS_REG, | ||
149 | PRESSURE_BMP085_T_MEAS); | ||
150 | msleep(5); | ||
151 | result = | ||
152 | inv_serial_read(mlsl_handle, pdata->address, | ||
153 | PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, | ||
154 | (unsigned char *)data); | ||
155 | if (result) { | ||
156 | LOG_RESULT_LOCATION(result); | ||
157 | return result; | ||
158 | } | ||
159 | ut = (data[0] << 8) | data[1]; | ||
160 | |||
161 | x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15; | ||
162 | divisor = x1 + cal_param.md; | ||
163 | if (!divisor) | ||
164 | return INV_ERROR_DIVIDE_BY_ZERO; | ||
165 | |||
166 | x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md); | ||
167 | cal_param.param_b5 = x1 + x2; | ||
168 | /* temperature in 0.1 degree C */ | ||
169 | temperature = (short)((cal_param.param_b5 + 8) >> 4); | ||
170 | |||
171 | /* get pressure */ | ||
172 | result = inv_serial_single_write(mlsl_handle, pdata->address, | ||
173 | PRESSURE_BMP085_CTRL_MEAS_REG, | ||
174 | PRESSURE_BMP085_P_MEAS_OSS_0); | ||
175 | msleep(5); | ||
176 | result = | ||
177 | inv_serial_read(mlsl_handle, pdata->address, | ||
178 | PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, | ||
179 | (unsigned char *)data); | ||
180 | if (result) { | ||
181 | LOG_RESULT_LOCATION(result); | ||
182 | return result; | ||
183 | } | ||
184 | up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1])); | ||
185 | |||
186 | b6 = cal_param.param_b5 - 4000; | ||
187 | /* calculate B3 */ | ||
188 | x1 = (b6*b6) >> 12; | ||
189 | x1 *= cal_param.b2; | ||
190 | x1 >>= 11; | ||
191 | |||
192 | x2 = (cal_param.ac2*b6); | ||
193 | x2 >>= 11; | ||
194 | |||
195 | x3 = x1 + x2; | ||
196 | |||
197 | b3 = (((((long)cal_param.ac1) * 4 + x3) | ||
198 | << oversampling_setting) + 2) >> 2; | ||
199 | |||
200 | /* calculate B4 */ | ||
201 | x1 = (cal_param.ac3 * b6) >> 13; | ||
202 | x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16; | ||
203 | x3 = ((x1 + x2) + 2) >> 2; | ||
204 | b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15; | ||
205 | if (!b4) | ||
206 | return INV_ERROR; | ||
207 | |||
208 | b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting)); | ||
209 | if (b7 < 0x80000000) | ||
210 | pressure = (b7 << 1) / b4; | ||
211 | else | ||
212 | pressure = (b7 / b4) << 1; | ||
213 | |||
214 | x1 = pressure >> 8; | ||
215 | x1 *= x1; | ||
216 | x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16; | ||
217 | x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16; | ||
218 | /* pressure in Pa */ | ||
219 | pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4; | ||
220 | |||
221 | data[0] = (unsigned char)(pressure >> 16); | ||
222 | data[1] = (unsigned char)(pressure >> 8); | ||
223 | data[2] = (unsigned char)(pressure & 0xFF); | ||
224 | |||
225 | return result; | ||
226 | } | ||
227 | |||
228 | static struct ext_slave_descr bma085_descr = { | ||
229 | .init = NULL, | ||
230 | .exit = NULL, | ||
231 | .suspend = bma085_suspend, | ||
232 | .resume = bma085_resume, | ||
233 | .read = bma085_read, | ||
234 | .config = NULL, | ||
235 | .get_config = NULL, | ||
236 | .name = "bma085", | ||
237 | .type = EXT_SLAVE_TYPE_PRESSURE, | ||
238 | .id = PRESSURE_ID_BMA085, | ||
239 | .read_reg = 0xF6, | ||
240 | .read_len = 3, | ||
241 | .endian = EXT_SLAVE_BIG_ENDIAN, | ||
242 | .range = {0, 0}, | ||
243 | }; | ||
244 | |||
245 | static | ||
246 | struct ext_slave_descr *bma085_get_slave_descr(void) | ||
247 | { | ||
248 | return &bma085_descr; | ||
249 | } | ||
250 | |||
251 | /* Platform data for the MPU */ | ||
252 | struct bma085_mod_private_data { | ||
253 | struct i2c_client *client; | ||
254 | struct ext_slave_platform_data *pdata; | ||
255 | }; | ||
256 | |||
257 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
258 | |||
259 | static int bma085_mod_probe(struct i2c_client *client, | ||
260 | const struct i2c_device_id *devid) | ||
261 | { | ||
262 | struct ext_slave_platform_data *pdata; | ||
263 | struct bma085_mod_private_data *private_data; | ||
264 | int result = 0; | ||
265 | |||
266 | dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); | ||
267 | |||
268 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
269 | result = -ENODEV; | ||
270 | goto out_no_free; | ||
271 | } | ||
272 | |||
273 | pdata = client->dev.platform_data; | ||
274 | if (!pdata) { | ||
275 | dev_err(&client->adapter->dev, | ||
276 | "Missing platform data for slave %s\n", devid->name); | ||
277 | result = -EFAULT; | ||
278 | goto out_no_free; | ||
279 | } | ||
280 | |||
281 | private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); | ||
282 | if (!private_data) { | ||
283 | result = -ENOMEM; | ||
284 | goto out_no_free; | ||
285 | } | ||
286 | |||
287 | i2c_set_clientdata(client, private_data); | ||
288 | private_data->client = client; | ||
289 | private_data->pdata = pdata; | ||
290 | |||
291 | result = inv_mpu_register_slave(THIS_MODULE, client, pdata, | ||
292 | bma085_get_slave_descr); | ||
293 | if (result) { | ||
294 | dev_err(&client->adapter->dev, | ||
295 | "Slave registration failed: %s, %d\n", | ||
296 | devid->name, result); | ||
297 | goto out_free_memory; | ||
298 | } | ||
299 | |||
300 | return result; | ||
301 | |||
302 | out_free_memory: | ||
303 | kfree(private_data); | ||
304 | out_no_free: | ||
305 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); | ||
306 | return result; | ||
307 | |||
308 | } | ||
309 | |||
310 | static int bma085_mod_remove(struct i2c_client *client) | ||
311 | { | ||
312 | struct bma085_mod_private_data *private_data = | ||
313 | i2c_get_clientdata(client); | ||
314 | |||
315 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
316 | |||
317 | inv_mpu_unregister_slave(client, private_data->pdata, | ||
318 | bma085_get_slave_descr); | ||
319 | |||
320 | kfree(private_data); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static const struct i2c_device_id bma085_mod_id[] = { | ||
325 | { "bma085", PRESSURE_ID_BMA085 }, | ||
326 | {} | ||
327 | }; | ||
328 | |||
329 | MODULE_DEVICE_TABLE(i2c, bma085_mod_id); | ||
330 | |||
331 | static struct i2c_driver bma085_mod_driver = { | ||
332 | .class = I2C_CLASS_HWMON, | ||
333 | .probe = bma085_mod_probe, | ||
334 | .remove = bma085_mod_remove, | ||
335 | .id_table = bma085_mod_id, | ||
336 | .driver = { | ||
337 | .owner = THIS_MODULE, | ||
338 | .name = "bma085_mod", | ||
339 | }, | ||
340 | .address_list = normal_i2c, | ||
341 | }; | ||
342 | |||
343 | static int __init bma085_mod_init(void) | ||
344 | { | ||
345 | int res = i2c_add_driver(&bma085_mod_driver); | ||
346 | pr_info("%s: Probe name %s\n", __func__, "bma085_mod"); | ||
347 | if (res) | ||
348 | pr_err("%s failed\n", __func__); | ||
349 | return res; | ||
350 | } | ||
351 | |||
352 | static void __exit bma085_mod_exit(void) | ||
353 | { | ||
354 | pr_info("%s\n", __func__); | ||
355 | i2c_del_driver(&bma085_mod_driver); | ||
356 | } | ||
357 | |||
358 | module_init(bma085_mod_init); | ||
359 | module_exit(bma085_mod_exit); | ||
360 | |||
361 | MODULE_AUTHOR("Invensense Corporation"); | ||
362 | MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU"); | ||
363 | MODULE_LICENSE("GPL"); | ||
364 | MODULE_ALIAS("bma085_mod"); | ||
365 | /** | ||
366 | * @} | ||
367 | **/ | ||
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c new file mode 100644 index 00000000000..22cfa4e458e --- /dev/null +++ b/drivers/misc/inv_mpu/slaveirq.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/stat.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/signal.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/i2c-dev.h> | ||
30 | #include <linux/poll.h> | ||
31 | |||
32 | #include <linux/errno.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/mm.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/wait.h> | ||
37 | #include <linux/uaccess.h> | ||
38 | #include <linux/io.h> | ||
39 | #include <linux/wait.h> | ||
40 | #include <linux/slab.h> | ||
41 | |||
42 | #include <linux/mpu.h> | ||
43 | #include "slaveirq.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | |||
46 | /* function which gets slave data and sends it to SLAVE */ | ||
47 | |||
48 | struct slaveirq_dev_data { | ||
49 | struct miscdevice dev; | ||
50 | struct i2c_client *slave_client; | ||
51 | struct mpuirq_data data; | ||
52 | wait_queue_head_t slaveirq_wait; | ||
53 | int irq; | ||
54 | int pid; | ||
55 | int data_ready; | ||
56 | int timeout; | ||
57 | }; | ||
58 | |||
59 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
60 | * drivers: misc: pass miscdevice pointer via file private data | ||
61 | */ | ||
62 | static int slaveirq_open(struct inode *inode, struct file *file) | ||
63 | { | ||
64 | /* Device node is availabe in the file->private_data, this is | ||
65 | * exactly what we want so we leave it there */ | ||
66 | struct slaveirq_dev_data *data = | ||
67 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
68 | |||
69 | dev_dbg(data->dev.this_device, | ||
70 | "%s current->pid %d\n", __func__, current->pid); | ||
71 | data->pid = current->pid; | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int slaveirq_release(struct inode *inode, struct file *file) | ||
76 | { | ||
77 | struct slaveirq_dev_data *data = | ||
78 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
79 | dev_dbg(data->dev.this_device, "slaveirq_release\n"); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* read function called when from /dev/slaveirq is read */ | ||
84 | static ssize_t slaveirq_read(struct file *file, | ||
85 | char *buf, size_t count, loff_t *ppos) | ||
86 | { | ||
87 | int len, err; | ||
88 | struct slaveirq_dev_data *data = | ||
89 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
90 | |||
91 | if (!data->data_ready && data->timeout && | ||
92 | !(file->f_flags & O_NONBLOCK)) { | ||
93 | wait_event_interruptible_timeout(data->slaveirq_wait, | ||
94 | data->data_ready, | ||
95 | data->timeout); | ||
96 | } | ||
97 | |||
98 | if (data->data_ready && NULL != buf && count >= sizeof(data->data)) { | ||
99 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
100 | data->data.data_type = 0; | ||
101 | } else { | ||
102 | return 0; | ||
103 | } | ||
104 | if (err != 0) { | ||
105 | dev_err(data->dev.this_device, | ||
106 | "Copy to user returned %d\n", err); | ||
107 | return -EFAULT; | ||
108 | } | ||
109 | data->data_ready = 0; | ||
110 | len = sizeof(data->data); | ||
111 | return len; | ||
112 | } | ||
113 | |||
114 | static unsigned int slaveirq_poll(struct file *file, | ||
115 | struct poll_table_struct *poll) | ||
116 | { | ||
117 | int mask = 0; | ||
118 | struct slaveirq_dev_data *data = | ||
119 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
120 | |||
121 | poll_wait(file, &data->slaveirq_wait, poll); | ||
122 | if (data->data_ready) | ||
123 | mask |= POLLIN | POLLRDNORM; | ||
124 | return mask; | ||
125 | } | ||
126 | |||
127 | /* ioctl - I/O control */ | ||
128 | static long slaveirq_ioctl(struct file *file, | ||
129 | unsigned int cmd, unsigned long arg) | ||
130 | { | ||
131 | int retval = 0; | ||
132 | int tmp; | ||
133 | struct slaveirq_dev_data *data = | ||
134 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
135 | |||
136 | switch (cmd) { | ||
137 | case SLAVEIRQ_SET_TIMEOUT: | ||
138 | data->timeout = arg; | ||
139 | break; | ||
140 | |||
141 | case SLAVEIRQ_GET_INTERRUPT_CNT: | ||
142 | tmp = data->data.interruptcount - 1; | ||
143 | if (data->data.interruptcount > 1) | ||
144 | data->data.interruptcount = 1; | ||
145 | |||
146 | if (copy_to_user((int *)arg, &tmp, sizeof(int))) | ||
147 | return -EFAULT; | ||
148 | break; | ||
149 | case SLAVEIRQ_GET_IRQ_TIME: | ||
150 | if (copy_to_user((int *)arg, &data->data.irqtime, | ||
151 | sizeof(data->data.irqtime))) | ||
152 | return -EFAULT; | ||
153 | data->data.irqtime = 0; | ||
154 | break; | ||
155 | default: | ||
156 | retval = -EINVAL; | ||
157 | } | ||
158 | return retval; | ||
159 | } | ||
160 | |||
161 | static irqreturn_t slaveirq_handler(int irq, void *dev_id) | ||
162 | { | ||
163 | struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; | ||
164 | static int mycount; | ||
165 | struct timeval irqtime; | ||
166 | mycount++; | ||
167 | |||
168 | data->data.interruptcount++; | ||
169 | |||
170 | /* wake up (unblock) for reading data from userspace */ | ||
171 | data->data_ready = 1; | ||
172 | |||
173 | do_gettimeofday(&irqtime); | ||
174 | data->data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
175 | data->data.irqtime += irqtime.tv_usec; | ||
176 | data->data.data_type |= 1; | ||
177 | |||
178 | wake_up_interruptible(&data->slaveirq_wait); | ||
179 | |||
180 | return IRQ_HANDLED; | ||
181 | |||
182 | } | ||
183 | |||
184 | /* define which file operations are supported */ | ||
185 | static const struct file_operations slaveirq_fops = { | ||
186 | .owner = THIS_MODULE, | ||
187 | .read = slaveirq_read, | ||
188 | .poll = slaveirq_poll, | ||
189 | |||
190 | #if HAVE_COMPAT_IOCTL | ||
191 | .compat_ioctl = slaveirq_ioctl, | ||
192 | #endif | ||
193 | #if HAVE_UNLOCKED_IOCTL | ||
194 | .unlocked_ioctl = slaveirq_ioctl, | ||
195 | #endif | ||
196 | .open = slaveirq_open, | ||
197 | .release = slaveirq_release, | ||
198 | }; | ||
199 | |||
200 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
201 | struct ext_slave_platform_data *pdata, char *name) | ||
202 | { | ||
203 | |||
204 | int res; | ||
205 | struct slaveirq_dev_data *data; | ||
206 | |||
207 | if (!pdata->irq) | ||
208 | return -EINVAL; | ||
209 | |||
210 | pdata->irq_data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
211 | data = (struct slaveirq_dev_data *)pdata->irq_data; | ||
212 | if (!data) | ||
213 | return -ENOMEM; | ||
214 | |||
215 | data->dev.minor = MISC_DYNAMIC_MINOR; | ||
216 | data->dev.name = name; | ||
217 | data->dev.fops = &slaveirq_fops; | ||
218 | data->irq = pdata->irq; | ||
219 | data->pid = 0; | ||
220 | data->data_ready = 0; | ||
221 | data->timeout = 0; | ||
222 | |||
223 | init_waitqueue_head(&data->slaveirq_wait); | ||
224 | |||
225 | res = request_irq(data->irq, slaveirq_handler, | ||
226 | IRQF_TRIGGER_RISING | IRQF_SHARED, | ||
227 | data->dev.name, data); | ||
228 | |||
229 | if (res) { | ||
230 | dev_err(&slave_adapter->dev, | ||
231 | "myirqtest: cannot register IRQ %d\n", data->irq); | ||
232 | goto out_request_irq; | ||
233 | } | ||
234 | |||
235 | res = misc_register(&data->dev); | ||
236 | if (res < 0) { | ||
237 | dev_err(&slave_adapter->dev, | ||
238 | "misc_register returned %d\n", res); | ||
239 | goto out_misc_register; | ||
240 | } | ||
241 | |||
242 | return res; | ||
243 | |||
244 | out_misc_register: | ||
245 | free_irq(data->irq, data); | ||
246 | out_request_irq: | ||
247 | kfree(pdata->irq_data); | ||
248 | pdata->irq_data = NULL; | ||
249 | |||
250 | return res; | ||
251 | } | ||
252 | EXPORT_SYMBOL(slaveirq_init); | ||
253 | |||
254 | void slaveirq_exit(struct ext_slave_platform_data *pdata) | ||
255 | { | ||
256 | struct slaveirq_dev_data *data = pdata->irq_data; | ||
257 | |||
258 | if (!pdata->irq_data || data->irq <= 0) | ||
259 | return; | ||
260 | |||
261 | dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name); | ||
262 | |||
263 | free_irq(data->irq, data); | ||
264 | misc_deregister(&data->dev); | ||
265 | kfree(pdata->irq_data); | ||
266 | pdata->irq_data = NULL; | ||
267 | } | ||
268 | EXPORT_SYMBOL(slaveirq_exit); | ||
diff --git a/drivers/misc/inv_mpu/slaveirq.h b/drivers/misc/inv_mpu/slaveirq.h new file mode 100644 index 00000000000..6926634ff94 --- /dev/null +++ b/drivers/misc/inv_mpu/slaveirq.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #ifndef __SLAVEIRQ__ | ||
21 | #define __SLAVEIRQ__ | ||
22 | |||
23 | #include <linux/i2c-dev.h> | ||
24 | |||
25 | #include <linux/mpu.h> | ||
26 | #include "mpuirq.h" | ||
27 | |||
28 | #define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long) | ||
29 | #define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long) | ||
30 | #define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long) | ||
31 | |||
32 | void slaveirq_exit(struct ext_slave_platform_data *pdata); | ||
33 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
34 | struct ext_slave_platform_data *pdata, char *name); | ||
35 | |||
36 | #endif | ||
diff --git a/drivers/misc/inv_mpu/timerirq.c b/drivers/misc/inv_mpu/timerirq.c new file mode 100644 index 00000000000..601858f9c4d --- /dev/null +++ b/drivers/misc/inv_mpu/timerirq.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/stat.h> | ||
25 | #include <linux/signal.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/i2c-dev.h> | ||
29 | #include <linux/poll.h> | ||
30 | |||
31 | #include <linux/errno.h> | ||
32 | #include <linux/fs.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/sched.h> | ||
35 | #include <linux/wait.h> | ||
36 | #include <linux/uaccess.h> | ||
37 | #include <linux/io.h> | ||
38 | #include <linux/timer.h> | ||
39 | #include <linux/slab.h> | ||
40 | |||
41 | #include <linux/mpu.h> | ||
42 | #include "mltypes.h" | ||
43 | #include "timerirq.h" | ||
44 | |||
45 | /* function which gets timer data and sends it to TIMER */ | ||
46 | struct timerirq_data { | ||
47 | int pid; | ||
48 | int data_ready; | ||
49 | int run; | ||
50 | int timeout; | ||
51 | unsigned long period; | ||
52 | struct mpuirq_data data; | ||
53 | struct completion timer_done; | ||
54 | wait_queue_head_t timerirq_wait; | ||
55 | struct timer_list timer; | ||
56 | struct miscdevice *dev; | ||
57 | }; | ||
58 | |||
59 | static struct miscdevice *timerirq_dev_data; | ||
60 | |||
61 | static void timerirq_handler(unsigned long arg) | ||
62 | { | ||
63 | struct timerirq_data *data = (struct timerirq_data *)arg; | ||
64 | struct timeval irqtime; | ||
65 | |||
66 | data->data.interruptcount++; | ||
67 | |||
68 | data->data_ready = 1; | ||
69 | |||
70 | do_gettimeofday(&irqtime); | ||
71 | data->data.irqtime = (((long long)irqtime.tv_sec) << 32); | ||
72 | data->data.irqtime += irqtime.tv_usec; | ||
73 | data->data.data_type |= 1; | ||
74 | |||
75 | dev_dbg(data->dev->this_device, | ||
76 | "%s, %lld, %ld\n", __func__, data->data.irqtime, | ||
77 | (unsigned long)data); | ||
78 | |||
79 | wake_up_interruptible(&data->timerirq_wait); | ||
80 | |||
81 | if (data->run) | ||
82 | mod_timer(&data->timer, | ||
83 | jiffies + msecs_to_jiffies(data->period)); | ||
84 | else | ||
85 | complete(&data->timer_done); | ||
86 | } | ||
87 | |||
88 | static int start_timerirq(struct timerirq_data *data) | ||
89 | { | ||
90 | dev_dbg(data->dev->this_device, | ||
91 | "%s current->pid %d\n", __func__, current->pid); | ||
92 | |||
93 | /* Timer already running... success */ | ||
94 | if (data->run) | ||
95 | return 0; | ||
96 | |||
97 | /* Don't allow a period of 0 since this would fire constantly */ | ||
98 | if (!data->period) | ||
99 | return -EINVAL; | ||
100 | |||
101 | data->run = true; | ||
102 | data->data_ready = false; | ||
103 | |||
104 | init_completion(&data->timer_done); | ||
105 | setup_timer(&data->timer, timerirq_handler, (unsigned long)data); | ||
106 | |||
107 | return mod_timer(&data->timer, | ||
108 | jiffies + msecs_to_jiffies(data->period)); | ||
109 | } | ||
110 | |||
111 | static int stop_timerirq(struct timerirq_data *data) | ||
112 | { | ||
113 | dev_dbg(data->dev->this_device, | ||
114 | "%s current->pid %lx\n", __func__, (unsigned long)data); | ||
115 | |||
116 | if (data->run) { | ||
117 | data->run = false; | ||
118 | mod_timer(&data->timer, jiffies + 1); | ||
119 | wait_for_completion(&data->timer_done); | ||
120 | } | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
125 | * drivers: misc: pass miscdevice pointer via file private data | ||
126 | */ | ||
127 | static int timerirq_open(struct inode *inode, struct file *file) | ||
128 | { | ||
129 | /* Device node is availabe in the file->private_data, this is | ||
130 | * exactly what we want so we leave it there */ | ||
131 | struct miscdevice *dev_data = file->private_data; | ||
132 | struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
133 | if (!data) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | data->dev = dev_data; | ||
137 | file->private_data = data; | ||
138 | data->pid = current->pid; | ||
139 | init_waitqueue_head(&data->timerirq_wait); | ||
140 | |||
141 | dev_dbg(data->dev->this_device, | ||
142 | "%s current->pid %d\n", __func__, current->pid); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int timerirq_release(struct inode *inode, struct file *file) | ||
147 | { | ||
148 | struct timerirq_data *data = file->private_data; | ||
149 | dev_dbg(data->dev->this_device, "timerirq_release\n"); | ||
150 | if (data->run) | ||
151 | stop_timerirq(data); | ||
152 | kfree(data); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /* read function called when from /dev/timerirq is read */ | ||
157 | static ssize_t timerirq_read(struct file *file, | ||
158 | char *buf, size_t count, loff_t *ppos) | ||
159 | { | ||
160 | int len, err; | ||
161 | struct timerirq_data *data = file->private_data; | ||
162 | |||
163 | if (!data->data_ready && data->timeout && | ||
164 | !(file->f_flags & O_NONBLOCK)) { | ||
165 | wait_event_interruptible_timeout(data->timerirq_wait, | ||
166 | data->data_ready, | ||
167 | data->timeout); | ||
168 | } | ||
169 | |||
170 | if (data->data_ready && NULL != buf && count >= sizeof(data->data)) { | ||
171 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
172 | data->data.data_type = 0; | ||
173 | } else { | ||
174 | return 0; | ||
175 | } | ||
176 | if (err != 0) { | ||
177 | dev_err(data->dev->this_device, | ||
178 | "Copy to user returned %d\n", err); | ||
179 | return -EFAULT; | ||
180 | } | ||
181 | data->data_ready = 0; | ||
182 | len = sizeof(data->data); | ||
183 | return len; | ||
184 | } | ||
185 | |||
186 | static unsigned int timerirq_poll(struct file *file, | ||
187 | struct poll_table_struct *poll) | ||
188 | { | ||
189 | int mask = 0; | ||
190 | struct timerirq_data *data = file->private_data; | ||
191 | |||
192 | poll_wait(file, &data->timerirq_wait, poll); | ||
193 | if (data->data_ready) | ||
194 | mask |= POLLIN | POLLRDNORM; | ||
195 | return mask; | ||
196 | } | ||
197 | |||
198 | /* ioctl - I/O control */ | ||
199 | static long timerirq_ioctl(struct file *file, | ||
200 | unsigned int cmd, unsigned long arg) | ||
201 | { | ||
202 | int retval = 0; | ||
203 | int tmp; | ||
204 | struct timerirq_data *data = file->private_data; | ||
205 | |||
206 | dev_dbg(data->dev->this_device, | ||
207 | "%s current->pid %d, %d, %ld\n", | ||
208 | __func__, current->pid, cmd, arg); | ||
209 | |||
210 | if (!data) | ||
211 | return -EFAULT; | ||
212 | |||
213 | switch (cmd) { | ||
214 | case TIMERIRQ_SET_TIMEOUT: | ||
215 | data->timeout = arg; | ||
216 | break; | ||
217 | case TIMERIRQ_GET_INTERRUPT_CNT: | ||
218 | tmp = data->data.interruptcount - 1; | ||
219 | if (data->data.interruptcount > 1) | ||
220 | data->data.interruptcount = 1; | ||
221 | |||
222 | if (copy_to_user((int *)arg, &tmp, sizeof(int))) | ||
223 | return -EFAULT; | ||
224 | break; | ||
225 | case TIMERIRQ_START: | ||
226 | data->period = arg; | ||
227 | retval = start_timerirq(data); | ||
228 | break; | ||
229 | case TIMERIRQ_STOP: | ||
230 | retval = stop_timerirq(data); | ||
231 | break; | ||
232 | default: | ||
233 | retval = -EINVAL; | ||
234 | } | ||
235 | return retval; | ||
236 | } | ||
237 | |||
238 | /* define which file operations are supported */ | ||
239 | static const struct file_operations timerirq_fops = { | ||
240 | .owner = THIS_MODULE, | ||
241 | .read = timerirq_read, | ||
242 | .poll = timerirq_poll, | ||
243 | |||
244 | #if HAVE_COMPAT_IOCTL | ||
245 | .compat_ioctl = timerirq_ioctl, | ||
246 | #endif | ||
247 | #if HAVE_UNLOCKED_IOCTL | ||
248 | .unlocked_ioctl = timerirq_ioctl, | ||
249 | #endif | ||
250 | .open = timerirq_open, | ||
251 | .release = timerirq_release, | ||
252 | }; | ||
253 | |||
254 | static int __init timerirq_init(void) | ||
255 | { | ||
256 | |||
257 | int res; | ||
258 | static struct miscdevice *data; | ||
259 | |||
260 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
261 | if (!data) | ||
262 | return -ENOMEM; | ||
263 | timerirq_dev_data = data; | ||
264 | data->minor = MISC_DYNAMIC_MINOR; | ||
265 | data->name = "timerirq"; | ||
266 | data->fops = &timerirq_fops; | ||
267 | |||
268 | res = misc_register(data); | ||
269 | if (res < 0) { | ||
270 | dev_err(data->this_device, "misc_register returned %d\n", res); | ||
271 | return res; | ||
272 | } | ||
273 | |||
274 | return res; | ||
275 | } | ||
276 | |||
277 | module_init(timerirq_init); | ||
278 | |||
279 | static void __exit timerirq_exit(void) | ||
280 | { | ||
281 | struct miscdevice *data = timerirq_dev_data; | ||
282 | |||
283 | dev_info(data->this_device, "Unregistering %s\n", data->name); | ||
284 | |||
285 | misc_deregister(data); | ||
286 | kfree(data); | ||
287 | |||
288 | timerirq_dev_data = NULL; | ||
289 | } | ||
290 | |||
291 | module_exit(timerirq_exit); | ||
292 | |||
293 | MODULE_AUTHOR("Invensense Corporation"); | ||
294 | MODULE_DESCRIPTION("Timer IRQ device driver."); | ||
295 | MODULE_LICENSE("GPL"); | ||
296 | MODULE_ALIAS("timerirq"); | ||
diff --git a/drivers/misc/inv_mpu/timerirq.h b/drivers/misc/inv_mpu/timerirq.h new file mode 100644 index 00000000000..f69f07a45a3 --- /dev/null +++ b/drivers/misc/inv_mpu/timerirq.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | |||
20 | #ifndef __TIMERIRQ__ | ||
21 | #define __TIMERIRQ__ | ||
22 | |||
23 | #include <linux/mpu.h> | ||
24 | |||
25 | #define TIMERIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x60, unsigned long) | ||
26 | #define TIMERIRQ_GET_INTERRUPT_CNT _IOW(MPU_IOCTL, 0x61, unsigned long) | ||
27 | #define TIMERIRQ_START _IOW(MPU_IOCTL, 0x62, unsigned long) | ||
28 | #define TIMERIRQ_STOP _IO(MPU_IOCTL, 0x63) | ||
29 | |||
30 | #endif | ||