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/mpu3050 | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/mpu3050')
-rw-r--r-- | drivers/misc/mpu3050/Kconfig | 65 | ||||
-rw-r--r-- | drivers/misc/mpu3050/Makefile | 132 | ||||
-rw-r--r-- | drivers/misc/mpu3050/accel/kxtf9.c | 669 | ||||
-rw-r--r-- | drivers/misc/mpu3050/compass/ak8975.c | 258 | ||||
-rw-r--r-- | drivers/misc/mpu3050/log.h | 306 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mldl_cfg.c | 1739 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mldl_cfg.h | 199 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mlos-kernel.c | 89 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mlos.h | 73 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mlsl-kernel.c | 331 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mlsl.h | 103 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mltypes.h | 227 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mpu-dev.c | 1310 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mpu-i2c.c | 196 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mpu-i2c.h | 58 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mpuirq.c | 319 | ||||
-rw-r--r-- | drivers/misc/mpu3050/mpuirq.h | 42 | ||||
-rw-r--r-- | drivers/misc/mpu3050/slaveirq.c | 273 | ||||
-rw-r--r-- | drivers/misc/mpu3050/slaveirq.h | 43 | ||||
-rw-r--r-- | drivers/misc/mpu3050/timerirq.c | 299 | ||||
-rw-r--r-- | drivers/misc/mpu3050/timerirq.h | 30 |
21 files changed, 6761 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/Kconfig b/drivers/misc/mpu3050/Kconfig new file mode 100644 index 00000000000..de240fa0ad8 --- /dev/null +++ b/drivers/misc/mpu3050/Kconfig | |||
@@ -0,0 +1,65 @@ | |||
1 | |||
2 | menu "Motion Sensors Support" | ||
3 | |||
4 | choice | ||
5 | tristate "Motion Processing Unit" | ||
6 | depends on I2C | ||
7 | optional | ||
8 | |||
9 | config MPU_SENSORS_MPU3050 | ||
10 | tristate "MPU3050" | ||
11 | help | ||
12 | If you say yes here you get support for the MPU3050 Gyroscope driver | ||
13 | This driver can also be built as a module. If so, the module | ||
14 | will be called mpu3050. | ||
15 | |||
16 | config MPU_SENSORS_MPU6000 | ||
17 | tristate "MPU6000" | ||
18 | help | ||
19 | If you say yes here you get support for the MPU6000 Gyroscope driver | ||
20 | This driver can also be built as a module. If so, the module | ||
21 | will be called mpu6000. | ||
22 | |||
23 | endchoice | ||
24 | |||
25 | choice | ||
26 | prompt "Accelerometer Type" | ||
27 | depends on MPU_SENSORS_MPU3050 | ||
28 | optional | ||
29 | |||
30 | config MPU_SENSORS_KXTF9 | ||
31 | bool "Kionix KXTF9" | ||
32 | help | ||
33 | This enables support for the Kionix KXFT9 accelerometer | ||
34 | |||
35 | endchoice | ||
36 | |||
37 | choice | ||
38 | prompt "Compass Type" | ||
39 | depends on MPU_SENSORS_MPU6000 || MPU_SENSORS_MPU3050 | ||
40 | optional | ||
41 | |||
42 | config MPU_SENSORS_AK8975 | ||
43 | bool "AKM ak8975" | ||
44 | help | ||
45 | This enables support for the AKM ak8975 compass | ||
46 | |||
47 | endchoice | ||
48 | |||
49 | config MPU_SENSORS_TIMERIRQ | ||
50 | tristate "Timer IRQ" | ||
51 | help | ||
52 | If you say yes here you get access to the timerirq device handle which | ||
53 | can be used to select on. This can be used instead of IRQ's, sleeping, | ||
54 | or timer threads. Reading from this device returns the same type of | ||
55 | information as reading from the MPU and slave IRQ's. | ||
56 | |||
57 | config MPU_SENSORS_DEBUG | ||
58 | bool "MPU debug" | ||
59 | depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6000 || MPU_SENSORS_TIMERIRQ | ||
60 | help | ||
61 | If you say yes here you get extra debug messages from the MPU3050 | ||
62 | and other slave sensors. | ||
63 | |||
64 | endmenu | ||
65 | |||
diff --git a/drivers/misc/mpu3050/Makefile b/drivers/misc/mpu3050/Makefile new file mode 100644 index 00000000000..89ac46fdac5 --- /dev/null +++ b/drivers/misc/mpu3050/Makefile | |||
@@ -0,0 +1,132 @@ | |||
1 | |||
2 | # Kernel makefile for motions sensors | ||
3 | # | ||
4 | # | ||
5 | |||
6 | # MPU | ||
7 | obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o | ||
8 | mpu3050-objs += mpuirq.o \ | ||
9 | slaveirq.o \ | ||
10 | mpu-dev.o \ | ||
11 | mpu-i2c.o \ | ||
12 | mlsl-kernel.o \ | ||
13 | mlos-kernel.o \ | ||
14 | $(MLLITE_DIR)mldl_cfg.o | ||
15 | |||
16 | # | ||
17 | # Accel options | ||
18 | # | ||
19 | ifdef CONFIG_MPU_SENSORS_ADXL346 | ||
20 | mpu3050-objs += $(MLLITE_DIR)accel/adxl346.o | ||
21 | endif | ||
22 | |||
23 | ifdef CONFIG_MPU_SENSORS_BMA150 | ||
24 | mpu3050-objs += $(MLLITE_DIR)accel/bma150.o | ||
25 | endif | ||
26 | |||
27 | ifdef CONFIG_MPU_SENSORS_BMA222 | ||
28 | mpu3050-objs += $(MLLITE_DIR)accel/bma222.o | ||
29 | endif | ||
30 | |||
31 | ifdef CONFIG_MPU_SENSORS_KXSD9 | ||
32 | mpu3050-objs += $(MLLITE_DIR)accel/kxsd9.o | ||
33 | endif | ||
34 | |||
35 | ifdef CONFIG_MPU_SENSORS_KXTF9 | ||
36 | mpu3050-objs += $(MLLITE_DIR)accel/kxtf9.o | ||
37 | endif | ||
38 | |||
39 | ifdef CONFIG_MPU_SENSORS_LIS331DLH | ||
40 | mpu3050-objs += $(MLLITE_DIR)accel/lis331.o | ||
41 | endif | ||
42 | |||
43 | ifdef CONFIG_MPU_SENSORS_LIS3DH | ||
44 | mpu3050-objs += $(MLLITE_DIR)accel/lis3dh.o | ||
45 | endif | ||
46 | |||
47 | ifdef CONFIG_MPU_SENSORS_LSM303DLHA | ||
48 | mpu3050-objs += $(MLLITE_DIR)accel/lsm303a.o | ||
49 | endif | ||
50 | |||
51 | ifdef CONFIG_MPU_SENSORS_MMA8450 | ||
52 | mpu3050-objs += $(MLLITE_DIR)accel/mma8450.o | ||
53 | endif | ||
54 | |||
55 | ifdef CONFIG_MPU_SENSORS_MMA845X | ||
56 | mpu3050-objs += $(MLLITE_DIR)accel/mma845x.o | ||
57 | endif | ||
58 | |||
59 | # | ||
60 | # Compass options | ||
61 | # | ||
62 | ifdef CONFIG_MPU_SENSORS_AK8975 | ||
63 | mpu3050-objs += $(MLLITE_DIR)compass/ak8975.o | ||
64 | endif | ||
65 | |||
66 | ifdef CONFIG_MPU_SENSORS_AMI30X | ||
67 | mpu3050-objs += $(MLLITE_DIR)compass/ami30x.o | ||
68 | endif | ||
69 | |||
70 | ifdef CONFIG_MPU_SENSORS_AMI306 | ||
71 | mpu3050-objs += $(MLLITE_DIR)compass/ami306.o | ||
72 | endif | ||
73 | |||
74 | ifdef CONFIG_MPU_SENSORS_HMC5883 | ||
75 | mpu3050-objs += $(MLLITE_DIR)compass/hmc5883.o | ||
76 | endif | ||
77 | |||
78 | ifdef CONFIG_MPU_SENSORS_LSM303DLHM | ||
79 | mpu3050-objs += $(MLLITE_DIR)compass/lsm303m.o | ||
80 | endif | ||
81 | |||
82 | ifdef CONFIG_MPU_SENSORS_MMC314X | ||
83 | mpu3050-objs += $(MLLITE_DIR)compass/mmc314x.o | ||
84 | endif | ||
85 | |||
86 | ifdef CONFIG_MPU_SENSORS_YAS529 | ||
87 | mpu3050-objs += $(MLLITE_DIR)compass/yas529-kernel.o | ||
88 | endif | ||
89 | |||
90 | ifdef CONFIG_MPU_SENSORS_YAS530 | ||
91 | mpu3050-objs += $(MLLITE_DIR)compass/yas530.o | ||
92 | endif | ||
93 | |||
94 | ifdef CONFIG_MPU_SENSORS_HSCDTD002B | ||
95 | mpu3050-objs += $(MLLITE_DIR)compass/hscdtd002b.o | ||
96 | endif | ||
97 | |||
98 | ifdef CONFIG_MPU_SENSORS_HSCDTD004A | ||
99 | mpu3050-objs += $(MLLITE_DIR)compass/hscdtd004a.o | ||
100 | endif | ||
101 | # | ||
102 | # Pressure options | ||
103 | # | ||
104 | ifdef CONFIG_MPU_SENSORS_BMA085 | ||
105 | mpu3050-objs += $(MLLITE_DIR)pressure/bma085.o | ||
106 | endif | ||
107 | |||
108 | EXTRA_CFLAGS += -I$(M)/$(MLLITE_DIR) \ | ||
109 | -I$(M)/../../include \ | ||
110 | -Idrivers/misc/mpu3050 \ | ||
111 | -Iinclude/linux | ||
112 | |||
113 | obj-$(CONFIG_MPU_SENSORS_MPU6000)+= mpu6000.o | ||
114 | mpu6000-objs += mpuirq.o \ | ||
115 | slaveirq.o \ | ||
116 | mpu-dev.o \ | ||
117 | mpu-i2c.o \ | ||
118 | mlsl-kernel.o \ | ||
119 | mlos-kernel.o \ | ||
120 | $(MLLITE_DIR)mldl_cfg.o \ | ||
121 | $(MLLITE_DIR)accel/mantis.o | ||
122 | |||
123 | ifdef CONFIG_MPU_SENSORS_AK8975 | ||
124 | mpu6000-objs += $(MLLITE_DIR)compass/ak8975.o | ||
125 | endif | ||
126 | |||
127 | ifdef CONFIG_MPU_SENSORS_MPU6000 | ||
128 | EXTRA_CFLAGS += -DM_HW | ||
129 | endif | ||
130 | |||
131 | obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o | ||
132 | |||
diff --git a/drivers/misc/mpu3050/accel/kxtf9.c b/drivers/misc/mpu3050/accel/kxtf9.c new file mode 100644 index 00000000000..938cd572a8f --- /dev/null +++ b/drivers/misc/mpu3050/accel/kxtf9.c | |||
@@ -0,0 +1,669 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 - Accelerometer Driver Layer) | ||
22 | * @brief Provides the interface to setup and handle an accelerometers | ||
23 | * connected to the secondary I2C interface of the gyroscope. | ||
24 | * | ||
25 | * @{ | ||
26 | * @file kxtf9.c | ||
27 | * @brief Accelerometer setup and handling methods. | ||
28 | */ | ||
29 | |||
30 | /* ------------------ */ | ||
31 | /* - Include Files. - */ | ||
32 | /* ------------------ */ | ||
33 | |||
34 | #undef MPL_LOG_NDEBUG | ||
35 | #define MPL_LOG_NDEBUG 1 | ||
36 | |||
37 | #ifdef __KERNEL__ | ||
38 | #include <linux/module.h> | ||
39 | #endif | ||
40 | |||
41 | #include "mpu.h" | ||
42 | #include "mlsl.h" | ||
43 | #include "mlos.h" | ||
44 | |||
45 | #include <log.h> | ||
46 | #undef MPL_LOG_TAG | ||
47 | #define MPL_LOG_TAG "MPL-acc" | ||
48 | |||
49 | #define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */ | ||
50 | #define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */ | ||
51 | #define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */ | ||
52 | #define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */ | ||
53 | #define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */ | ||
54 | #define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */ | ||
55 | #define KXTF9_XOUT_L (0x06) /* 0000 0110 */ | ||
56 | #define KXTF9_XOUT_H (0x07) /* 0000 0111 */ | ||
57 | #define KXTF9_YOUT_L (0x08) /* 0000 1000 */ | ||
58 | #define KXTF9_YOUT_H (0x09) /* 0000 1001 */ | ||
59 | #define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */ | ||
60 | #define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */ | ||
61 | #define KXTF9_ST_RESP (0x0C) /* 0000 1100 */ | ||
62 | #define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */ | ||
63 | #define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */ | ||
64 | #define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */ | ||
65 | #define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */ | ||
66 | #define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */ | ||
67 | #define KXTF9_STATUS_REG (0x18) /* 0001 1000 */ | ||
68 | #define KXTF9_INT_REL (0x1A) /* 0001 1010 */ | ||
69 | #define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */ | ||
70 | #define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */ | ||
71 | #define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */ | ||
72 | #define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */ | ||
73 | #define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */ | ||
74 | #define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */ | ||
75 | #define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */ | ||
76 | #define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */ | ||
77 | #define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */ | ||
78 | #define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */ | ||
79 | #define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */ | ||
80 | #define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */ | ||
81 | #define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */ | ||
82 | #define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */ | ||
83 | #define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */ | ||
84 | #define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */ | ||
85 | #define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */ | ||
86 | #define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */ | ||
87 | #define KXTF9_HYST_SET (0x5F) /* 0101 1111 */ | ||
88 | |||
89 | #define KXTF9_MAX_DUR (0xFF) | ||
90 | #define KXTF9_MAX_THS (0xFF) | ||
91 | #define KXTF9_THS_COUNTS_P_G (32) | ||
92 | |||
93 | /* --------------------- */ | ||
94 | /* - Variables. - */ | ||
95 | /* --------------------- */ | ||
96 | |||
97 | struct kxtf9_config { | ||
98 | unsigned int odr; /* Output data rate mHz */ | ||
99 | unsigned int fsr; /* full scale range mg */ | ||
100 | unsigned int ths; /* Motion no-motion thseshold mg */ | ||
101 | unsigned int dur; /* Motion no-motion duration ms */ | ||
102 | unsigned int irq_type; | ||
103 | unsigned char reg_ths; | ||
104 | unsigned char reg_dur; | ||
105 | unsigned char reg_odr; | ||
106 | unsigned char reg_int_cfg1; | ||
107 | unsigned char reg_int_cfg2; | ||
108 | unsigned char ctrl_reg1; | ||
109 | }; | ||
110 | |||
111 | struct kxtf9_private_data { | ||
112 | struct kxtf9_config suspend; | ||
113 | struct kxtf9_config resume; | ||
114 | }; | ||
115 | |||
116 | /***************************************** | ||
117 | Accelerometer Initialization Functions | ||
118 | *****************************************/ | ||
119 | |||
120 | static int kxtf9_set_ths(void *mlsl_handle, | ||
121 | struct ext_slave_platform_data *pdata, | ||
122 | struct kxtf9_config *config, | ||
123 | int apply, | ||
124 | long ths) | ||
125 | { | ||
126 | int result = ML_SUCCESS; | ||
127 | if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS) | ||
128 | ths = (KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G; | ||
129 | |||
130 | if (ths < 0) | ||
131 | ths = 0; | ||
132 | |||
133 | config->ths = ths; | ||
134 | config->reg_ths = (unsigned char) | ||
135 | ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000); | ||
136 | MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths); | ||
137 | if (apply) | ||
138 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
139 | KXTF9_WUF_THRESH, | ||
140 | config->reg_ths); | ||
141 | return result; | ||
142 | } | ||
143 | |||
144 | static int kxtf9_set_dur(void *mlsl_handle, | ||
145 | struct ext_slave_platform_data *pdata, | ||
146 | struct kxtf9_config *config, | ||
147 | int apply, | ||
148 | long dur) | ||
149 | { | ||
150 | int result = ML_SUCCESS; | ||
151 | long reg_dur = (dur * config->odr) / 1000000; | ||
152 | config->dur = dur; | ||
153 | |||
154 | if (reg_dur > KXTF9_MAX_DUR) | ||
155 | reg_dur = KXTF9_MAX_DUR; | ||
156 | |||
157 | config->reg_dur = (unsigned char) reg_dur; | ||
158 | MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur); | ||
159 | if (apply) | ||
160 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
161 | KXTF9_WUF_TIMER, | ||
162 | (unsigned char)reg_dur); | ||
163 | return result; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Sets the IRQ to fire when one of the IRQ events occur. Threshold and | ||
168 | * duration will not be used uless the type is MOT or NMOT. | ||
169 | * | ||
170 | * @param config configuration to apply to, suspend or resume | ||
171 | * @param irq_type The type of IRQ. Valid values are | ||
172 | * - MPU_SLAVE_IRQ_TYPE_NONE | ||
173 | * - MPU_SLAVE_IRQ_TYPE_MOTION | ||
174 | * - MPU_SLAVE_IRQ_TYPE_DATA_READY | ||
175 | */ | ||
176 | static int kxtf9_set_irq(void *mlsl_handle, | ||
177 | struct ext_slave_platform_data *pdata, | ||
178 | struct kxtf9_config *config, | ||
179 | int apply, | ||
180 | long irq_type) | ||
181 | { | ||
182 | int result = ML_SUCCESS; | ||
183 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
184 | |||
185 | config->irq_type = (unsigned char)irq_type; | ||
186 | config->ctrl_reg1 &= ~0x22; | ||
187 | if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) { | ||
188 | config->ctrl_reg1 |= 0x20; | ||
189 | config->reg_int_cfg1 = 0x38; | ||
190 | config->reg_int_cfg2 = 0x00; | ||
191 | } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) { | ||
192 | config->ctrl_reg1 |= 0x02; | ||
193 | if ((unsigned long) config == | ||
194 | (unsigned long) &private_data->suspend) | ||
195 | config->reg_int_cfg1 = 0x34; | ||
196 | else | ||
197 | config->reg_int_cfg1 = 0x24; | ||
198 | config->reg_int_cfg2 = 0xE0; | ||
199 | } else { | ||
200 | config->reg_int_cfg1 = 0x00; | ||
201 | config->reg_int_cfg2 = 0x00; | ||
202 | } | ||
203 | |||
204 | if (apply) { | ||
205 | /* Must clear bit 7 before writing new configuration */ | ||
206 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
207 | KXTF9_CTRL_REG1, 0x40); | ||
208 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
209 | KXTF9_INT_CTRL_REG1, | ||
210 | config->reg_int_cfg1); | ||
211 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
212 | KXTF9_INT_CTRL_REG2, | ||
213 | config->reg_int_cfg2); | ||
214 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
215 | KXTF9_CTRL_REG1, | ||
216 | config->ctrl_reg1); | ||
217 | } | ||
218 | MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n", | ||
219 | (unsigned long)config->ctrl_reg1, | ||
220 | (unsigned long)config->reg_int_cfg1, | ||
221 | (unsigned long)config->reg_int_cfg2); | ||
222 | |||
223 | return result; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * Set the Output data rate for the particular configuration | ||
228 | * | ||
229 | * @param config Config to modify with new ODR | ||
230 | * @param odr Output data rate in units of 1/1000Hz | ||
231 | */ | ||
232 | static int kxtf9_set_odr(void *mlsl_handle, | ||
233 | struct ext_slave_platform_data *pdata, | ||
234 | struct kxtf9_config *config, | ||
235 | int apply, | ||
236 | long odr) | ||
237 | { | ||
238 | unsigned char bits; | ||
239 | int result = ML_SUCCESS; | ||
240 | |||
241 | /* Data sheet says there is 12.5 hz, but that seems to produce a single | ||
242 | * correct data value, thus we remove it from the table */ | ||
243 | if (odr > 400000) { | ||
244 | config->odr = 800000; | ||
245 | bits = 0x06; | ||
246 | } else if (odr > 200000) { | ||
247 | config->odr = 400000; | ||
248 | bits = 0x05; | ||
249 | } else if (odr > 100000) { | ||
250 | config->odr = 200000; | ||
251 | bits = 0x04; | ||
252 | } else if (odr > 50000) { | ||
253 | config->odr = 100000; | ||
254 | bits = 0x03; | ||
255 | } else if (odr > 25000) { | ||
256 | config->odr = 50000; | ||
257 | bits = 0x02; | ||
258 | } else if (odr != 0) { | ||
259 | config->odr = 25000; | ||
260 | bits = 0x01; | ||
261 | } else { | ||
262 | config->odr = 0; | ||
263 | bits = 0; | ||
264 | } | ||
265 | |||
266 | config->reg_odr = bits; | ||
267 | kxtf9_set_dur(mlsl_handle, pdata, | ||
268 | config, apply, config->dur); | ||
269 | MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1); | ||
270 | if (apply) { | ||
271 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
272 | KXTF9_DATA_CTRL_REG, | ||
273 | config->reg_odr); | ||
274 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
275 | KXTF9_CTRL_REG1, | ||
276 | 0x40); | ||
277 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
278 | KXTF9_CTRL_REG1, | ||
279 | config->ctrl_reg1); | ||
280 | } | ||
281 | return result; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Set the full scale range of the accels | ||
286 | * | ||
287 | * @param config pointer to configuration | ||
288 | * @param fsr requested full scale range | ||
289 | */ | ||
290 | static int kxtf9_set_fsr(void *mlsl_handle, | ||
291 | struct ext_slave_platform_data *pdata, | ||
292 | struct kxtf9_config *config, | ||
293 | int apply, | ||
294 | long fsr) | ||
295 | { | ||
296 | int result = ML_SUCCESS; | ||
297 | |||
298 | config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7); | ||
299 | if (fsr <= 2000) { | ||
300 | config->fsr = 2000; | ||
301 | config->ctrl_reg1 |= 0x00; | ||
302 | } else if (fsr <= 4000) { | ||
303 | config->fsr = 4000; | ||
304 | config->ctrl_reg1 |= 0x08; | ||
305 | } else { | ||
306 | config->fsr = 8000; | ||
307 | config->ctrl_reg1 |= 0x10; | ||
308 | } | ||
309 | |||
310 | MPL_LOGV("FSR: %d\n", config->fsr); | ||
311 | if (apply) { | ||
312 | /* Must clear bit 7 before writing new configuration */ | ||
313 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
314 | KXTF9_CTRL_REG1, 0x40); | ||
315 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
316 | KXTF9_CTRL_REG1, config->ctrl_reg1); | ||
317 | } | ||
318 | return result; | ||
319 | } | ||
320 | |||
321 | static int kxtf9_suspend(void *mlsl_handle, | ||
322 | struct ext_slave_descr *slave, | ||
323 | struct ext_slave_platform_data *pdata) | ||
324 | { | ||
325 | int result; | ||
326 | unsigned char data; | ||
327 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
328 | |||
329 | /* Wake up */ | ||
330 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
331 | KXTF9_CTRL_REG1, 0x40); | ||
332 | ERROR_CHECK(result); | ||
333 | /* INT_CTRL_REG1: */ | ||
334 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
335 | KXTF9_INT_CTRL_REG1, | ||
336 | private_data->suspend.reg_int_cfg1); | ||
337 | ERROR_CHECK(result); | ||
338 | /* WUF_THRESH: */ | ||
339 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
340 | KXTF9_WUF_THRESH, | ||
341 | private_data->suspend.reg_ths); | ||
342 | ERROR_CHECK(result); | ||
343 | /* DATA_CTRL_REG */ | ||
344 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
345 | KXTF9_DATA_CTRL_REG, | ||
346 | private_data->suspend.reg_odr); | ||
347 | ERROR_CHECK(result); | ||
348 | /* WUF_TIMER */ | ||
349 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
350 | KXTF9_WUF_TIMER, private_data->suspend.reg_dur); | ||
351 | ERROR_CHECK(result); | ||
352 | |||
353 | /* Normal operation */ | ||
354 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
355 | KXTF9_CTRL_REG1, | ||
356 | private_data->suspend.ctrl_reg1); | ||
357 | ERROR_CHECK(result); | ||
358 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
359 | KXTF9_INT_REL, 1, &data); | ||
360 | ERROR_CHECK(result); | ||
361 | |||
362 | return result; | ||
363 | } | ||
364 | |||
365 | /* full scale setting - register and mask */ | ||
366 | #define ACCEL_KIONIX_CTRL_REG (0x1b) | ||
367 | #define ACCEL_KIONIX_CTRL_MASK (0x18) | ||
368 | |||
369 | static int kxtf9_resume(void *mlsl_handle, | ||
370 | struct ext_slave_descr *slave, | ||
371 | struct ext_slave_platform_data *pdata) | ||
372 | { | ||
373 | int result = ML_SUCCESS; | ||
374 | unsigned char data; | ||
375 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
376 | |||
377 | /* Wake up */ | ||
378 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
379 | KXTF9_CTRL_REG1, 0x40); | ||
380 | ERROR_CHECK(result); | ||
381 | /* INT_CTRL_REG1: */ | ||
382 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
383 | KXTF9_INT_CTRL_REG1, | ||
384 | private_data->resume.reg_int_cfg1); | ||
385 | ERROR_CHECK(result); | ||
386 | /* WUF_THRESH: */ | ||
387 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
388 | KXTF9_WUF_THRESH, private_data->resume.reg_ths); | ||
389 | ERROR_CHECK(result); | ||
390 | /* DATA_CTRL_REG */ | ||
391 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
392 | KXTF9_DATA_CTRL_REG, | ||
393 | private_data->resume.reg_odr); | ||
394 | ERROR_CHECK(result); | ||
395 | /* WUF_TIMER */ | ||
396 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
397 | KXTF9_WUF_TIMER, private_data->resume.reg_dur); | ||
398 | ERROR_CHECK(result); | ||
399 | |||
400 | /* Normal operation */ | ||
401 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
402 | KXTF9_CTRL_REG1, | ||
403 | private_data->resume.ctrl_reg1); | ||
404 | ERROR_CHECK(result); | ||
405 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
406 | KXTF9_INT_REL, 1, &data); | ||
407 | ERROR_CHECK(result); | ||
408 | |||
409 | return ML_SUCCESS; | ||
410 | } | ||
411 | |||
412 | static int kxtf9_init(void *mlsl_handle, | ||
413 | struct ext_slave_descr *slave, | ||
414 | struct ext_slave_platform_data *pdata) | ||
415 | { | ||
416 | |||
417 | struct kxtf9_private_data *private_data; | ||
418 | int result = ML_SUCCESS; | ||
419 | |||
420 | private_data = (struct kxtf9_private_data *) | ||
421 | MLOSMalloc(sizeof(struct kxtf9_private_data)); | ||
422 | |||
423 | if (!private_data) | ||
424 | return ML_ERROR_MEMORY_EXAUSTED; | ||
425 | |||
426 | /* RAM reset */ | ||
427 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
428 | KXTF9_CTRL_REG1, | ||
429 | 0x40); /* Fastest Reset */ | ||
430 | ERROR_CHECK(result); | ||
431 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
432 | KXTF9_DATA_CTRL_REG, | ||
433 | 0x36); /* Fastest Reset */ | ||
434 | ERROR_CHECK(result); | ||
435 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
436 | KXTF9_CTRL_REG3, 0xcd); /* Reset */ | ||
437 | ERROR_CHECK(result); | ||
438 | MLOSSleep(2); | ||
439 | |||
440 | pdata->private_data = private_data; | ||
441 | |||
442 | private_data->resume.ctrl_reg1 = 0xC0; | ||
443 | private_data->suspend.ctrl_reg1 = 0x40; | ||
444 | |||
445 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend, | ||
446 | FALSE, 1000); | ||
447 | ERROR_CHECK(result); | ||
448 | result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume, | ||
449 | FALSE, 2540); | ||
450 | ERROR_CHECK(result); | ||
451 | |||
452 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend, | ||
453 | FALSE, 50000); | ||
454 | ERROR_CHECK(result); | ||
455 | result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume, | ||
456 | FALSE, 200000); | ||
457 | |||
458 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend, | ||
459 | FALSE, 2000); | ||
460 | ERROR_CHECK(result); | ||
461 | result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume, | ||
462 | FALSE, 2000); | ||
463 | ERROR_CHECK(result); | ||
464 | |||
465 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend, | ||
466 | FALSE, 80); | ||
467 | ERROR_CHECK(result); | ||
468 | result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume, | ||
469 | FALSE, 40); | ||
470 | ERROR_CHECK(result); | ||
471 | |||
472 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend, | ||
473 | FALSE, | ||
474 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
475 | ERROR_CHECK(result); | ||
476 | result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume, | ||
477 | FALSE, | ||
478 | MPU_SLAVE_IRQ_TYPE_NONE); | ||
479 | ERROR_CHECK(result); | ||
480 | return result; | ||
481 | } | ||
482 | |||
483 | static int kxtf9_exit(void *mlsl_handle, | ||
484 | struct ext_slave_descr *slave, | ||
485 | struct ext_slave_platform_data *pdata) | ||
486 | { | ||
487 | if (pdata->private_data) | ||
488 | return MLOSFree(pdata->private_data); | ||
489 | else | ||
490 | return ML_SUCCESS; | ||
491 | } | ||
492 | |||
493 | static int kxtf9_config(void *mlsl_handle, | ||
494 | struct ext_slave_descr *slave, | ||
495 | struct ext_slave_platform_data *pdata, | ||
496 | struct ext_slave_config *data) | ||
497 | { | ||
498 | int retval; | ||
499 | long odr; | ||
500 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
501 | if (!data->data) | ||
502 | return ML_ERROR_INVALID_PARAMETER; | ||
503 | |||
504 | switch (data->key) { | ||
505 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
506 | return kxtf9_set_odr(mlsl_handle, pdata, | ||
507 | &private_data->suspend, | ||
508 | data->apply, | ||
509 | *((long *)data->data)); | ||
510 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
511 | odr = *((long *)data->data); | ||
512 | if (odr != 0) | ||
513 | private_data->resume.ctrl_reg1 |= 0x80; | ||
514 | |||
515 | retval = kxtf9_set_odr(mlsl_handle, pdata, | ||
516 | &private_data->resume, | ||
517 | data->apply, | ||
518 | odr); | ||
519 | return retval; | ||
520 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
521 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
522 | &private_data->suspend, | ||
523 | data->apply, | ||
524 | *((long *)data->data)); | ||
525 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
526 | return kxtf9_set_fsr(mlsl_handle, pdata, | ||
527 | &private_data->resume, | ||
528 | data->apply, | ||
529 | *((long *)data->data)); | ||
530 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
531 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
532 | &private_data->suspend, | ||
533 | data->apply, | ||
534 | *((long *)data->data)); | ||
535 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
536 | return kxtf9_set_ths(mlsl_handle, pdata, | ||
537 | &private_data->resume, | ||
538 | data->apply, | ||
539 | *((long *)data->data)); | ||
540 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
541 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
542 | &private_data->suspend, | ||
543 | data->apply, | ||
544 | *((long *)data->data)); | ||
545 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
546 | return kxtf9_set_dur(mlsl_handle, pdata, | ||
547 | &private_data->resume, | ||
548 | data->apply, | ||
549 | *((long *)data->data)); | ||
550 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
551 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
552 | &private_data->suspend, | ||
553 | data->apply, | ||
554 | *((long *)data->data)); | ||
555 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
556 | return kxtf9_set_irq(mlsl_handle, pdata, | ||
557 | &private_data->resume, | ||
558 | data->apply, | ||
559 | *((long *)data->data)); | ||
560 | default: | ||
561 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
562 | }; | ||
563 | |||
564 | return ML_SUCCESS; | ||
565 | } | ||
566 | |||
567 | static int kxtf9_get_config(void *mlsl_handle, | ||
568 | struct ext_slave_descr *slave, | ||
569 | struct ext_slave_platform_data *pdata, | ||
570 | struct ext_slave_config *data) | ||
571 | { | ||
572 | struct kxtf9_private_data *private_data = pdata->private_data; | ||
573 | if (!data->data) | ||
574 | return ML_ERROR_INVALID_PARAMETER; | ||
575 | |||
576 | switch (data->key) { | ||
577 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
578 | (*(unsigned long *)data->data) = | ||
579 | (unsigned long) private_data->suspend.odr; | ||
580 | break; | ||
581 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
582 | (*(unsigned long *)data->data) = | ||
583 | (unsigned long) private_data->resume.odr; | ||
584 | break; | ||
585 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
586 | (*(unsigned long *)data->data) = | ||
587 | (unsigned long) private_data->suspend.fsr; | ||
588 | break; | ||
589 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
590 | (*(unsigned long *)data->data) = | ||
591 | (unsigned long) private_data->resume.fsr; | ||
592 | break; | ||
593 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
594 | (*(unsigned long *)data->data) = | ||
595 | (unsigned long) private_data->suspend.ths; | ||
596 | break; | ||
597 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
598 | (*(unsigned long *)data->data) = | ||
599 | (unsigned long) private_data->resume.ths; | ||
600 | break; | ||
601 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
602 | (*(unsigned long *)data->data) = | ||
603 | (unsigned long) private_data->suspend.dur; | ||
604 | break; | ||
605 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
606 | (*(unsigned long *)data->data) = | ||
607 | (unsigned long) private_data->resume.dur; | ||
608 | break; | ||
609 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
610 | (*(unsigned long *)data->data) = | ||
611 | (unsigned long) private_data->suspend.irq_type; | ||
612 | break; | ||
613 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
614 | (*(unsigned long *)data->data) = | ||
615 | (unsigned long) private_data->resume.irq_type; | ||
616 | break; | ||
617 | default: | ||
618 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
619 | }; | ||
620 | |||
621 | return ML_SUCCESS; | ||
622 | } | ||
623 | |||
624 | static int kxtf9_read(void *mlsl_handle, | ||
625 | struct ext_slave_descr *slave, | ||
626 | struct ext_slave_platform_data *pdata, | ||
627 | unsigned char *data) | ||
628 | { | ||
629 | int result; | ||
630 | unsigned char reg; | ||
631 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
632 | KXTF9_INT_SRC_REG2, 1, ®); | ||
633 | ERROR_CHECK(result); | ||
634 | |||
635 | if (!(reg & 0x10)) | ||
636 | return ML_ERROR_ACCEL_DATA_NOT_READY; | ||
637 | |||
638 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
639 | slave->reg, slave->len, data); | ||
640 | ERROR_CHECK(result); | ||
641 | return result; | ||
642 | } | ||
643 | |||
644 | static struct ext_slave_descr kxtf9_descr = { | ||
645 | /*.init = */ kxtf9_init, | ||
646 | /*.exit = */ kxtf9_exit, | ||
647 | /*.suspend = */ kxtf9_suspend, | ||
648 | /*.resume = */ kxtf9_resume, | ||
649 | /*.read = */ kxtf9_read, | ||
650 | /*.config = */ kxtf9_config, | ||
651 | /*.get_config = */ kxtf9_get_config, | ||
652 | /*.name = */ "kxtf9", | ||
653 | /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER, | ||
654 | /*.id = */ ACCEL_ID_KXTF9, | ||
655 | /*.reg = */ 0x06, | ||
656 | /*.len = */ 6, | ||
657 | /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN, | ||
658 | /*.range = */ {2, 0}, | ||
659 | }; | ||
660 | |||
661 | struct ext_slave_descr *kxtf9_get_slave_descr(void) | ||
662 | { | ||
663 | return &kxtf9_descr; | ||
664 | } | ||
665 | EXPORT_SYMBOL(kxtf9_get_slave_descr); | ||
666 | |||
667 | /** | ||
668 | * @} | ||
669 | **/ | ||
diff --git a/drivers/misc/mpu3050/compass/ak8975.c b/drivers/misc/mpu3050/compass/ak8975.c new file mode 100644 index 00000000000..b8aed30ba39 --- /dev/null +++ b/drivers/misc/mpu3050/compass/ak8975.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 COMPASSDL (Motion Library - Accelerometer Driver Layer) | ||
22 | * @brief Provides the interface to setup and handle an accelerometers | ||
23 | * connected to the secondary I2C interface of the gyroscope. | ||
24 | * | ||
25 | * @{ | ||
26 | * @file AK8975.c | ||
27 | * @brief Magnetometer setup and handling methods for AKM 8975 compass. | ||
28 | */ | ||
29 | |||
30 | /* ------------------ */ | ||
31 | /* - Include Files. - */ | ||
32 | /* ------------------ */ | ||
33 | |||
34 | #include <string.h> | ||
35 | |||
36 | #ifdef __KERNEL__ | ||
37 | #include <linux/module.h> | ||
38 | #endif | ||
39 | |||
40 | #include "mpu.h" | ||
41 | #include "mlsl.h" | ||
42 | #include "mlos.h" | ||
43 | |||
44 | #include <log.h> | ||
45 | #undef MPL_LOG_TAG | ||
46 | #define MPL_LOG_TAG "MPL-compass" | ||
47 | |||
48 | |||
49 | #define AK8975_REG_ST1 (0x02) | ||
50 | #define AK8975_REG_HXL (0x03) | ||
51 | #define AK8975_REG_ST2 (0x09) | ||
52 | |||
53 | #define AK8975_REG_CNTL (0x0A) | ||
54 | |||
55 | #define AK8975_CNTL_MODE_POWER_DOWN (0x00) | ||
56 | #define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01) | ||
57 | |||
58 | int ak8975_suspend(void *mlsl_handle, | ||
59 | struct ext_slave_descr *slave, | ||
60 | struct ext_slave_platform_data *pdata) | ||
61 | { | ||
62 | int result = ML_SUCCESS; | ||
63 | result = | ||
64 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
65 | AK8975_REG_CNTL, | ||
66 | AK8975_CNTL_MODE_POWER_DOWN); | ||
67 | MLOSSleep(1); /* wait at least 100us */ | ||
68 | ERROR_CHECK(result); | ||
69 | return result; | ||
70 | } | ||
71 | |||
72 | int ak8975_resume(void *mlsl_handle, | ||
73 | struct ext_slave_descr *slave, | ||
74 | struct ext_slave_platform_data *pdata) | ||
75 | { | ||
76 | int result = ML_SUCCESS; | ||
77 | result = | ||
78 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
79 | AK8975_REG_CNTL, | ||
80 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
81 | ERROR_CHECK(result); | ||
82 | return result; | ||
83 | } | ||
84 | |||
85 | int ak8975_read(void *mlsl_handle, | ||
86 | struct ext_slave_descr *slave, | ||
87 | struct ext_slave_platform_data *pdata, unsigned char *data) | ||
88 | { | ||
89 | unsigned char regs[8]; | ||
90 | unsigned char *stat = ®s[0]; | ||
91 | unsigned char *stat2 = ®s[7]; | ||
92 | int result = ML_SUCCESS; | ||
93 | int status = ML_SUCCESS; | ||
94 | |||
95 | result = | ||
96 | MLSLSerialRead(mlsl_handle, pdata->address, AK8975_REG_ST1, | ||
97 | 8, regs); | ||
98 | ERROR_CHECK(result); | ||
99 | |||
100 | /* | ||
101 | * ST : data ready - | ||
102 | * Measurement has been completed and data is ready to be read. | ||
103 | */ | ||
104 | if (*stat & 0x01) { | ||
105 | memcpy(data, ®s[1], 6); | ||
106 | status = ML_SUCCESS; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * ST2 : data error - | ||
111 | * occurs when data read is started outside of a readable period; | ||
112 | * data read would not be correct. | ||
113 | * Valid in continuous measurement mode only. | ||
114 | * In single measurement mode this error should not occour but we | ||
115 | * stil account for it and return an error, since the data would be | ||
116 | * corrupted. | ||
117 | * DERR bit is self-clearing when ST2 register is read. | ||
118 | */ | ||
119 | if (*stat2 & 0x04) | ||
120 | status = ML_ERROR_COMPASS_DATA_ERROR; | ||
121 | /* | ||
122 | * ST2 : overflow - | ||
123 | * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. | ||
124 | * This is likely to happen in presence of an external magnetic | ||
125 | * disturbance; it indicates, the sensor data is incorrect and should | ||
126 | * be ignored. | ||
127 | * An error is returned. | ||
128 | * HOFL bit clears when a new measurement starts. | ||
129 | */ | ||
130 | if (*stat2 & 0x08) | ||
131 | status = ML_ERROR_COMPASS_DATA_OVERFLOW; | ||
132 | /* | ||
133 | * ST : overrun - | ||
134 | * the previous sample was not fetched and lost. | ||
135 | * Valid in continuous measurement mode only. | ||
136 | * In single measurement mode this error should not occour and we | ||
137 | * don't consider this condition an error. | ||
138 | * DOR bit is self-clearing when ST2 or any meas. data register is | ||
139 | * read. | ||
140 | */ | ||
141 | if (*stat & 0x02) { | ||
142 | /* status = ML_ERROR_COMPASS_DATA_UNDERFLOW; */ | ||
143 | status = ML_SUCCESS; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * trigger next measurement if: | ||
148 | * - stat is non zero; | ||
149 | * - if stat is zero and stat2 is non zero. | ||
150 | * Won't trigger if data is not ready and there was no error. | ||
151 | */ | ||
152 | if (*stat != 0x00 || *stat2 != 0x00) { | ||
153 | result = | ||
154 | MLSLSerialWriteSingle(mlsl_handle, pdata->address, | ||
155 | AK8975_REG_CNTL, | ||
156 | AK8975_CNTL_MODE_SINGLE_MEASUREMENT); | ||
157 | ERROR_CHECK(result); | ||
158 | } | ||
159 | |||
160 | return status; | ||
161 | } | ||
162 | |||
163 | static int ak8975_config(void *mlsl_handle, | ||
164 | struct ext_slave_descr *slave, | ||
165 | struct ext_slave_platform_data *pdata, | ||
166 | struct ext_slave_config *data) | ||
167 | { | ||
168 | int result; | ||
169 | if (!data->data) | ||
170 | return ML_ERROR_INVALID_PARAMETER; | ||
171 | |||
172 | switch (data->key) { | ||
173 | case MPU_SLAVE_WRITE_REGISTERS: | ||
174 | result = MLSLSerialWrite(mlsl_handle, pdata->address, | ||
175 | data->len, | ||
176 | (unsigned char *)data->data); | ||
177 | ERROR_CHECK(result); | ||
178 | break; | ||
179 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
180 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
181 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
182 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
183 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
184 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
185 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
186 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
187 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
188 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
189 | default: | ||
190 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
191 | }; | ||
192 | |||
193 | return ML_SUCCESS; | ||
194 | } | ||
195 | |||
196 | static int ak8975_get_config(void *mlsl_handle, | ||
197 | struct ext_slave_descr *slave, | ||
198 | struct ext_slave_platform_data *pdata, | ||
199 | struct ext_slave_config *data) | ||
200 | { | ||
201 | int result; | ||
202 | if (!data->data) | ||
203 | return ML_ERROR_INVALID_PARAMETER; | ||
204 | |||
205 | switch (data->key) { | ||
206 | case MPU_SLAVE_READ_REGISTERS: | ||
207 | { | ||
208 | unsigned char *serial_data = (unsigned char *)data->data; | ||
209 | result = MLSLSerialRead(mlsl_handle, pdata->address, | ||
210 | serial_data[0], | ||
211 | data->len - 1, | ||
212 | &serial_data[1]); | ||
213 | ERROR_CHECK(result); | ||
214 | break; | ||
215 | } | ||
216 | case MPU_SLAVE_CONFIG_ODR_SUSPEND: | ||
217 | case MPU_SLAVE_CONFIG_ODR_RESUME: | ||
218 | case MPU_SLAVE_CONFIG_FSR_SUSPEND: | ||
219 | case MPU_SLAVE_CONFIG_FSR_RESUME: | ||
220 | case MPU_SLAVE_CONFIG_MOT_THS: | ||
221 | case MPU_SLAVE_CONFIG_NMOT_THS: | ||
222 | case MPU_SLAVE_CONFIG_MOT_DUR: | ||
223 | case MPU_SLAVE_CONFIG_NMOT_DUR: | ||
224 | case MPU_SLAVE_CONFIG_IRQ_SUSPEND: | ||
225 | case MPU_SLAVE_CONFIG_IRQ_RESUME: | ||
226 | default: | ||
227 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
228 | }; | ||
229 | |||
230 | return ML_SUCCESS; | ||
231 | } | ||
232 | |||
233 | struct ext_slave_descr ak8975_descr = { | ||
234 | /*.init = */ NULL, | ||
235 | /*.exit = */ NULL, | ||
236 | /*.suspend = */ ak8975_suspend, | ||
237 | /*.resume = */ ak8975_resume, | ||
238 | /*.read = */ ak8975_read, | ||
239 | /*.config = */ ak8975_config, | ||
240 | /*.get_config = */ ak8975_get_config, | ||
241 | /*.name = */ "ak8975", | ||
242 | /*.type = */ EXT_SLAVE_TYPE_COMPASS, | ||
243 | /*.id = */ COMPASS_ID_AKM, | ||
244 | /*.reg = */ 0x01, | ||
245 | /*.len = */ 9, | ||
246 | /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN, | ||
247 | /*.range = */ {9830, 4000} | ||
248 | }; | ||
249 | |||
250 | struct ext_slave_descr *ak8975_get_slave_descr(void) | ||
251 | { | ||
252 | return &ak8975_descr; | ||
253 | } | ||
254 | EXPORT_SYMBOL(ak8975_get_slave_descr); | ||
255 | |||
256 | /** | ||
257 | * @} | ||
258 | */ | ||
diff --git a/drivers/misc/mpu3050/log.h b/drivers/misc/mpu3050/log.h new file mode 100644 index 00000000000..f2f9ea7ece8 --- /dev/null +++ b/drivers/misc/mpu3050/log.h | |||
@@ -0,0 +1,306 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 InvenSense Inc | ||
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 | * C/C++ logging functions. See the logging documentation for API details. | ||
19 | * | ||
20 | * We'd like these to be available from C code (in case we import some from | ||
21 | * somewhere), so this has a C interface. | ||
22 | * | ||
23 | * The output will be correct when the log file is shared between multiple | ||
24 | * threads and/or multiple processes so long as the operating system | ||
25 | * supports O_APPEND. These calls have mutex-protected data structures | ||
26 | * and so are NOT reentrant. Do not use MPL_LOG in a signal handler. | ||
27 | */ | ||
28 | #ifndef _LIBS_CUTILS_MPL_LOG_H | ||
29 | #define _LIBS_CUTILS_MPL_LOG_H | ||
30 | |||
31 | #include <stdarg.h> | ||
32 | |||
33 | #ifdef ANDROID | ||
34 | #include <utils/Log.h> /* For the LOG macro */ | ||
35 | #endif | ||
36 | |||
37 | #ifdef __KERNEL__ | ||
38 | #include <linux/kernel.h> | ||
39 | #endif | ||
40 | |||
41 | #ifdef __cplusplus | ||
42 | extern "C" { | ||
43 | #endif | ||
44 | |||
45 | /* --------------------------------------------------------------------- */ | ||
46 | |||
47 | /* | ||
48 | * Normally we strip MPL_LOGV (VERBOSE messages) from release builds. | ||
49 | * You can modify this (for example with "#define MPL_LOG_NDEBUG 0" | ||
50 | * at the top of your source file) to change that behavior. | ||
51 | */ | ||
52 | #ifndef MPL_LOG_NDEBUG | ||
53 | #ifdef NDEBUG | ||
54 | #define MPL_LOG_NDEBUG 1 | ||
55 | #else | ||
56 | #define MPL_LOG_NDEBUG 0 | ||
57 | #endif | ||
58 | #endif | ||
59 | |||
60 | #ifdef __KERNEL__ | ||
61 | #define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE | ||
62 | #define MPL_LOG_DEFAULT KERN_DEFAULT | ||
63 | #define MPL_LOG_VERBOSE KERN_CONT | ||
64 | #define MPL_LOG_DEBUG KERN_NOTICE | ||
65 | #define MPL_LOG_INFO KERN_INFO | ||
66 | #define MPL_LOG_WARN KERN_WARNING | ||
67 | #define MPL_LOG_ERROR KERN_ERR | ||
68 | #define MPL_LOG_SILENT MPL_LOG_VERBOSE | ||
69 | |||
70 | #else | ||
71 | /* Based off the log priorities in android | ||
72 | /system/core/include/android/log.h */ | ||
73 | #define MPL_LOG_UNKNOWN (0) | ||
74 | #define MPL_LOG_DEFAULT (1) | ||
75 | #define MPL_LOG_VERBOSE (2) | ||
76 | #define MPL_LOG_DEBUG (3) | ||
77 | #define MPL_LOG_INFO (4) | ||
78 | #define MPL_LOG_WARN (5) | ||
79 | #define MPL_LOG_ERROR (6) | ||
80 | #define MPL_LOG_SILENT (8) | ||
81 | #endif | ||
82 | |||
83 | |||
84 | /* | ||
85 | * This is the local tag used for the following simplified | ||
86 | * logging macros. You can change this preprocessor definition | ||
87 | * before using the other macros to change the tag. | ||
88 | */ | ||
89 | #ifndef MPL_LOG_TAG | ||
90 | #ifdef __KERNEL__ | ||
91 | #define MPL_LOG_TAG | ||
92 | #else | ||
93 | #define MPL_LOG_TAG NULL | ||
94 | #endif | ||
95 | #endif | ||
96 | |||
97 | /* --------------------------------------------------------------------- */ | ||
98 | |||
99 | /* | ||
100 | * Simplified macro to send a verbose log message using the current MPL_LOG_TAG. | ||
101 | */ | ||
102 | #ifndef MPL_LOGV | ||
103 | #if MPL_LOG_NDEBUG | ||
104 | #define MPL_LOGV(fmt, ...) \ | ||
105 | do { \ | ||
106 | if (0) \ | ||
107 | MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\ | ||
108 | } while (0) | ||
109 | #else | ||
110 | #define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
111 | #endif | ||
112 | #endif | ||
113 | |||
114 | #ifndef CONDITION | ||
115 | #define CONDITION(cond) ((cond) != 0) | ||
116 | #endif | ||
117 | |||
118 | #ifndef MPL_LOGV_IF | ||
119 | #if MPL_LOG_NDEBUG | ||
120 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
121 | do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0) | ||
122 | #else | ||
123 | #define MPL_LOGV_IF(cond, fmt, ...) \ | ||
124 | ((CONDITION(cond)) \ | ||
125 | ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
126 | : (void)0) | ||
127 | #endif | ||
128 | #endif | ||
129 | |||
130 | /* | ||
131 | * Simplified macro to send a debug log message using the current MPL_LOG_TAG. | ||
132 | */ | ||
133 | #ifndef MPL_LOGD | ||
134 | #define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
135 | #endif | ||
136 | |||
137 | #ifndef MPL_LOGD_IF | ||
138 | #define MPL_LOGD_IF(cond, fmt, ...) \ | ||
139 | ((CONDITION(cond)) \ | ||
140 | ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
141 | : (void)0) | ||
142 | #endif | ||
143 | |||
144 | /* | ||
145 | * Simplified macro to send an info log message using the current MPL_LOG_TAG. | ||
146 | */ | ||
147 | #ifndef MPL_LOGI | ||
148 | #define MPL_LOGI(fmt, ...) MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
149 | #endif | ||
150 | |||
151 | #ifndef MPL_LOGI_IF | ||
152 | #define MPL_LOGI_IF(cond, fmt, ...) \ | ||
153 | ((CONDITION(cond)) \ | ||
154 | ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
155 | : (void)0) | ||
156 | #endif | ||
157 | |||
158 | /* | ||
159 | * Simplified macro to send a warning log message using the current MPL_LOG_TAG. | ||
160 | */ | ||
161 | #ifndef MPL_LOGW | ||
162 | #ifdef __KERNEL__ | ||
163 | #define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
164 | #else | ||
165 | #define MPL_LOGW(fmt, ...) MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
166 | #endif | ||
167 | #endif | ||
168 | |||
169 | #ifndef MPL_LOGW_IF | ||
170 | #define MPL_LOGW_IF(cond, fmt, ...) \ | ||
171 | ((CONDITION(cond)) \ | ||
172 | ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
173 | : (void)0) | ||
174 | #endif | ||
175 | |||
176 | /* | ||
177 | * Simplified macro to send an error log message using the current MPL_LOG_TAG. | ||
178 | */ | ||
179 | #ifndef MPL_LOGE | ||
180 | #ifdef __KERNEL__ | ||
181 | #define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__) | ||
182 | #else | ||
183 | #define MPL_LOGE(fmt, ...) MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) | ||
184 | #endif | ||
185 | #endif | ||
186 | |||
187 | #ifndef MPL_LOGE_IF | ||
188 | #define MPL_LOGE_IF(cond, fmt, ...) \ | ||
189 | ((CONDITION(cond)) \ | ||
190 | ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \ | ||
191 | : (void)0) | ||
192 | #endif | ||
193 | |||
194 | /* --------------------------------------------------------------------- */ | ||
195 | |||
196 | /* | ||
197 | * Log a fatal error. If the given condition fails, this stops program | ||
198 | * execution like a normal assertion, but also generating the given message. | ||
199 | * It is NOT stripped from release builds. Note that the condition test | ||
200 | * is -inverted- from the normal assert() semantics. | ||
201 | */ | ||
202 | #define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \ | ||
203 | ((CONDITION(cond)) \ | ||
204 | ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \ | ||
205 | fmt, ##__VA_ARGS__)) \ | ||
206 | : (void)0) | ||
207 | |||
208 | #define MPL_LOG_ALWAYS_FATAL(fmt, ...) \ | ||
209 | (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__))) | ||
210 | |||
211 | /* | ||
212 | * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that | ||
213 | * are stripped out of release builds. | ||
214 | */ | ||
215 | #if MPL_LOG_NDEBUG | ||
216 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
217 | do { \ | ||
218 | if (0) \ | ||
219 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \ | ||
220 | } while (0) | ||
221 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
222 | do { \ | ||
223 | if (0) \ | ||
224 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \ | ||
225 | } while (0) | ||
226 | #else | ||
227 | #define MPL_LOG_FATAL_IF(cond, fmt, ...) \ | ||
228 | MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__) | ||
229 | #define MPL_LOG_FATAL(fmt, ...) \ | ||
230 | MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) | ||
231 | #endif | ||
232 | |||
233 | /* | ||
234 | * Assertion that generates a log message when the assertion fails. | ||
235 | * Stripped out of release builds. Uses the current MPL_LOG_TAG. | ||
236 | */ | ||
237 | #define MPL_LOG_ASSERT(cond, fmt, ...) \ | ||
238 | MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__) | ||
239 | |||
240 | /* --------------------------------------------------------------------- */ | ||
241 | |||
242 | /* | ||
243 | * Basic log message macro. | ||
244 | * | ||
245 | * Example: | ||
246 | * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno); | ||
247 | * | ||
248 | * The second argument may be NULL or "" to indicate the "global" tag. | ||
249 | */ | ||
250 | #ifndef MPL_LOG | ||
251 | #define MPL_LOG(priority, tag, fmt, ...) \ | ||
252 | MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__) | ||
253 | #endif | ||
254 | |||
255 | /* | ||
256 | * Log macro that allows you to specify a number for the priority. | ||
257 | */ | ||
258 | #ifndef MPL_LOG_PRI | ||
259 | #ifdef ANDROID | ||
260 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
261 | LOG(priority, tag, fmt, ##__VA_ARGS__) | ||
262 | #elif defined __KERNEL__ | ||
263 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
264 | pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__) | ||
265 | #else | ||
266 | #define MPL_LOG_PRI(priority, tag, fmt, ...) \ | ||
267 | _MLPrintLog(MPL_##priority, tag, fmt, ##__VA_ARGS__) | ||
268 | #endif | ||
269 | #endif | ||
270 | |||
271 | /* | ||
272 | * Log macro that allows you to pass in a varargs ("args" is a va_list). | ||
273 | */ | ||
274 | #ifndef MPL_LOG_PRI_VA | ||
275 | #ifdef ANDROID | ||
276 | #define MPL_LOG_PRI_VA(priority, tag, fmt, args) \ | ||
277 | android_vprintLog(priority, NULL, tag, fmt, args) | ||
278 | #elif defined __KERNEL__ | ||
279 | /* not allowed in the Kernel because there is no dev_dbg that takes a va_list */ | ||
280 | #else | ||
281 | #define MPL_LOG_PRI_VA(priority, tag, fmt, args) \ | ||
282 | _MLPrintVaLog(priority, NULL, tag, fmt, args) | ||
283 | #endif | ||
284 | #endif | ||
285 | |||
286 | /* --------------------------------------------------------------------- */ | ||
287 | |||
288 | /* | ||
289 | * =========================================================================== | ||
290 | * | ||
291 | * The stuff in the rest of this file should not be used directly. | ||
292 | */ | ||
293 | |||
294 | #ifndef ANDROID | ||
295 | int _MLPrintLog(int priority, const char *tag, const char *fmt, | ||
296 | ...); | ||
297 | int _MLPrintVaLog(int priority, const char *tag, const char *fmt, | ||
298 | va_list args); | ||
299 | /* Final implementation of actual writing to a character device */ | ||
300 | int _MLWriteLog(const char *buf, int buflen); | ||
301 | #endif | ||
302 | |||
303 | #ifdef __cplusplus | ||
304 | } | ||
305 | #endif | ||
306 | #endif /* _LIBS_CUTILS_MPL_LOG_H */ | ||
diff --git a/drivers/misc/mpu3050/mldl_cfg.c b/drivers/misc/mpu3050/mldl_cfg.c new file mode 100644 index 00000000000..9cc4cf69038 --- /dev/null +++ b/drivers/misc/mpu3050/mldl_cfg.c | |||
@@ -0,0 +1,1739 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 Files. - */ | ||
30 | /* ------------------ */ | ||
31 | |||
32 | #include <stddef.h> | ||
33 | |||
34 | #include "mldl_cfg.h" | ||
35 | #include "mpu.h" | ||
36 | |||
37 | #include "mlsl.h" | ||
38 | #include "mlos.h" | ||
39 | |||
40 | #include "log.h" | ||
41 | #undef MPL_LOG_TAG | ||
42 | #define MPL_LOG_TAG "mldl_cfg:" | ||
43 | |||
44 | /* --------------------- */ | ||
45 | /* - Variables. - */ | ||
46 | /* --------------------- */ | ||
47 | #ifdef M_HW | ||
48 | #define SLEEP 0 | ||
49 | #define WAKE_UP 7 | ||
50 | #define RESET 1 | ||
51 | #define STANDBY 1 | ||
52 | #else | ||
53 | /* licteral significance of all parameters used in MLDLPowerMgmtMPU */ | ||
54 | #define SLEEP 1 | ||
55 | #define WAKE_UP 0 | ||
56 | #define RESET 1 | ||
57 | #define STANDBY 1 | ||
58 | #endif | ||
59 | |||
60 | /*---------------------*/ | ||
61 | /*- Prototypes. -*/ | ||
62 | /*---------------------*/ | ||
63 | |||
64 | /*----------------------*/ | ||
65 | /*- Static Functions. -*/ | ||
66 | /*----------------------*/ | ||
67 | |||
68 | static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
69 | { | ||
70 | unsigned char userCtrlReg; | ||
71 | int result; | ||
72 | |||
73 | if (!mldl_cfg->dmp_is_running) | ||
74 | return ML_SUCCESS; | ||
75 | |||
76 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
77 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
78 | ERROR_CHECK(result); | ||
79 | userCtrlReg = (userCtrlReg & (~BIT_FIFO_EN)) | BIT_FIFO_RST; | ||
80 | userCtrlReg = (userCtrlReg & (~BIT_DMP_EN)) | BIT_DMP_RST; | ||
81 | |||
82 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
83 | MPUREG_USER_CTRL, userCtrlReg); | ||
84 | ERROR_CHECK(result); | ||
85 | mldl_cfg->dmp_is_running = 0; | ||
86 | |||
87 | return result; | ||
88 | |||
89 | } | ||
90 | /** | ||
91 | * @brief Starts the DMP running | ||
92 | * | ||
93 | * @return ML_SUCCESS or non-zero error code | ||
94 | */ | ||
95 | static int dmp_start(struct mldl_cfg *pdata, void *mlsl_handle) | ||
96 | { | ||
97 | unsigned char userCtrlReg; | ||
98 | int result; | ||
99 | |||
100 | if (pdata->dmp_is_running == pdata->dmp_enable) | ||
101 | return ML_SUCCESS; | ||
102 | |||
103 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
104 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
105 | ERROR_CHECK(result); | ||
106 | |||
107 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
108 | MPUREG_USER_CTRL, | ||
109 | ((userCtrlReg & (~BIT_FIFO_EN)) | ||
110 | | BIT_FIFO_RST)); | ||
111 | ERROR_CHECK(result); | ||
112 | |||
113 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
114 | MPUREG_USER_CTRL, userCtrlReg); | ||
115 | ERROR_CHECK(result); | ||
116 | |||
117 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
118 | MPUREG_USER_CTRL, 1, &userCtrlReg); | ||
119 | ERROR_CHECK(result); | ||
120 | |||
121 | if (pdata->dmp_enable) | ||
122 | userCtrlReg |= BIT_DMP_EN; | ||
123 | else | ||
124 | userCtrlReg &= ~BIT_DMP_EN; | ||
125 | |||
126 | if (pdata->fifo_enable) | ||
127 | userCtrlReg |= BIT_FIFO_EN; | ||
128 | else | ||
129 | userCtrlReg &= ~BIT_FIFO_EN; | ||
130 | |||
131 | userCtrlReg |= BIT_DMP_RST; | ||
132 | |||
133 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
134 | MPUREG_USER_CTRL, userCtrlReg); | ||
135 | ERROR_CHECK(result); | ||
136 | pdata->dmp_is_running = pdata->dmp_enable; | ||
137 | |||
138 | return result; | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * @brief enables/disables the I2C bypass to an external device | ||
143 | * connected to MPU's secondary I2C bus. | ||
144 | * @param enable | ||
145 | * Non-zero to enable pass through. | ||
146 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
147 | */ | ||
148 | static int MLDLSetI2CBypass(struct mldl_cfg *mldl_cfg, | ||
149 | void *mlsl_handle, | ||
150 | unsigned char enable) | ||
151 | { | ||
152 | unsigned char b; | ||
153 | int result; | ||
154 | |||
155 | if ((mldl_cfg->gyro_is_bypassed && enable) || | ||
156 | (!mldl_cfg->gyro_is_bypassed && !enable)) | ||
157 | return ML_SUCCESS; | ||
158 | |||
159 | /*---- get current 'USER_CTRL' into b ----*/ | ||
160 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
161 | MPUREG_USER_CTRL, 1, &b); | ||
162 | ERROR_CHECK(result); | ||
163 | |||
164 | b &= ~BIT_AUX_IF_EN; | ||
165 | |||
166 | if (!enable) { | ||
167 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
168 | MPUREG_USER_CTRL, | ||
169 | (b | BIT_AUX_IF_EN)); | ||
170 | ERROR_CHECK(result); | ||
171 | } else { | ||
172 | /* Coming out of I2C is tricky due to several erratta. Do not | ||
173 | * modify this algorithm | ||
174 | */ | ||
175 | /* | ||
176 | * 1) wait for the right time and send the command to change | ||
177 | * the aux i2c slave address to an invalid address that will | ||
178 | * get nack'ed | ||
179 | * | ||
180 | * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. | ||
181 | */ | ||
182 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
183 | MPUREG_AUX_SLV_ADDR, 0x7F); | ||
184 | ERROR_CHECK(result); | ||
185 | /* | ||
186 | * 2) wait enough time for a nack to occur, then go into | ||
187 | * bypass mode: | ||
188 | */ | ||
189 | MLOSSleep(2); | ||
190 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
191 | MPUREG_USER_CTRL, (b)); | ||
192 | ERROR_CHECK(result); | ||
193 | /* | ||
194 | * 3) wait for up to one MPU cycle then restore the slave | ||
195 | * address | ||
196 | */ | ||
197 | MLOSSleep(SAMPLING_PERIOD_US(mldl_cfg) / 1000); | ||
198 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
199 | MPUREG_AUX_SLV_ADDR, | ||
200 | mldl_cfg->pdata-> | ||
201 | accel.address); | ||
202 | ERROR_CHECK(result); | ||
203 | |||
204 | /* | ||
205 | * 4) reset the ime interface | ||
206 | */ | ||
207 | #ifdef M_HW | ||
208 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
209 | MPUREG_USER_CTRL, | ||
210 | (b | BIT_I2C_MST_RST)); | ||
211 | |||
212 | #else | ||
213 | result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr, | ||
214 | MPUREG_USER_CTRL, | ||
215 | (b | BIT_AUX_IF_RST)); | ||
216 | #endif | ||
217 | ERROR_CHECK(result); | ||
218 | MLOSSleep(2); | ||
219 | } | ||
220 | mldl_cfg->gyro_is_bypassed = enable; | ||
221 | |||
222 | return result; | ||
223 | } | ||
224 | |||
225 | struct tsProdRevMap { | ||
226 | unsigned char siliconRev; | ||
227 | unsigned short sensTrim; | ||
228 | }; | ||
229 | |||
230 | #define NUM_OF_PROD_REVS (DIM(prodRevsMap)) | ||
231 | |||
232 | /* NOTE : 'npp' is a non production part */ | ||
233 | #ifdef M_HW | ||
234 | #define OLDEST_PROD_REV_SUPPORTED 1 | ||
235 | static struct tsProdRevMap prodRevsMap[] = { | ||
236 | {0, 0}, | ||
237 | {MPU_SILICON_REV_A1, 131}, /* 1 A1 (npp) */ | ||
238 | {MPU_SILICON_REV_A1, 131}, /* 2 A1 (npp) */ | ||
239 | {MPU_SILICON_REV_A1, 131}, /* 3 A1 (npp) */ | ||
240 | {MPU_SILICON_REV_A1, 131}, /* 4 A1 (npp) */ | ||
241 | {MPU_SILICON_REV_A1, 131}, /* 5 A1 (npp) */ | ||
242 | {MPU_SILICON_REV_A1, 131}, /* 6 A1 (npp) */ | ||
243 | {MPU_SILICON_REV_A1, 131}, /* 7 A1 (npp) */ | ||
244 | {MPU_SILICON_REV_A1, 131}, /* 8 A1 (npp) */ | ||
245 | }; | ||
246 | |||
247 | #else /* !M_HW */ | ||
248 | #define OLDEST_PROD_REV_SUPPORTED 11 | ||
249 | |||
250 | static struct tsProdRevMap prodRevsMap[] = { | ||
251 | {0, 0}, | ||
252 | {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */ | ||
253 | {MPU_SILICON_REV_A4, 131}, /* 2 | */ | ||
254 | {MPU_SILICON_REV_A4, 131}, /* 3 V */ | ||
255 | {MPU_SILICON_REV_A4, 131}, /* 4 */ | ||
256 | {MPU_SILICON_REV_A4, 131}, /* 5 */ | ||
257 | {MPU_SILICON_REV_A4, 131}, /* 6 */ | ||
258 | {MPU_SILICON_REV_A4, 131}, /* 7 */ | ||
259 | {MPU_SILICON_REV_A4, 131}, /* 8 */ | ||
260 | {MPU_SILICON_REV_A4, 131}, /* 9 */ | ||
261 | {MPU_SILICON_REV_A4, 131}, /* 10 */ | ||
262 | {MPU_SILICON_REV_B1, 131}, /* 11 B1 */ | ||
263 | {MPU_SILICON_REV_B1, 131}, /* 12 | */ | ||
264 | {MPU_SILICON_REV_B1, 131}, /* 13 V */ | ||
265 | {MPU_SILICON_REV_B1, 131}, /* 14 B4 */ | ||
266 | {MPU_SILICON_REV_B4, 131}, /* 15 | */ | ||
267 | {MPU_SILICON_REV_B4, 131}, /* 16 V */ | ||
268 | {MPU_SILICON_REV_B4, 131}, /* 17 */ | ||
269 | {MPU_SILICON_REV_B4, 131}, /* 18 */ | ||
270 | {MPU_SILICON_REV_B4, 115}, /* 19 */ | ||
271 | {MPU_SILICON_REV_B4, 115}, /* 20 */ | ||
272 | {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */ | ||
273 | {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */ | ||
274 | {MPU_SILICON_REV_B6, 0}, /* 23 B6 (npp) */ | ||
275 | {MPU_SILICON_REV_B6, 0}, /* 24 | (npp) */ | ||
276 | {MPU_SILICON_REV_B6, 0}, /* 25 V (npp) */ | ||
277 | {MPU_SILICON_REV_B6, 131}, /* 26 (B6/A11) */ | ||
278 | }; | ||
279 | #endif /* !M_HW */ | ||
280 | |||
281 | /** | ||
282 | * @internal | ||
283 | * @brief Get the silicon revision ID from OTP. | ||
284 | * The silicon revision number is in read from OTP bank 0, | ||
285 | * ADDR6[7:2]. The corresponding ID is retrieved by lookup | ||
286 | * in a map. | ||
287 | * @return The silicon revision ID (0 on error). | ||
288 | */ | ||
289 | static int MLDLGetSiliconRev(struct mldl_cfg *pdata, | ||
290 | void *mlsl_handle) | ||
291 | { | ||
292 | int result; | ||
293 | unsigned char index = 0x00; | ||
294 | unsigned char bank = | ||
295 | (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); | ||
296 | unsigned short memAddr = ((bank << 8) | 0x06); | ||
297 | |||
298 | result = MLSLSerialReadMem(mlsl_handle, pdata->addr, | ||
299 | memAddr, 1, &index); | ||
300 | ERROR_CHECK(result); | ||
301 | if (result) | ||
302 | return result; | ||
303 | index >>= 2; | ||
304 | |||
305 | /* clean the prefetch and cfg user bank bits */ | ||
306 | result = | ||
307 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
308 | MPUREG_BANK_SEL, 0); | ||
309 | ERROR_CHECK(result); | ||
310 | if (result) | ||
311 | return result; | ||
312 | |||
313 | if (index < OLDEST_PROD_REV_SUPPORTED || NUM_OF_PROD_REVS <= index) { | ||
314 | pdata->silicon_revision = 0; | ||
315 | pdata->trim = 0; | ||
316 | MPL_LOGE("Unsupported Product Revision Detected : %d\n", index); | ||
317 | return ML_ERROR_INVALID_MODULE; | ||
318 | } | ||
319 | |||
320 | pdata->silicon_revision = prodRevsMap[index].siliconRev; | ||
321 | pdata->trim = prodRevsMap[index].sensTrim; | ||
322 | |||
323 | if (pdata->trim == 0) { | ||
324 | MPL_LOGE("sensitivity trim is 0" | ||
325 | " - unsupported non production part.\n"); | ||
326 | return ML_ERROR_INVALID_MODULE; | ||
327 | } | ||
328 | |||
329 | return result; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * @brief Enable / Disable the use MPU's secondary I2C interface level | ||
334 | * shifters. | ||
335 | * When enabled the secondary I2C interface to which the external | ||
336 | * device is connected runs at VDD voltage (main supply). | ||
337 | * When disabled the 2nd interface runs at VDDIO voltage. | ||
338 | * See the device specification for more details. | ||
339 | * | ||
340 | * @note using this API may produce unpredictable results, depending on how | ||
341 | * the MPU and slave device are setup on the target platform. | ||
342 | * Use of this API should entirely be restricted to system | ||
343 | * integrators. Once the correct value is found, there should be no | ||
344 | * need to change the level shifter at runtime. | ||
345 | * | ||
346 | * @pre Must be called after MLSerialOpen(). | ||
347 | * @note Typically called before MLDmpOpen(). | ||
348 | * | ||
349 | * @param[in] enable: | ||
350 | * 0 to run at VDDIO (default), | ||
351 | * 1 to run at VDD. | ||
352 | * | ||
353 | * @return ML_SUCCESS if successfull, a non-zero error code otherwise. | ||
354 | */ | ||
355 | static int MLDLSetLevelShifterBit(struct mldl_cfg *pdata, | ||
356 | void *mlsl_handle, | ||
357 | unsigned char enable) | ||
358 | { | ||
359 | #ifndef M_HW | ||
360 | int result; | ||
361 | unsigned char reg; | ||
362 | unsigned char mask; | ||
363 | unsigned char regval; | ||
364 | |||
365 | if (0 == pdata->silicon_revision) | ||
366 | return ML_ERROR_INVALID_PARAMETER; | ||
367 | |||
368 | /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR -- | ||
369 | NOTE: this is incompatible with ST accelerometers where the VDDIO | ||
370 | bit MUST be set to enable ST's internal logic to autoincrement | ||
371 | the register address on burst reads --*/ | ||
372 | if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) { | ||
373 | reg = MPUREG_ACCEL_BURST_ADDR; | ||
374 | mask = 0x80; | ||
375 | } else { | ||
376 | /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 => | ||
377 | the mask is always 0x04 --*/ | ||
378 | reg = MPUREG_FIFO_EN2; | ||
379 | mask = 0x04; | ||
380 | } | ||
381 | |||
382 | result = MLSLSerialRead(mlsl_handle, pdata->addr, reg, 1, ®val); | ||
383 | if (result) | ||
384 | return result; | ||
385 | |||
386 | if (enable) | ||
387 | regval |= mask; | ||
388 | else | ||
389 | regval &= ~mask; | ||
390 | |||
391 | result = | ||
392 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, reg, regval); | ||
393 | |||
394 | return result; | ||
395 | #else | ||
396 | return ML_SUCCESS; | ||
397 | #endif | ||
398 | } | ||
399 | |||
400 | |||
401 | #ifdef M_HW | ||
402 | /** | ||
403 | * @internal | ||
404 | * @param reset 1 to reset hardware | ||
405 | */ | ||
406 | static tMLError mpu60xx_pwr_mgmt(struct mldl_cfg *pdata, | ||
407 | void *mlsl_handle, | ||
408 | unsigned char reset, | ||
409 | unsigned char powerselection) | ||
410 | { | ||
411 | unsigned char b; | ||
412 | tMLError result; | ||
413 | |||
414 | if (powerselection < 0 || powerselection > 7) | ||
415 | return ML_ERROR_INVALID_PARAMETER; | ||
416 | |||
417 | result = | ||
418 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_1, 1, | ||
419 | &b); | ||
420 | ERROR_CHECK(result); | ||
421 | |||
422 | b &= ~(BITS_PWRSEL); | ||
423 | |||
424 | if (reset) { | ||
425 | /* Current sillicon has an errata where the reset will get | ||
426 | * nacked. Ignore the error code for now. */ | ||
427 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
428 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
429 | #define M_HW_RESET_ERRATTA | ||
430 | #ifndef M_HW_RESET_ERRATTA | ||
431 | ERROR_CHECK(result); | ||
432 | #else | ||
433 | MLOSSleep(50); | ||
434 | #endif | ||
435 | } | ||
436 | |||
437 | b |= (powerselection << 4); | ||
438 | |||
439 | if (b & BITS_PWRSEL) | ||
440 | pdata->gyro_is_suspended = FALSE; | ||
441 | else | ||
442 | pdata->gyro_is_suspended = TRUE; | ||
443 | |||
444 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
445 | MPUREG_PWR_MGM, b); | ||
446 | ERROR_CHECK(result); | ||
447 | |||
448 | return ML_SUCCESS; | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * @internal | ||
453 | */ | ||
454 | static tMLError MLDLStandByGyros(struct mldl_cfg *pdata, | ||
455 | void *mlsl_handle, | ||
456 | unsigned char disable_gx, | ||
457 | unsigned char disable_gy, | ||
458 | unsigned char disable_gz) | ||
459 | { | ||
460 | unsigned char b; | ||
461 | tMLError result; | ||
462 | |||
463 | result = | ||
464 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, | ||
465 | &b); | ||
466 | ERROR_CHECK(result); | ||
467 | |||
468 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
469 | b |= (disable_gx << 2 | disable_gy << 1 | disable_gz); | ||
470 | |||
471 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
472 | MPUREG_PWR_MGMT_2, b); | ||
473 | ERROR_CHECK(result); | ||
474 | |||
475 | return ML_SUCCESS; | ||
476 | } | ||
477 | |||
478 | /** | ||
479 | * @internal | ||
480 | */ | ||
481 | static tMLError MLDLStandByAccels(struct mldl_cfg *pdata, | ||
482 | void *mlsl_handle, | ||
483 | unsigned char disable_ax, | ||
484 | unsigned char disable_ay, | ||
485 | unsigned char disable_az) | ||
486 | { | ||
487 | unsigned char b; | ||
488 | tMLError result; | ||
489 | |||
490 | result = | ||
491 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1, | ||
492 | &b); | ||
493 | ERROR_CHECK(result); | ||
494 | |||
495 | b &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA); | ||
496 | b |= (disable_ax << 2 | disable_ay << 1 | disable_az); | ||
497 | |||
498 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
499 | MPUREG_PWR_MGMT_2, b); | ||
500 | ERROR_CHECK(result); | ||
501 | |||
502 | return ML_SUCCESS; | ||
503 | } | ||
504 | |||
505 | #else /* ! M_HW */ | ||
506 | |||
507 | /** | ||
508 | * @internal | ||
509 | * @brief This function controls the power management on the MPU device. | ||
510 | * The entire chip can be put to low power sleep mode, or individual | ||
511 | * gyros can be turned on/off. | ||
512 | * | ||
513 | * Putting the device into sleep mode depending upon the changing needs | ||
514 | * of the associated applications is a recommended method for reducing | ||
515 | * power consuption. It is a safe opearation in that sleep/wake up of | ||
516 | * gyros while running will not result in any interruption of data. | ||
517 | * | ||
518 | * Although it is entirely allowed to put the device into full sleep | ||
519 | * while running the DMP, it is not recomended because it will disrupt | ||
520 | * the ongoing calculations carried on inside the DMP and consequently | ||
521 | * the sensor fusion algorithm. Furthermore, while in sleep mode | ||
522 | * read & write operation from the app processor on both registers and | ||
523 | * memory are disabled and can only regained by restoring the MPU in | ||
524 | * normal power mode. | ||
525 | * Disabling any of the gyro axis will reduce the associated power | ||
526 | * consuption from the PLL but will not stop the DMP from running | ||
527 | * state. | ||
528 | * | ||
529 | * @param reset | ||
530 | * Non-zero to reset the device. Note that this setting | ||
531 | * is volatile and the corresponding register bit will | ||
532 | * clear itself right after being applied. | ||
533 | * @param sleep | ||
534 | * Non-zero to put device into full sleep. | ||
535 | * @param disable_gx | ||
536 | * Non-zero to disable gyro X. | ||
537 | * @param disable_gy | ||
538 | * Non-zero to disable gyro Y. | ||
539 | * @param disable_gz | ||
540 | * Non-zero to disable gyro Z. | ||
541 | * | ||
542 | * @return ML_SUCCESS if successfull; a non-zero error code otherwise. | ||
543 | */ | ||
544 | static int MLDLPowerMgmtMPU(struct mldl_cfg *pdata, | ||
545 | void *mlsl_handle, | ||
546 | unsigned char reset, | ||
547 | unsigned char sleep, | ||
548 | unsigned char disable_gx, | ||
549 | unsigned char disable_gy, | ||
550 | unsigned char disable_gz) | ||
551 | { | ||
552 | unsigned char b; | ||
553 | int result; | ||
554 | |||
555 | result = | ||
556 | MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1, | ||
557 | &b); | ||
558 | ERROR_CHECK(result); | ||
559 | |||
560 | /* If we are awake, we need to put it in bypass before resetting */ | ||
561 | if ((!(b & BIT_SLEEP)) && reset) | ||
562 | result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); | ||
563 | |||
564 | /* If we are awake, we need stop the dmp sleeping */ | ||
565 | if ((!(b & BIT_SLEEP)) && sleep) | ||
566 | dmp_stop(pdata, mlsl_handle); | ||
567 | |||
568 | /* Reset if requested */ | ||
569 | if (reset) { | ||
570 | MPL_LOGV("Reset MPU3050\n"); | ||
571 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
572 | MPUREG_PWR_MGM, b | BIT_H_RESET); | ||
573 | ERROR_CHECK(result); | ||
574 | MLOSSleep(5); | ||
575 | pdata->gyro_needs_reset = FALSE; | ||
576 | /* Some chips are awake after reset and some are asleep, | ||
577 | * check the status */ | ||
578 | result = MLSLSerialRead(mlsl_handle, pdata->addr, | ||
579 | MPUREG_PWR_MGM, 1, &b); | ||
580 | ERROR_CHECK(result); | ||
581 | } | ||
582 | |||
583 | /* Update the suspended state just in case we return early */ | ||
584 | if (b & BIT_SLEEP) | ||
585 | pdata->gyro_is_suspended = TRUE; | ||
586 | else | ||
587 | pdata->gyro_is_suspended = FALSE; | ||
588 | |||
589 | /* if power status match requested, nothing else's left to do */ | ||
590 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
591 | (((sleep != 0) * BIT_SLEEP) | | ||
592 | ((disable_gx != 0) * BIT_STBY_XG) | | ||
593 | ((disable_gy != 0) * BIT_STBY_YG) | | ||
594 | ((disable_gz != 0) * BIT_STBY_ZG))) { | ||
595 | return ML_SUCCESS; | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | * This specific transition between states needs to be reinterpreted: | ||
600 | * (1,1,1,1) -> (0,1,1,1) has to become | ||
601 | * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1) | ||
602 | * where | ||
603 | * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1) | ||
604 | */ | ||
605 | if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) == | ||
606 | (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) | ||
607 | && ((!sleep) && disable_gx && disable_gy && disable_gz)) { | ||
608 | result = MLDLPowerMgmtMPU(pdata, mlsl_handle, 0, 1, 0, 0, 0); | ||
609 | if (result) | ||
610 | return result; | ||
611 | b |= BIT_SLEEP; | ||
612 | b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG); | ||
613 | } | ||
614 | |||
615 | if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) { | ||
616 | if (sleep) { | ||
617 | result = MLDLSetI2CBypass(pdata, mlsl_handle, 1); | ||
618 | ERROR_CHECK(result); | ||
619 | b |= BIT_SLEEP; | ||
620 | result = | ||
621 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
622 | MPUREG_PWR_MGM, b); | ||
623 | ERROR_CHECK(result); | ||
624 | pdata->gyro_is_suspended = TRUE; | ||
625 | } else { | ||
626 | b &= ~BIT_SLEEP; | ||
627 | result = | ||
628 | MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
629 | MPUREG_PWR_MGM, b); | ||
630 | ERROR_CHECK(result); | ||
631 | pdata->gyro_is_suspended = FALSE; | ||
632 | MLOSSleep(5); | ||
633 | } | ||
634 | } | ||
635 | /*--- | ||
636 | WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE | ||
637 | 1) put one axis at a time in stand-by | ||
638 | ---*/ | ||
639 | if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) { | ||
640 | b ^= BIT_STBY_XG; | ||
641 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
642 | MPUREG_PWR_MGM, b); | ||
643 | ERROR_CHECK(result); | ||
644 | } | ||
645 | if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) { | ||
646 | b ^= BIT_STBY_YG; | ||
647 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
648 | MPUREG_PWR_MGM, b); | ||
649 | ERROR_CHECK(result); | ||
650 | } | ||
651 | if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) { | ||
652 | b ^= BIT_STBY_ZG; | ||
653 | result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr, | ||
654 | MPUREG_PWR_MGM, b); | ||
655 | ERROR_CHECK(result); | ||
656 | } | ||
657 | |||
658 | return ML_SUCCESS; | ||
659 | } | ||
660 | #endif /* M_HW */ | ||
661 | |||
662 | |||
663 | void mpu_print_cfg(struct mldl_cfg *mldl_cfg) | ||
664 | { | ||
665 | struct mpu3050_platform_data *pdata = mldl_cfg->pdata; | ||
666 | struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel; | ||
667 | struct ext_slave_platform_data *compass = | ||
668 | &mldl_cfg->pdata->compass; | ||
669 | struct ext_slave_platform_data *pressure = | ||
670 | &mldl_cfg->pdata->pressure; | ||
671 | |||
672 | MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr); | ||
673 | MPL_LOGD("mldl_cfg.int_config = %02x\n", | ||
674 | mldl_cfg->int_config); | ||
675 | MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync); | ||
676 | MPL_LOGD("mldl_cfg.full_scale = %02x\n", | ||
677 | mldl_cfg->full_scale); | ||
678 | MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf); | ||
679 | MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src); | ||
680 | MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider); | ||
681 | MPL_LOGD("mldl_cfg.dmp_enable = %02x\n", | ||
682 | mldl_cfg->dmp_enable); | ||
683 | MPL_LOGD("mldl_cfg.fifo_enable = %02x\n", | ||
684 | mldl_cfg->fifo_enable); | ||
685 | MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1); | ||
686 | MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2); | ||
687 | MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n", | ||
688 | mldl_cfg->offset_tc[0]); | ||
689 | MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n", | ||
690 | mldl_cfg->offset_tc[1]); | ||
691 | MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n", | ||
692 | mldl_cfg->offset_tc[2]); | ||
693 | MPL_LOGD("mldl_cfg.silicon_revision = %02x\n", | ||
694 | mldl_cfg->silicon_revision); | ||
695 | MPL_LOGD("mldl_cfg.product_id = %02x\n", | ||
696 | mldl_cfg->product_id); | ||
697 | MPL_LOGD("mldl_cfg.trim = %02x\n", mldl_cfg->trim); | ||
698 | MPL_LOGD("mldl_cfg.requested_sensors= %04lx\n", | ||
699 | mldl_cfg->requested_sensors); | ||
700 | |||
701 | if (mldl_cfg->accel) { | ||
702 | MPL_LOGD("slave_accel->suspend = %02x\n", | ||
703 | (int) mldl_cfg->accel->suspend); | ||
704 | MPL_LOGD("slave_accel->resume = %02x\n", | ||
705 | (int) mldl_cfg->accel->resume); | ||
706 | MPL_LOGD("slave_accel->read = %02x\n", | ||
707 | (int) mldl_cfg->accel->read); | ||
708 | MPL_LOGD("slave_accel->type = %02x\n", | ||
709 | mldl_cfg->accel->type); | ||
710 | MPL_LOGD("slave_accel->reg = %02x\n", | ||
711 | mldl_cfg->accel->reg); | ||
712 | MPL_LOGD("slave_accel->len = %02x\n", | ||
713 | mldl_cfg->accel->len); | ||
714 | MPL_LOGD("slave_accel->endian = %02x\n", | ||
715 | mldl_cfg->accel->endian); | ||
716 | MPL_LOGD("slave_accel->range.mantissa= %02lx\n", | ||
717 | mldl_cfg->accel->range.mantissa); | ||
718 | MPL_LOGD("slave_accel->range.fraction= %02lx\n", | ||
719 | mldl_cfg->accel->range.fraction); | ||
720 | } else { | ||
721 | MPL_LOGD("slave_accel = NULL\n"); | ||
722 | } | ||
723 | |||
724 | if (mldl_cfg->compass) { | ||
725 | MPL_LOGD("slave_compass->suspend = %02x\n", | ||
726 | (int) mldl_cfg->compass->suspend); | ||
727 | MPL_LOGD("slave_compass->resume = %02x\n", | ||
728 | (int) mldl_cfg->compass->resume); | ||
729 | MPL_LOGD("slave_compass->read = %02x\n", | ||
730 | (int) mldl_cfg->compass->read); | ||
731 | MPL_LOGD("slave_compass->type = %02x\n", | ||
732 | mldl_cfg->compass->type); | ||
733 | MPL_LOGD("slave_compass->reg = %02x\n", | ||
734 | mldl_cfg->compass->reg); | ||
735 | MPL_LOGD("slave_compass->len = %02x\n", | ||
736 | mldl_cfg->compass->len); | ||
737 | MPL_LOGD("slave_compass->endian = %02x\n", | ||
738 | mldl_cfg->compass->endian); | ||
739 | MPL_LOGD("slave_compass->range.mantissa= %02lx\n", | ||
740 | mldl_cfg->compass->range.mantissa); | ||
741 | MPL_LOGD("slave_compass->range.fraction= %02lx\n", | ||
742 | mldl_cfg->compass->range.fraction); | ||
743 | |||
744 | } else { | ||
745 | MPL_LOGD("slave_compass = NULL\n"); | ||
746 | } | ||
747 | |||
748 | if (mldl_cfg->pressure) { | ||
749 | MPL_LOGD("slave_pressure->suspend = %02x\n", | ||
750 | (int) mldl_cfg->pressure->suspend); | ||
751 | MPL_LOGD("slave_pressure->resume = %02x\n", | ||
752 | (int) mldl_cfg->pressure->resume); | ||
753 | MPL_LOGD("slave_pressure->read = %02x\n", | ||
754 | (int) mldl_cfg->pressure->read); | ||
755 | MPL_LOGD("slave_pressure->type = %02x\n", | ||
756 | mldl_cfg->pressure->type); | ||
757 | MPL_LOGD("slave_pressure->reg = %02x\n", | ||
758 | mldl_cfg->pressure->reg); | ||
759 | MPL_LOGD("slave_pressure->len = %02x\n", | ||
760 | mldl_cfg->pressure->len); | ||
761 | MPL_LOGD("slave_pressure->endian = %02x\n", | ||
762 | mldl_cfg->pressure->endian); | ||
763 | MPL_LOGD("slave_pressure->range.mantissa= %02lx\n", | ||
764 | mldl_cfg->pressure->range.mantissa); | ||
765 | MPL_LOGD("slave_pressure->range.fraction= %02lx\n", | ||
766 | mldl_cfg->pressure->range.fraction); | ||
767 | |||
768 | } else { | ||
769 | MPL_LOGD("slave_pressure = NULL\n"); | ||
770 | } | ||
771 | MPL_LOGD("accel->get_slave_descr = %x\n", | ||
772 | (unsigned int) accel->get_slave_descr); | ||
773 | MPL_LOGD("accel->irq = %02x\n", accel->irq); | ||
774 | MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num); | ||
775 | MPL_LOGD("accel->bus = %02x\n", accel->bus); | ||
776 | MPL_LOGD("accel->address = %02x\n", accel->address); | ||
777 | MPL_LOGD("accel->orientation =\n" | ||
778 | " %2d %2d %2d\n" | ||
779 | " %2d %2d %2d\n" | ||
780 | " %2d %2d %2d\n", | ||
781 | accel->orientation[0], accel->orientation[1], | ||
782 | accel->orientation[2], accel->orientation[3], | ||
783 | accel->orientation[4], accel->orientation[5], | ||
784 | accel->orientation[6], accel->orientation[7], | ||
785 | accel->orientation[8]); | ||
786 | MPL_LOGD("compass->get_slave_descr = %x\n", | ||
787 | (unsigned int) compass->get_slave_descr); | ||
788 | MPL_LOGD("compass->irq = %02x\n", compass->irq); | ||
789 | MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num); | ||
790 | MPL_LOGD("compass->bus = %02x\n", compass->bus); | ||
791 | MPL_LOGD("compass->address = %02x\n", compass->address); | ||
792 | MPL_LOGD("compass->orientation =\n" | ||
793 | " %2d %2d %2d\n" | ||
794 | " %2d %2d %2d\n" | ||
795 | " %2d %2d %2d\n", | ||
796 | compass->orientation[0], compass->orientation[1], | ||
797 | compass->orientation[2], compass->orientation[3], | ||
798 | compass->orientation[4], compass->orientation[5], | ||
799 | compass->orientation[6], compass->orientation[7], | ||
800 | compass->orientation[8]); | ||
801 | MPL_LOGD("pressure->get_slave_descr = %x\n", | ||
802 | (unsigned int) pressure->get_slave_descr); | ||
803 | MPL_LOGD("pressure->irq = %02x\n", pressure->irq); | ||
804 | MPL_LOGD("pressure->adapt_num = %02x\n", pressure->adapt_num); | ||
805 | MPL_LOGD("pressure->bus = %02x\n", pressure->bus); | ||
806 | MPL_LOGD("pressure->address = %02x\n", pressure->address); | ||
807 | MPL_LOGD("pressure->orientation =\n" | ||
808 | " %2d %2d %2d\n" | ||
809 | " %2d %2d %2d\n" | ||
810 | " %2d %2d %2d\n", | ||
811 | pressure->orientation[0], pressure->orientation[1], | ||
812 | pressure->orientation[2], pressure->orientation[3], | ||
813 | pressure->orientation[4], pressure->orientation[5], | ||
814 | pressure->orientation[6], pressure->orientation[7], | ||
815 | pressure->orientation[8]); | ||
816 | |||
817 | MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config); | ||
818 | MPL_LOGD("pdata->level_shifter = %02x\n", | ||
819 | pdata->level_shifter); | ||
820 | MPL_LOGD("pdata->orientation =\n" | ||
821 | " %2d %2d %2d\n" | ||
822 | " %2d %2d %2d\n" | ||
823 | " %2d %2d %2d\n", | ||
824 | pdata->orientation[0], pdata->orientation[1], | ||
825 | pdata->orientation[2], pdata->orientation[3], | ||
826 | pdata->orientation[4], pdata->orientation[5], | ||
827 | pdata->orientation[6], pdata->orientation[7], | ||
828 | pdata->orientation[8]); | ||
829 | |||
830 | MPL_LOGD("Struct sizes: mldl_cfg: %d, " | ||
831 | "ext_slave_descr:%d, " | ||
832 | "mpu3050_platform_data:%d: RamOffset: %d\n", | ||
833 | sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr), | ||
834 | sizeof(struct mpu3050_platform_data), | ||
835 | offsetof(struct mldl_cfg, ram)); | ||
836 | } | ||
837 | |||
838 | int mpu_set_slave(struct mldl_cfg *mldl_cfg, | ||
839 | void *gyro_handle, | ||
840 | struct ext_slave_descr *slave, | ||
841 | struct ext_slave_platform_data *slave_pdata) | ||
842 | { | ||
843 | int result; | ||
844 | unsigned char reg; | ||
845 | unsigned char slave_reg; | ||
846 | unsigned char slave_len; | ||
847 | unsigned char slave_endian; | ||
848 | unsigned char slave_address; | ||
849 | |||
850 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
851 | |||
852 | if (NULL == slave || NULL == slave_pdata) { | ||
853 | slave_reg = 0; | ||
854 | slave_len = 0; | ||
855 | slave_endian = 0; | ||
856 | slave_address = 0; | ||
857 | } else { | ||
858 | slave_reg = slave->reg; | ||
859 | slave_len = slave->len; | ||
860 | slave_endian = slave->endian; | ||
861 | slave_address = slave_pdata->address; | ||
862 | } | ||
863 | |||
864 | /* Address */ | ||
865 | result = MLSLSerialWriteSingle(gyro_handle, | ||
866 | mldl_cfg->addr, | ||
867 | MPUREG_AUX_SLV_ADDR, | ||
868 | slave_address); | ||
869 | ERROR_CHECK(result); | ||
870 | /* Register */ | ||
871 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
872 | MPUREG_ACCEL_BURST_ADDR, 1, | ||
873 | ®); | ||
874 | ERROR_CHECK(result); | ||
875 | reg = ((reg & 0x80) | slave_reg); | ||
876 | result = MLSLSerialWriteSingle(gyro_handle, | ||
877 | mldl_cfg->addr, | ||
878 | MPUREG_ACCEL_BURST_ADDR, | ||
879 | reg); | ||
880 | ERROR_CHECK(result); | ||
881 | |||
882 | #ifdef M_HW | ||
883 | /* Length, byte swapping, grouping & enable */ | ||
884 | if (slave_len > BITS_SLV_LENG) { | ||
885 | MPL_LOGW("Limiting slave burst read length to " | ||
886 | "the allowed maximum (15B, req. %d)\n", | ||
887 | slave_len); | ||
888 | slave_len = BITS_SLV_LENG; | ||
889 | } | ||
890 | reg = slave_len; | ||
891 | if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) | ||
892 | reg |= BIT_SLV_BYTE_SW; | ||
893 | reg |= BIT_SLV_GRP; | ||
894 | reg |= BIT_SLV_ENABLE; | ||
895 | |||
896 | result = MLSLSerialWriteSingle(gyro_handle, | ||
897 | mldl_cfg->addr, | ||
898 | MPUREG_I2C_SLV0_CTRL, | ||
899 | reg); | ||
900 | #else | ||
901 | /* Length */ | ||
902 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
903 | MPUREG_USER_CTRL, 1, ®); | ||
904 | ERROR_CHECK(result); | ||
905 | reg = (reg & ~BIT_AUX_RD_LENG); | ||
906 | result = MLSLSerialWriteSingle(gyro_handle, | ||
907 | mldl_cfg->addr, | ||
908 | MPUREG_USER_CTRL, reg); | ||
909 | ERROR_CHECK(result); | ||
910 | #endif | ||
911 | |||
912 | if (slave_address) { | ||
913 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, FALSE); | ||
914 | ERROR_CHECK(result); | ||
915 | } | ||
916 | return result; | ||
917 | } | ||
918 | |||
919 | /** | ||
920 | * Check to see if the gyro was reset by testing a couple of registers known | ||
921 | * to change on reset. | ||
922 | * | ||
923 | * @param mldl_cfg mldl configuration structure | ||
924 | * @param gyro_handle handle used to communicate with the gyro | ||
925 | * | ||
926 | * @return ML_SUCCESS or non-zero error code | ||
927 | */ | ||
928 | static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
929 | { | ||
930 | int result = ML_SUCCESS; | ||
931 | unsigned char reg; | ||
932 | |||
933 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
934 | MPUREG_DMP_CFG_2, 1, ®); | ||
935 | ERROR_CHECK(result); | ||
936 | |||
937 | if (mldl_cfg->dmp_cfg2 != reg) | ||
938 | return TRUE; | ||
939 | |||
940 | if (0 != mldl_cfg->dmp_cfg1) | ||
941 | return FALSE; | ||
942 | |||
943 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
944 | MPUREG_SMPLRT_DIV, 1, ®); | ||
945 | ERROR_CHECK(result); | ||
946 | if (reg != mldl_cfg->divider) | ||
947 | return TRUE; | ||
948 | |||
949 | if (0 != mldl_cfg->divider) | ||
950 | return FALSE; | ||
951 | |||
952 | /* Inconclusive assume it was reset */ | ||
953 | return TRUE; | ||
954 | } | ||
955 | |||
956 | static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle) | ||
957 | { | ||
958 | int result; | ||
959 | int ii; | ||
960 | int jj; | ||
961 | unsigned char reg; | ||
962 | unsigned char regs[7]; | ||
963 | |||
964 | /* Wake up the part */ | ||
965 | #ifdef M_HW | ||
966 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, RESET, | ||
967 | WAKE_UP); | ||
968 | ERROR_CHECK(result); | ||
969 | |||
970 | /* Configure the MPU */ | ||
971 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); | ||
972 | ERROR_CHECK(result); | ||
973 | /* setting int_config with the propert flag BIT_BYPASS_EN | ||
974 | should be done by the setup functions */ | ||
975 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
976 | MPUREG_INT_PIN_CFG, | ||
977 | (mldl_cfg->pdata->int_config | | ||
978 | BIT_BYPASS_EN)); | ||
979 | ERROR_CHECK(result); | ||
980 | /* temporary: masking out higher bits to avoid switching | ||
981 | intelligence */ | ||
982 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
983 | MPUREG_INT_ENABLE, | ||
984 | (mldl_cfg->int_config)); | ||
985 | ERROR_CHECK(result); | ||
986 | #else | ||
987 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 0, 0, | ||
988 | mldl_cfg->gyro_power & BIT_STBY_XG, | ||
989 | mldl_cfg->gyro_power & BIT_STBY_YG, | ||
990 | mldl_cfg->gyro_power & BIT_STBY_ZG); | ||
991 | |||
992 | if (!mldl_cfg->gyro_needs_reset && | ||
993 | !mpu_was_reset(mldl_cfg, gyro_handle)) { | ||
994 | return ML_SUCCESS; | ||
995 | } | ||
996 | |||
997 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 1, 0, | ||
998 | mldl_cfg->gyro_power & BIT_STBY_XG, | ||
999 | mldl_cfg->gyro_power & BIT_STBY_YG, | ||
1000 | mldl_cfg->gyro_power & BIT_STBY_ZG); | ||
1001 | ERROR_CHECK(result); | ||
1002 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1003 | MPUREG_INT_CFG, | ||
1004 | (mldl_cfg->int_config | | ||
1005 | mldl_cfg->pdata->int_config)); | ||
1006 | ERROR_CHECK(result); | ||
1007 | #endif | ||
1008 | |||
1009 | result = MLSLSerialRead(gyro_handle, mldl_cfg->addr, | ||
1010 | MPUREG_PWR_MGM, 1, ®); | ||
1011 | ERROR_CHECK(result); | ||
1012 | reg &= ~BITS_CLKSEL; | ||
1013 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1014 | MPUREG_PWR_MGM, | ||
1015 | mldl_cfg->clk_src | reg); | ||
1016 | ERROR_CHECK(result); | ||
1017 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1018 | MPUREG_SMPLRT_DIV, | ||
1019 | mldl_cfg->divider); | ||
1020 | ERROR_CHECK(result); | ||
1021 | |||
1022 | #ifdef M_HW | ||
1023 | reg = DLPF_FS_SYNC_VALUE(0, mldl_cfg->full_scale, 0); | ||
1024 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1025 | MPUREG_GYRO_CONFIG, reg); | ||
1026 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, 0, mldl_cfg->lpf); | ||
1027 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1028 | MPUREG_CONFIG, reg); | ||
1029 | #else | ||
1030 | reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, | ||
1031 | mldl_cfg->full_scale, mldl_cfg->lpf); | ||
1032 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1033 | MPUREG_DLPF_FS_SYNC, reg); | ||
1034 | #endif | ||
1035 | ERROR_CHECK(result); | ||
1036 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1037 | MPUREG_DMP_CFG_1, | ||
1038 | mldl_cfg->dmp_cfg1); | ||
1039 | ERROR_CHECK(result); | ||
1040 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1041 | MPUREG_DMP_CFG_2, | ||
1042 | mldl_cfg->dmp_cfg2); | ||
1043 | ERROR_CHECK(result); | ||
1044 | |||
1045 | /* Write and verify memory */ | ||
1046 | for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) { | ||
1047 | unsigned char read[MPU_MEM_BANK_SIZE]; | ||
1048 | |||
1049 | result = MLSLSerialWriteMem(gyro_handle, | ||
1050 | mldl_cfg->addr, | ||
1051 | ((ii << 8) | 0x00), | ||
1052 | MPU_MEM_BANK_SIZE, | ||
1053 | mldl_cfg->ram[ii]); | ||
1054 | ERROR_CHECK(result); | ||
1055 | result = MLSLSerialReadMem(gyro_handle, mldl_cfg->addr, | ||
1056 | ((ii << 8) | 0x00), | ||
1057 | MPU_MEM_BANK_SIZE, read); | ||
1058 | ERROR_CHECK(result); | ||
1059 | |||
1060 | #ifdef M_HW | ||
1061 | #define ML_SKIP_CHECK 38 | ||
1062 | #else | ||
1063 | #define ML_SKIP_CHECK 20 | ||
1064 | #endif | ||
1065 | for (jj = 0; jj < MPU_MEM_BANK_SIZE; jj++) { | ||
1066 | /* skip the register memory locations */ | ||
1067 | if (ii == 0 && jj < ML_SKIP_CHECK) | ||
1068 | continue; | ||
1069 | if (mldl_cfg->ram[ii][jj] != read[jj]) { | ||
1070 | result = ML_ERROR_SERIAL_WRITE; | ||
1071 | break; | ||
1072 | } | ||
1073 | } | ||
1074 | ERROR_CHECK(result); | ||
1075 | } | ||
1076 | |||
1077 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1078 | MPUREG_XG_OFFS_TC, | ||
1079 | mldl_cfg->offset_tc[0]); | ||
1080 | ERROR_CHECK(result); | ||
1081 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1082 | MPUREG_YG_OFFS_TC, | ||
1083 | mldl_cfg->offset_tc[1]); | ||
1084 | ERROR_CHECK(result); | ||
1085 | result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr, | ||
1086 | MPUREG_ZG_OFFS_TC, | ||
1087 | mldl_cfg->offset_tc[2]); | ||
1088 | ERROR_CHECK(result); | ||
1089 | |||
1090 | regs[0] = MPUREG_X_OFFS_USRH; | ||
1091 | for (ii = 0; ii < DIM(mldl_cfg->offset); ii++) { | ||
1092 | regs[1 + ii * 2] = | ||
1093 | (unsigned char)(mldl_cfg->offset[ii] >> 8) | ||
1094 | & 0xff; | ||
1095 | regs[1 + ii * 2 + 1] = | ||
1096 | (unsigned char)(mldl_cfg->offset[ii] & 0xff); | ||
1097 | } | ||
1098 | result = MLSLSerialWrite(gyro_handle, mldl_cfg->addr, 7, regs); | ||
1099 | ERROR_CHECK(result); | ||
1100 | |||
1101 | /* Configure slaves */ | ||
1102 | result = MLDLSetLevelShifterBit(mldl_cfg, gyro_handle, | ||
1103 | mldl_cfg->pdata->level_shifter); | ||
1104 | ERROR_CHECK(result); | ||
1105 | return result; | ||
1106 | } | ||
1107 | /******************************************************************************* | ||
1108 | ******************************************************************************* | ||
1109 | * Exported functions | ||
1110 | ******************************************************************************* | ||
1111 | ******************************************************************************/ | ||
1112 | |||
1113 | /** | ||
1114 | * Initializes the pdata structure to defaults. | ||
1115 | * | ||
1116 | * Opens the device to read silicon revision, product id and whoami. | ||
1117 | * | ||
1118 | * @param mldl_cfg | ||
1119 | * The internal device configuration data structure. | ||
1120 | * @param mlsl_handle | ||
1121 | * The serial communication handle. | ||
1122 | * | ||
1123 | * @return ML_SUCCESS if silicon revision, product id and woami are supported | ||
1124 | * by this software. | ||
1125 | */ | ||
1126 | int mpu3050_open(struct mldl_cfg *mldl_cfg, | ||
1127 | void *mlsl_handle, | ||
1128 | void *accel_handle, | ||
1129 | void *compass_handle, | ||
1130 | void *pressure_handle) | ||
1131 | { | ||
1132 | int result; | ||
1133 | /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */ | ||
1134 | mldl_cfg->ignore_system_suspend = FALSE; | ||
1135 | mldl_cfg->int_config = BIT_INT_ANYRD_2CLEAR | BIT_DMP_INT_EN; | ||
1136 | mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ; | ||
1137 | mldl_cfg->lpf = MPU_FILTER_42HZ; | ||
1138 | mldl_cfg->full_scale = MPU_FS_2000DPS; | ||
1139 | mldl_cfg->divider = 4; | ||
1140 | mldl_cfg->dmp_enable = 1; | ||
1141 | mldl_cfg->fifo_enable = 1; | ||
1142 | mldl_cfg->ext_sync = 0; | ||
1143 | mldl_cfg->dmp_cfg1 = 0; | ||
1144 | mldl_cfg->dmp_cfg2 = 0; | ||
1145 | mldl_cfg->gyro_power = 0; | ||
1146 | mldl_cfg->gyro_is_bypassed = TRUE; | ||
1147 | mldl_cfg->dmp_is_running = FALSE; | ||
1148 | mldl_cfg->gyro_is_suspended = TRUE; | ||
1149 | mldl_cfg->accel_is_suspended = TRUE; | ||
1150 | mldl_cfg->compass_is_suspended = TRUE; | ||
1151 | mldl_cfg->pressure_is_suspended = TRUE; | ||
1152 | mldl_cfg->gyro_needs_reset = FALSE; | ||
1153 | if (mldl_cfg->addr == 0) { | ||
1154 | #ifdef __KERNEL__ | ||
1155 | return ML_ERROR_INVALID_PARAMETER; | ||
1156 | #else | ||
1157 | mldl_cfg->addr = 0x68; | ||
1158 | #endif | ||
1159 | } | ||
1160 | |||
1161 | /* | ||
1162 | * Reset, | ||
1163 | * Take the DMP out of sleep, and | ||
1164 | * read the product_id, sillicon rev and whoami | ||
1165 | */ | ||
1166 | #ifdef M_HW | ||
1167 | result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, | ||
1168 | RESET, WAKE_UP); | ||
1169 | #else | ||
1170 | result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, RESET, 0, 0, 0, 0); | ||
1171 | #endif | ||
1172 | ERROR_CHECK(result); | ||
1173 | |||
1174 | result = MLDLGetSiliconRev(mldl_cfg, mlsl_handle); | ||
1175 | ERROR_CHECK(result); | ||
1176 | #ifndef M_HW | ||
1177 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
1178 | MPUREG_PRODUCT_ID, 1, | ||
1179 | &mldl_cfg->product_id); | ||
1180 | ERROR_CHECK(result); | ||
1181 | #endif | ||
1182 | |||
1183 | /* Get the factory temperature compensation offsets */ | ||
1184 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
1185 | MPUREG_XG_OFFS_TC, 1, | ||
1186 | &mldl_cfg->offset_tc[0]); | ||
1187 | ERROR_CHECK(result); | ||
1188 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
1189 | MPUREG_YG_OFFS_TC, 1, | ||
1190 | &mldl_cfg->offset_tc[1]); | ||
1191 | ERROR_CHECK(result); | ||
1192 | result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr, | ||
1193 | MPUREG_ZG_OFFS_TC, 1, | ||
1194 | &mldl_cfg->offset_tc[2]); | ||
1195 | ERROR_CHECK(result); | ||
1196 | |||
1197 | /* Configure the MPU */ | ||
1198 | #ifdef M_HW | ||
1199 | result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle, | ||
1200 | FALSE, SLEEP); | ||
1201 | #else | ||
1202 | result = | ||
1203 | MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, SLEEP, 0, 0, 0); | ||
1204 | #endif | ||
1205 | ERROR_CHECK(result); | ||
1206 | |||
1207 | if (mldl_cfg->accel && mldl_cfg->accel->init) { | ||
1208 | result = mldl_cfg->accel->init(accel_handle, | ||
1209 | mldl_cfg->accel, | ||
1210 | &mldl_cfg->pdata->accel); | ||
1211 | ERROR_CHECK(result); | ||
1212 | } | ||
1213 | |||
1214 | if (mldl_cfg->compass && mldl_cfg->compass->init) { | ||
1215 | result = mldl_cfg->compass->init(compass_handle, | ||
1216 | mldl_cfg->compass, | ||
1217 | &mldl_cfg->pdata->compass); | ||
1218 | if (ML_SUCCESS != result) { | ||
1219 | MPL_LOGE("mldl_cfg->compass->init returned %d\n", | ||
1220 | result); | ||
1221 | goto out_accel; | ||
1222 | } | ||
1223 | } | ||
1224 | if (mldl_cfg->pressure && mldl_cfg->pressure->init) { | ||
1225 | result = mldl_cfg->pressure->init(pressure_handle, | ||
1226 | mldl_cfg->pressure, | ||
1227 | &mldl_cfg->pdata->pressure); | ||
1228 | if (ML_SUCCESS != result) { | ||
1229 | MPL_LOGE("mldl_cfg->pressure->init returned %d\n", | ||
1230 | result); | ||
1231 | goto out_compass; | ||
1232 | } | ||
1233 | } | ||
1234 | |||
1235 | mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO; | ||
1236 | if (mldl_cfg->accel && mldl_cfg->accel->resume) | ||
1237 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL; | ||
1238 | |||
1239 | if (mldl_cfg->compass && mldl_cfg->compass->resume) | ||
1240 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS; | ||
1241 | |||
1242 | if (mldl_cfg->pressure && mldl_cfg->pressure->resume) | ||
1243 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE; | ||
1244 | |||
1245 | return result; | ||
1246 | |||
1247 | out_compass: | ||
1248 | if (mldl_cfg->compass->init) | ||
1249 | mldl_cfg->compass->exit(compass_handle, | ||
1250 | mldl_cfg->compass, | ||
1251 | &mldl_cfg->pdata->compass); | ||
1252 | out_accel: | ||
1253 | if (mldl_cfg->accel->init) | ||
1254 | mldl_cfg->accel->exit(accel_handle, | ||
1255 | mldl_cfg->accel, | ||
1256 | &mldl_cfg->pdata->accel); | ||
1257 | return result; | ||
1258 | |||
1259 | } | ||
1260 | |||
1261 | /** | ||
1262 | * Close the mpu3050 interface | ||
1263 | * | ||
1264 | * @param mldl_cfg pointer to the configuration structure | ||
1265 | * @param mlsl_handle pointer to the serial layer handle | ||
1266 | * | ||
1267 | * @return ML_SUCCESS or non-zero error code | ||
1268 | */ | ||
1269 | int mpu3050_close(struct mldl_cfg *mldl_cfg, | ||
1270 | void *mlsl_handle, | ||
1271 | void *accel_handle, | ||
1272 | void *compass_handle, | ||
1273 | void *pressure_handle) | ||
1274 | { | ||
1275 | int result = ML_SUCCESS; | ||
1276 | int ret_result = ML_SUCCESS; | ||
1277 | |||
1278 | if (mldl_cfg->accel && mldl_cfg->accel->exit) { | ||
1279 | result = mldl_cfg->accel->exit(accel_handle, | ||
1280 | mldl_cfg->accel, | ||
1281 | &mldl_cfg->pdata->accel); | ||
1282 | if (ML_SUCCESS != result) | ||
1283 | MPL_LOGE("Accel exit failed %d\n", result); | ||
1284 | ret_result = result; | ||
1285 | } | ||
1286 | if (ML_SUCCESS == ret_result) | ||
1287 | ret_result = result; | ||
1288 | |||
1289 | if (mldl_cfg->compass && mldl_cfg->compass->exit) { | ||
1290 | result = mldl_cfg->compass->exit(compass_handle, | ||
1291 | mldl_cfg->compass, | ||
1292 | &mldl_cfg->pdata->compass); | ||
1293 | if (ML_SUCCESS != result) | ||
1294 | MPL_LOGE("Compass exit failed %d\n", result); | ||
1295 | } | ||
1296 | if (ML_SUCCESS == ret_result) | ||
1297 | ret_result = result; | ||
1298 | |||
1299 | if (mldl_cfg->pressure && mldl_cfg->pressure->exit) { | ||
1300 | result = mldl_cfg->pressure->exit(pressure_handle, | ||
1301 | mldl_cfg->pressure, | ||
1302 | &mldl_cfg->pdata->pressure); | ||
1303 | if (ML_SUCCESS != result) | ||
1304 | MPL_LOGE("Pressure exit failed %d\n", result); | ||
1305 | } | ||
1306 | if (ML_SUCCESS == ret_result) | ||
1307 | ret_result = result; | ||
1308 | |||
1309 | return ret_result; | ||
1310 | } | ||
1311 | |||
1312 | /** | ||
1313 | * @brief resume the MPU3050 device and all the other sensor | ||
1314 | * devices from their low power state. | ||
1315 | * | ||
1316 | * @param mldl_cfg | ||
1317 | * pointer to the configuration structure | ||
1318 | * @param gyro_handle | ||
1319 | * the main file handle to the MPU3050 device. | ||
1320 | * @param accel_handle | ||
1321 | * an handle to the accelerometer device, if sitting | ||
1322 | * onto a separate bus. Can match mlsl_handle if | ||
1323 | * the accelerometer device operates on the same | ||
1324 | * primary bus of MPU. | ||
1325 | * @param compass_handle | ||
1326 | * an handle to the compass device, if sitting | ||
1327 | * onto a separate bus. Can match mlsl_handle if | ||
1328 | * the compass device operates on the same | ||
1329 | * primary bus of MPU. | ||
1330 | * @param pressure_handle | ||
1331 | * an handle to the pressure sensor device, if sitting | ||
1332 | * onto a separate bus. Can match mlsl_handle if | ||
1333 | * the pressure sensor device operates on the same | ||
1334 | * primary bus of MPU. | ||
1335 | * @param resume_gyro | ||
1336 | * whether resuming the gyroscope device is | ||
1337 | * actually needed (if the device supports low power | ||
1338 | * mode of some sort). | ||
1339 | * @param resume_accel | ||
1340 | * whether resuming the accelerometer device is | ||
1341 | * actually needed (if the device supports low power | ||
1342 | * mode of some sort). | ||
1343 | * @param resume_compass | ||
1344 | * whether resuming the compass device is | ||
1345 | * actually needed (if the device supports low power | ||
1346 | * mode of some sort). | ||
1347 | * @param resume_pressure | ||
1348 | * whether resuming the pressure sensor device is | ||
1349 | * actually needed (if the device supports low power | ||
1350 | * mode of some sort). | ||
1351 | * @return ML_SUCCESS or a non-zero error code. | ||
1352 | */ | ||
1353 | int mpu3050_resume(struct mldl_cfg *mldl_cfg, | ||
1354 | void *gyro_handle, | ||
1355 | void *accel_handle, | ||
1356 | void *compass_handle, | ||
1357 | void *pressure_handle, | ||
1358 | bool resume_gyro, | ||
1359 | bool resume_accel, | ||
1360 | bool resume_compass, | ||
1361 | bool resume_pressure) | ||
1362 | { | ||
1363 | int result = ML_SUCCESS; | ||
1364 | |||
1365 | #ifdef CONFIG_MPU_SENSORS_DEBUG | ||
1366 | mpu_print_cfg(mldl_cfg); | ||
1367 | #endif | ||
1368 | |||
1369 | if (resume_accel && | ||
1370 | ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume))) | ||
1371 | return ML_ERROR_INVALID_PARAMETER; | ||
1372 | if (resume_compass && | ||
1373 | ((!mldl_cfg->compass) || (!mldl_cfg->compass->resume))) | ||
1374 | return ML_ERROR_INVALID_PARAMETER; | ||
1375 | if (resume_pressure && | ||
1376 | ((!mldl_cfg->pressure) || (!mldl_cfg->pressure->resume))) | ||
1377 | return ML_ERROR_INVALID_PARAMETER; | ||
1378 | |||
1379 | if (resume_gyro && mldl_cfg->gyro_is_suspended) { | ||
1380 | result = gyro_resume(mldl_cfg, gyro_handle); | ||
1381 | ERROR_CHECK(result); | ||
1382 | } | ||
1383 | |||
1384 | if (resume_accel && mldl_cfg->accel_is_suspended) { | ||
1385 | if (!mldl_cfg->gyro_is_suspended && | ||
1386 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
1387 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
1388 | ERROR_CHECK(result); | ||
1389 | } | ||
1390 | result = mldl_cfg->accel->resume(accel_handle, | ||
1391 | mldl_cfg->accel, | ||
1392 | &mldl_cfg->pdata->accel); | ||
1393 | ERROR_CHECK(result); | ||
1394 | mldl_cfg->accel_is_suspended = FALSE; | ||
1395 | } | ||
1396 | |||
1397 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->accel_is_suspended && | ||
1398 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
1399 | result = mpu_set_slave(mldl_cfg, | ||
1400 | gyro_handle, | ||
1401 | mldl_cfg->accel, | ||
1402 | &mldl_cfg->pdata->accel); | ||
1403 | ERROR_CHECK(result); | ||
1404 | } | ||
1405 | |||
1406 | if (resume_compass && mldl_cfg->compass_is_suspended) { | ||
1407 | if (!mldl_cfg->gyro_is_suspended && | ||
1408 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
1409 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
1410 | ERROR_CHECK(result); | ||
1411 | } | ||
1412 | result = mldl_cfg->compass->resume(compass_handle, | ||
1413 | mldl_cfg->compass, | ||
1414 | &mldl_cfg->pdata-> | ||
1415 | compass); | ||
1416 | ERROR_CHECK(result); | ||
1417 | mldl_cfg->compass_is_suspended = FALSE; | ||
1418 | } | ||
1419 | |||
1420 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->compass_is_suspended && | ||
1421 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
1422 | result = mpu_set_slave(mldl_cfg, | ||
1423 | gyro_handle, | ||
1424 | mldl_cfg->compass, | ||
1425 | &mldl_cfg->pdata->compass); | ||
1426 | ERROR_CHECK(result); | ||
1427 | } | ||
1428 | |||
1429 | if (resume_pressure && mldl_cfg->pressure_is_suspended) { | ||
1430 | if (!mldl_cfg->gyro_is_suspended && | ||
1431 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
1432 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE); | ||
1433 | ERROR_CHECK(result); | ||
1434 | } | ||
1435 | result = mldl_cfg->pressure->resume(pressure_handle, | ||
1436 | mldl_cfg->pressure, | ||
1437 | &mldl_cfg->pdata-> | ||
1438 | pressure); | ||
1439 | ERROR_CHECK(result); | ||
1440 | mldl_cfg->pressure_is_suspended = FALSE; | ||
1441 | } | ||
1442 | |||
1443 | if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->pressure_is_suspended && | ||
1444 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
1445 | result = mpu_set_slave(mldl_cfg, | ||
1446 | gyro_handle, | ||
1447 | mldl_cfg->pressure, | ||
1448 | &mldl_cfg->pdata->pressure); | ||
1449 | ERROR_CHECK(result); | ||
1450 | } | ||
1451 | |||
1452 | /* Now start */ | ||
1453 | if (resume_gyro) { | ||
1454 | result = dmp_start(mldl_cfg, gyro_handle); | ||
1455 | ERROR_CHECK(result); | ||
1456 | } | ||
1457 | |||
1458 | return result; | ||
1459 | } | ||
1460 | |||
1461 | /** | ||
1462 | * @brief suspend the MPU3050 device and all the other sensor | ||
1463 | * devices into their low power state. | ||
1464 | * @param gyro_handle | ||
1465 | * the main file handle to the MPU3050 device. | ||
1466 | * @param accel_handle | ||
1467 | * an handle to the accelerometer device, if sitting | ||
1468 | * onto a separate bus. Can match gyro_handle if | ||
1469 | * the accelerometer device operates on the same | ||
1470 | * primary bus of MPU. | ||
1471 | * @param compass_handle | ||
1472 | * an handle to the compass device, if sitting | ||
1473 | * onto a separate bus. Can match gyro_handle if | ||
1474 | * the compass device operates on the same | ||
1475 | * primary bus of MPU. | ||
1476 | * @param pressure_handle | ||
1477 | * an handle to the pressure sensor device, if sitting | ||
1478 | * onto a separate bus. Can match gyro_handle if | ||
1479 | * the pressure sensor device operates on the same | ||
1480 | * primary bus of MPU. | ||
1481 | * @param accel | ||
1482 | * whether suspending the accelerometer device is | ||
1483 | * actually needed (if the device supports low power | ||
1484 | * mode of some sort). | ||
1485 | * @param compass | ||
1486 | * whether suspending the compass device is | ||
1487 | * actually needed (if the device supports low power | ||
1488 | * mode of some sort). | ||
1489 | * @param pressure | ||
1490 | * whether suspending the pressure sensor device is | ||
1491 | * actually needed (if the device supports low power | ||
1492 | * mode of some sort). | ||
1493 | * @return ML_SUCCESS or a non-zero error code. | ||
1494 | */ | ||
1495 | int mpu3050_suspend(struct mldl_cfg *mldl_cfg, | ||
1496 | void *gyro_handle, | ||
1497 | void *accel_handle, | ||
1498 | void *compass_handle, | ||
1499 | void *pressure_handle, | ||
1500 | bool suspend_gyro, | ||
1501 | bool suspend_accel, | ||
1502 | bool suspend_compass, | ||
1503 | bool suspend_pressure) | ||
1504 | { | ||
1505 | int result = ML_SUCCESS; | ||
1506 | |||
1507 | if (suspend_gyro && !mldl_cfg->gyro_is_suspended) { | ||
1508 | #ifdef M_HW | ||
1509 | return ML_SUCCESS; | ||
1510 | /* This puts the bus into bypass mode */ | ||
1511 | result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1); | ||
1512 | ERROR_CHECK(result); | ||
1513 | result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP); | ||
1514 | #else | ||
1515 | result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, | ||
1516 | 0, SLEEP, 0, 0, 0); | ||
1517 | #endif | ||
1518 | ERROR_CHECK(result); | ||
1519 | } | ||
1520 | |||
1521 | if (!mldl_cfg->accel_is_suspended && suspend_accel && | ||
1522 | mldl_cfg->accel && mldl_cfg->accel->suspend) { | ||
1523 | if (!mldl_cfg->gyro_is_suspended && | ||
1524 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) { | ||
1525 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
1526 | NULL, NULL); | ||
1527 | ERROR_CHECK(result); | ||
1528 | } | ||
1529 | result = mldl_cfg->accel->suspend(accel_handle, | ||
1530 | mldl_cfg->accel, | ||
1531 | &mldl_cfg->pdata->accel); | ||
1532 | ERROR_CHECK(result); | ||
1533 | mldl_cfg->accel_is_suspended = TRUE; | ||
1534 | } | ||
1535 | |||
1536 | if (!mldl_cfg->compass_is_suspended && suspend_compass && | ||
1537 | mldl_cfg->compass && mldl_cfg->compass->suspend) { | ||
1538 | if (!mldl_cfg->gyro_is_suspended && | ||
1539 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) { | ||
1540 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
1541 | NULL, NULL); | ||
1542 | ERROR_CHECK(result); | ||
1543 | } | ||
1544 | result = mldl_cfg->compass->suspend(compass_handle, | ||
1545 | mldl_cfg->compass, | ||
1546 | &mldl_cfg-> | ||
1547 | pdata->compass); | ||
1548 | ERROR_CHECK(result); | ||
1549 | mldl_cfg->compass_is_suspended = TRUE; | ||
1550 | } | ||
1551 | |||
1552 | if (!mldl_cfg->pressure_is_suspended && suspend_pressure && | ||
1553 | mldl_cfg->pressure && mldl_cfg->pressure->suspend) { | ||
1554 | if (!mldl_cfg->gyro_is_suspended && | ||
1555 | EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) { | ||
1556 | result = mpu_set_slave(mldl_cfg, gyro_handle, | ||
1557 | NULL, NULL); | ||
1558 | ERROR_CHECK(result); | ||
1559 | } | ||
1560 | result = mldl_cfg->pressure->suspend(pressure_handle, | ||
1561 | mldl_cfg->pressure, | ||
1562 | &mldl_cfg-> | ||
1563 | pdata->pressure); | ||
1564 | ERROR_CHECK(result); | ||
1565 | mldl_cfg->pressure_is_suspended = TRUE; | ||
1566 | } | ||
1567 | return result; | ||
1568 | } | ||
1569 | |||
1570 | |||
1571 | /** | ||
1572 | * @brief read raw sensor data from the accelerometer device | ||
1573 | * in use. | ||
1574 | * @param mldl_cfg | ||
1575 | * A pointer to the struct mldl_cfg data structure. | ||
1576 | * @param accel_handle | ||
1577 | * The handle to the device the accelerometer is connected to. | ||
1578 | * @param data | ||
1579 | * a buffer to store the raw sensor data. | ||
1580 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
1581 | */ | ||
1582 | int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, | ||
1583 | void *accel_handle, unsigned char *data) | ||
1584 | { | ||
1585 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->read) | ||
1586 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) | ||
1587 | && (!mldl_cfg->gyro_is_bypassed)) | ||
1588 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
1589 | else | ||
1590 | return mldl_cfg->accel->read(accel_handle, | ||
1591 | mldl_cfg->accel, | ||
1592 | &mldl_cfg->pdata->accel, | ||
1593 | data); | ||
1594 | else | ||
1595 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1596 | } | ||
1597 | |||
1598 | /** | ||
1599 | * @brief read raw sensor data from the compass device | ||
1600 | * in use. | ||
1601 | * @param mldl_cfg | ||
1602 | * A pointer to the struct mldl_cfg data structure. | ||
1603 | * @param compass_handle | ||
1604 | * The handle to the device the compass is connected to. | ||
1605 | * @param data | ||
1606 | * a buffer to store the raw sensor data. | ||
1607 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
1608 | */ | ||
1609 | int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, | ||
1610 | void *compass_handle, unsigned char *data) | ||
1611 | { | ||
1612 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->read) | ||
1613 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) | ||
1614 | && (!mldl_cfg->gyro_is_bypassed)) | ||
1615 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
1616 | else | ||
1617 | return mldl_cfg->compass->read(compass_handle, | ||
1618 | mldl_cfg->compass, | ||
1619 | &mldl_cfg->pdata->compass, | ||
1620 | data); | ||
1621 | else | ||
1622 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1623 | } | ||
1624 | |||
1625 | /** | ||
1626 | * @brief read raw sensor data from the pressure device | ||
1627 | * in use. | ||
1628 | * @param mldl_cfg | ||
1629 | * A pointer to the struct mldl_cfg data structure. | ||
1630 | * @param pressure_handle | ||
1631 | * The handle to the device the pressure sensor is connected to. | ||
1632 | * @param data | ||
1633 | * a buffer to store the raw sensor data. | ||
1634 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
1635 | */ | ||
1636 | int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, | ||
1637 | void *pressure_handle, unsigned char *data) | ||
1638 | { | ||
1639 | if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->read) | ||
1640 | if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) | ||
1641 | && (!mldl_cfg->gyro_is_bypassed)) | ||
1642 | return ML_ERROR_FEATURE_NOT_ENABLED; | ||
1643 | else | ||
1644 | return mldl_cfg->pressure->read( | ||
1645 | pressure_handle, | ||
1646 | mldl_cfg->pressure, | ||
1647 | &mldl_cfg->pdata->pressure, | ||
1648 | data); | ||
1649 | else | ||
1650 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1651 | } | ||
1652 | |||
1653 | int mpu3050_config_accel(struct mldl_cfg *mldl_cfg, | ||
1654 | void *accel_handle, | ||
1655 | struct ext_slave_config *data) | ||
1656 | { | ||
1657 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->config) | ||
1658 | return mldl_cfg->accel->config(accel_handle, | ||
1659 | mldl_cfg->accel, | ||
1660 | &mldl_cfg->pdata->accel, | ||
1661 | data); | ||
1662 | else | ||
1663 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1664 | |||
1665 | } | ||
1666 | |||
1667 | int mpu3050_config_compass(struct mldl_cfg *mldl_cfg, | ||
1668 | void *compass_handle, | ||
1669 | struct ext_slave_config *data) | ||
1670 | { | ||
1671 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->config) | ||
1672 | return mldl_cfg->compass->config(compass_handle, | ||
1673 | mldl_cfg->compass, | ||
1674 | &mldl_cfg->pdata->compass, | ||
1675 | data); | ||
1676 | else | ||
1677 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1678 | |||
1679 | } | ||
1680 | |||
1681 | int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg, | ||
1682 | void *pressure_handle, | ||
1683 | struct ext_slave_config *data) | ||
1684 | { | ||
1685 | if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->config) | ||
1686 | return mldl_cfg->pressure->config(pressure_handle, | ||
1687 | mldl_cfg->pressure, | ||
1688 | &mldl_cfg->pdata->pressure, | ||
1689 | data); | ||
1690 | else | ||
1691 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1692 | } | ||
1693 | |||
1694 | int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg, | ||
1695 | void *accel_handle, | ||
1696 | struct ext_slave_config *data) | ||
1697 | { | ||
1698 | if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->get_config) | ||
1699 | return mldl_cfg->accel->get_config(accel_handle, | ||
1700 | mldl_cfg->accel, | ||
1701 | &mldl_cfg->pdata->accel, | ||
1702 | data); | ||
1703 | else | ||
1704 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1705 | |||
1706 | } | ||
1707 | |||
1708 | int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg, | ||
1709 | void *compass_handle, | ||
1710 | struct ext_slave_config *data) | ||
1711 | { | ||
1712 | if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->get_config) | ||
1713 | return mldl_cfg->compass->get_config(compass_handle, | ||
1714 | mldl_cfg->compass, | ||
1715 | &mldl_cfg->pdata->compass, | ||
1716 | data); | ||
1717 | else | ||
1718 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1719 | |||
1720 | } | ||
1721 | |||
1722 | int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg, | ||
1723 | void *pressure_handle, | ||
1724 | struct ext_slave_config *data) | ||
1725 | { | ||
1726 | if (NULL != mldl_cfg->pressure && | ||
1727 | NULL != mldl_cfg->pressure->get_config) | ||
1728 | return mldl_cfg->pressure->get_config(pressure_handle, | ||
1729 | mldl_cfg->pressure, | ||
1730 | &mldl_cfg->pdata->pressure, | ||
1731 | data); | ||
1732 | else | ||
1733 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
1734 | } | ||
1735 | |||
1736 | |||
1737 | /** | ||
1738 | *@} | ||
1739 | */ | ||
diff --git a/drivers/misc/mpu3050/mldl_cfg.h b/drivers/misc/mpu3050/mldl_cfg.h new file mode 100644 index 00000000000..ad6a211c5d8 --- /dev/null +++ b/drivers/misc/mpu3050/mldl_cfg.h | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 | /* ------------------ */ | ||
32 | /* - Include Files. - */ | ||
33 | /* ------------------ */ | ||
34 | |||
35 | #include "mlsl.h" | ||
36 | #include "mpu.h" | ||
37 | |||
38 | /* --------------------- */ | ||
39 | /* - Defines. - */ | ||
40 | /* --------------------- */ | ||
41 | |||
42 | /*************************************************************************/ | ||
43 | /* Sensors */ | ||
44 | /*************************************************************************/ | ||
45 | |||
46 | #define ML_X_GYRO (0x0001) | ||
47 | #define ML_Y_GYRO (0x0002) | ||
48 | #define ML_Z_GYRO (0x0004) | ||
49 | #define ML_DMP_PROCESSOR (0x0008) | ||
50 | |||
51 | #define ML_X_ACCEL (0x0010) | ||
52 | #define ML_Y_ACCEL (0x0020) | ||
53 | #define ML_Z_ACCEL (0x0040) | ||
54 | |||
55 | #define ML_X_COMPASS (0x0080) | ||
56 | #define ML_Y_COMPASS (0x0100) | ||
57 | #define ML_Z_COMPASS (0x0200) | ||
58 | |||
59 | #define ML_X_PRESSURE (0x0300) | ||
60 | #define ML_Y_PRESSURE (0x0800) | ||
61 | #define ML_Z_PRESSURE (0x1000) | ||
62 | |||
63 | #define ML_TEMPERATURE (0x2000) | ||
64 | #define ML_TIME (0x4000) | ||
65 | |||
66 | #define ML_THREE_AXIS_GYRO (0x000F) | ||
67 | #define ML_THREE_AXIS_ACCEL (0x0070) | ||
68 | #define ML_THREE_AXIS_COMPASS (0x0380) | ||
69 | #define ML_THREE_AXIS_PRESSURE (0x1C00) | ||
70 | |||
71 | #define ML_FIVE_AXIS (0x007B) | ||
72 | #define ML_SIX_AXIS_GYRO_ACCEL (0x007F) | ||
73 | #define ML_SIX_AXIS_ACCEL_COMPASS (0x03F0) | ||
74 | #define ML_NINE_AXIS (0x03FF) | ||
75 | #define ML_ALL_SENSORS (0x7FFF) | ||
76 | |||
77 | #define SAMPLING_RATE_HZ(mldl_cfg) \ | ||
78 | ((((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \ | ||
79 | ? (8000) \ | ||
80 | : (1000)) \ | ||
81 | / ((mldl_cfg)->divider + 1)) | ||
82 | |||
83 | #define SAMPLING_PERIOD_US(mldl_cfg) \ | ||
84 | ((1000000L * ((mldl_cfg)->divider + 1)) / \ | ||
85 | (((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \ | ||
86 | ? (8000) \ | ||
87 | : (1000))) | ||
88 | /* --------------------- */ | ||
89 | /* - Variables. - */ | ||
90 | /* --------------------- */ | ||
91 | |||
92 | /* Platform data for the MPU */ | ||
93 | struct mldl_cfg { | ||
94 | /* MPU related configuration */ | ||
95 | unsigned long requested_sensors; | ||
96 | unsigned char ignore_system_suspend; | ||
97 | unsigned char addr; | ||
98 | unsigned char int_config; | ||
99 | unsigned char ext_sync; | ||
100 | unsigned char full_scale; | ||
101 | unsigned char lpf; | ||
102 | unsigned char clk_src; | ||
103 | unsigned char divider; | ||
104 | unsigned char dmp_enable; | ||
105 | unsigned char fifo_enable; | ||
106 | unsigned char dmp_cfg1; | ||
107 | unsigned char dmp_cfg2; | ||
108 | unsigned char gyro_power; | ||
109 | unsigned char offset_tc[MPU_NUM_AXES]; | ||
110 | unsigned short offset[MPU_NUM_AXES]; | ||
111 | unsigned char ram[MPU_MEM_NUM_RAM_BANKS][MPU_MEM_BANK_SIZE]; | ||
112 | |||
113 | /* MPU Related stored status and info */ | ||
114 | unsigned char silicon_revision; | ||
115 | unsigned char product_id; | ||
116 | unsigned short trim; | ||
117 | |||
118 | /* Driver/Kernel related state information */ | ||
119 | int gyro_is_bypassed; | ||
120 | int dmp_is_running; | ||
121 | int gyro_is_suspended; | ||
122 | int accel_is_suspended; | ||
123 | int compass_is_suspended; | ||
124 | int pressure_is_suspended; | ||
125 | int gyro_needs_reset; | ||
126 | |||
127 | /* Slave related information */ | ||
128 | struct ext_slave_descr *accel; | ||
129 | struct ext_slave_descr *compass; | ||
130 | struct ext_slave_descr *pressure; | ||
131 | |||
132 | /* Platform Data */ | ||
133 | struct mpu3050_platform_data *pdata; | ||
134 | }; | ||
135 | |||
136 | |||
137 | int mpu3050_open(struct mldl_cfg *mldl_cfg, | ||
138 | void *mlsl_handle, | ||
139 | void *accel_handle, | ||
140 | void *compass_handle, | ||
141 | void *pressure_handle); | ||
142 | int mpu3050_close(struct mldl_cfg *mldl_cfg, | ||
143 | void *mlsl_handle, | ||
144 | void *accel_handle, | ||
145 | void *compass_handle, | ||
146 | void *pressure_handle); | ||
147 | int mpu3050_resume(struct mldl_cfg *mldl_cfg, | ||
148 | void *gyro_handle, | ||
149 | void *accel_handle, | ||
150 | void *compass_handle, | ||
151 | void *pressure_handle, | ||
152 | bool resume_gyro, | ||
153 | bool resume_accel, | ||
154 | bool resume_compass, | ||
155 | bool resume_pressure); | ||
156 | int mpu3050_suspend(struct mldl_cfg *mldl_cfg, | ||
157 | void *gyro_handle, | ||
158 | void *accel_handle, | ||
159 | void *compass_handle, | ||
160 | void *pressure_handle, | ||
161 | bool suspend_gyro, | ||
162 | bool suspend_accel, | ||
163 | bool suspend_compass, | ||
164 | bool suspend_pressure); | ||
165 | int mpu3050_read_accel(struct mldl_cfg *mldl_cfg, | ||
166 | void *accel_handle, | ||
167 | unsigned char *data); | ||
168 | int mpu3050_read_compass(struct mldl_cfg *mldl_cfg, | ||
169 | void *compass_handle, | ||
170 | unsigned char *data); | ||
171 | int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, void *mlsl_handle, | ||
172 | unsigned char *data); | ||
173 | |||
174 | int mpu3050_config_accel(struct mldl_cfg *mldl_cfg, | ||
175 | void *accel_handle, | ||
176 | struct ext_slave_config *data); | ||
177 | int mpu3050_config_compass(struct mldl_cfg *mldl_cfg, | ||
178 | void *compass_handle, | ||
179 | struct ext_slave_config *data); | ||
180 | int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg, | ||
181 | void *pressure_handle, | ||
182 | struct ext_slave_config *data); | ||
183 | |||
184 | int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg, | ||
185 | void *accel_handle, | ||
186 | struct ext_slave_config *data); | ||
187 | int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg, | ||
188 | void *compass_handle, | ||
189 | struct ext_slave_config *data); | ||
190 | int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg, | ||
191 | void *pressure_handle, | ||
192 | struct ext_slave_config *data); | ||
193 | |||
194 | |||
195 | #endif /* __MLDL_CFG_H__ */ | ||
196 | |||
197 | /** | ||
198 | *@} | ||
199 | */ | ||
diff --git a/drivers/misc/mpu3050/mlos-kernel.c b/drivers/misc/mpu3050/mlos-kernel.c new file mode 100644 index 00000000000..ced9ba4f6f3 --- /dev/null +++ b/drivers/misc/mpu3050/mlos-kernel.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 | * @defgroup | ||
21 | * @brief | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mlos-kernel.c | ||
25 | * @brief | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include "mlos.h" | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/slab.h> | ||
33 | |||
34 | void *MLOSMalloc(unsigned int numBytes) | ||
35 | { | ||
36 | return kmalloc(numBytes, GFP_KERNEL); | ||
37 | } | ||
38 | |||
39 | tMLError MLOSFree(void *ptr) | ||
40 | { | ||
41 | kfree(ptr); | ||
42 | return ML_SUCCESS; | ||
43 | } | ||
44 | |||
45 | tMLError MLOSCreateMutex(HANDLE *mutex) | ||
46 | { | ||
47 | /* @todo implement if needed */ | ||
48 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
49 | } | ||
50 | |||
51 | tMLError MLOSLockMutex(HANDLE mutex) | ||
52 | { | ||
53 | /* @todo implement if needed */ | ||
54 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
55 | } | ||
56 | |||
57 | tMLError MLOSUnlockMutex(HANDLE mutex) | ||
58 | { | ||
59 | /* @todo implement if needed */ | ||
60 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
61 | } | ||
62 | |||
63 | tMLError MLOSDestroyMutex(HANDLE handle) | ||
64 | { | ||
65 | /* @todo implement if needed */ | ||
66 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
67 | } | ||
68 | |||
69 | FILE *MLOSFOpen(char *filename) | ||
70 | { | ||
71 | /* @todo implement if needed */ | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | void MLOSFClose(FILE *fp) | ||
76 | { | ||
77 | /* @todo implement if needed */ | ||
78 | } | ||
79 | |||
80 | void MLOSSleep(int mSecs) | ||
81 | { | ||
82 | msleep(mSecs); | ||
83 | } | ||
84 | |||
85 | unsigned long MLOSGetTickCount(void) | ||
86 | { | ||
87 | /* @todo implement if needed */ | ||
88 | return ML_ERROR_FEATURE_NOT_IMPLEMENTED; | ||
89 | } | ||
diff --git a/drivers/misc/mpu3050/mlos.h b/drivers/misc/mpu3050/mlos.h new file mode 100644 index 00000000000..4ebb86c9fa5 --- /dev/null +++ b/drivers/misc/mpu3050/mlos.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 _MLOS_H | ||
21 | #define _MLOS_H | ||
22 | |||
23 | #ifndef __KERNEL__ | ||
24 | #include <stdio.h> | ||
25 | #endif | ||
26 | |||
27 | #include "mltypes.h" | ||
28 | |||
29 | #ifdef __cplusplus | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | /* ------------ */ | ||
34 | /* - Defines. - */ | ||
35 | /* ------------ */ | ||
36 | |||
37 | /* - MLOSCreateFile defines. - */ | ||
38 | |||
39 | #define MLOS_GENERIC_READ ((unsigned int)0x80000000) | ||
40 | #define MLOS_GENERIC_WRITE ((unsigned int)0x40000000) | ||
41 | #define MLOS_FILE_SHARE_READ ((unsigned int)0x00000001) | ||
42 | #define MLOS_FILE_SHARE_WRITE ((unsigned int)0x00000002) | ||
43 | #define MLOS_OPEN_EXISTING ((unsigned int)0x00000003) | ||
44 | |||
45 | /* ---------- */ | ||
46 | /* - Enums. - */ | ||
47 | /* ---------- */ | ||
48 | |||
49 | /* --------------- */ | ||
50 | /* - Structures. - */ | ||
51 | /* --------------- */ | ||
52 | |||
53 | /* --------------------- */ | ||
54 | /* - Function p-types. - */ | ||
55 | /* --------------------- */ | ||
56 | |||
57 | void *MLOSMalloc(unsigned int numBytes); | ||
58 | tMLError MLOSFree(void *ptr); | ||
59 | tMLError MLOSCreateMutex(HANDLE *mutex); | ||
60 | tMLError MLOSLockMutex(HANDLE mutex); | ||
61 | tMLError MLOSUnlockMutex(HANDLE mutex); | ||
62 | FILE *MLOSFOpen(char *filename); | ||
63 | void MLOSFClose(FILE *fp); | ||
64 | |||
65 | tMLError MLOSDestroyMutex(HANDLE handle); | ||
66 | |||
67 | void MLOSSleep(int mSecs); | ||
68 | unsigned long MLOSGetTickCount(void); | ||
69 | |||
70 | #ifdef __cplusplus | ||
71 | } | ||
72 | #endif | ||
73 | #endif /* _MLOS_H */ | ||
diff --git a/drivers/misc/mpu3050/mlsl-kernel.c b/drivers/misc/mpu3050/mlsl-kernel.c new file mode 100644 index 00000000000..cb1605131cb --- /dev/null +++ b/drivers/misc/mpu3050/mlsl-kernel.c | |||
@@ -0,0 +1,331 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 "mpu-i2c.h" | ||
22 | |||
23 | /* ------------ */ | ||
24 | /* - Defines. - */ | ||
25 | /* ------------ */ | ||
26 | |||
27 | /* ---------------------- */ | ||
28 | /* - Types definitions. - */ | ||
29 | /* ---------------------- */ | ||
30 | |||
31 | /* --------------------- */ | ||
32 | /* - Function p-types. - */ | ||
33 | /* --------------------- */ | ||
34 | |||
35 | /** | ||
36 | * @brief used to open the I2C or SPI serial port. | ||
37 | * This port is used to send and receive data to the MPU device. | ||
38 | * @param portNum | ||
39 | * The COM port number associated with the device in use. | ||
40 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
41 | */ | ||
42 | tMLError MLSLSerialOpen(char const *port, void **sl_handle) | ||
43 | { | ||
44 | return ML_SUCCESS; | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @brief used to reset any buffering the driver may be doing | ||
49 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
50 | */ | ||
51 | tMLError MLSLSerialReset(void *sl_handle) | ||
52 | { | ||
53 | return ML_SUCCESS; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * @brief used to close the I2C or SPI serial port. | ||
58 | * This port is used to send and receive data to the MPU device. | ||
59 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
60 | */ | ||
61 | tMLError MLSLSerialClose(void *sl_handle) | ||
62 | { | ||
63 | return ML_SUCCESS; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @brief used to read a single byte of data. | ||
68 | * This should be sent by I2C or SPI. | ||
69 | * | ||
70 | * @param slaveAddr I2C slave address of device. | ||
71 | * @param registerAddr Register address to read. | ||
72 | * @param data Single byte of data to read. | ||
73 | * | ||
74 | * @return ML_SUCCESS if the command is successful, an error code otherwise. | ||
75 | */ | ||
76 | tMLError MLSLSerialWriteSingle(void *sl_handle, | ||
77 | unsigned char slaveAddr, | ||
78 | unsigned char registerAddr, | ||
79 | unsigned char data) | ||
80 | { | ||
81 | return sensor_i2c_write_register((struct i2c_adapter *) sl_handle, | ||
82 | slaveAddr, registerAddr, data); | ||
83 | } | ||
84 | |||
85 | |||
86 | /** | ||
87 | * @brief used to write multiple bytes of data from registers. | ||
88 | * This should be sent by I2C. | ||
89 | * | ||
90 | * @param slaveAddr I2C slave address of device. | ||
91 | * @param registerAddr Register address to write. | ||
92 | * @param length Length of burst of data. | ||
93 | * @param data Pointer to block of data. | ||
94 | * | ||
95 | * @return ML_SUCCESS if successful, a non-zero error code otherwise. | ||
96 | */ | ||
97 | tMLError MLSLSerialWrite(void *sl_handle, | ||
98 | unsigned char slaveAddr, | ||
99 | unsigned short length, | ||
100 | unsigned char const *data) | ||
101 | { | ||
102 | tMLError result; | ||
103 | const unsigned short dataLength = length - 1; | ||
104 | const unsigned char startRegAddr = data[0]; | ||
105 | unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
106 | unsigned short bytesWritten = 0; | ||
107 | |||
108 | while (bytesWritten < dataLength) { | ||
109 | unsigned short thisLen = min(SERIAL_MAX_TRANSFER_SIZE, | ||
110 | dataLength - bytesWritten); | ||
111 | if (bytesWritten == 0) { | ||
112 | result = sensor_i2c_write((struct i2c_adapter *) | ||
113 | sl_handle, slaveAddr, | ||
114 | 1 + thisLen, data); | ||
115 | } else { | ||
116 | /* manually increment register addr between chunks */ | ||
117 | i2cWrite[0] = startRegAddr + bytesWritten; | ||
118 | memcpy(&i2cWrite[1], &data[1 + bytesWritten], | ||
119 | thisLen); | ||
120 | result = sensor_i2c_write((struct i2c_adapter *) | ||
121 | sl_handle, slaveAddr, | ||
122 | 1 + thisLen, i2cWrite); | ||
123 | } | ||
124 | if (ML_SUCCESS != result) | ||
125 | return result; | ||
126 | bytesWritten += thisLen; | ||
127 | } | ||
128 | return ML_SUCCESS; | ||
129 | } | ||
130 | |||
131 | |||
132 | /** | ||
133 | * @brief used to read multiple bytes of data from registers. | ||
134 | * This should be sent by I2C. | ||
135 | * | ||
136 | * @param slaveAddr I2C slave address of device. | ||
137 | * @param registerAddr Register address to read. | ||
138 | * @param length Length of burst of data. | ||
139 | * @param data Pointer to block of data. | ||
140 | * | ||
141 | * @return Zero if successful; an error code otherwise | ||
142 | */ | ||
143 | tMLError MLSLSerialRead(void *sl_handle, | ||
144 | unsigned char slaveAddr, | ||
145 | unsigned char registerAddr, | ||
146 | unsigned short length, unsigned char *data) | ||
147 | { | ||
148 | tMLError result; | ||
149 | unsigned short bytesRead = 0; | ||
150 | |||
151 | if (registerAddr == MPUREG_FIFO_R_W | ||
152 | || registerAddr == MPUREG_MEM_R_W) { | ||
153 | return ML_ERROR_INVALID_PARAMETER; | ||
154 | } | ||
155 | while (bytesRead < length) { | ||
156 | unsigned short thisLen = | ||
157 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
158 | result = | ||
159 | sensor_i2c_read((struct i2c_adapter *) sl_handle, | ||
160 | slaveAddr, registerAddr + bytesRead, | ||
161 | thisLen, &data[bytesRead]); | ||
162 | if (ML_SUCCESS != result) | ||
163 | return result; | ||
164 | bytesRead += thisLen; | ||
165 | } | ||
166 | return ML_SUCCESS; | ||
167 | } | ||
168 | |||
169 | |||
170 | /** | ||
171 | * @brief used to write multiple bytes of data to the memory. | ||
172 | * This should be sent by I2C. | ||
173 | * | ||
174 | * @param slaveAddr I2C slave address of device. | ||
175 | * @param memAddr The location in the memory to write to. | ||
176 | * @param length Length of burst data. | ||
177 | * @param data Pointer to block of data. | ||
178 | * | ||
179 | * @return Zero if successful; an error code otherwise | ||
180 | */ | ||
181 | tMLError MLSLSerialWriteMem(void *sl_handle, | ||
182 | unsigned char slaveAddr, | ||
183 | unsigned short memAddr, | ||
184 | unsigned short length, | ||
185 | unsigned char const *data) | ||
186 | { | ||
187 | tMLError result; | ||
188 | unsigned short bytesWritten = 0; | ||
189 | |||
190 | if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
191 | pr_err("memory read length (%d B) extends beyond its" | ||
192 | " limits (%d) if started at location %d\n", length, | ||
193 | MPU_MEM_BANK_SIZE, memAddr & 0xFF); | ||
194 | return ML_ERROR_INVALID_PARAMETER; | ||
195 | } | ||
196 | while (bytesWritten < length) { | ||
197 | unsigned short thisLen = | ||
198 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten); | ||
199 | result = | ||
200 | mpu_memory_write((struct i2c_adapter *) sl_handle, | ||
201 | slaveAddr, memAddr + bytesWritten, | ||
202 | thisLen, &data[bytesWritten]); | ||
203 | if (ML_SUCCESS != result) | ||
204 | return result; | ||
205 | bytesWritten += thisLen; | ||
206 | } | ||
207 | return ML_SUCCESS; | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * @brief used to read multiple bytes of data from the memory. | ||
213 | * This should be sent by I2C. | ||
214 | * | ||
215 | * @param slaveAddr I2C slave address of device. | ||
216 | * @param memAddr The location in the memory to read from. | ||
217 | * @param length Length of burst data. | ||
218 | * @param data Pointer to block of data. | ||
219 | * | ||
220 | * @return Zero if successful; an error code otherwise | ||
221 | */ | ||
222 | tMLError MLSLSerialReadMem(void *sl_handle, | ||
223 | unsigned char slaveAddr, | ||
224 | unsigned short memAddr, | ||
225 | unsigned short length, unsigned char *data) | ||
226 | { | ||
227 | tMLError result; | ||
228 | unsigned short bytesRead = 0; | ||
229 | |||
230 | if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) { | ||
231 | printk | ||
232 | ("memory read length (%d B) extends beyond its limits (%d) " | ||
233 | "if started at location %d\n", length, | ||
234 | MPU_MEM_BANK_SIZE, memAddr & 0xFF); | ||
235 | return ML_ERROR_INVALID_PARAMETER; | ||
236 | } | ||
237 | while (bytesRead < length) { | ||
238 | unsigned short thisLen = | ||
239 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
240 | result = | ||
241 | mpu_memory_read((struct i2c_adapter *) sl_handle, | ||
242 | slaveAddr, memAddr + bytesRead, | ||
243 | thisLen, &data[bytesRead]); | ||
244 | if (ML_SUCCESS != result) | ||
245 | return result; | ||
246 | bytesRead += thisLen; | ||
247 | } | ||
248 | return ML_SUCCESS; | ||
249 | } | ||
250 | |||
251 | |||
252 | /** | ||
253 | * @brief used to write multiple bytes of data to the fifo. | ||
254 | * This should be sent by I2C. | ||
255 | * | ||
256 | * @param slaveAddr I2C slave address of device. | ||
257 | * @param length Length of burst of data. | ||
258 | * @param data Pointer to block of data. | ||
259 | * | ||
260 | * @return Zero if successful; an error code otherwise | ||
261 | */ | ||
262 | tMLError MLSLSerialWriteFifo(void *sl_handle, | ||
263 | unsigned char slaveAddr, | ||
264 | unsigned short length, | ||
265 | unsigned char const *data) | ||
266 | { | ||
267 | tMLError result; | ||
268 | unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1]; | ||
269 | unsigned short bytesWritten = 0; | ||
270 | |||
271 | if (length > FIFO_HW_SIZE) { | ||
272 | printk(KERN_ERR | ||
273 | "maximum fifo write length is %d\n", FIFO_HW_SIZE); | ||
274 | return ML_ERROR_INVALID_PARAMETER; | ||
275 | } | ||
276 | while (bytesWritten < length) { | ||
277 | unsigned short thisLen = | ||
278 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten); | ||
279 | i2cWrite[0] = MPUREG_FIFO_R_W; | ||
280 | memcpy(&i2cWrite[1], &data[bytesWritten], thisLen); | ||
281 | result = sensor_i2c_write((struct i2c_adapter *) sl_handle, | ||
282 | slaveAddr, thisLen + 1, | ||
283 | i2cWrite); | ||
284 | if (ML_SUCCESS != result) | ||
285 | return result; | ||
286 | bytesWritten += thisLen; | ||
287 | } | ||
288 | return ML_SUCCESS; | ||
289 | } | ||
290 | |||
291 | |||
292 | /** | ||
293 | * @brief used to read multiple bytes of data from the fifo. | ||
294 | * This should be sent by I2C. | ||
295 | * | ||
296 | * @param slaveAddr I2C slave address of device. | ||
297 | * @param length Length of burst of data. | ||
298 | * @param data Pointer to block of data. | ||
299 | * | ||
300 | * @return Zero if successful; an error code otherwise | ||
301 | */ | ||
302 | tMLError MLSLSerialReadFifo(void *sl_handle, | ||
303 | unsigned char slaveAddr, | ||
304 | unsigned short length, unsigned char *data) | ||
305 | { | ||
306 | tMLError result; | ||
307 | unsigned short bytesRead = 0; | ||
308 | |||
309 | if (length > FIFO_HW_SIZE) { | ||
310 | printk(KERN_ERR | ||
311 | "maximum fifo read length is %d\n", FIFO_HW_SIZE); | ||
312 | return ML_ERROR_INVALID_PARAMETER; | ||
313 | } | ||
314 | while (bytesRead < length) { | ||
315 | unsigned short thisLen = | ||
316 | min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead); | ||
317 | result = | ||
318 | sensor_i2c_read((struct i2c_adapter *) sl_handle, | ||
319 | slaveAddr, MPUREG_FIFO_R_W, thisLen, | ||
320 | &data[bytesRead]); | ||
321 | if (ML_SUCCESS != result) | ||
322 | return result; | ||
323 | bytesRead += thisLen; | ||
324 | } | ||
325 | |||
326 | return ML_SUCCESS; | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * @} | ||
331 | */ | ||
diff --git a/drivers/misc/mpu3050/mlsl.h b/drivers/misc/mpu3050/mlsl.h new file mode 100644 index 00000000000..76f69c77ba9 --- /dev/null +++ b/drivers/misc/mpu3050/mlsl.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 __MSSL_H__ | ||
21 | #define __MSSL_H__ | ||
22 | |||
23 | #include "mltypes.h" | ||
24 | #include "mpu.h" | ||
25 | |||
26 | #ifdef __cplusplus | ||
27 | extern "C" { | ||
28 | #endif | ||
29 | |||
30 | /* ------------ */ | ||
31 | /* - Defines. - */ | ||
32 | /* ------------ */ | ||
33 | |||
34 | /* | ||
35 | * NOTE : to properly support Yamaha compass reads, | ||
36 | * the max transfer size should be at least 9 B. | ||
37 | * Length in bytes, typically a power of 2 >= 2 | ||
38 | */ | ||
39 | #define SERIAL_MAX_TRANSFER_SIZE 128 | ||
40 | |||
41 | /* ---------------------- */ | ||
42 | /* - Types definitions. - */ | ||
43 | /* ---------------------- */ | ||
44 | |||
45 | /* --------------------- */ | ||
46 | /* - Function p-types. - */ | ||
47 | /* --------------------- */ | ||
48 | |||
49 | tMLError MLSLSerialOpen(char const *port, | ||
50 | void **sl_handle); | ||
51 | tMLError MLSLSerialReset(void *sl_handle); | ||
52 | tMLError MLSLSerialClose(void *sl_handle); | ||
53 | |||
54 | tMLError MLSLSerialWriteSingle(void *sl_handle, | ||
55 | unsigned char slaveAddr, | ||
56 | unsigned char registerAddr, | ||
57 | unsigned char data); | ||
58 | |||
59 | tMLError MLSLSerialRead(void *sl_handle, | ||
60 | unsigned char slaveAddr, | ||
61 | unsigned char registerAddr, | ||
62 | unsigned short length, | ||
63 | unsigned char *data); | ||
64 | |||
65 | tMLError MLSLSerialWrite(void *sl_handle, | ||
66 | unsigned char slaveAddr, | ||
67 | unsigned short length, | ||
68 | unsigned char const *data); | ||
69 | |||
70 | tMLError MLSLSerialReadMem(void *sl_handle, | ||
71 | unsigned char slaveAddr, | ||
72 | unsigned short memAddr, | ||
73 | unsigned short length, | ||
74 | unsigned char *data); | ||
75 | |||
76 | tMLError MLSLSerialWriteMem(void *sl_handle, | ||
77 | unsigned char slaveAddr, | ||
78 | unsigned short memAddr, | ||
79 | unsigned short length, | ||
80 | unsigned char const *data); | ||
81 | |||
82 | tMLError MLSLSerialReadFifo(void *sl_handle, | ||
83 | unsigned char slaveAddr, | ||
84 | unsigned short length, | ||
85 | unsigned char *data); | ||
86 | |||
87 | tMLError MLSLSerialWriteFifo(void *sl_handle, | ||
88 | unsigned char slaveAddr, | ||
89 | unsigned short length, | ||
90 | unsigned char const *data); | ||
91 | |||
92 | tMLError MLSLWriteCal(unsigned char *cal, unsigned int len); | ||
93 | tMLError MLSLReadCal(unsigned char *cal, unsigned int len); | ||
94 | tMLError MLSLGetCalLength(unsigned int *len); | ||
95 | |||
96 | #ifdef __cplusplus | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | /** | ||
101 | * @} | ||
102 | */ | ||
103 | #endif /* MLSL_H */ | ||
diff --git a/drivers/misc/mpu3050/mltypes.h b/drivers/misc/mpu3050/mltypes.h new file mode 100644 index 00000000000..d0b27fa89e7 --- /dev/null +++ b/drivers/misc/mpu3050/mltypes.h | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 Motion Library - Error definitions. | ||
23 | * Definition of the error codes used within the MPL and returned | ||
24 | * to the user. | ||
25 | * Every function tries to return a meaningful error code basing | ||
26 | * on the occuring error condition. The error code is numeric. | ||
27 | * | ||
28 | * The available error codes and their associated values are: | ||
29 | * - (0) ML_SUCCESS | ||
30 | * - (1) ML_ERROR | ||
31 | * - (2) ML_ERROR_INVALID_PARAMETER | ||
32 | * - (3) ML_ERROR_FEATURE_NOT_ENABLED | ||
33 | * - (4) ML_ERROR_FEATURE_NOT_IMPLEMENTED | ||
34 | * - (6) ML_ERROR_DMP_NOT_STARTED | ||
35 | * - (7) ML_ERROR_DMP_STARTED | ||
36 | * - (8) ML_ERROR_NOT_OPENED | ||
37 | * - (9) ML_ERROR_OPENED | ||
38 | * - (10) ML_ERROR_INVALID_MODULE | ||
39 | * - (11) ML_ERROR_MEMORY_EXAUSTED | ||
40 | * - (12) ML_ERROR_DIVIDE_BY_ZERO | ||
41 | * - (13) ML_ERROR_ASSERTION_FAILURE | ||
42 | * - (14) ML_ERROR_FILE_OPEN | ||
43 | * - (15) ML_ERROR_FILE_READ | ||
44 | * - (16) ML_ERROR_FILE_WRITE | ||
45 | * - (20) ML_ERROR_SERIAL_CLOSED | ||
46 | * - (21) ML_ERROR_SERIAL_OPEN_ERROR | ||
47 | * - (22) ML_ERROR_SERIAL_READ | ||
48 | * - (23) ML_ERROR_SERIAL_WRITE | ||
49 | * - (24) ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED | ||
50 | * - (25) ML_ERROR_SM_TRANSITION | ||
51 | * - (26) ML_ERROR_SM_IMPROPER_STATE | ||
52 | * - (30) ML_ERROR_FIFO_OVERFLOW | ||
53 | * - (31) ML_ERROR_FIFO_FOOTER | ||
54 | * - (32) ML_ERROR_FIFO_READ_COUNT | ||
55 | * - (33) ML_ERROR_FIFO_READ_DATA | ||
56 | * - (40) ML_ERROR_MEMORY_SET | ||
57 | * - (50) ML_ERROR_LOG_MEMORY_ERROR | ||
58 | * - (51) ML_ERROR_LOG_OUTPUT_ERROR | ||
59 | * - (60) ML_ERROR_OS_BAD_PTR | ||
60 | * - (61) ML_ERROR_OS_BAD_HANDLE | ||
61 | * - (62) ML_ERROR_OS_CREATE_FAILED | ||
62 | * - (63) ML_ERROR_OS_LOCK_FAILED | ||
63 | * - (70) ML_ERROR_COMPASS_DATA_OVERFLOW | ||
64 | * - (71) ML_ERROR_COMPASS_DATA_UNDERFLOW | ||
65 | * - (72) ML_ERROR_COMPASS_DATA_NOT_READY | ||
66 | * - (73) ML_ERROR_COMPASS_DATA_ERROR | ||
67 | * - (75) ML_ERROR_CALIBRATION_LOAD | ||
68 | * - (76) ML_ERROR_CALIBRATION_STORE | ||
69 | * - (77) ML_ERROR_CALIBRATION_LEN | ||
70 | * - (78) ML_ERROR_CALIBRATION_CHECKSUM | ||
71 | * | ||
72 | * @{ | ||
73 | * @file mltypes.h | ||
74 | * @} | ||
75 | */ | ||
76 | |||
77 | #ifndef MLTYPES_H | ||
78 | #define MLTYPES_H | ||
79 | |||
80 | #ifdef __KERNEL__ | ||
81 | #include <linux/types.h> | ||
82 | #else | ||
83 | #include "stdint_invensense.h" | ||
84 | #endif | ||
85 | #include "log.h" | ||
86 | |||
87 | /*--------------------------- | ||
88 | ML Types | ||
89 | ---------------------------*/ | ||
90 | |||
91 | /** | ||
92 | * @struct tMLError mltypes.h "mltypes" | ||
93 | * @brief The MPL Error Code return type. | ||
94 | * | ||
95 | * @code | ||
96 | * typedef unsigned char tMLError; | ||
97 | * @endcode | ||
98 | */ | ||
99 | typedef unsigned char tMLError; | ||
100 | |||
101 | #if defined(LINUX) || defined(__KERNEL__) | ||
102 | typedef unsigned int HANDLE; | ||
103 | #endif | ||
104 | |||
105 | #ifdef __KERNEL__ | ||
106 | typedef HANDLE FILE; | ||
107 | #endif | ||
108 | |||
109 | #ifndef __cplusplus | ||
110 | #ifndef __KERNEL__ | ||
111 | typedef int_fast8_t bool; | ||
112 | #endif | ||
113 | #endif | ||
114 | |||
115 | /*--------------------------- | ||
116 | ML Defines | ||
117 | ---------------------------*/ | ||
118 | |||
119 | #ifndef NULL | ||
120 | #define NULL 0 | ||
121 | #endif | ||
122 | |||
123 | #ifndef TRUE | ||
124 | #define TRUE 1 | ||
125 | #endif | ||
126 | |||
127 | #ifndef FALSE | ||
128 | #define FALSE 0 | ||
129 | #endif | ||
130 | |||
131 | /* Dimension of an array */ | ||
132 | #ifndef DIM | ||
133 | #define DIM(array) (sizeof(array)/sizeof((array)[0])) | ||
134 | #endif | ||
135 | |||
136 | /* - ML Errors. - */ | ||
137 | #define ERROR_NAME(x) (#x) | ||
138 | #define ERROR_CHECK(x) \ | ||
139 | do { \ | ||
140 | if (ML_SUCCESS != x) { \ | ||
141 | MPL_LOGE("%s|%s|%d returning %d\n", \ | ||
142 | __FILE__, __func__, __LINE__, x); \ | ||
143 | return x; \ | ||
144 | } \ | ||
145 | } while (0) | ||
146 | |||
147 | #define ERROR_CHECK_FIRST(first, x) \ | ||
148 | { if (ML_SUCCESS == first) first = x; } | ||
149 | |||
150 | #define ML_SUCCESS (0) | ||
151 | /* Generic Error code. Proprietary Error Codes only */ | ||
152 | #define ML_ERROR (1) | ||
153 | |||
154 | /* Compatibility and other generic error codes */ | ||
155 | #define ML_ERROR_INVALID_PARAMETER (2) | ||
156 | #define ML_ERROR_FEATURE_NOT_ENABLED (3) | ||
157 | #define ML_ERROR_FEATURE_NOT_IMPLEMENTED (4) | ||
158 | #define ML_ERROR_DMP_NOT_STARTED (6) | ||
159 | #define ML_ERROR_DMP_STARTED (7) | ||
160 | #define ML_ERROR_NOT_OPENED (8) | ||
161 | #define ML_ERROR_OPENED (9) | ||
162 | #define ML_ERROR_INVALID_MODULE (10) | ||
163 | #define ML_ERROR_MEMORY_EXAUSTED (11) | ||
164 | #define ML_ERROR_DIVIDE_BY_ZERO (12) | ||
165 | #define ML_ERROR_ASSERTION_FAILURE (13) | ||
166 | #define ML_ERROR_FILE_OPEN (14) | ||
167 | #define ML_ERROR_FILE_READ (15) | ||
168 | #define ML_ERROR_FILE_WRITE (16) | ||
169 | #define ML_ERROR_INVALID_CONFIGURATION (17) | ||
170 | |||
171 | /* Serial Communication */ | ||
172 | #define ML_ERROR_SERIAL_CLOSED (20) | ||
173 | #define ML_ERROR_SERIAL_OPEN_ERROR (21) | ||
174 | #define ML_ERROR_SERIAL_READ (22) | ||
175 | #define ML_ERROR_SERIAL_WRITE (23) | ||
176 | #define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (24) | ||
177 | |||
178 | /* SM = State Machine */ | ||
179 | #define ML_ERROR_SM_TRANSITION (25) | ||
180 | #define ML_ERROR_SM_IMPROPER_STATE (26) | ||
181 | |||
182 | /* Fifo */ | ||
183 | #define ML_ERROR_FIFO_OVERFLOW (30) | ||
184 | #define ML_ERROR_FIFO_FOOTER (31) | ||
185 | #define ML_ERROR_FIFO_READ_COUNT (32) | ||
186 | #define ML_ERROR_FIFO_READ_DATA (33) | ||
187 | |||
188 | /* Memory & Registers, Set & Get */ | ||
189 | #define ML_ERROR_MEMORY_SET (40) | ||
190 | |||
191 | #define ML_ERROR_LOG_MEMORY_ERROR (50) | ||
192 | #define ML_ERROR_LOG_OUTPUT_ERROR (51) | ||
193 | |||
194 | /* OS interface errors */ | ||
195 | #define ML_ERROR_OS_BAD_PTR (60) | ||
196 | #define ML_ERROR_OS_BAD_HANDLE (61) | ||
197 | #define ML_ERROR_OS_CREATE_FAILED (62) | ||
198 | #define ML_ERROR_OS_LOCK_FAILED (63) | ||
199 | |||
200 | /* Compass errors */ | ||
201 | #define ML_ERROR_COMPASS_DATA_OVERFLOW (70) | ||
202 | #define ML_ERROR_COMPASS_DATA_UNDERFLOW (71) | ||
203 | #define ML_ERROR_COMPASS_DATA_NOT_READY (72) | ||
204 | #define ML_ERROR_COMPASS_DATA_ERROR (73) | ||
205 | |||
206 | /* Load/Store calibration */ | ||
207 | #define ML_ERROR_CALIBRATION_LOAD (75) | ||
208 | #define ML_ERROR_CALIBRATION_STORE (76) | ||
209 | #define ML_ERROR_CALIBRATION_LEN (77) | ||
210 | #define ML_ERROR_CALIBRATION_CHECKSUM (78) | ||
211 | |||
212 | /* Accel errors */ | ||
213 | #define ML_ERROR_ACCEL_DATA_OVERFLOW (79) | ||
214 | #define ML_ERROR_ACCEL_DATA_UNDERFLOW (80) | ||
215 | #define ML_ERROR_ACCEL_DATA_NOT_READY (81) | ||
216 | #define ML_ERROR_ACCEL_DATA_ERROR (82) | ||
217 | |||
218 | /* For Linux coding compliance */ | ||
219 | #ifndef __KERNEL__ | ||
220 | #define EXPORT_SYMBOL(x) | ||
221 | #endif | ||
222 | |||
223 | /*--------------------------- | ||
224 | p-Types | ||
225 | ---------------------------*/ | ||
226 | |||
227 | #endif /* MLTYPES_H */ | ||
diff --git a/drivers/misc/mpu3050/mpu-dev.c b/drivers/misc/mpu3050/mpu-dev.c new file mode 100644 index 00000000000..4dba44d4548 --- /dev/null +++ b/drivers/misc/mpu3050/mpu-dev.c | |||
@@ -0,0 +1,1310 @@ | |||
1 | /* | ||
2 | mpu-dev.c - mpu3050 char device interface 2 $License: | ||
3 | |||
4 | Copyright (C) 1995-97 Simon G. Vogl | ||
5 | Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> | ||
6 | Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | ||
7 | |||
8 | Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
22 | */ | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/i2c-dev.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/stat.h> | ||
31 | #include <linux/irq.h> | ||
32 | #include <linux/gpio.h> | ||
33 | #include <linux/signal.h> | ||
34 | #include <linux/miscdevice.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/version.h> | ||
37 | #include <linux/pm.h> | ||
38 | #include <linux/mutex.h> | ||
39 | #include <linux/suspend.h> | ||
40 | #include <linux/poll.h> | ||
41 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
42 | #include <linux/earlysuspend.h> | ||
43 | #endif | ||
44 | |||
45 | #include <linux/errno.h> | ||
46 | #include <linux/fs.h> | ||
47 | #include <linux/mm.h> | ||
48 | #include <linux/sched.h> | ||
49 | #include <linux/wait.h> | ||
50 | #include <linux/uaccess.h> | ||
51 | #include <linux/io.h> | ||
52 | |||
53 | #include "mpuirq.h" | ||
54 | #include "slaveirq.h" | ||
55 | #include "mlsl.h" | ||
56 | #include "mpu-i2c.h" | ||
57 | #include "mldl_cfg.h" | ||
58 | #include "mpu.h" | ||
59 | |||
60 | #define MPU3050_EARLY_SUSPEND_IN_DRIVER 0 | ||
61 | |||
62 | /* Platform data for the MPU */ | ||
63 | struct mpu_private_data { | ||
64 | struct mldl_cfg mldl_cfg; | ||
65 | |||
66 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
67 | struct early_suspend early_suspend; | ||
68 | #endif | ||
69 | struct mutex mutex; | ||
70 | wait_queue_head_t mpu_event_wait; | ||
71 | struct completion completion; | ||
72 | struct timer_list timeout; | ||
73 | struct notifier_block nb; | ||
74 | struct mpuirq_data mpu_pm_event; | ||
75 | int response_timeout; /* In seconds */ | ||
76 | unsigned long event; | ||
77 | int pid; | ||
78 | }; | ||
79 | |||
80 | static struct i2c_client *this_client; | ||
81 | |||
82 | |||
83 | static void | ||
84 | mpu_pm_timeout(u_long data) | ||
85 | { | ||
86 | struct mpu_private_data *mpu = (struct mpu_private_data *) data; | ||
87 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
88 | complete(&mpu->completion); | ||
89 | } | ||
90 | |||
91 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
92 | unsigned long event, | ||
93 | void *unused) | ||
94 | { | ||
95 | struct mpu_private_data *mpu = | ||
96 | container_of(nb, struct mpu_private_data, nb); | ||
97 | struct timeval event_time; | ||
98 | dev_dbg(&this_client->adapter->dev, "%s: %ld\n", __func__, event); | ||
99 | |||
100 | /* Prevent the file handle from being closed before we initialize | ||
101 | the completion event */ | ||
102 | mutex_lock(&mpu->mutex); | ||
103 | if (!(mpu->pid) || | ||
104 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
105 | mutex_unlock(&mpu->mutex); | ||
106 | return NOTIFY_OK; | ||
107 | } | ||
108 | |||
109 | do_gettimeofday(&event_time); | ||
110 | mpu->mpu_pm_event.interruptcount++; | ||
111 | mpu->mpu_pm_event.irqtime = | ||
112 | (((long long) event_time.tv_sec) << 32) + | ||
113 | event_time.tv_usec; | ||
114 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
115 | mpu->mpu_pm_event.data_size = sizeof(unsigned long); | ||
116 | mpu->mpu_pm_event.data = &mpu->event; | ||
117 | |||
118 | if (event == PM_SUSPEND_PREPARE) | ||
119 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
120 | if (event == PM_POST_SUSPEND) | ||
121 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
122 | |||
123 | if (mpu->response_timeout > 0) { | ||
124 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
125 | add_timer(&mpu->timeout); | ||
126 | } | ||
127 | INIT_COMPLETION(mpu->completion); | ||
128 | mutex_unlock(&mpu->mutex); | ||
129 | |||
130 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
131 | wait_for_completion(&mpu->completion); | ||
132 | del_timer_sync(&mpu->timeout); | ||
133 | dev_dbg(&this_client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
134 | return NOTIFY_OK; | ||
135 | } | ||
136 | |||
137 | static int mpu_open(struct inode *inode, struct file *file) | ||
138 | { | ||
139 | struct mpu_private_data *mpu = | ||
140 | (struct mpu_private_data *) i2c_get_clientdata(this_client); | ||
141 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
142 | int result; | ||
143 | dev_dbg(&this_client->adapter->dev, "mpu_open\n"); | ||
144 | dev_dbg(&this_client->adapter->dev, "current->pid %d\n", | ||
145 | current->pid); | ||
146 | mpu->pid = current->pid; | ||
147 | file->private_data = this_client; | ||
148 | /* we could do some checking on the flags supplied by "open" */ | ||
149 | /* i.e. O_NONBLOCK */ | ||
150 | /* -> set some flag to disable interruptible_sleep_on in mpu_read */ | ||
151 | |||
152 | /* Reset the sensors to the default */ | ||
153 | result = mutex_lock_interruptible(&mpu->mutex); | ||
154 | if (result) { | ||
155 | dev_err(&this_client->adapter->dev, | ||
156 | "%s: mutex_lock_interruptible returned %d\n", | ||
157 | __func__, result); | ||
158 | return result; | ||
159 | } | ||
160 | mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO; | ||
161 | if (mldl_cfg->accel && mldl_cfg->accel->resume) | ||
162 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL; | ||
163 | |||
164 | if (mldl_cfg->compass && mldl_cfg->compass->resume) | ||
165 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS; | ||
166 | |||
167 | if (mldl_cfg->pressure && mldl_cfg->pressure->resume) | ||
168 | mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE; | ||
169 | mutex_unlock(&mpu->mutex); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
174 | static int mpu_release(struct inode *inode, struct file *file) | ||
175 | { | ||
176 | struct i2c_client *client = | ||
177 | (struct i2c_client *) file->private_data; | ||
178 | struct mpu_private_data *mpu = | ||
179 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
180 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
181 | struct i2c_adapter *accel_adapter; | ||
182 | struct i2c_adapter *compass_adapter; | ||
183 | struct i2c_adapter *pressure_adapter; | ||
184 | int result = 0; | ||
185 | |||
186 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
187 | compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
188 | pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
189 | |||
190 | mutex_lock(&mpu->mutex); | ||
191 | result = mpu3050_suspend(mldl_cfg, client->adapter, | ||
192 | accel_adapter, compass_adapter, | ||
193 | pressure_adapter, | ||
194 | TRUE, TRUE, TRUE, TRUE); | ||
195 | mpu->pid = 0; | ||
196 | mutex_unlock(&mpu->mutex); | ||
197 | complete(&mpu->completion); | ||
198 | dev_dbg(&this_client->adapter->dev, "mpu_release\n"); | ||
199 | return result; | ||
200 | } | ||
201 | |||
202 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
203 | static ssize_t mpu_read(struct file *file, | ||
204 | char __user *buf, size_t count, loff_t *offset) | ||
205 | { | ||
206 | struct mpuirq_data local_mpu_pm_event; | ||
207 | struct i2c_client *client = | ||
208 | (struct i2c_client *) file->private_data; | ||
209 | struct mpu_private_data *mpu = | ||
210 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
211 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
212 | int err; | ||
213 | |||
214 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
215 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
216 | |||
217 | if (!mpu->event || NULL == buf | ||
218 | || count < sizeof(mpu->mpu_pm_event) + sizeof(unsigned long)) | ||
219 | return 0; | ||
220 | |||
221 | err = copy_from_user(&local_mpu_pm_event, buf, | ||
222 | sizeof(mpu->mpu_pm_event)); | ||
223 | if (err != 0) { | ||
224 | dev_err(&this_client->adapter->dev, | ||
225 | "Copy from user returned %d\n", err); | ||
226 | return -EFAULT; | ||
227 | } | ||
228 | |||
229 | mpu->mpu_pm_event.data = local_mpu_pm_event.data; | ||
230 | err = copy_to_user((unsigned long __user *)local_mpu_pm_event.data, | ||
231 | &mpu->event, | ||
232 | sizeof(mpu->event)); | ||
233 | if (err != 0) { | ||
234 | dev_err(&this_client->adapter->dev, | ||
235 | "Copy to user returned %d\n", err); | ||
236 | return -EFAULT; | ||
237 | } | ||
238 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
239 | if (err != 0) { | ||
240 | dev_err(&this_client->adapter->dev, | ||
241 | "Copy to user returned %d\n", err); | ||
242 | return -EFAULT; | ||
243 | } | ||
244 | mpu->event = 0; | ||
245 | return len; | ||
246 | } | ||
247 | |||
248 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
249 | { | ||
250 | struct i2c_client *client = | ||
251 | (struct i2c_client *) file->private_data; | ||
252 | struct mpu_private_data *mpu = | ||
253 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
254 | int mask = 0; | ||
255 | |||
256 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
257 | if (mpu->event) | ||
258 | mask |= POLLIN | POLLRDNORM; | ||
259 | return mask; | ||
260 | } | ||
261 | |||
262 | static int | ||
263 | mpu_ioctl_set_mpu_pdata(struct i2c_client *client, unsigned long arg) | ||
264 | { | ||
265 | int ii; | ||
266 | struct mpu_private_data *mpu = | ||
267 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
268 | struct mpu3050_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
269 | struct mpu3050_platform_data local_pdata; | ||
270 | |||
271 | if (copy_from_user(&local_pdata, (unsigned char __user *) arg, | ||
272 | sizeof(local_pdata))) | ||
273 | return -EFAULT; | ||
274 | |||
275 | pdata->int_config = local_pdata.int_config; | ||
276 | for (ii = 0; ii < DIM(pdata->orientation); ii++) | ||
277 | pdata->orientation[ii] = local_pdata.orientation[ii]; | ||
278 | pdata->level_shifter = local_pdata.level_shifter; | ||
279 | |||
280 | pdata->accel.address = local_pdata.accel.address; | ||
281 | for (ii = 0; ii < DIM(pdata->accel.orientation); ii++) | ||
282 | pdata->accel.orientation[ii] = | ||
283 | local_pdata.accel.orientation[ii]; | ||
284 | |||
285 | pdata->compass.address = local_pdata.compass.address; | ||
286 | for (ii = 0; ii < DIM(pdata->compass.orientation); ii++) | ||
287 | pdata->compass.orientation[ii] = | ||
288 | local_pdata.compass.orientation[ii]; | ||
289 | |||
290 | pdata->pressure.address = local_pdata.pressure.address; | ||
291 | for (ii = 0; ii < DIM(pdata->pressure.orientation); ii++) | ||
292 | pdata->pressure.orientation[ii] = | ||
293 | local_pdata.pressure.orientation[ii]; | ||
294 | |||
295 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
296 | |||
297 | return ML_SUCCESS; | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | mpu_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg) | ||
302 | { | ||
303 | int ii; | ||
304 | int result = ML_SUCCESS; | ||
305 | struct mpu_private_data *mpu = | ||
306 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
307 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
308 | struct mldl_cfg *temp_mldl_cfg; | ||
309 | |||
310 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
311 | |||
312 | temp_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL); | ||
313 | if (NULL == temp_mldl_cfg) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | /* | ||
317 | * User space is not allowed to modify accel compass pressure or | ||
318 | * pdata structs, as well as silicon_revision product_id or trim | ||
319 | */ | ||
320 | if (copy_from_user(temp_mldl_cfg, (struct mldl_cfg __user *) arg, | ||
321 | offsetof(struct mldl_cfg, silicon_revision))) { | ||
322 | result = -EFAULT; | ||
323 | goto out; | ||
324 | } | ||
325 | |||
326 | if (mldl_cfg->gyro_is_suspended) { | ||
327 | if (mldl_cfg->addr != temp_mldl_cfg->addr) | ||
328 | mldl_cfg->gyro_needs_reset = TRUE; | ||
329 | |||
330 | if (mldl_cfg->int_config != temp_mldl_cfg->int_config) | ||
331 | mldl_cfg->gyro_needs_reset = TRUE; | ||
332 | |||
333 | if (mldl_cfg->ext_sync != temp_mldl_cfg->ext_sync) | ||
334 | mldl_cfg->gyro_needs_reset = TRUE; | ||
335 | |||
336 | if (mldl_cfg->full_scale != temp_mldl_cfg->full_scale) | ||
337 | mldl_cfg->gyro_needs_reset = TRUE; | ||
338 | |||
339 | if (mldl_cfg->lpf != temp_mldl_cfg->lpf) | ||
340 | mldl_cfg->gyro_needs_reset = TRUE; | ||
341 | |||
342 | if (mldl_cfg->clk_src != temp_mldl_cfg->clk_src) | ||
343 | mldl_cfg->gyro_needs_reset = TRUE; | ||
344 | |||
345 | if (mldl_cfg->divider != temp_mldl_cfg->divider) | ||
346 | mldl_cfg->gyro_needs_reset = TRUE; | ||
347 | |||
348 | if (mldl_cfg->dmp_enable != temp_mldl_cfg->dmp_enable) | ||
349 | mldl_cfg->gyro_needs_reset = TRUE; | ||
350 | |||
351 | if (mldl_cfg->fifo_enable != temp_mldl_cfg->fifo_enable) | ||
352 | mldl_cfg->gyro_needs_reset = TRUE; | ||
353 | |||
354 | if (mldl_cfg->dmp_cfg1 != temp_mldl_cfg->dmp_cfg1) | ||
355 | mldl_cfg->gyro_needs_reset = TRUE; | ||
356 | |||
357 | if (mldl_cfg->dmp_cfg2 != temp_mldl_cfg->dmp_cfg2) | ||
358 | mldl_cfg->gyro_needs_reset = TRUE; | ||
359 | |||
360 | if (mldl_cfg->gyro_power != temp_mldl_cfg->gyro_power) | ||
361 | mldl_cfg->gyro_needs_reset = TRUE; | ||
362 | |||
363 | for (ii = 0; ii < MPU_NUM_AXES; ii++) | ||
364 | if (mldl_cfg->offset_tc[ii] != | ||
365 | temp_mldl_cfg->offset_tc[ii]) | ||
366 | mldl_cfg->gyro_needs_reset = TRUE; | ||
367 | |||
368 | for (ii = 0; ii < MPU_NUM_AXES; ii++) | ||
369 | if (mldl_cfg->offset[ii] != temp_mldl_cfg->offset[ii]) | ||
370 | mldl_cfg->gyro_needs_reset = TRUE; | ||
371 | |||
372 | if (memcmp(mldl_cfg->ram, temp_mldl_cfg->ram, | ||
373 | MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE * | ||
374 | sizeof(unsigned char))) | ||
375 | mldl_cfg->gyro_needs_reset = TRUE; | ||
376 | } | ||
377 | |||
378 | memcpy(mldl_cfg, temp_mldl_cfg, | ||
379 | offsetof(struct mldl_cfg, silicon_revision)); | ||
380 | |||
381 | out: | ||
382 | kfree(temp_mldl_cfg); | ||
383 | return result; | ||
384 | } | ||
385 | |||
386 | static int | ||
387 | mpu_ioctl_get_mpu_config(struct i2c_client *client, unsigned long arg) | ||
388 | { | ||
389 | /* Have to be careful as there are 3 pointers in the mldl_cfg | ||
390 | * structure */ | ||
391 | struct mpu_private_data *mpu = | ||
392 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
393 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
394 | struct mldl_cfg *local_mldl_cfg; | ||
395 | int retval = 0; | ||
396 | |||
397 | local_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL); | ||
398 | if (NULL == local_mldl_cfg) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | retval = | ||
402 | copy_from_user(local_mldl_cfg, (struct mldl_cfg __user *) arg, | ||
403 | sizeof(struct mldl_cfg)); | ||
404 | if (retval) { | ||
405 | dev_err(&this_client->adapter->dev, | ||
406 | "%s|%s:%d: EFAULT on arg\n", | ||
407 | __FILE__, __func__, __LINE__); | ||
408 | retval = -EFAULT; | ||
409 | goto out; | ||
410 | } | ||
411 | |||
412 | /* Fill in the accel, compass, pressure and pdata pointers */ | ||
413 | if (mldl_cfg->accel) { | ||
414 | retval = copy_to_user((void __user *)local_mldl_cfg->accel, | ||
415 | mldl_cfg->accel, | ||
416 | sizeof(*mldl_cfg->accel)); | ||
417 | if (retval) { | ||
418 | dev_err(&this_client->adapter->dev, | ||
419 | "%s|%s:%d: EFAULT on accel\n", | ||
420 | __FILE__, __func__, __LINE__); | ||
421 | retval = -EFAULT; | ||
422 | goto out; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | if (mldl_cfg->compass) { | ||
427 | retval = copy_to_user((void __user *)local_mldl_cfg->compass, | ||
428 | mldl_cfg->compass, | ||
429 | sizeof(*mldl_cfg->compass)); | ||
430 | if (retval) { | ||
431 | dev_err(&this_client->adapter->dev, | ||
432 | "%s|%s:%d: EFAULT on compass\n", | ||
433 | __FILE__, __func__, __LINE__); | ||
434 | retval = -EFAULT; | ||
435 | goto out; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | if (mldl_cfg->pressure) { | ||
440 | retval = copy_to_user((void __user *)local_mldl_cfg->pressure, | ||
441 | mldl_cfg->pressure, | ||
442 | sizeof(*mldl_cfg->pressure)); | ||
443 | if (retval) { | ||
444 | dev_err(&this_client->adapter->dev, | ||
445 | "%s|%s:%d: EFAULT on pressure\n", | ||
446 | __FILE__, __func__, __LINE__); | ||
447 | retval = -EFAULT; | ||
448 | goto out; | ||
449 | } | ||
450 | } | ||
451 | |||
452 | if (mldl_cfg->pdata) { | ||
453 | retval = copy_to_user((void __user *)local_mldl_cfg->pdata, | ||
454 | mldl_cfg->pdata, | ||
455 | sizeof(*mldl_cfg->pdata)); | ||
456 | if (retval) { | ||
457 | dev_err(&this_client->adapter->dev, | ||
458 | "%s|%s:%d: EFAULT on pdata\n", | ||
459 | __FILE__, __func__, __LINE__); | ||
460 | retval = -EFAULT; | ||
461 | goto out; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /* Do not modify the accel, compass, pressure and pdata pointers */ | ||
466 | retval = copy_to_user((struct mldl_cfg __user *) arg, | ||
467 | mldl_cfg, offsetof(struct mldl_cfg, accel)); | ||
468 | |||
469 | if (retval) | ||
470 | retval = -EFAULT; | ||
471 | out: | ||
472 | kfree(local_mldl_cfg); | ||
473 | return retval; | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * Pass a requested slave configuration to the slave sensor | ||
478 | * | ||
479 | * @param adapter the adaptor to use to communicate with the slave | ||
480 | * @param mldl_cfg the mldl configuration structuer | ||
481 | * @param slave pointer to the slave descriptor | ||
482 | * @param usr_config The configuration to pass to the slave sensor | ||
483 | * | ||
484 | * @return 0 or non-zero error code | ||
485 | */ | ||
486 | static int slave_config(void *adapter, | ||
487 | struct mldl_cfg *mldl_cfg, | ||
488 | struct ext_slave_descr *slave, | ||
489 | struct ext_slave_platform_data *pdata, | ||
490 | struct ext_slave_config __user *usr_config) | ||
491 | { | ||
492 | int retval = ML_SUCCESS; | ||
493 | struct ext_slave_config config; | ||
494 | if ((!slave) || (!slave->config)) | ||
495 | return retval; | ||
496 | |||
497 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
498 | if (retval) | ||
499 | return -EFAULT; | ||
500 | |||
501 | if (config.len && config.data) { | ||
502 | int *data; | ||
503 | data = kzalloc(config.len, GFP_KERNEL); | ||
504 | if (!data) | ||
505 | return ML_ERROR_MEMORY_EXAUSTED; | ||
506 | |||
507 | retval = copy_from_user(data, | ||
508 | (void __user *)config.data, | ||
509 | config.len); | ||
510 | if (retval) { | ||
511 | retval = -EFAULT; | ||
512 | kfree(data); | ||
513 | return retval; | ||
514 | } | ||
515 | config.data = data; | ||
516 | } | ||
517 | retval = slave->config(adapter, slave, pdata, &config); | ||
518 | kfree(config.data); | ||
519 | return retval; | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * Get a requested slave configuration from the slave sensor | ||
524 | * | ||
525 | * @param adapter the adaptor to use to communicate with the slave | ||
526 | * @param mldl_cfg the mldl configuration structuer | ||
527 | * @param slave pointer to the slave descriptor | ||
528 | * @param usr_config The configuration for the slave to fill out | ||
529 | * | ||
530 | * @return 0 or non-zero error code | ||
531 | */ | ||
532 | static int slave_get_config(void *adapter, | ||
533 | struct mldl_cfg *mldl_cfg, | ||
534 | struct ext_slave_descr *slave, | ||
535 | struct ext_slave_platform_data *pdata, | ||
536 | struct ext_slave_config __user *usr_config) | ||
537 | { | ||
538 | int retval = ML_SUCCESS; | ||
539 | struct ext_slave_config config; | ||
540 | void *user_data; | ||
541 | if (!(slave) || !(slave->get_config)) | ||
542 | return ML_SUCCESS; | ||
543 | |||
544 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
545 | if (retval) | ||
546 | return -EFAULT; | ||
547 | |||
548 | user_data = config.data; | ||
549 | if (config.len && config.data) { | ||
550 | int *data; | ||
551 | data = kzalloc(config.len, GFP_KERNEL); | ||
552 | if (!data) | ||
553 | return ML_ERROR_MEMORY_EXAUSTED; | ||
554 | |||
555 | retval = copy_from_user(data, | ||
556 | (void __user *)config.data, | ||
557 | config.len); | ||
558 | if (retval) { | ||
559 | retval = -EFAULT; | ||
560 | kfree(data); | ||
561 | return retval; | ||
562 | } | ||
563 | config.data = data; | ||
564 | } | ||
565 | retval = slave->get_config(adapter, slave, pdata, &config); | ||
566 | if (retval) { | ||
567 | kfree(config.data); | ||
568 | return retval; | ||
569 | } | ||
570 | retval = copy_to_user((unsigned char __user *) user_data, | ||
571 | config.data, | ||
572 | config.len); | ||
573 | kfree(config.data); | ||
574 | return retval; | ||
575 | } | ||
576 | |||
577 | static int mpu_handle_mlsl(void *sl_handle, | ||
578 | unsigned char addr, | ||
579 | unsigned int cmd, | ||
580 | struct mpu_read_write __user *usr_msg) | ||
581 | { | ||
582 | int retval = ML_SUCCESS; | ||
583 | struct mpu_read_write msg; | ||
584 | unsigned char *user_data; | ||
585 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
586 | if (retval) | ||
587 | return -EFAULT; | ||
588 | |||
589 | user_data = msg.data; | ||
590 | if (msg.length && msg.data) { | ||
591 | unsigned char *data; | ||
592 | data = kzalloc(msg.length, GFP_KERNEL); | ||
593 | if (!data) | ||
594 | return ML_ERROR_MEMORY_EXAUSTED; | ||
595 | |||
596 | retval = copy_from_user(data, | ||
597 | (void __user *)msg.data, | ||
598 | msg.length); | ||
599 | if (retval) { | ||
600 | retval = -EFAULT; | ||
601 | kfree(data); | ||
602 | return retval; | ||
603 | } | ||
604 | msg.data = data; | ||
605 | } else { | ||
606 | return ML_ERROR_INVALID_PARAMETER; | ||
607 | } | ||
608 | |||
609 | switch (cmd) { | ||
610 | case MPU_READ: | ||
611 | retval = MLSLSerialRead(sl_handle, addr, | ||
612 | msg.address, msg.length, msg.data); | ||
613 | break; | ||
614 | case MPU_WRITE: | ||
615 | retval = MLSLSerialWrite(sl_handle, addr, | ||
616 | msg.length, msg.data); | ||
617 | break; | ||
618 | case MPU_READ_MEM: | ||
619 | retval = MLSLSerialReadMem(sl_handle, addr, | ||
620 | msg.address, msg.length, msg.data); | ||
621 | break; | ||
622 | case MPU_WRITE_MEM: | ||
623 | retval = MLSLSerialWriteMem(sl_handle, addr, | ||
624 | msg.address, msg.length, msg.data); | ||
625 | break; | ||
626 | case MPU_READ_FIFO: | ||
627 | retval = MLSLSerialReadFifo(sl_handle, addr, | ||
628 | msg.length, msg.data); | ||
629 | break; | ||
630 | case MPU_WRITE_FIFO: | ||
631 | retval = MLSLSerialWriteFifo(sl_handle, addr, | ||
632 | msg.length, msg.data); | ||
633 | break; | ||
634 | |||
635 | }; | ||
636 | retval = copy_to_user((unsigned char __user *) user_data, | ||
637 | msg.data, | ||
638 | msg.length); | ||
639 | kfree(msg.data); | ||
640 | return retval; | ||
641 | } | ||
642 | |||
643 | /* ioctl - I/O control */ | ||
644 | static long mpu_ioctl(struct file *file, | ||
645 | unsigned int cmd, unsigned long arg) | ||
646 | { | ||
647 | struct i2c_client *client = | ||
648 | (struct i2c_client *) file->private_data; | ||
649 | struct mpu_private_data *mpu = | ||
650 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
651 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
652 | int retval = 0; | ||
653 | struct i2c_adapter *accel_adapter; | ||
654 | struct i2c_adapter *compass_adapter; | ||
655 | struct i2c_adapter *pressure_adapter; | ||
656 | |||
657 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
658 | compass_adapter = | ||
659 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
660 | pressure_adapter = | ||
661 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
662 | |||
663 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
664 | if (retval) { | ||
665 | dev_err(&this_client->adapter->dev, | ||
666 | "%s: mutex_lock_interruptible returned %d\n", | ||
667 | __func__, retval); | ||
668 | return retval; | ||
669 | } | ||
670 | |||
671 | switch (cmd) { | ||
672 | case MPU_SET_MPU_CONFIG: | ||
673 | retval = mpu_ioctl_set_mpu_config(client, arg); | ||
674 | break; | ||
675 | case MPU_SET_PLATFORM_DATA: | ||
676 | retval = mpu_ioctl_set_mpu_pdata(client, arg); | ||
677 | break; | ||
678 | case MPU_GET_MPU_CONFIG: | ||
679 | retval = mpu_ioctl_get_mpu_config(client, arg); | ||
680 | break; | ||
681 | case MPU_READ: | ||
682 | case MPU_WRITE: | ||
683 | case MPU_READ_MEM: | ||
684 | case MPU_WRITE_MEM: | ||
685 | case MPU_READ_FIFO: | ||
686 | case MPU_WRITE_FIFO: | ||
687 | retval = mpu_handle_mlsl(client->adapter, mldl_cfg->addr, cmd, | ||
688 | (struct mpu_read_write __user *) arg); | ||
689 | break; | ||
690 | case MPU_CONFIG_ACCEL: | ||
691 | retval = slave_config(accel_adapter, mldl_cfg, | ||
692 | mldl_cfg->accel, | ||
693 | &mldl_cfg->pdata->accel, | ||
694 | (struct ext_slave_config __user *) arg); | ||
695 | break; | ||
696 | case MPU_CONFIG_COMPASS: | ||
697 | retval = slave_config(compass_adapter, mldl_cfg, | ||
698 | mldl_cfg->compass, | ||
699 | &mldl_cfg->pdata->compass, | ||
700 | (struct ext_slave_config __user *) arg); | ||
701 | break; | ||
702 | case MPU_CONFIG_PRESSURE: | ||
703 | retval = slave_config(pressure_adapter, mldl_cfg, | ||
704 | mldl_cfg->pressure, | ||
705 | &mldl_cfg->pdata->pressure, | ||
706 | (struct ext_slave_config __user *) arg); | ||
707 | break; | ||
708 | case MPU_GET_CONFIG_ACCEL: | ||
709 | retval = slave_get_config(accel_adapter, mldl_cfg, | ||
710 | mldl_cfg->accel, | ||
711 | &mldl_cfg->pdata->accel, | ||
712 | (struct ext_slave_config __user *) arg); | ||
713 | break; | ||
714 | case MPU_GET_CONFIG_COMPASS: | ||
715 | retval = slave_get_config(compass_adapter, mldl_cfg, | ||
716 | mldl_cfg->compass, | ||
717 | &mldl_cfg->pdata->compass, | ||
718 | (struct ext_slave_config __user *) arg); | ||
719 | break; | ||
720 | case MPU_GET_CONFIG_PRESSURE: | ||
721 | retval = slave_get_config(pressure_adapter, mldl_cfg, | ||
722 | mldl_cfg->pressure, | ||
723 | &mldl_cfg->pdata->pressure, | ||
724 | (struct ext_slave_config __user *) arg); | ||
725 | break; | ||
726 | case MPU_SUSPEND: | ||
727 | { | ||
728 | unsigned long sensors; | ||
729 | sensors = ~(mldl_cfg->requested_sensors); | ||
730 | retval = mpu3050_suspend(mldl_cfg, | ||
731 | client->adapter, | ||
732 | accel_adapter, | ||
733 | compass_adapter, | ||
734 | pressure_adapter, | ||
735 | ((sensors & ML_THREE_AXIS_GYRO) | ||
736 | == ML_THREE_AXIS_GYRO), | ||
737 | ((sensors & ML_THREE_AXIS_ACCEL) | ||
738 | == ML_THREE_AXIS_ACCEL), | ||
739 | ((sensors & ML_THREE_AXIS_COMPASS) | ||
740 | == ML_THREE_AXIS_COMPASS), | ||
741 | ((sensors & ML_THREE_AXIS_PRESSURE) | ||
742 | == ML_THREE_AXIS_PRESSURE)); | ||
743 | } | ||
744 | break; | ||
745 | case MPU_RESUME: | ||
746 | { | ||
747 | unsigned long sensors; | ||
748 | sensors = mldl_cfg->requested_sensors; | ||
749 | retval = mpu3050_resume(mldl_cfg, | ||
750 | client->adapter, | ||
751 | accel_adapter, | ||
752 | compass_adapter, | ||
753 | pressure_adapter, | ||
754 | sensors & ML_THREE_AXIS_GYRO, | ||
755 | sensors & ML_THREE_AXIS_ACCEL, | ||
756 | sensors & ML_THREE_AXIS_COMPASS, | ||
757 | sensors & ML_THREE_AXIS_PRESSURE); | ||
758 | } | ||
759 | break; | ||
760 | case MPU_PM_EVENT_HANDLED: | ||
761 | dev_dbg(&this_client->adapter->dev, | ||
762 | "%s: %d\n", __func__, cmd); | ||
763 | complete(&mpu->completion); | ||
764 | break; | ||
765 | case MPU_READ_ACCEL: | ||
766 | { | ||
767 | unsigned char data[6]; | ||
768 | retval = mpu3050_read_accel(mldl_cfg, client->adapter, | ||
769 | data); | ||
770 | if ((ML_SUCCESS == retval) && | ||
771 | (copy_to_user((unsigned char __user *) arg, | ||
772 | data, sizeof(data)))) | ||
773 | retval = -EFAULT; | ||
774 | } | ||
775 | break; | ||
776 | case MPU_READ_COMPASS: | ||
777 | { | ||
778 | unsigned char data[6]; | ||
779 | struct i2c_adapter *compass_adapt = | ||
780 | i2c_get_adapter(mldl_cfg->pdata->compass. | ||
781 | adapt_num); | ||
782 | retval = mpu3050_read_compass(mldl_cfg, compass_adapt, | ||
783 | data); | ||
784 | if ((ML_SUCCESS == retval) && | ||
785 | (copy_to_user((unsigned char *) arg, | ||
786 | data, sizeof(data)))) | ||
787 | retval = -EFAULT; | ||
788 | } | ||
789 | break; | ||
790 | case MPU_READ_PRESSURE: | ||
791 | { | ||
792 | unsigned char data[3]; | ||
793 | struct i2c_adapter *pressure_adapt = | ||
794 | i2c_get_adapter(mldl_cfg->pdata->pressure. | ||
795 | adapt_num); | ||
796 | retval = | ||
797 | mpu3050_read_pressure(mldl_cfg, pressure_adapt, | ||
798 | data); | ||
799 | if ((ML_SUCCESS == retval) && | ||
800 | (copy_to_user((unsigned char __user *) arg, | ||
801 | data, sizeof(data)))) | ||
802 | retval = -EFAULT; | ||
803 | } | ||
804 | break; | ||
805 | default: | ||
806 | dev_err(&this_client->adapter->dev, | ||
807 | "%s: Unknown cmd %x, arg %lu\n", __func__, cmd, | ||
808 | arg); | ||
809 | retval = -EINVAL; | ||
810 | } | ||
811 | |||
812 | mutex_unlock(&mpu->mutex); | ||
813 | return retval; | ||
814 | } | ||
815 | |||
816 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
817 | void mpu3050_early_suspend(struct early_suspend *h) | ||
818 | { | ||
819 | struct mpu_private_data *mpu = container_of(h, | ||
820 | struct | ||
821 | mpu_private_data, | ||
822 | early_suspend); | ||
823 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
824 | struct i2c_adapter *accel_adapter; | ||
825 | struct i2c_adapter *compass_adapter; | ||
826 | struct i2c_adapter *pressure_adapter; | ||
827 | |||
828 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
829 | compass_adapter = | ||
830 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
831 | pressure_adapter = | ||
832 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
833 | |||
834 | dev_dbg(&this_client->adapter->dev, "%s: %d, %d\n", __func__, | ||
835 | h->level, mpu->mldl_cfg.gyro_is_suspended); | ||
836 | if (MPU3050_EARLY_SUSPEND_IN_DRIVER) { | ||
837 | mutex_lock(&mpu->mutex); | ||
838 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
839 | accel_adapter, compass_adapter, | ||
840 | pressure_adapter, TRUE, TRUE, TRUE, TRUE); | ||
841 | mutex_unlock(&mpu->mutex); | ||
842 | } | ||
843 | } | ||
844 | |||
845 | void mpu3050_early_resume(struct early_suspend *h) | ||
846 | { | ||
847 | struct mpu_private_data *mpu = container_of(h, | ||
848 | struct | ||
849 | mpu_private_data, | ||
850 | early_suspend); | ||
851 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
852 | struct i2c_adapter *accel_adapter; | ||
853 | struct i2c_adapter *compass_adapter; | ||
854 | struct i2c_adapter *pressure_adapter; | ||
855 | |||
856 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
857 | compass_adapter = | ||
858 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
859 | pressure_adapter = | ||
860 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
861 | |||
862 | if (MPU3050_EARLY_SUSPEND_IN_DRIVER) { | ||
863 | if (mpu->pid) { | ||
864 | unsigned long sensors = mldl_cfg->requested_sensors; | ||
865 | mutex_lock(&mpu->mutex); | ||
866 | (void) mpu3050_resume(mldl_cfg, | ||
867 | this_client->adapter, | ||
868 | accel_adapter, | ||
869 | compass_adapter, | ||
870 | pressure_adapter, | ||
871 | sensors & ML_THREE_AXIS_GYRO, | ||
872 | sensors & ML_THREE_AXIS_ACCEL, | ||
873 | sensors & ML_THREE_AXIS_COMPASS, | ||
874 | sensors & ML_THREE_AXIS_PRESSURE); | ||
875 | mutex_unlock(&mpu->mutex); | ||
876 | dev_dbg(&this_client->adapter->dev, | ||
877 | "%s for pid %d\n", __func__, mpu->pid); | ||
878 | } | ||
879 | } | ||
880 | dev_dbg(&this_client->adapter->dev, "%s: %d\n", __func__, h->level); | ||
881 | } | ||
882 | #endif | ||
883 | |||
884 | void mpu_shutdown(struct i2c_client *client) | ||
885 | { | ||
886 | struct mpu_private_data *mpu = | ||
887 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
888 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
889 | struct i2c_adapter *accel_adapter; | ||
890 | struct i2c_adapter *compass_adapter; | ||
891 | struct i2c_adapter *pressure_adapter; | ||
892 | |||
893 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
894 | compass_adapter = | ||
895 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
896 | pressure_adapter = | ||
897 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
898 | |||
899 | mutex_lock(&mpu->mutex); | ||
900 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
901 | accel_adapter, compass_adapter, pressure_adapter, | ||
902 | TRUE, TRUE, TRUE, TRUE); | ||
903 | mutex_unlock(&mpu->mutex); | ||
904 | dev_dbg(&this_client->adapter->dev, "%s\n", __func__); | ||
905 | } | ||
906 | |||
907 | int mpu_suspend(struct i2c_client *client, pm_message_t mesg) | ||
908 | { | ||
909 | struct mpu_private_data *mpu = | ||
910 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
911 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
912 | struct i2c_adapter *accel_adapter; | ||
913 | struct i2c_adapter *compass_adapter; | ||
914 | struct i2c_adapter *pressure_adapter; | ||
915 | |||
916 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
917 | compass_adapter = | ||
918 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
919 | pressure_adapter = | ||
920 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
921 | |||
922 | mutex_lock(&mpu->mutex); | ||
923 | if (!mldl_cfg->ignore_system_suspend) { | ||
924 | dev_dbg(&this_client->adapter->dev, | ||
925 | "%s: suspending on event %d\n", __func__, | ||
926 | mesg.event); | ||
927 | (void) mpu3050_suspend(mldl_cfg, this_client->adapter, | ||
928 | accel_adapter, compass_adapter, | ||
929 | pressure_adapter, | ||
930 | TRUE, TRUE, TRUE, TRUE); | ||
931 | } else { | ||
932 | dev_dbg(&this_client->adapter->dev, | ||
933 | "%s: Already suspended %d\n", __func__, | ||
934 | mesg.event); | ||
935 | } | ||
936 | mutex_unlock(&mpu->mutex); | ||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | int mpu_resume(struct i2c_client *client) | ||
941 | { | ||
942 | struct mpu_private_data *mpu = | ||
943 | (struct mpu_private_data *) i2c_get_clientdata(client); | ||
944 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
945 | struct i2c_adapter *accel_adapter; | ||
946 | struct i2c_adapter *compass_adapter; | ||
947 | struct i2c_adapter *pressure_adapter; | ||
948 | |||
949 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
950 | compass_adapter = | ||
951 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
952 | pressure_adapter = | ||
953 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
954 | |||
955 | mutex_lock(&mpu->mutex); | ||
956 | if (mpu->pid && !mldl_cfg->ignore_system_suspend) { | ||
957 | unsigned long sensors = mldl_cfg->requested_sensors; | ||
958 | (void) mpu3050_resume(mldl_cfg, this_client->adapter, | ||
959 | accel_adapter, | ||
960 | compass_adapter, | ||
961 | pressure_adapter, | ||
962 | sensors & ML_THREE_AXIS_GYRO, | ||
963 | sensors & ML_THREE_AXIS_ACCEL, | ||
964 | sensors & ML_THREE_AXIS_COMPASS, | ||
965 | sensors & ML_THREE_AXIS_PRESSURE); | ||
966 | dev_dbg(&this_client->adapter->dev, | ||
967 | "%s for pid %d\n", __func__, mpu->pid); | ||
968 | } | ||
969 | mutex_unlock(&mpu->mutex); | ||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | /* define which file operations are supported */ | ||
974 | static const struct file_operations mpu_fops = { | ||
975 | .owner = THIS_MODULE, | ||
976 | .read = mpu_read, | ||
977 | .poll = mpu_poll, | ||
978 | |||
979 | #if HAVE_COMPAT_IOCTL | ||
980 | .compat_ioctl = mpu_ioctl, | ||
981 | #endif | ||
982 | #if HAVE_UNLOCKED_IOCTL | ||
983 | .unlocked_ioctl = mpu_ioctl, | ||
984 | #endif | ||
985 | .open = mpu_open, | ||
986 | .release = mpu_release, | ||
987 | }; | ||
988 | |||
989 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
990 | |||
991 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
992 | I2C_CLIENT_INSMOD; | ||
993 | #endif | ||
994 | |||
995 | static struct miscdevice i2c_mpu_device = { | ||
996 | .minor = MISC_DYNAMIC_MINOR, | ||
997 | .name = "mpu", /* Same for both 3050 and 6000 */ | ||
998 | .fops = &mpu_fops, | ||
999 | }; | ||
1000 | |||
1001 | |||
1002 | int mpu3050_probe(struct i2c_client *client, | ||
1003 | const struct i2c_device_id *devid) | ||
1004 | { | ||
1005 | struct mpu3050_platform_data *pdata; | ||
1006 | struct mpu_private_data *mpu; | ||
1007 | struct mldl_cfg *mldl_cfg; | ||
1008 | int res = 0; | ||
1009 | struct i2c_adapter *accel_adapter = NULL; | ||
1010 | struct i2c_adapter *compass_adapter = NULL; | ||
1011 | struct i2c_adapter *pressure_adapter = NULL; | ||
1012 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
1013 | |||
1014 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
1015 | res = -ENODEV; | ||
1016 | goto out_check_functionality_failed; | ||
1017 | } | ||
1018 | |||
1019 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
1020 | if (!mpu) { | ||
1021 | res = -ENOMEM; | ||
1022 | goto out_alloc_data_failed; | ||
1023 | } | ||
1024 | |||
1025 | i2c_set_clientdata(client, mpu); | ||
1026 | this_client = client; | ||
1027 | mldl_cfg = &mpu->mldl_cfg; | ||
1028 | |||
1029 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
1030 | |||
1031 | mutex_init(&mpu->mutex); | ||
1032 | init_completion(&mpu->completion); | ||
1033 | |||
1034 | mpu->response_timeout = 60; /* Seconds */ | ||
1035 | mpu->timeout.function = mpu_pm_timeout; | ||
1036 | mpu->timeout.data = (u_long) mpu; | ||
1037 | init_timer(&mpu->timeout); | ||
1038 | |||
1039 | /* FIXME: | ||
1040 | * Do not register the pm_notifier as it causes | ||
1041 | * issues with resume sequence as there is no response | ||
1042 | * from user-space for power notifications for approx | ||
1043 | * 60 sec. Refer NV bug 858630 for more details. | ||
1044 | */ | ||
1045 | #if 0 | ||
1046 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
1047 | mpu->nb.priority = 0; | ||
1048 | register_pm_notifier(&mpu->nb); | ||
1049 | #endif | ||
1050 | |||
1051 | pdata = (struct mpu3050_platform_data *) client->dev.platform_data; | ||
1052 | if (!pdata) { | ||
1053 | dev_warn(&this_client->adapter->dev, | ||
1054 | "Missing platform data for mpu3050\n"); | ||
1055 | } else { | ||
1056 | mldl_cfg->pdata = pdata; | ||
1057 | |||
1058 | #if defined(CONFIG_MPU_SENSORS_MPU3050_MODULE) || \ | ||
1059 | defined(CONFIG_MPU_SENSORS_MPU6000_MODULE) | ||
1060 | pdata->accel.get_slave_descr = get_accel_slave_descr; | ||
1061 | pdata->compass.get_slave_descr = get_compass_slave_descr; | ||
1062 | pdata->pressure.get_slave_descr = get_pressure_slave_descr; | ||
1063 | #endif | ||
1064 | |||
1065 | if (pdata->accel.get_slave_descr) { | ||
1066 | mldl_cfg->accel = | ||
1067 | pdata->accel.get_slave_descr(); | ||
1068 | dev_info(&this_client->adapter->dev, | ||
1069 | "%s: +%s\n", MPU_NAME, | ||
1070 | mldl_cfg->accel->name); | ||
1071 | accel_adapter = | ||
1072 | i2c_get_adapter(pdata->accel.adapt_num); | ||
1073 | if (pdata->accel.irq > 0) { | ||
1074 | dev_info(&this_client->adapter->dev, | ||
1075 | "Installing Accel irq using %d\n", | ||
1076 | pdata->accel.irq); | ||
1077 | res = slaveirq_init(accel_adapter, | ||
1078 | &pdata->accel, | ||
1079 | "accelirq"); | ||
1080 | if (res) | ||
1081 | goto out_accelirq_failed; | ||
1082 | } else { | ||
1083 | dev_warn(&this_client->adapter->dev, | ||
1084 | "WARNING: Accel irq not assigned\n"); | ||
1085 | } | ||
1086 | } else { | ||
1087 | dev_warn(&this_client->adapter->dev, | ||
1088 | "%s: No Accel Present\n", MPU_NAME); | ||
1089 | } | ||
1090 | |||
1091 | if (pdata->compass.get_slave_descr) { | ||
1092 | mldl_cfg->compass = | ||
1093 | pdata->compass.get_slave_descr(); | ||
1094 | dev_info(&this_client->adapter->dev, | ||
1095 | "%s: +%s\n", MPU_NAME, | ||
1096 | mldl_cfg->compass->name); | ||
1097 | compass_adapter = | ||
1098 | i2c_get_adapter(pdata->compass.adapt_num); | ||
1099 | if (pdata->compass.irq > 0) { | ||
1100 | dev_info(&this_client->adapter->dev, | ||
1101 | "Installing Compass irq using %d\n", | ||
1102 | pdata->compass.irq); | ||
1103 | res = slaveirq_init(compass_adapter, | ||
1104 | &pdata->compass, | ||
1105 | "compassirq"); | ||
1106 | if (res) | ||
1107 | goto out_compassirq_failed; | ||
1108 | } else { | ||
1109 | dev_warn(&this_client->adapter->dev, | ||
1110 | "WARNING: Compass irq not assigned\n"); | ||
1111 | } | ||
1112 | } else { | ||
1113 | dev_warn(&this_client->adapter->dev, | ||
1114 | "%s: No Compass Present\n", MPU_NAME); | ||
1115 | } | ||
1116 | |||
1117 | if (pdata->pressure.get_slave_descr) { | ||
1118 | mldl_cfg->pressure = | ||
1119 | pdata->pressure.get_slave_descr(); | ||
1120 | dev_info(&this_client->adapter->dev, | ||
1121 | "%s: +%s\n", MPU_NAME, | ||
1122 | mldl_cfg->pressure->name); | ||
1123 | pressure_adapter = | ||
1124 | i2c_get_adapter(pdata->pressure.adapt_num); | ||
1125 | |||
1126 | if (pdata->pressure.irq > 0) { | ||
1127 | dev_info(&this_client->adapter->dev, | ||
1128 | "Installing Pressure irq using %d\n", | ||
1129 | pdata->pressure.irq); | ||
1130 | res = slaveirq_init(pressure_adapter, | ||
1131 | &pdata->pressure, | ||
1132 | "pressureirq"); | ||
1133 | if (res) | ||
1134 | goto out_pressureirq_failed; | ||
1135 | } else { | ||
1136 | dev_warn(&this_client->adapter->dev, | ||
1137 | "WARNING: Pressure irq not assigned\n"); | ||
1138 | } | ||
1139 | } else { | ||
1140 | dev_warn(&this_client->adapter->dev, | ||
1141 | "%s: No Pressure Present\n", MPU_NAME); | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1145 | mldl_cfg->addr = client->addr; | ||
1146 | res = mpu3050_open(&mpu->mldl_cfg, client->adapter, | ||
1147 | accel_adapter, compass_adapter, pressure_adapter); | ||
1148 | |||
1149 | if (res) { | ||
1150 | dev_err(&this_client->adapter->dev, | ||
1151 | "Unable to open %s %d\n", MPU_NAME, res); | ||
1152 | res = -ENODEV; | ||
1153 | goto out_whoami_failed; | ||
1154 | } | ||
1155 | |||
1156 | res = misc_register(&i2c_mpu_device); | ||
1157 | if (res < 0) { | ||
1158 | dev_err(&this_client->adapter->dev, | ||
1159 | "ERROR: misc_register returned %d\n", res); | ||
1160 | goto out_misc_register_failed; | ||
1161 | } | ||
1162 | |||
1163 | if (this_client->irq > 0) { | ||
1164 | dev_info(&this_client->adapter->dev, | ||
1165 | "Installing irq using %d\n", this_client->irq); | ||
1166 | res = mpuirq_init(this_client); | ||
1167 | if (res) | ||
1168 | goto out_mpuirq_failed; | ||
1169 | } else { | ||
1170 | dev_warn(&this_client->adapter->dev, | ||
1171 | "Missing %s IRQ\n", MPU_NAME); | ||
1172 | } | ||
1173 | |||
1174 | |||
1175 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
1176 | mpu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; | ||
1177 | mpu->early_suspend.suspend = mpu3050_early_suspend; | ||
1178 | mpu->early_suspend.resume = mpu3050_early_resume; | ||
1179 | register_early_suspend(&mpu->early_suspend); | ||
1180 | #endif | ||
1181 | return res; | ||
1182 | |||
1183 | out_mpuirq_failed: | ||
1184 | misc_deregister(&i2c_mpu_device); | ||
1185 | out_misc_register_failed: | ||
1186 | mpu3050_close(&mpu->mldl_cfg, client->adapter, | ||
1187 | accel_adapter, compass_adapter, pressure_adapter); | ||
1188 | out_whoami_failed: | ||
1189 | if (pdata && | ||
1190 | pdata->pressure.get_slave_descr && | ||
1191 | pdata->pressure.irq) | ||
1192 | slaveirq_exit(&pdata->pressure); | ||
1193 | out_pressureirq_failed: | ||
1194 | if (pdata && | ||
1195 | pdata->compass.get_slave_descr && | ||
1196 | pdata->compass.irq) | ||
1197 | slaveirq_exit(&pdata->compass); | ||
1198 | out_compassirq_failed: | ||
1199 | if (pdata && | ||
1200 | pdata->accel.get_slave_descr && | ||
1201 | pdata->accel.irq) | ||
1202 | slaveirq_exit(&pdata->accel); | ||
1203 | out_accelirq_failed: | ||
1204 | kfree(mpu); | ||
1205 | out_alloc_data_failed: | ||
1206 | out_check_functionality_failed: | ||
1207 | dev_err(&this_client->adapter->dev, "%s failed %d\n", __func__, | ||
1208 | res); | ||
1209 | return res; | ||
1210 | |||
1211 | } | ||
1212 | |||
1213 | static int mpu3050_remove(struct i2c_client *client) | ||
1214 | { | ||
1215 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
1216 | struct i2c_adapter *accel_adapter; | ||
1217 | struct i2c_adapter *compass_adapter; | ||
1218 | struct i2c_adapter *pressure_adapter; | ||
1219 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1220 | struct mpu3050_platform_data *pdata = mldl_cfg->pdata; | ||
1221 | |||
1222 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
1223 | compass_adapter = | ||
1224 | i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num); | ||
1225 | pressure_adapter = | ||
1226 | i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num); | ||
1227 | |||
1228 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
1229 | |||
1230 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
1231 | unregister_early_suspend(&mpu->early_suspend); | ||
1232 | #endif | ||
1233 | mpu3050_close(mldl_cfg, client->adapter, | ||
1234 | accel_adapter, compass_adapter, pressure_adapter); | ||
1235 | |||
1236 | if (client->irq) | ||
1237 | mpuirq_exit(); | ||
1238 | |||
1239 | if (pdata && | ||
1240 | pdata->pressure.get_slave_descr && | ||
1241 | pdata->pressure.irq) | ||
1242 | slaveirq_exit(&pdata->pressure); | ||
1243 | |||
1244 | if (pdata && | ||
1245 | pdata->compass.get_slave_descr && | ||
1246 | pdata->compass.irq) | ||
1247 | slaveirq_exit(&pdata->compass); | ||
1248 | |||
1249 | if (pdata && | ||
1250 | pdata->accel.get_slave_descr && | ||
1251 | pdata->accel.irq) | ||
1252 | slaveirq_exit(&pdata->accel); | ||
1253 | |||
1254 | misc_deregister(&i2c_mpu_device); | ||
1255 | kfree(mpu); | ||
1256 | |||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static const struct i2c_device_id mpu3050_id[] = { | ||
1261 | {MPU_NAME, 0}, | ||
1262 | {} | ||
1263 | }; | ||
1264 | |||
1265 | MODULE_DEVICE_TABLE(i2c, mpu3050_id); | ||
1266 | |||
1267 | static struct i2c_driver mpu3050_driver = { | ||
1268 | .class = I2C_CLASS_HWMON, | ||
1269 | .probe = mpu3050_probe, | ||
1270 | .remove = mpu3050_remove, | ||
1271 | .id_table = mpu3050_id, | ||
1272 | .driver = { | ||
1273 | .owner = THIS_MODULE, | ||
1274 | .name = MPU_NAME, | ||
1275 | }, | ||
1276 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
1277 | .address_data = &addr_data, | ||
1278 | #else | ||
1279 | .address_list = normal_i2c, | ||
1280 | #endif | ||
1281 | |||
1282 | .shutdown = mpu_shutdown, /* optional */ | ||
1283 | .suspend = mpu_suspend, /* optional */ | ||
1284 | .resume = mpu_resume, /* optional */ | ||
1285 | |||
1286 | }; | ||
1287 | |||
1288 | static int __init mpu_init(void) | ||
1289 | { | ||
1290 | int res = i2c_add_driver(&mpu3050_driver); | ||
1291 | pr_debug("%s\n", __func__); | ||
1292 | if (res) | ||
1293 | pr_err("%s failed\n", | ||
1294 | __func__); | ||
1295 | return res; | ||
1296 | } | ||
1297 | |||
1298 | static void __exit mpu_exit(void) | ||
1299 | { | ||
1300 | pr_debug("%s\n", __func__); | ||
1301 | i2c_del_driver(&mpu3050_driver); | ||
1302 | } | ||
1303 | |||
1304 | module_init(mpu_init); | ||
1305 | module_exit(mpu_exit); | ||
1306 | |||
1307 | MODULE_AUTHOR("Invensense Corporation"); | ||
1308 | MODULE_DESCRIPTION("User space character device interface for MPU3050"); | ||
1309 | MODULE_LICENSE("GPL"); | ||
1310 | MODULE_ALIAS(MPU_NAME); | ||
diff --git a/drivers/misc/mpu3050/mpu-i2c.c b/drivers/misc/mpu3050/mpu-i2c.c new file mode 100644 index 00000000000..b1298d313ab --- /dev/null +++ b/drivers/misc/mpu3050/mpu-i2c.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 mpu-i2c.c | ||
26 | * @brief | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/i2c.h> | ||
31 | #include "mpu.h" | ||
32 | |||
33 | int sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
34 | unsigned char address, | ||
35 | unsigned int len, unsigned char const *data) | ||
36 | { | ||
37 | struct i2c_msg msgs[1]; | ||
38 | int res; | ||
39 | |||
40 | if (NULL == data || NULL == i2c_adap) | ||
41 | return -EINVAL; | ||
42 | |||
43 | msgs[0].addr = address; | ||
44 | msgs[0].flags = 0; /* write */ | ||
45 | msgs[0].buf = (unsigned char *) data; | ||
46 | msgs[0].len = len; | ||
47 | |||
48 | res = i2c_transfer(i2c_adap, msgs, 1); | ||
49 | if (res < 1) | ||
50 | return res; | ||
51 | else | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | int sensor_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
56 | unsigned char address, | ||
57 | unsigned char reg, unsigned char value) | ||
58 | { | ||
59 | unsigned char data[2]; | ||
60 | |||
61 | data[0] = reg; | ||
62 | data[1] = value; | ||
63 | return sensor_i2c_write(i2c_adap, address, 2, data); | ||
64 | } | ||
65 | |||
66 | int sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
67 | unsigned char address, | ||
68 | unsigned char reg, | ||
69 | unsigned int len, unsigned char *data) | ||
70 | { | ||
71 | struct i2c_msg msgs[2]; | ||
72 | int res; | ||
73 | |||
74 | if (NULL == data || NULL == i2c_adap) | ||
75 | return -EINVAL; | ||
76 | |||
77 | msgs[0].addr = address; | ||
78 | msgs[0].flags = 0; /* write */ | ||
79 | msgs[0].buf = ® | ||
80 | msgs[0].len = 1; | ||
81 | |||
82 | msgs[1].addr = address; | ||
83 | msgs[1].flags = I2C_M_RD; | ||
84 | msgs[1].buf = data; | ||
85 | msgs[1].len = len; | ||
86 | |||
87 | res = i2c_transfer(i2c_adap, msgs, 2); | ||
88 | if (res < 2) | ||
89 | return res; | ||
90 | else | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
95 | unsigned char mpu_addr, | ||
96 | unsigned short mem_addr, | ||
97 | unsigned int len, unsigned char *data) | ||
98 | { | ||
99 | unsigned char bank[2]; | ||
100 | unsigned char addr[2]; | ||
101 | unsigned char buf; | ||
102 | |||
103 | struct i2c_msg msgs[4]; | ||
104 | int ret; | ||
105 | |||
106 | if (NULL == data || NULL == i2c_adap) | ||
107 | return -EINVAL; | ||
108 | |||
109 | bank[0] = MPUREG_BANK_SEL; | ||
110 | bank[1] = mem_addr >> 8; | ||
111 | |||
112 | addr[0] = MPUREG_MEM_START_ADDR; | ||
113 | addr[1] = mem_addr & 0xFF; | ||
114 | |||
115 | buf = MPUREG_MEM_R_W; | ||
116 | |||
117 | /* Write Message */ | ||
118 | msgs[0].addr = mpu_addr; | ||
119 | msgs[0].flags = 0; | ||
120 | msgs[0].buf = bank; | ||
121 | msgs[0].len = sizeof(bank); | ||
122 | |||
123 | msgs[1].addr = mpu_addr; | ||
124 | msgs[1].flags = 0; | ||
125 | msgs[1].buf = addr; | ||
126 | msgs[1].len = sizeof(addr); | ||
127 | |||
128 | msgs[2].addr = mpu_addr; | ||
129 | msgs[2].flags = 0; | ||
130 | msgs[2].buf = &buf; | ||
131 | msgs[2].len = 1; | ||
132 | |||
133 | msgs[3].addr = mpu_addr; | ||
134 | msgs[3].flags = I2C_M_RD; | ||
135 | msgs[3].buf = data; | ||
136 | msgs[3].len = len; | ||
137 | |||
138 | ret = i2c_transfer(i2c_adap, msgs, 4); | ||
139 | if (ret != 4) | ||
140 | return ret; | ||
141 | else | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
146 | unsigned char mpu_addr, | ||
147 | unsigned short mem_addr, | ||
148 | unsigned int len, unsigned char const *data) | ||
149 | { | ||
150 | unsigned char bank[2]; | ||
151 | unsigned char addr[2]; | ||
152 | unsigned char buf[513]; | ||
153 | |||
154 | struct i2c_msg msgs[3]; | ||
155 | int ret; | ||
156 | |||
157 | if (NULL == data || NULL == i2c_adap) | ||
158 | return -EINVAL; | ||
159 | if (len >= (sizeof(buf) - 1)) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | bank[0] = MPUREG_BANK_SEL; | ||
163 | bank[1] = mem_addr >> 8; | ||
164 | |||
165 | addr[0] = MPUREG_MEM_START_ADDR; | ||
166 | addr[1] = mem_addr & 0xFF; | ||
167 | |||
168 | buf[0] = MPUREG_MEM_R_W; | ||
169 | memcpy(buf + 1, data, len); | ||
170 | |||
171 | /* Write Message */ | ||
172 | msgs[0].addr = mpu_addr; | ||
173 | msgs[0].flags = 0; | ||
174 | msgs[0].buf = bank; | ||
175 | msgs[0].len = sizeof(bank); | ||
176 | |||
177 | msgs[1].addr = mpu_addr; | ||
178 | msgs[1].flags = 0; | ||
179 | msgs[1].buf = addr; | ||
180 | msgs[1].len = sizeof(addr); | ||
181 | |||
182 | msgs[2].addr = mpu_addr; | ||
183 | msgs[2].flags = 0; | ||
184 | msgs[2].buf = (unsigned char *) buf; | ||
185 | msgs[2].len = len + 1; | ||
186 | |||
187 | ret = i2c_transfer(i2c_adap, msgs, 3); | ||
188 | if (ret != 3) | ||
189 | return ret; | ||
190 | else | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * @} | ||
196 | */ | ||
diff --git a/drivers/misc/mpu3050/mpu-i2c.h b/drivers/misc/mpu3050/mpu-i2c.h new file mode 100644 index 00000000000..0bbc8c64594 --- /dev/null +++ b/drivers/misc/mpu3050/mpu-i2c.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 | * @defgroup | ||
21 | * @brief | ||
22 | * | ||
23 | * @{ | ||
24 | * @file mpu-i2c.c | ||
25 | * @brief | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef __MPU_I2C_H__ | ||
31 | #define __MPU_I2C_H__ | ||
32 | |||
33 | #include <linux/i2c.h> | ||
34 | |||
35 | int sensor_i2c_write(struct i2c_adapter *i2c_adap, | ||
36 | unsigned char address, | ||
37 | unsigned int len, unsigned char const *data); | ||
38 | |||
39 | int sensor_i2c_write_register(struct i2c_adapter *i2c_adap, | ||
40 | unsigned char address, | ||
41 | unsigned char reg, unsigned char value); | ||
42 | |||
43 | int sensor_i2c_read(struct i2c_adapter *i2c_adap, | ||
44 | unsigned char address, | ||
45 | unsigned char reg, | ||
46 | unsigned int len, unsigned char *data); | ||
47 | |||
48 | int mpu_memory_read(struct i2c_adapter *i2c_adap, | ||
49 | unsigned char mpu_addr, | ||
50 | unsigned short mem_addr, | ||
51 | unsigned int len, unsigned char *data); | ||
52 | |||
53 | int mpu_memory_write(struct i2c_adapter *i2c_adap, | ||
54 | unsigned char mpu_addr, | ||
55 | unsigned short mem_addr, | ||
56 | unsigned int len, unsigned char const *data); | ||
57 | |||
58 | #endif /* __MPU_I2C_H__ */ | ||
diff --git a/drivers/misc/mpu3050/mpuirq.c b/drivers/misc/mpu3050/mpuirq.c new file mode 100644 index 00000000000..ce1ad409cbf --- /dev/null +++ b/drivers/misc/mpu3050/mpuirq.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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/workqueue.h> | ||
31 | #include <linux/poll.h> | ||
32 | |||
33 | #include <linux/errno.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/mm.h> | ||
36 | #include <linux/sched.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/uaccess.h> | ||
39 | #include <linux/io.h> | ||
40 | |||
41 | #include "mpu.h" | ||
42 | #include "mpuirq.h" | ||
43 | #include "mldl_cfg.h" | ||
44 | #include "mpu-i2c.h" | ||
45 | |||
46 | #define MPUIRQ_NAME "mpuirq" | ||
47 | |||
48 | /* function which gets accel data and sends it to MPU */ | ||
49 | |||
50 | DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait); | ||
51 | |||
52 | struct mpuirq_dev_data { | ||
53 | struct work_struct work; | ||
54 | struct i2c_client *mpu_client; | ||
55 | struct miscdevice *dev; | ||
56 | int irq; | ||
57 | int pid; | ||
58 | int accel_divider; | ||
59 | int data_ready; | ||
60 | int timeout; | ||
61 | }; | ||
62 | |||
63 | static struct mpuirq_dev_data mpuirq_dev_data; | ||
64 | static struct mpuirq_data mpuirq_data; | ||
65 | static char *interface = MPUIRQ_NAME; | ||
66 | |||
67 | static void mpu_accel_data_work_fcn(struct work_struct *work); | ||
68 | |||
69 | static int mpuirq_open(struct inode *inode, struct file *file) | ||
70 | { | ||
71 | dev_dbg(mpuirq_dev_data.dev->this_device, | ||
72 | "%s current->pid %d\n", __func__, current->pid); | ||
73 | mpuirq_dev_data.pid = current->pid; | ||
74 | file->private_data = &mpuirq_dev_data; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /* close function - called when the "file" /dev/mpuirq is closed in userspace */ | ||
79 | static int mpuirq_release(struct inode *inode, struct file *file) | ||
80 | { | ||
81 | dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n"); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /* read function called when from /dev/mpuirq is read */ | ||
86 | static ssize_t mpuirq_read(struct file *file, | ||
87 | char *buf, size_t count, loff_t *ppos) | ||
88 | { | ||
89 | int len, err; | ||
90 | struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data; | ||
91 | |||
92 | if (!mpuirq_dev_data.data_ready && | ||
93 | mpuirq_dev_data.timeout && | ||
94 | (!(file->f_flags & O_NONBLOCK))) { | ||
95 | wait_event_interruptible_timeout(mpuirq_wait, | ||
96 | mpuirq_dev_data. | ||
97 | data_ready, | ||
98 | mpuirq_dev_data.timeout); | ||
99 | } | ||
100 | |||
101 | if (mpuirq_dev_data.data_ready && NULL != buf | ||
102 | && count >= sizeof(mpuirq_data)) { | ||
103 | err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data)); | ||
104 | mpuirq_data.data_type = 0; | ||
105 | } else { | ||
106 | return 0; | ||
107 | } | ||
108 | if (err != 0) { | ||
109 | dev_err(p_mpuirq_dev_data->dev->this_device, | ||
110 | "Copy to user returned %d\n", err); | ||
111 | return -EFAULT; | ||
112 | } | ||
113 | mpuirq_dev_data.data_ready = 0; | ||
114 | len = sizeof(mpuirq_data); | ||
115 | return len; | ||
116 | } | ||
117 | |||
118 | unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll) | ||
119 | { | ||
120 | int mask = 0; | ||
121 | |||
122 | poll_wait(file, &mpuirq_wait, poll); | ||
123 | if (mpuirq_dev_data.data_ready) | ||
124 | mask |= POLLIN | POLLRDNORM; | ||
125 | return mask; | ||
126 | } | ||
127 | |||
128 | /* ioctl - I/O control */ | ||
129 | static long mpuirq_ioctl(struct file *file, | ||
130 | unsigned int cmd, unsigned long arg) | ||
131 | { | ||
132 | int retval = 0; | ||
133 | int data; | ||
134 | |||
135 | switch (cmd) { | ||
136 | case MPUIRQ_SET_TIMEOUT: | ||
137 | mpuirq_dev_data.timeout = arg; | ||
138 | break; | ||
139 | |||
140 | case MPUIRQ_GET_INTERRUPT_CNT: | ||
141 | data = mpuirq_data.interruptcount - 1; | ||
142 | if (mpuirq_data.interruptcount > 1) | ||
143 | mpuirq_data.interruptcount = 1; | ||
144 | |||
145 | if (copy_to_user((int *) arg, &data, sizeof(int))) | ||
146 | return -EFAULT; | ||
147 | break; | ||
148 | case MPUIRQ_GET_IRQ_TIME: | ||
149 | if (copy_to_user((int *) arg, &mpuirq_data.irqtime, | ||
150 | sizeof(mpuirq_data.irqtime))) | ||
151 | return -EFAULT; | ||
152 | mpuirq_data.irqtime = 0; | ||
153 | break; | ||
154 | case MPUIRQ_SET_FREQUENCY_DIVIDER: | ||
155 | mpuirq_dev_data.accel_divider = arg; | ||
156 | break; | ||
157 | default: | ||
158 | retval = -EINVAL; | ||
159 | } | ||
160 | return retval; | ||
161 | } | ||
162 | |||
163 | static void mpu_accel_data_work_fcn(struct work_struct *work) | ||
164 | { | ||
165 | struct mpuirq_dev_data *mpuirq_dev_data = | ||
166 | (struct mpuirq_dev_data *) work; | ||
167 | struct mldl_cfg *mldl_cfg = | ||
168 | (struct mldl_cfg *) | ||
169 | i2c_get_clientdata(mpuirq_dev_data->mpu_client); | ||
170 | struct i2c_adapter *accel_adapter; | ||
171 | unsigned char wbuff[16]; | ||
172 | unsigned char rbuff[16]; | ||
173 | int ii; | ||
174 | |||
175 | accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num); | ||
176 | mldl_cfg->accel->read(accel_adapter, | ||
177 | mldl_cfg->accel, | ||
178 | &mldl_cfg->pdata->accel, rbuff); | ||
179 | |||
180 | |||
181 | /* @todo add other data formats here as well */ | ||
182 | if (EXT_SLAVE_BIG_ENDIAN == mldl_cfg->accel->endian) { | ||
183 | for (ii = 0; ii < 3; ii++) { | ||
184 | wbuff[2 * ii + 1] = rbuff[2 * ii + 1]; | ||
185 | wbuff[2 * ii + 2] = rbuff[2 * ii + 0]; | ||
186 | } | ||
187 | } else { | ||
188 | memcpy(wbuff + 1, rbuff, mldl_cfg->accel->len); | ||
189 | } | ||
190 | |||
191 | wbuff[7] = 0; | ||
192 | wbuff[8] = 1; /*set semaphore */ | ||
193 | |||
194 | mpu_memory_write(mpuirq_dev_data->mpu_client->adapter, | ||
195 | mldl_cfg->addr, 0x0108, 8, wbuff); | ||
196 | } | ||
197 | |||
198 | static irqreturn_t mpuirq_handler(int irq, void *dev_id) | ||
199 | { | ||
200 | static int mycount; | ||
201 | struct timeval irqtime; | ||
202 | mycount++; | ||
203 | |||
204 | mpuirq_data.interruptcount++; | ||
205 | |||
206 | /* wake up (unblock) for reading data from userspace */ | ||
207 | /* and ignore first interrupt generated in module init */ | ||
208 | mpuirq_dev_data.data_ready = 1; | ||
209 | |||
210 | do_gettimeofday(&irqtime); | ||
211 | mpuirq_data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
212 | mpuirq_data.irqtime += irqtime.tv_usec; | ||
213 | |||
214 | if ((mpuirq_dev_data.accel_divider >= 0) && | ||
215 | (0 == (mycount % (mpuirq_dev_data.accel_divider + 1)))) { | ||
216 | schedule_work((struct work_struct | ||
217 | *) (&mpuirq_dev_data)); | ||
218 | } | ||
219 | |||
220 | wake_up_interruptible(&mpuirq_wait); | ||
221 | |||
222 | return IRQ_HANDLED; | ||
223 | |||
224 | } | ||
225 | |||
226 | /* define which file operations are supported */ | ||
227 | const struct file_operations mpuirq_fops = { | ||
228 | .owner = THIS_MODULE, | ||
229 | .read = mpuirq_read, | ||
230 | .poll = mpuirq_poll, | ||
231 | |||
232 | #if HAVE_COMPAT_IOCTL | ||
233 | .compat_ioctl = mpuirq_ioctl, | ||
234 | #endif | ||
235 | #if HAVE_UNLOCKED_IOCTL | ||
236 | .unlocked_ioctl = mpuirq_ioctl, | ||
237 | #endif | ||
238 | .open = mpuirq_open, | ||
239 | .release = mpuirq_release, | ||
240 | }; | ||
241 | |||
242 | static struct miscdevice mpuirq_device = { | ||
243 | .minor = MISC_DYNAMIC_MINOR, | ||
244 | .name = MPUIRQ_NAME, | ||
245 | .fops = &mpuirq_fops, | ||
246 | }; | ||
247 | |||
248 | int mpuirq_init(struct i2c_client *mpu_client) | ||
249 | { | ||
250 | |||
251 | int res; | ||
252 | struct mldl_cfg *mldl_cfg = | ||
253 | (struct mldl_cfg *) i2c_get_clientdata(mpu_client); | ||
254 | |||
255 | /* work_struct initialization */ | ||
256 | INIT_WORK((struct work_struct *) &mpuirq_dev_data, | ||
257 | mpu_accel_data_work_fcn); | ||
258 | mpuirq_dev_data.mpu_client = mpu_client; | ||
259 | |||
260 | dev_info(&mpu_client->adapter->dev, | ||
261 | "Module Param interface = %s\n", interface); | ||
262 | |||
263 | mpuirq_dev_data.irq = mpu_client->irq; | ||
264 | mpuirq_dev_data.pid = 0; | ||
265 | mpuirq_dev_data.accel_divider = -1; | ||
266 | mpuirq_dev_data.data_ready = 0; | ||
267 | mpuirq_dev_data.timeout = 0; | ||
268 | mpuirq_dev_data.dev = &mpuirq_device; | ||
269 | |||
270 | if (mpuirq_dev_data.irq) { | ||
271 | unsigned long flags; | ||
272 | if (BIT_ACTL_LOW == | ||
273 | ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
274 | flags = IRQF_TRIGGER_FALLING; | ||
275 | else | ||
276 | flags = IRQF_TRIGGER_RISING; | ||
277 | |||
278 | res = | ||
279 | request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags, | ||
280 | interface, &mpuirq_dev_data.irq); | ||
281 | if (res) { | ||
282 | dev_err(&mpu_client->adapter->dev, | ||
283 | "myirqtest: cannot register IRQ %d\n", | ||
284 | mpuirq_dev_data.irq); | ||
285 | } else { | ||
286 | res = misc_register(&mpuirq_device); | ||
287 | if (res < 0) { | ||
288 | dev_err(&mpu_client->adapter->dev, | ||
289 | "misc_register returned %d\n", | ||
290 | res); | ||
291 | free_irq(mpuirq_dev_data.irq, | ||
292 | &mpuirq_dev_data.irq); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | } else { | ||
297 | res = 0; | ||
298 | } | ||
299 | |||
300 | return res; | ||
301 | } | ||
302 | |||
303 | void mpuirq_exit(void) | ||
304 | { | ||
305 | /* Free the IRQ first before flushing the work */ | ||
306 | if (mpuirq_dev_data.irq > 0) | ||
307 | free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq); | ||
308 | |||
309 | flush_scheduled_work(); | ||
310 | |||
311 | dev_info(mpuirq_device.this_device, "Unregistering %s\n", | ||
312 | MPUIRQ_NAME); | ||
313 | misc_deregister(&mpuirq_device); | ||
314 | |||
315 | return; | ||
316 | } | ||
317 | |||
318 | module_param(interface, charp, S_IRUGO | S_IWUSR); | ||
319 | MODULE_PARM_DESC(interface, "The Interface name"); | ||
diff --git a/drivers/misc/mpu3050/mpuirq.h b/drivers/misc/mpu3050/mpuirq.h new file mode 100644 index 00000000000..a71c79c75e8 --- /dev/null +++ b/drivers/misc/mpu3050/mpuirq.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 | #ifdef __KERNEL__ | ||
24 | #include <linux/i2c-dev.h> | ||
25 | #include <linux/time.h> | ||
26 | #else | ||
27 | #include <sys/time.h> | ||
28 | #endif | ||
29 | |||
30 | #define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long) | ||
31 | #define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long) | ||
32 | #define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval) | ||
33 | #define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long) | ||
34 | |||
35 | #ifdef __KERNEL__ | ||
36 | |||
37 | void mpuirq_exit(void); | ||
38 | int mpuirq_init(struct i2c_client *mpu_client); | ||
39 | |||
40 | #endif | ||
41 | |||
42 | #endif | ||
diff --git a/drivers/misc/mpu3050/slaveirq.c b/drivers/misc/mpu3050/slaveirq.c new file mode 100644 index 00000000000..a3c7bfec4b4 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.c | |||
@@ -0,0 +1,273 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 "mpu.h" | ||
43 | #include "slaveirq.h" | ||
44 | #include "mldl_cfg.h" | ||
45 | #include "mpu-i2c.h" | ||
46 | |||
47 | /* function which gets slave data and sends it to SLAVE */ | ||
48 | |||
49 | struct slaveirq_dev_data { | ||
50 | struct miscdevice dev; | ||
51 | struct i2c_client *slave_client; | ||
52 | struct mpuirq_data data; | ||
53 | wait_queue_head_t slaveirq_wait; | ||
54 | int irq; | ||
55 | int pid; | ||
56 | int data_ready; | ||
57 | int timeout; | ||
58 | }; | ||
59 | |||
60 | /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 | ||
61 | * drivers: misc: pass miscdevice pointer via file private data | ||
62 | */ | ||
63 | static int slaveirq_open(struct inode *inode, struct file *file) | ||
64 | { | ||
65 | /* Device node is availabe in the file->private_data, this is | ||
66 | * exactly what we want so we leave it there */ | ||
67 | struct slaveirq_dev_data *data = | ||
68 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
69 | |||
70 | dev_dbg(data->dev.this_device, | ||
71 | "%s current->pid %d\n", __func__, current->pid); | ||
72 | data->pid = current->pid; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int slaveirq_release(struct inode *inode, struct file *file) | ||
77 | { | ||
78 | struct slaveirq_dev_data *data = | ||
79 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
80 | dev_dbg(data->dev.this_device, "slaveirq_release\n"); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* read function called when from /dev/slaveirq is read */ | ||
85 | static ssize_t slaveirq_read(struct file *file, | ||
86 | char *buf, size_t count, loff_t *ppos) | ||
87 | { | ||
88 | int len, err; | ||
89 | struct slaveirq_dev_data *data = | ||
90 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
91 | |||
92 | if (!data->data_ready && | ||
93 | data->timeout && | ||
94 | !(file->f_flags & O_NONBLOCK)) { | ||
95 | wait_event_interruptible_timeout(data->slaveirq_wait, | ||
96 | data->data_ready, | ||
97 | data->timeout); | ||
98 | } | ||
99 | |||
100 | if (data->data_ready && NULL != buf | ||
101 | && count >= sizeof(data->data)) { | ||
102 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
103 | data->data.data_type = 0; | ||
104 | } else { | ||
105 | return 0; | ||
106 | } | ||
107 | if (err != 0) { | ||
108 | dev_err(data->dev.this_device, | ||
109 | "Copy to user returned %d\n", err); | ||
110 | return -EFAULT; | ||
111 | } | ||
112 | data->data_ready = 0; | ||
113 | len = sizeof(data->data); | ||
114 | return len; | ||
115 | } | ||
116 | |||
117 | static unsigned int slaveirq_poll(struct file *file, | ||
118 | struct poll_table_struct *poll) | ||
119 | { | ||
120 | int mask = 0; | ||
121 | struct slaveirq_dev_data *data = | ||
122 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
123 | |||
124 | poll_wait(file, &data->slaveirq_wait, poll); | ||
125 | if (data->data_ready) | ||
126 | mask |= POLLIN | POLLRDNORM; | ||
127 | return mask; | ||
128 | } | ||
129 | |||
130 | /* ioctl - I/O control */ | ||
131 | static long slaveirq_ioctl(struct file *file, | ||
132 | unsigned int cmd, unsigned long arg) | ||
133 | { | ||
134 | int retval = 0; | ||
135 | int tmp; | ||
136 | struct slaveirq_dev_data *data = | ||
137 | container_of(file->private_data, struct slaveirq_dev_data, dev); | ||
138 | |||
139 | switch (cmd) { | ||
140 | case SLAVEIRQ_SET_TIMEOUT: | ||
141 | data->timeout = arg; | ||
142 | break; | ||
143 | |||
144 | case SLAVEIRQ_GET_INTERRUPT_CNT: | ||
145 | tmp = data->data.interruptcount - 1; | ||
146 | if (data->data.interruptcount > 1) | ||
147 | data->data.interruptcount = 1; | ||
148 | |||
149 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
150 | return -EFAULT; | ||
151 | break; | ||
152 | case SLAVEIRQ_GET_IRQ_TIME: | ||
153 | if (copy_to_user((int *) arg, &data->data.irqtime, | ||
154 | sizeof(data->data.irqtime))) | ||
155 | return -EFAULT; | ||
156 | data->data.irqtime = 0; | ||
157 | break; | ||
158 | default: | ||
159 | retval = -EINVAL; | ||
160 | } | ||
161 | return retval; | ||
162 | } | ||
163 | |||
164 | static irqreturn_t slaveirq_handler(int irq, void *dev_id) | ||
165 | { | ||
166 | struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; | ||
167 | static int mycount; | ||
168 | struct timeval irqtime; | ||
169 | mycount++; | ||
170 | |||
171 | data->data.interruptcount++; | ||
172 | |||
173 | /* wake up (unblock) for reading data from userspace */ | ||
174 | data->data_ready = 1; | ||
175 | |||
176 | do_gettimeofday(&irqtime); | ||
177 | data->data.irqtime = (((long long) irqtime.tv_sec) << 32); | ||
178 | data->data.irqtime += irqtime.tv_usec; | ||
179 | data->data.data_type |= 1; | ||
180 | |||
181 | wake_up_interruptible(&data->slaveirq_wait); | ||
182 | |||
183 | return IRQ_HANDLED; | ||
184 | |||
185 | } | ||
186 | |||
187 | /* define which file operations are supported */ | ||
188 | static const struct file_operations slaveirq_fops = { | ||
189 | .owner = THIS_MODULE, | ||
190 | .read = slaveirq_read, | ||
191 | .poll = slaveirq_poll, | ||
192 | |||
193 | #if HAVE_COMPAT_IOCTL | ||
194 | .compat_ioctl = slaveirq_ioctl, | ||
195 | #endif | ||
196 | #if HAVE_UNLOCKED_IOCTL | ||
197 | .unlocked_ioctl = slaveirq_ioctl, | ||
198 | #endif | ||
199 | .open = slaveirq_open, | ||
200 | .release = slaveirq_release, | ||
201 | }; | ||
202 | |||
203 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
204 | struct ext_slave_platform_data *pdata, | ||
205 | char *name) | ||
206 | { | ||
207 | |||
208 | int res; | ||
209 | struct slaveirq_dev_data *data; | ||
210 | |||
211 | if (!pdata->irq) | ||
212 | return -EINVAL; | ||
213 | |||
214 | pdata->irq_data = kzalloc(sizeof(*data), | ||
215 | GFP_KERNEL); | ||
216 | data = (struct slaveirq_dev_data *) pdata->irq_data; | ||
217 | if (!data) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | data->dev.minor = MISC_DYNAMIC_MINOR; | ||
221 | data->dev.name = name; | ||
222 | data->dev.fops = &slaveirq_fops; | ||
223 | data->irq = pdata->irq; | ||
224 | data->pid = 0; | ||
225 | data->data_ready = 0; | ||
226 | data->timeout = 0; | ||
227 | |||
228 | init_waitqueue_head(&data->slaveirq_wait); | ||
229 | |||
230 | res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING, | ||
231 | data->dev.name, data); | ||
232 | |||
233 | if (res) { | ||
234 | dev_err(&slave_adapter->dev, | ||
235 | "myirqtest: cannot register IRQ %d\n", | ||
236 | data->irq); | ||
237 | goto out_request_irq; | ||
238 | } | ||
239 | |||
240 | res = misc_register(&data->dev); | ||
241 | if (res < 0) { | ||
242 | dev_err(&slave_adapter->dev, | ||
243 | "misc_register returned %d\n", | ||
244 | res); | ||
245 | goto out_misc_register; | ||
246 | } | ||
247 | |||
248 | return res; | ||
249 | |||
250 | out_misc_register: | ||
251 | free_irq(data->irq, data); | ||
252 | out_request_irq: | ||
253 | kfree(pdata->irq_data); | ||
254 | pdata->irq_data = NULL; | ||
255 | |||
256 | return res; | ||
257 | } | ||
258 | |||
259 | void slaveirq_exit(struct ext_slave_platform_data *pdata) | ||
260 | { | ||
261 | struct slaveirq_dev_data *data = pdata->irq_data; | ||
262 | |||
263 | if (!pdata->irq_data || data->irq <= 0) | ||
264 | return; | ||
265 | |||
266 | dev_info(data->dev.this_device, "Unregistering %s\n", | ||
267 | data->dev.name); | ||
268 | |||
269 | free_irq(data->irq, data); | ||
270 | misc_deregister(&data->dev); | ||
271 | kfree(pdata->irq_data); | ||
272 | pdata->irq_data = NULL; | ||
273 | } | ||
diff --git a/drivers/misc/mpu3050/slaveirq.h b/drivers/misc/mpu3050/slaveirq.h new file mode 100644 index 00000000000..b4e1115f1b0 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 | #ifdef __KERNEL__ | ||
24 | #include <linux/i2c-dev.h> | ||
25 | #endif | ||
26 | |||
27 | #include "mpu.h" | ||
28 | #include "mpuirq.h" | ||
29 | |||
30 | #define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long) | ||
31 | #define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long) | ||
32 | #define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long) | ||
33 | |||
34 | #ifdef __KERNEL__ | ||
35 | |||
36 | void slaveirq_exit(struct ext_slave_platform_data *pdata); | ||
37 | int slaveirq_init(struct i2c_adapter *slave_adapter, | ||
38 | struct ext_slave_platform_data *pdata, | ||
39 | char *name); | ||
40 | |||
41 | #endif | ||
42 | |||
43 | #endif | ||
diff --git a/drivers/misc/mpu3050/timerirq.c b/drivers/misc/mpu3050/timerirq.c new file mode 100644 index 00000000000..41c3ac98101 --- /dev/null +++ b/drivers/misc/mpu3050/timerirq.c | |||
@@ -0,0 +1,299 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 "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 && | ||
164 | data->timeout && | ||
165 | !(file->f_flags & O_NONBLOCK)) { | ||
166 | wait_event_interruptible_timeout(data->timerirq_wait, | ||
167 | data->data_ready, | ||
168 | data->timeout); | ||
169 | } | ||
170 | |||
171 | if (data->data_ready && NULL != buf | ||
172 | && count >= sizeof(data->data)) { | ||
173 | err = copy_to_user(buf, &data->data, sizeof(data->data)); | ||
174 | data->data.data_type = 0; | ||
175 | } else { | ||
176 | return 0; | ||
177 | } | ||
178 | if (err != 0) { | ||
179 | dev_err(data->dev->this_device, | ||
180 | "Copy to user returned %d\n", err); | ||
181 | return -EFAULT; | ||
182 | } | ||
183 | data->data_ready = 0; | ||
184 | len = sizeof(data->data); | ||
185 | return len; | ||
186 | } | ||
187 | |||
188 | static unsigned int timerirq_poll(struct file *file, | ||
189 | struct poll_table_struct *poll) | ||
190 | { | ||
191 | int mask = 0; | ||
192 | struct timerirq_data *data = file->private_data; | ||
193 | |||
194 | poll_wait(file, &data->timerirq_wait, poll); | ||
195 | if (data->data_ready) | ||
196 | mask |= POLLIN | POLLRDNORM; | ||
197 | return mask; | ||
198 | } | ||
199 | |||
200 | /* ioctl - I/O control */ | ||
201 | static long timerirq_ioctl(struct file *file, | ||
202 | unsigned int cmd, unsigned long arg) | ||
203 | { | ||
204 | int retval = 0; | ||
205 | int tmp; | ||
206 | struct timerirq_data *data = file->private_data; | ||
207 | |||
208 | dev_dbg(data->dev->this_device, | ||
209 | "%s current->pid %d, %d, %ld\n", | ||
210 | __func__, current->pid, cmd, arg); | ||
211 | |||
212 | if (!data) | ||
213 | return -EFAULT; | ||
214 | |||
215 | switch (cmd) { | ||
216 | case TIMERIRQ_SET_TIMEOUT: | ||
217 | data->timeout = arg; | ||
218 | break; | ||
219 | case TIMERIRQ_GET_INTERRUPT_CNT: | ||
220 | tmp = data->data.interruptcount - 1; | ||
221 | if (data->data.interruptcount > 1) | ||
222 | data->data.interruptcount = 1; | ||
223 | |||
224 | if (copy_to_user((int *) arg, &tmp, sizeof(int))) | ||
225 | return -EFAULT; | ||
226 | break; | ||
227 | case TIMERIRQ_START: | ||
228 | data->period = arg; | ||
229 | retval = start_timerirq(data); | ||
230 | break; | ||
231 | case TIMERIRQ_STOP: | ||
232 | retval = stop_timerirq(data); | ||
233 | break; | ||
234 | default: | ||
235 | retval = -EINVAL; | ||
236 | } | ||
237 | return retval; | ||
238 | } | ||
239 | |||
240 | /* define which file operations are supported */ | ||
241 | static const struct file_operations timerirq_fops = { | ||
242 | .owner = THIS_MODULE, | ||
243 | .read = timerirq_read, | ||
244 | .poll = timerirq_poll, | ||
245 | |||
246 | #if HAVE_COMPAT_IOCTL | ||
247 | .compat_ioctl = timerirq_ioctl, | ||
248 | #endif | ||
249 | #if HAVE_UNLOCKED_IOCTL | ||
250 | .unlocked_ioctl = timerirq_ioctl, | ||
251 | #endif | ||
252 | .open = timerirq_open, | ||
253 | .release = timerirq_release, | ||
254 | }; | ||
255 | |||
256 | static int __init timerirq_init(void) | ||
257 | { | ||
258 | |||
259 | int res; | ||
260 | static struct miscdevice *data; | ||
261 | |||
262 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
263 | if (!data) | ||
264 | return -ENOMEM; | ||
265 | timerirq_dev_data = data; | ||
266 | data->minor = MISC_DYNAMIC_MINOR; | ||
267 | data->name = "timerirq"; | ||
268 | data->fops = &timerirq_fops; | ||
269 | |||
270 | res = misc_register(data); | ||
271 | if (res < 0) { | ||
272 | dev_err(data->this_device, | ||
273 | "misc_register returned %d\n", | ||
274 | res); | ||
275 | return res; | ||
276 | } | ||
277 | |||
278 | return res; | ||
279 | } | ||
280 | module_init(timerirq_init); | ||
281 | |||
282 | static void __exit timerirq_exit(void) | ||
283 | { | ||
284 | struct miscdevice *data = timerirq_dev_data; | ||
285 | |||
286 | dev_info(data->this_device, "Unregistering %s\n", | ||
287 | data->name); | ||
288 | |||
289 | misc_deregister(data); | ||
290 | kfree(data); | ||
291 | |||
292 | timerirq_dev_data = NULL; | ||
293 | } | ||
294 | module_exit(timerirq_exit); | ||
295 | |||
296 | MODULE_AUTHOR("Invensense Corporation"); | ||
297 | MODULE_DESCRIPTION("Timer IRQ device driver."); | ||
298 | MODULE_LICENSE("GPL"); | ||
299 | MODULE_ALIAS("timerirq"); | ||
diff --git a/drivers/misc/mpu3050/timerirq.h b/drivers/misc/mpu3050/timerirq.h new file mode 100644 index 00000000000..ec2c1e29f08 --- /dev/null +++ b/drivers/misc/mpu3050/timerirq.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2010 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 "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 | ||