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/mpu-dev.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/mpu3050/mpu-dev.c')
-rw-r--r-- | drivers/misc/mpu3050/mpu-dev.c | 1310 |
1 files changed, 1310 insertions, 0 deletions
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); | ||