diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/inv_mpu/mpu3050/mpu-dev.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/inv_mpu/mpu3050/mpu-dev.c')
-rw-r--r-- | drivers/misc/inv_mpu/mpu3050/mpu-dev.c | 1250 |
1 files changed, 1250 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/mpu3050/mpu-dev.c b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c new file mode 100644 index 00000000000..c98ed3b4a80 --- /dev/null +++ b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c | |||
@@ -0,0 +1,1250 @@ | |||
1 | /* | ||
2 | $License: | ||
3 | Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | $ | ||
18 | */ | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/i2c-dev.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/stat.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/signal.h> | ||
30 | #include <linux/miscdevice.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/version.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/suspend.h> | ||
36 | #include <linux/poll.h> | ||
37 | |||
38 | #include <linux/errno.h> | ||
39 | #include <linux/fs.h> | ||
40 | #include <linux/mm.h> | ||
41 | #include <linux/sched.h> | ||
42 | #include <linux/wait.h> | ||
43 | #include <linux/uaccess.h> | ||
44 | #include <linux/io.h> | ||
45 | |||
46 | #include "mpuirq.h" | ||
47 | #include "slaveirq.h" | ||
48 | #include "mlsl.h" | ||
49 | #include "mldl_cfg.h" | ||
50 | #include <linux/mpu.h> | ||
51 | |||
52 | |||
53 | /* Platform data for the MPU */ | ||
54 | struct mpu_private_data { | ||
55 | struct miscdevice dev; | ||
56 | struct i2c_client *client; | ||
57 | |||
58 | /* mldl_cfg data */ | ||
59 | struct mldl_cfg mldl_cfg; | ||
60 | struct mpu_ram mpu_ram; | ||
61 | struct mpu_gyro_cfg mpu_gyro_cfg; | ||
62 | struct mpu_offsets mpu_offsets; | ||
63 | struct mpu_chip_info mpu_chip_info; | ||
64 | struct inv_mpu_cfg inv_mpu_cfg; | ||
65 | struct inv_mpu_state inv_mpu_state; | ||
66 | |||
67 | struct mutex mutex; | ||
68 | wait_queue_head_t mpu_event_wait; | ||
69 | struct completion completion; | ||
70 | struct timer_list timeout; | ||
71 | struct notifier_block nb; | ||
72 | struct mpuirq_data mpu_pm_event; | ||
73 | int response_timeout; /* In seconds */ | ||
74 | unsigned long event; | ||
75 | int pid; | ||
76 | struct module *slave_modules[EXT_SLAVE_NUM_TYPES]; | ||
77 | }; | ||
78 | |||
79 | struct mpu_private_data *mpu_private_data; | ||
80 | |||
81 | static void mpu_pm_timeout(u_long data) | ||
82 | { | ||
83 | struct mpu_private_data *mpu = (struct mpu_private_data *)data; | ||
84 | struct i2c_client *client = mpu->client; | ||
85 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
86 | complete(&mpu->completion); | ||
87 | } | ||
88 | |||
89 | static int mpu_pm_notifier_callback(struct notifier_block *nb, | ||
90 | unsigned long event, void *unused) | ||
91 | { | ||
92 | struct mpu_private_data *mpu = | ||
93 | container_of(nb, struct mpu_private_data, nb); | ||
94 | struct i2c_client *client = mpu->client; | ||
95 | struct timeval event_time; | ||
96 | dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event); | ||
97 | |||
98 | /* Prevent the file handle from being closed before we initialize | ||
99 | the completion event */ | ||
100 | mutex_lock(&mpu->mutex); | ||
101 | if (!(mpu->pid) || | ||
102 | (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) { | ||
103 | mutex_unlock(&mpu->mutex); | ||
104 | return NOTIFY_OK; | ||
105 | } | ||
106 | |||
107 | if (event == PM_SUSPEND_PREPARE) | ||
108 | mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE; | ||
109 | if (event == PM_POST_SUSPEND) | ||
110 | mpu->event = MPU_PM_EVENT_POST_SUSPEND; | ||
111 | |||
112 | do_gettimeofday(&event_time); | ||
113 | mpu->mpu_pm_event.interruptcount++; | ||
114 | mpu->mpu_pm_event.irqtime = | ||
115 | (((long long)event_time.tv_sec) << 32) + event_time.tv_usec; | ||
116 | mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT; | ||
117 | mpu->mpu_pm_event.data = mpu->event; | ||
118 | |||
119 | if (mpu->response_timeout > 0) { | ||
120 | mpu->timeout.expires = jiffies + mpu->response_timeout * HZ; | ||
121 | add_timer(&mpu->timeout); | ||
122 | } | ||
123 | INIT_COMPLETION(mpu->completion); | ||
124 | mutex_unlock(&mpu->mutex); | ||
125 | |||
126 | wake_up_interruptible(&mpu->mpu_event_wait); | ||
127 | wait_for_completion(&mpu->completion); | ||
128 | del_timer_sync(&mpu->timeout); | ||
129 | dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event); | ||
130 | return NOTIFY_OK; | ||
131 | } | ||
132 | |||
133 | static int mpu_dev_open(struct inode *inode, struct file *file) | ||
134 | { | ||
135 | struct mpu_private_data *mpu = | ||
136 | container_of(file->private_data, struct mpu_private_data, dev); | ||
137 | struct i2c_client *client = mpu->client; | ||
138 | int result; | ||
139 | int ii; | ||
140 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
141 | dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid); | ||
142 | |||
143 | result = mutex_lock_interruptible(&mpu->mutex); | ||
144 | if (mpu->pid) { | ||
145 | mutex_unlock(&mpu->mutex); | ||
146 | return -EBUSY; | ||
147 | } | ||
148 | mpu->pid = current->pid; | ||
149 | |||
150 | /* Reset the sensors to the default */ | ||
151 | if (result) { | ||
152 | dev_err(&client->adapter->dev, | ||
153 | "%s: mutex_lock_interruptible returned %d\n", | ||
154 | __func__, result); | ||
155 | return result; | ||
156 | } | ||
157 | |||
158 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
159 | __module_get(mpu->slave_modules[ii]); | ||
160 | |||
161 | mutex_unlock(&mpu->mutex); | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | /* close function - called when the "file" /dev/mpu is closed in userspace */ | ||
166 | static int mpu_release(struct inode *inode, struct file *file) | ||
167 | { | ||
168 | struct mpu_private_data *mpu = | ||
169 | container_of(file->private_data, struct mpu_private_data, dev); | ||
170 | struct i2c_client *client = mpu->client; | ||
171 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
172 | int result = 0; | ||
173 | int ii; | ||
174 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
175 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
176 | |||
177 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
178 | if (!pdata_slave[ii]) | ||
179 | slave_adapter[ii] = NULL; | ||
180 | else | ||
181 | slave_adapter[ii] = | ||
182 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
183 | } | ||
184 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
185 | |||
186 | mutex_lock(&mpu->mutex); | ||
187 | mldl_cfg->inv_mpu_cfg->requested_sensors = 0; | ||
188 | result = inv_mpu_suspend(mldl_cfg, | ||
189 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
190 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
191 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
192 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
193 | INV_ALL_SENSORS); | ||
194 | mpu->pid = 0; | ||
195 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) | ||
196 | module_put(mpu->slave_modules[ii]); | ||
197 | |||
198 | mutex_unlock(&mpu->mutex); | ||
199 | complete(&mpu->completion); | ||
200 | dev_dbg(&client->adapter->dev, "mpu_release\n"); | ||
201 | |||
202 | return result; | ||
203 | } | ||
204 | |||
205 | /* read function called when from /dev/mpu is read. Read from the FIFO */ | ||
206 | static ssize_t mpu_read(struct file *file, | ||
207 | char __user *buf, size_t count, loff_t *offset) | ||
208 | { | ||
209 | struct mpu_private_data *mpu = | ||
210 | container_of(file->private_data, struct mpu_private_data, dev); | ||
211 | struct i2c_client *client = mpu->client; | ||
212 | size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long); | ||
213 | int err; | ||
214 | |||
215 | if (!mpu->event && (!(file->f_flags & O_NONBLOCK))) | ||
216 | wait_event_interruptible(mpu->mpu_event_wait, mpu->event); | ||
217 | |||
218 | if (!mpu->event || !buf | ||
219 | || count < sizeof(mpu->mpu_pm_event)) | ||
220 | return 0; | ||
221 | |||
222 | err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event)); | ||
223 | if (err) { | ||
224 | dev_err(&client->adapter->dev, | ||
225 | "Copy to user returned %d\n", err); | ||
226 | return -EFAULT; | ||
227 | } | ||
228 | mpu->event = 0; | ||
229 | return len; | ||
230 | } | ||
231 | |||
232 | static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll) | ||
233 | { | ||
234 | struct mpu_private_data *mpu = | ||
235 | container_of(file->private_data, struct mpu_private_data, dev); | ||
236 | int mask = 0; | ||
237 | |||
238 | poll_wait(file, &mpu->mpu_event_wait, poll); | ||
239 | if (mpu->event) | ||
240 | mask |= POLLIN | POLLRDNORM; | ||
241 | return mask; | ||
242 | } | ||
243 | |||
244 | static int mpu_dev_ioctl_get_ext_slave_platform_data( | ||
245 | struct i2c_client *client, | ||
246 | struct ext_slave_platform_data __user *arg) | ||
247 | { | ||
248 | struct mpu_private_data *mpu = | ||
249 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
250 | struct ext_slave_platform_data *pdata_slave; | ||
251 | struct ext_slave_platform_data local_pdata_slave; | ||
252 | |||
253 | if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave))) | ||
254 | return -EFAULT; | ||
255 | |||
256 | if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
257 | return -EINVAL; | ||
258 | |||
259 | pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type]; | ||
260 | /* All but private data and irq_data */ | ||
261 | if (!pdata_slave) | ||
262 | return -ENODEV; | ||
263 | if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave))) | ||
264 | return -EFAULT; | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int mpu_dev_ioctl_get_mpu_platform_data( | ||
269 | struct i2c_client *client, | ||
270 | struct mpu_platform_data __user *arg) | ||
271 | { | ||
272 | struct mpu_private_data *mpu = | ||
273 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
274 | struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata; | ||
275 | |||
276 | if (copy_to_user(arg, pdata, sizeof(*pdata))) | ||
277 | return -EFAULT; | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int mpu_dev_ioctl_get_ext_slave_descr( | ||
282 | struct i2c_client *client, | ||
283 | struct ext_slave_descr __user *arg) | ||
284 | { | ||
285 | struct mpu_private_data *mpu = | ||
286 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
287 | struct ext_slave_descr *slave; | ||
288 | struct ext_slave_descr local_slave; | ||
289 | |||
290 | if (copy_from_user(&local_slave, arg, sizeof(local_slave))) | ||
291 | return -EFAULT; | ||
292 | |||
293 | if (local_slave.type >= EXT_SLAVE_NUM_TYPES) | ||
294 | return -EINVAL; | ||
295 | |||
296 | slave = mpu->mldl_cfg.slave[local_slave.type]; | ||
297 | /* All but private data and irq_data */ | ||
298 | if (!slave) | ||
299 | return -ENODEV; | ||
300 | if (copy_to_user(arg, slave, sizeof(*slave))) | ||
301 | return -EFAULT; | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | |||
306 | /** | ||
307 | * slave_config() - Pass a requested slave configuration to the slave sensor | ||
308 | * | ||
309 | * @adapter the adaptor to use to communicate with the slave | ||
310 | * @mldl_cfg the mldl configuration structuer | ||
311 | * @slave pointer to the slave descriptor | ||
312 | * @usr_config The configuration to pass to the slave sensor | ||
313 | * | ||
314 | * returns 0 or non-zero error code | ||
315 | */ | ||
316 | static int inv_mpu_config(struct mldl_cfg *mldl_cfg, | ||
317 | void *gyro_adapter, | ||
318 | struct ext_slave_config __user *usr_config) | ||
319 | { | ||
320 | int retval = 0; | ||
321 | struct ext_slave_config config; | ||
322 | |||
323 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
324 | if (retval) | ||
325 | return -EFAULT; | ||
326 | |||
327 | if (config.len && config.data) { | ||
328 | void *data; | ||
329 | data = kmalloc(config.len, GFP_KERNEL); | ||
330 | if (!data) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | retval = copy_from_user(data, | ||
334 | (void __user *)config.data, config.len); | ||
335 | if (retval) { | ||
336 | retval = -EFAULT; | ||
337 | kfree(data); | ||
338 | return retval; | ||
339 | } | ||
340 | config.data = data; | ||
341 | } | ||
342 | retval = gyro_config(gyro_adapter, mldl_cfg, &config); | ||
343 | kfree(config.data); | ||
344 | return retval; | ||
345 | } | ||
346 | |||
347 | static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg, | ||
348 | void *gyro_adapter, | ||
349 | struct ext_slave_config __user *usr_config) | ||
350 | { | ||
351 | int retval = 0; | ||
352 | struct ext_slave_config config; | ||
353 | void *user_data; | ||
354 | |||
355 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
356 | if (retval) | ||
357 | return -EFAULT; | ||
358 | |||
359 | user_data = config.data; | ||
360 | if (config.len && config.data) { | ||
361 | void *data; | ||
362 | data = kmalloc(config.len, GFP_KERNEL); | ||
363 | if (!data) | ||
364 | return -ENOMEM; | ||
365 | |||
366 | retval = copy_from_user(data, | ||
367 | (void __user *)config.data, config.len); | ||
368 | if (retval) { | ||
369 | retval = -EFAULT; | ||
370 | kfree(data); | ||
371 | return retval; | ||
372 | } | ||
373 | config.data = data; | ||
374 | } | ||
375 | retval = gyro_get_config(gyro_adapter, mldl_cfg, &config); | ||
376 | if (!retval) | ||
377 | retval = copy_to_user((unsigned char __user *)user_data, | ||
378 | config.data, config.len); | ||
379 | kfree(config.data); | ||
380 | return retval; | ||
381 | } | ||
382 | |||
383 | static int slave_config(struct mldl_cfg *mldl_cfg, | ||
384 | void *gyro_adapter, | ||
385 | void *slave_adapter, | ||
386 | struct ext_slave_descr *slave, | ||
387 | struct ext_slave_platform_data *pdata, | ||
388 | struct ext_slave_config __user *usr_config) | ||
389 | { | ||
390 | int retval = 0; | ||
391 | struct ext_slave_config config; | ||
392 | if ((!slave) || (!slave->config)) | ||
393 | return -ENODEV; | ||
394 | |||
395 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
396 | if (retval) | ||
397 | return -EFAULT; | ||
398 | |||
399 | if (config.len && config.data) { | ||
400 | void *data; | ||
401 | data = kmalloc(config.len, GFP_KERNEL); | ||
402 | if (!data) | ||
403 | return -ENOMEM; | ||
404 | |||
405 | retval = copy_from_user(data, | ||
406 | (void __user *)config.data, config.len); | ||
407 | if (retval) { | ||
408 | retval = -EFAULT; | ||
409 | kfree(data); | ||
410 | return retval; | ||
411 | } | ||
412 | config.data = data; | ||
413 | } | ||
414 | retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter, | ||
415 | &config, slave, pdata); | ||
416 | kfree(config.data); | ||
417 | return retval; | ||
418 | } | ||
419 | |||
420 | static int slave_get_config(struct mldl_cfg *mldl_cfg, | ||
421 | void *gyro_adapter, | ||
422 | void *slave_adapter, | ||
423 | struct ext_slave_descr *slave, | ||
424 | struct ext_slave_platform_data *pdata, | ||
425 | struct ext_slave_config __user *usr_config) | ||
426 | { | ||
427 | int retval = 0; | ||
428 | struct ext_slave_config config; | ||
429 | void *user_data; | ||
430 | if (!(slave) || !(slave->get_config)) | ||
431 | return -ENODEV; | ||
432 | |||
433 | retval = copy_from_user(&config, usr_config, sizeof(config)); | ||
434 | if (retval) | ||
435 | return -EFAULT; | ||
436 | |||
437 | user_data = config.data; | ||
438 | if (config.len && config.data) { | ||
439 | void *data; | ||
440 | data = kmalloc(config.len, GFP_KERNEL); | ||
441 | if (!data) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | retval = copy_from_user(data, | ||
445 | (void __user *)config.data, config.len); | ||
446 | if (retval) { | ||
447 | retval = -EFAULT; | ||
448 | kfree(data); | ||
449 | return retval; | ||
450 | } | ||
451 | config.data = data; | ||
452 | } | ||
453 | retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter, | ||
454 | slave_adapter, &config, slave, pdata); | ||
455 | if (retval) { | ||
456 | kfree(config.data); | ||
457 | return retval; | ||
458 | } | ||
459 | retval = copy_to_user((unsigned char __user *)user_data, | ||
460 | config.data, config.len); | ||
461 | kfree(config.data); | ||
462 | return retval; | ||
463 | } | ||
464 | |||
465 | static int inv_slave_read(struct mldl_cfg *mldl_cfg, | ||
466 | void *gyro_adapter, | ||
467 | void *slave_adapter, | ||
468 | struct ext_slave_descr *slave, | ||
469 | struct ext_slave_platform_data *pdata, | ||
470 | void __user *usr_data) | ||
471 | { | ||
472 | int retval; | ||
473 | unsigned char *data; | ||
474 | data = kzalloc(slave->read_len, GFP_KERNEL); | ||
475 | if (!data) | ||
476 | return -EFAULT; | ||
477 | |||
478 | retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter, | ||
479 | slave, pdata, data); | ||
480 | |||
481 | if ((!retval) && | ||
482 | (copy_to_user((unsigned char __user *)usr_data, | ||
483 | data, slave->read_len))) | ||
484 | retval = -EFAULT; | ||
485 | |||
486 | kfree(data); | ||
487 | return retval; | ||
488 | } | ||
489 | |||
490 | static int mpu_handle_mlsl(void *sl_handle, | ||
491 | unsigned char addr, | ||
492 | unsigned int cmd, | ||
493 | struct mpu_read_write __user *usr_msg) | ||
494 | { | ||
495 | int retval = 0; | ||
496 | struct mpu_read_write msg; | ||
497 | unsigned char *user_data; | ||
498 | retval = copy_from_user(&msg, usr_msg, sizeof(msg)); | ||
499 | if (retval) | ||
500 | return -EFAULT; | ||
501 | |||
502 | user_data = msg.data; | ||
503 | if (msg.length && msg.data) { | ||
504 | unsigned char *data; | ||
505 | data = kmalloc(msg.length, GFP_KERNEL); | ||
506 | if (!data) | ||
507 | return -ENOMEM; | ||
508 | |||
509 | retval = copy_from_user(data, | ||
510 | (void __user *)msg.data, msg.length); | ||
511 | if (retval) { | ||
512 | retval = -EFAULT; | ||
513 | kfree(data); | ||
514 | return retval; | ||
515 | } | ||
516 | msg.data = data; | ||
517 | } else { | ||
518 | return -EPERM; | ||
519 | } | ||
520 | |||
521 | switch (cmd) { | ||
522 | case MPU_READ: | ||
523 | retval = inv_serial_read(sl_handle, addr, | ||
524 | msg.address, msg.length, msg.data); | ||
525 | break; | ||
526 | case MPU_WRITE: | ||
527 | retval = inv_serial_write(sl_handle, addr, | ||
528 | msg.length, msg.data); | ||
529 | break; | ||
530 | case MPU_READ_MEM: | ||
531 | retval = inv_serial_read_mem(sl_handle, addr, | ||
532 | msg.address, msg.length, msg.data); | ||
533 | break; | ||
534 | case MPU_WRITE_MEM: | ||
535 | retval = inv_serial_write_mem(sl_handle, addr, | ||
536 | msg.address, msg.length, | ||
537 | msg.data); | ||
538 | break; | ||
539 | case MPU_READ_FIFO: | ||
540 | retval = inv_serial_read_fifo(sl_handle, addr, | ||
541 | msg.length, msg.data); | ||
542 | break; | ||
543 | case MPU_WRITE_FIFO: | ||
544 | retval = inv_serial_write_fifo(sl_handle, addr, | ||
545 | msg.length, msg.data); | ||
546 | break; | ||
547 | |||
548 | }; | ||
549 | if (retval) { | ||
550 | dev_err(&((struct i2c_adapter *)sl_handle)->dev, | ||
551 | "%s: i2c %d error %d\n", | ||
552 | __func__, cmd, retval); | ||
553 | kfree(msg.data); | ||
554 | return retval; | ||
555 | } | ||
556 | retval = copy_to_user((unsigned char __user *)user_data, | ||
557 | msg.data, msg.length); | ||
558 | kfree(msg.data); | ||
559 | return retval; | ||
560 | } | ||
561 | |||
562 | /* ioctl - I/O control */ | ||
563 | static long mpu_dev_ioctl(struct file *file, | ||
564 | unsigned int cmd, unsigned long arg) | ||
565 | { | ||
566 | struct mpu_private_data *mpu = | ||
567 | container_of(file->private_data, struct mpu_private_data, dev); | ||
568 | struct i2c_client *client = mpu->client; | ||
569 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
570 | int retval = 0; | ||
571 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
572 | struct ext_slave_descr **slave = mldl_cfg->slave; | ||
573 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
574 | int ii; | ||
575 | |||
576 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
577 | if (!pdata_slave[ii]) | ||
578 | slave_adapter[ii] = NULL; | ||
579 | else | ||
580 | slave_adapter[ii] = | ||
581 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
582 | } | ||
583 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
584 | |||
585 | retval = mutex_lock_interruptible(&mpu->mutex); | ||
586 | if (retval) { | ||
587 | dev_err(&client->adapter->dev, | ||
588 | "%s: mutex_lock_interruptible returned %d\n", | ||
589 | __func__, retval); | ||
590 | return retval; | ||
591 | } | ||
592 | |||
593 | switch (cmd) { | ||
594 | case MPU_GET_EXT_SLAVE_PLATFORM_DATA: | ||
595 | retval = mpu_dev_ioctl_get_ext_slave_platform_data( | ||
596 | client, | ||
597 | (struct ext_slave_platform_data __user *)arg); | ||
598 | break; | ||
599 | case MPU_GET_MPU_PLATFORM_DATA: | ||
600 | retval = mpu_dev_ioctl_get_mpu_platform_data( | ||
601 | client, | ||
602 | (struct mpu_platform_data __user *)arg); | ||
603 | break; | ||
604 | case MPU_GET_EXT_SLAVE_DESCR: | ||
605 | retval = mpu_dev_ioctl_get_ext_slave_descr( | ||
606 | client, | ||
607 | (struct ext_slave_descr __user *)arg); | ||
608 | break; | ||
609 | case MPU_READ: | ||
610 | case MPU_WRITE: | ||
611 | case MPU_READ_MEM: | ||
612 | case MPU_WRITE_MEM: | ||
613 | case MPU_READ_FIFO: | ||
614 | case MPU_WRITE_FIFO: | ||
615 | retval = mpu_handle_mlsl( | ||
616 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
617 | mldl_cfg->mpu_chip_info->addr, cmd, | ||
618 | (struct mpu_read_write __user *)arg); | ||
619 | break; | ||
620 | case MPU_CONFIG_GYRO: | ||
621 | retval = inv_mpu_config( | ||
622 | mldl_cfg, | ||
623 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
624 | (struct ext_slave_config __user *)arg); | ||
625 | break; | ||
626 | case MPU_CONFIG_ACCEL: | ||
627 | retval = slave_config( | ||
628 | mldl_cfg, | ||
629 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
630 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
631 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
632 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
633 | (struct ext_slave_config __user *)arg); | ||
634 | break; | ||
635 | case MPU_CONFIG_COMPASS: | ||
636 | retval = slave_config( | ||
637 | mldl_cfg, | ||
638 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
639 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
640 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
641 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
642 | (struct ext_slave_config __user *)arg); | ||
643 | break; | ||
644 | case MPU_CONFIG_PRESSURE: | ||
645 | retval = slave_config( | ||
646 | mldl_cfg, | ||
647 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
648 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
649 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
650 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
651 | (struct ext_slave_config __user *)arg); | ||
652 | break; | ||
653 | case MPU_GET_CONFIG_GYRO: | ||
654 | retval = inv_mpu_get_config( | ||
655 | mldl_cfg, | ||
656 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
657 | (struct ext_slave_config __user *)arg); | ||
658 | break; | ||
659 | case MPU_GET_CONFIG_ACCEL: | ||
660 | retval = slave_get_config( | ||
661 | mldl_cfg, | ||
662 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
663 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
664 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
665 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
666 | (struct ext_slave_config __user *)arg); | ||
667 | break; | ||
668 | case MPU_GET_CONFIG_COMPASS: | ||
669 | retval = slave_get_config( | ||
670 | mldl_cfg, | ||
671 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
672 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
673 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
674 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
675 | (struct ext_slave_config __user *)arg); | ||
676 | break; | ||
677 | case MPU_GET_CONFIG_PRESSURE: | ||
678 | retval = slave_get_config( | ||
679 | mldl_cfg, | ||
680 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
681 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
682 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
683 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
684 | (struct ext_slave_config __user *)arg); | ||
685 | break; | ||
686 | case MPU_SUSPEND: | ||
687 | retval = inv_mpu_suspend( | ||
688 | mldl_cfg, | ||
689 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
690 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
691 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
692 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
693 | arg); | ||
694 | break; | ||
695 | case MPU_RESUME: | ||
696 | retval = inv_mpu_resume( | ||
697 | mldl_cfg, | ||
698 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
699 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
700 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
701 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
702 | arg); | ||
703 | break; | ||
704 | case MPU_PM_EVENT_HANDLED: | ||
705 | dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd); | ||
706 | complete(&mpu->completion); | ||
707 | break; | ||
708 | case MPU_READ_ACCEL: | ||
709 | retval = inv_slave_read( | ||
710 | mldl_cfg, | ||
711 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
712 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
713 | slave[EXT_SLAVE_TYPE_ACCEL], | ||
714 | pdata_slave[EXT_SLAVE_TYPE_ACCEL], | ||
715 | (unsigned char __user *)arg); | ||
716 | break; | ||
717 | case MPU_READ_COMPASS: | ||
718 | retval = inv_slave_read( | ||
719 | mldl_cfg, | ||
720 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
721 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
722 | slave[EXT_SLAVE_TYPE_COMPASS], | ||
723 | pdata_slave[EXT_SLAVE_TYPE_COMPASS], | ||
724 | (unsigned char __user *)arg); | ||
725 | break; | ||
726 | case MPU_READ_PRESSURE: | ||
727 | retval = inv_slave_read( | ||
728 | mldl_cfg, | ||
729 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
730 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
731 | slave[EXT_SLAVE_TYPE_PRESSURE], | ||
732 | pdata_slave[EXT_SLAVE_TYPE_PRESSURE], | ||
733 | (unsigned char __user *)arg); | ||
734 | break; | ||
735 | case MPU_GET_REQUESTED_SENSORS: | ||
736 | if (copy_to_user( | ||
737 | (__u32 __user *)arg, | ||
738 | &mldl_cfg->inv_mpu_cfg->requested_sensors, | ||
739 | sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors))) | ||
740 | retval = -EFAULT; | ||
741 | break; | ||
742 | case MPU_SET_REQUESTED_SENSORS: | ||
743 | mldl_cfg->inv_mpu_cfg->requested_sensors = arg; | ||
744 | break; | ||
745 | case MPU_GET_IGNORE_SYSTEM_SUSPEND: | ||
746 | if (copy_to_user( | ||
747 | (unsigned char __user *)arg, | ||
748 | &mldl_cfg->inv_mpu_cfg->ignore_system_suspend, | ||
749 | sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend))) | ||
750 | retval = -EFAULT; | ||
751 | break; | ||
752 | case MPU_SET_IGNORE_SYSTEM_SUSPEND: | ||
753 | mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg; | ||
754 | break; | ||
755 | case MPU_GET_MLDL_STATUS: | ||
756 | if (copy_to_user( | ||
757 | (unsigned char __user *)arg, | ||
758 | &mldl_cfg->inv_mpu_state->status, | ||
759 | sizeof(mldl_cfg->inv_mpu_state->status))) | ||
760 | retval = -EFAULT; | ||
761 | break; | ||
762 | case MPU_GET_I2C_SLAVES_ENABLED: | ||
763 | if (copy_to_user( | ||
764 | (unsigned char __user *)arg, | ||
765 | &mldl_cfg->inv_mpu_state->i2c_slaves_enabled, | ||
766 | sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled))) | ||
767 | retval = -EFAULT; | ||
768 | break; | ||
769 | default: | ||
770 | dev_err(&client->adapter->dev, | ||
771 | "%s: Unknown cmd %x, arg %lu\n", | ||
772 | __func__, cmd, arg); | ||
773 | retval = -EINVAL; | ||
774 | }; | ||
775 | |||
776 | mutex_unlock(&mpu->mutex); | ||
777 | dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n", | ||
778 | __func__, cmd, arg, retval); | ||
779 | |||
780 | if (retval > 0) | ||
781 | retval = -retval; | ||
782 | |||
783 | return retval; | ||
784 | } | ||
785 | |||
786 | void mpu_shutdown(struct i2c_client *client) | ||
787 | { | ||
788 | struct mpu_private_data *mpu = | ||
789 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
790 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
791 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
792 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
793 | int ii; | ||
794 | |||
795 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
796 | if (!pdata_slave[ii]) | ||
797 | slave_adapter[ii] = NULL; | ||
798 | else | ||
799 | slave_adapter[ii] = | ||
800 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
801 | } | ||
802 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
803 | |||
804 | mutex_lock(&mpu->mutex); | ||
805 | (void)inv_mpu_suspend(mldl_cfg, | ||
806 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
807 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
808 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
809 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
810 | INV_ALL_SENSORS); | ||
811 | mutex_unlock(&mpu->mutex); | ||
812 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
813 | } | ||
814 | |||
815 | int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg) | ||
816 | { | ||
817 | struct mpu_private_data *mpu = | ||
818 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
819 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
820 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
821 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
822 | int ii; | ||
823 | |||
824 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
825 | if (!pdata_slave[ii]) | ||
826 | slave_adapter[ii] = NULL; | ||
827 | else | ||
828 | slave_adapter[ii] = | ||
829 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
830 | } | ||
831 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
832 | |||
833 | mutex_lock(&mpu->mutex); | ||
834 | if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
835 | dev_dbg(&client->adapter->dev, | ||
836 | "%s: suspending on event %d\n", __func__, mesg.event); | ||
837 | (void)inv_mpu_suspend(mldl_cfg, | ||
838 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
839 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
840 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
841 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
842 | INV_ALL_SENSORS); | ||
843 | } else { | ||
844 | dev_dbg(&client->adapter->dev, | ||
845 | "%s: Already suspended %d\n", __func__, mesg.event); | ||
846 | } | ||
847 | mutex_unlock(&mpu->mutex); | ||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | int mpu_dev_resume(struct i2c_client *client) | ||
852 | { | ||
853 | struct mpu_private_data *mpu = | ||
854 | (struct mpu_private_data *)i2c_get_clientdata(client); | ||
855 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
856 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
857 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
858 | int ii; | ||
859 | |||
860 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
861 | if (!pdata_slave[ii]) | ||
862 | slave_adapter[ii] = NULL; | ||
863 | else | ||
864 | slave_adapter[ii] = | ||
865 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
866 | } | ||
867 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
868 | |||
869 | mutex_lock(&mpu->mutex); | ||
870 | if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) { | ||
871 | (void)inv_mpu_resume(mldl_cfg, | ||
872 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
873 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
874 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
875 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE], | ||
876 | mldl_cfg->inv_mpu_cfg->requested_sensors); | ||
877 | dev_dbg(&client->adapter->dev, | ||
878 | "%s for pid %d\n", __func__, mpu->pid); | ||
879 | } | ||
880 | mutex_unlock(&mpu->mutex); | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | /* define which file operations are supported */ | ||
885 | static const struct file_operations mpu_fops = { | ||
886 | .owner = THIS_MODULE, | ||
887 | .read = mpu_read, | ||
888 | .poll = mpu_poll, | ||
889 | .unlocked_ioctl = mpu_dev_ioctl, | ||
890 | .open = mpu_dev_open, | ||
891 | .release = mpu_release, | ||
892 | }; | ||
893 | |||
894 | int inv_mpu_register_slave(struct module *slave_module, | ||
895 | struct i2c_client *slave_client, | ||
896 | struct ext_slave_platform_data *slave_pdata, | ||
897 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
898 | { | ||
899 | struct mpu_private_data *mpu = mpu_private_data; | ||
900 | struct mldl_cfg *mldl_cfg; | ||
901 | struct ext_slave_descr *slave_descr; | ||
902 | struct ext_slave_platform_data **pdata_slave; | ||
903 | char *irq_name = NULL; | ||
904 | int result = 0; | ||
905 | |||
906 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
907 | return -EINVAL; | ||
908 | |||
909 | if (!mpu) { | ||
910 | dev_err(&slave_client->adapter->dev, | ||
911 | "%s: Null mpu_private_data\n", __func__); | ||
912 | return -EINVAL; | ||
913 | } | ||
914 | mldl_cfg = &mpu->mldl_cfg; | ||
915 | pdata_slave = mldl_cfg->pdata_slave; | ||
916 | slave_descr = get_slave_descr(); | ||
917 | |||
918 | if (!slave_descr) { | ||
919 | dev_err(&slave_client->adapter->dev, | ||
920 | "%s: Null ext_slave_descr\n", __func__); | ||
921 | return -EINVAL; | ||
922 | } | ||
923 | |||
924 | mutex_lock(&mpu->mutex); | ||
925 | if (mpu->pid) { | ||
926 | mutex_unlock(&mpu->mutex); | ||
927 | return -EBUSY; | ||
928 | } | ||
929 | |||
930 | if (pdata_slave[slave_descr->type]) { | ||
931 | result = -EBUSY; | ||
932 | goto out_unlock_mutex; | ||
933 | } | ||
934 | |||
935 | slave_pdata->address = slave_client->addr; | ||
936 | slave_pdata->irq = slave_client->irq; | ||
937 | slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter); | ||
938 | |||
939 | dev_info(&slave_client->adapter->dev, | ||
940 | "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n", | ||
941 | __func__, | ||
942 | slave_descr->name, | ||
943 | slave_descr->type, | ||
944 | slave_pdata->address, | ||
945 | slave_pdata->irq, | ||
946 | slave_pdata->adapt_num); | ||
947 | |||
948 | switch (slave_descr->type) { | ||
949 | case EXT_SLAVE_TYPE_ACCEL: | ||
950 | irq_name = "accelirq"; | ||
951 | break; | ||
952 | case EXT_SLAVE_TYPE_COMPASS: | ||
953 | irq_name = "compassirq"; | ||
954 | break; | ||
955 | case EXT_SLAVE_TYPE_PRESSURE: | ||
956 | irq_name = "pressureirq"; | ||
957 | break; | ||
958 | default: | ||
959 | irq_name = "none"; | ||
960 | }; | ||
961 | if (slave_descr->init) { | ||
962 | result = slave_descr->init(slave_client->adapter, | ||
963 | slave_descr, | ||
964 | slave_pdata); | ||
965 | if (result) { | ||
966 | dev_err(&slave_client->adapter->dev, | ||
967 | "%s init failed %d\n", | ||
968 | slave_descr->name, result); | ||
969 | goto out_unlock_mutex; | ||
970 | } | ||
971 | } | ||
972 | |||
973 | pdata_slave[slave_descr->type] = slave_pdata; | ||
974 | mpu->slave_modules[slave_descr->type] = slave_module; | ||
975 | mldl_cfg->slave[slave_descr->type] = slave_descr; | ||
976 | |||
977 | goto out_unlock_mutex; | ||
978 | |||
979 | out_unlock_mutex: | ||
980 | mutex_unlock(&mpu->mutex); | ||
981 | |||
982 | if (!result && irq_name && (slave_pdata->irq > 0)) { | ||
983 | int warn_result; | ||
984 | dev_info(&slave_client->adapter->dev, | ||
985 | "Installing %s irq using %d\n", | ||
986 | irq_name, | ||
987 | slave_pdata->irq); | ||
988 | warn_result = slaveirq_init(slave_client->adapter, | ||
989 | slave_pdata, irq_name); | ||
990 | if (result) | ||
991 | dev_WARN(&slave_client->adapter->dev, | ||
992 | "%s irq assigned error: %d\n", | ||
993 | slave_descr->name, warn_result); | ||
994 | } else { | ||
995 | dev_info(&slave_client->adapter->dev, | ||
996 | "%s irq not assigned: %d %d %d\n", | ||
997 | slave_descr->name, | ||
998 | result, (int)irq_name, slave_pdata->irq); | ||
999 | } | ||
1000 | |||
1001 | return result; | ||
1002 | } | ||
1003 | EXPORT_SYMBOL(inv_mpu_register_slave); | ||
1004 | |||
1005 | void inv_mpu_unregister_slave(struct i2c_client *slave_client, | ||
1006 | struct ext_slave_platform_data *slave_pdata, | ||
1007 | struct ext_slave_descr *(*get_slave_descr)(void)) | ||
1008 | { | ||
1009 | struct mpu_private_data *mpu = mpu_private_data; | ||
1010 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1011 | struct ext_slave_descr *slave_descr; | ||
1012 | int result; | ||
1013 | |||
1014 | dev_info(&slave_client->adapter->dev, "%s\n", __func__); | ||
1015 | |||
1016 | if (!slave_client || !slave_pdata || !get_slave_descr) | ||
1017 | return; | ||
1018 | |||
1019 | if (slave_pdata->irq) | ||
1020 | slaveirq_exit(slave_pdata); | ||
1021 | |||
1022 | slave_descr = get_slave_descr(); | ||
1023 | if (!slave_descr) | ||
1024 | return; | ||
1025 | |||
1026 | mutex_lock(&mpu->mutex); | ||
1027 | |||
1028 | if (slave_descr->exit) { | ||
1029 | result = slave_descr->exit(slave_client->adapter, | ||
1030 | slave_descr, | ||
1031 | slave_pdata); | ||
1032 | if (result) | ||
1033 | dev_err(&slave_client->adapter->dev, | ||
1034 | "Accel exit failed %d\n", result); | ||
1035 | } | ||
1036 | mldl_cfg->slave[slave_descr->type] = NULL; | ||
1037 | mldl_cfg->pdata_slave[slave_descr->type] = NULL; | ||
1038 | mpu->slave_modules[slave_descr->type] = NULL; | ||
1039 | |||
1040 | mutex_unlock(&mpu->mutex); | ||
1041 | |||
1042 | } | ||
1043 | EXPORT_SYMBOL(inv_mpu_unregister_slave); | ||
1044 | |||
1045 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
1046 | |||
1047 | static const struct i2c_device_id mpu_id[] = { | ||
1048 | {"mpu3050", 0}, | ||
1049 | {"mpu6050", 0}, | ||
1050 | {"mpu6050_no_accel", 0}, | ||
1051 | {} | ||
1052 | }; | ||
1053 | MODULE_DEVICE_TABLE(i2c, mpu_id); | ||
1054 | |||
1055 | int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid) | ||
1056 | { | ||
1057 | struct mpu_platform_data *pdata; | ||
1058 | struct mpu_private_data *mpu; | ||
1059 | struct mldl_cfg *mldl_cfg; | ||
1060 | int res = 0; | ||
1061 | int ii = 0; | ||
1062 | unsigned long irq_flags; | ||
1063 | |||
1064 | dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++); | ||
1065 | |||
1066 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
1067 | res = -ENODEV; | ||
1068 | goto out_check_functionality_failed; | ||
1069 | } | ||
1070 | |||
1071 | mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL); | ||
1072 | if (!mpu) { | ||
1073 | res = -ENOMEM; | ||
1074 | goto out_alloc_data_failed; | ||
1075 | } | ||
1076 | mldl_cfg = &mpu->mldl_cfg; | ||
1077 | mldl_cfg->mpu_ram = &mpu->mpu_ram; | ||
1078 | mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg; | ||
1079 | mldl_cfg->mpu_offsets = &mpu->mpu_offsets; | ||
1080 | mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info; | ||
1081 | mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg; | ||
1082 | mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state; | ||
1083 | |||
1084 | mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE; | ||
1085 | mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL); | ||
1086 | if (!mldl_cfg->mpu_ram->ram) { | ||
1087 | res = -ENOMEM; | ||
1088 | goto out_alloc_ram_failed; | ||
1089 | } | ||
1090 | mpu_private_data = mpu; | ||
1091 | i2c_set_clientdata(client, mpu); | ||
1092 | mpu->client = client; | ||
1093 | |||
1094 | init_waitqueue_head(&mpu->mpu_event_wait); | ||
1095 | mutex_init(&mpu->mutex); | ||
1096 | init_completion(&mpu->completion); | ||
1097 | |||
1098 | mpu->response_timeout = 60; /* Seconds */ | ||
1099 | mpu->timeout.function = mpu_pm_timeout; | ||
1100 | mpu->timeout.data = (u_long) mpu; | ||
1101 | init_timer(&mpu->timeout); | ||
1102 | |||
1103 | mpu->nb.notifier_call = mpu_pm_notifier_callback; | ||
1104 | mpu->nb.priority = 0; | ||
1105 | res = register_pm_notifier(&mpu->nb); | ||
1106 | if (res) { | ||
1107 | dev_err(&client->adapter->dev, | ||
1108 | "Unable to register pm_notifier %d\n", res); | ||
1109 | goto out_register_pm_notifier_failed; | ||
1110 | } | ||
1111 | |||
1112 | pdata = (struct mpu_platform_data *)client->dev.platform_data; | ||
1113 | if (!pdata) { | ||
1114 | dev_WARN(&client->adapter->dev, | ||
1115 | "Missing platform data for mpu\n"); | ||
1116 | } | ||
1117 | mldl_cfg->pdata = pdata; | ||
1118 | |||
1119 | mldl_cfg->mpu_chip_info->addr = client->addr; | ||
1120 | res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1121 | |||
1122 | if (res) { | ||
1123 | dev_err(&client->adapter->dev, | ||
1124 | "Unable to open %s %d\n", MPU_NAME, res); | ||
1125 | res = -ENODEV; | ||
1126 | goto out_whoami_failed; | ||
1127 | } | ||
1128 | |||
1129 | mpu->dev.minor = MISC_DYNAMIC_MINOR; | ||
1130 | mpu->dev.name = "mpu"; | ||
1131 | mpu->dev.fops = &mpu_fops; | ||
1132 | res = misc_register(&mpu->dev); | ||
1133 | if (res < 0) { | ||
1134 | dev_err(&client->adapter->dev, | ||
1135 | "ERROR: misc_register returned %d\n", res); | ||
1136 | goto out_misc_register_failed; | ||
1137 | } | ||
1138 | |||
1139 | if (client->irq) { | ||
1140 | dev_info(&client->adapter->dev, | ||
1141 | "Installing irq using %d\n", client->irq); | ||
1142 | if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL)) | ||
1143 | irq_flags = IRQF_TRIGGER_FALLING; | ||
1144 | else | ||
1145 | irq_flags = IRQF_TRIGGER_RISING; | ||
1146 | res = mpuirq_init(client, mldl_cfg, irq_flags); | ||
1147 | |||
1148 | if (res) | ||
1149 | goto out_mpuirq_failed; | ||
1150 | } else { | ||
1151 | dev_WARN(&client->adapter->dev, | ||
1152 | "Missing %s IRQ\n", MPU_NAME); | ||
1153 | } | ||
1154 | return res; | ||
1155 | |||
1156 | out_mpuirq_failed: | ||
1157 | misc_deregister(&mpu->dev); | ||
1158 | out_misc_register_failed: | ||
1159 | inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL); | ||
1160 | out_whoami_failed: | ||
1161 | unregister_pm_notifier(&mpu->nb); | ||
1162 | out_register_pm_notifier_failed: | ||
1163 | kfree(mldl_cfg->mpu_ram->ram); | ||
1164 | mpu_private_data = NULL; | ||
1165 | out_alloc_ram_failed: | ||
1166 | kfree(mpu); | ||
1167 | out_alloc_data_failed: | ||
1168 | out_check_functionality_failed: | ||
1169 | dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res); | ||
1170 | return res; | ||
1171 | |||
1172 | } | ||
1173 | |||
1174 | static int mpu_remove(struct i2c_client *client) | ||
1175 | { | ||
1176 | struct mpu_private_data *mpu = i2c_get_clientdata(client); | ||
1177 | struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES]; | ||
1178 | struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg; | ||
1179 | struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave; | ||
1180 | int ii; | ||
1181 | |||
1182 | for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) { | ||
1183 | if (!pdata_slave[ii]) | ||
1184 | slave_adapter[ii] = NULL; | ||
1185 | else | ||
1186 | slave_adapter[ii] = | ||
1187 | i2c_get_adapter(pdata_slave[ii]->adapt_num); | ||
1188 | } | ||
1189 | |||
1190 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter; | ||
1191 | dev_dbg(&client->adapter->dev, "%s\n", __func__); | ||
1192 | |||
1193 | inv_mpu_close(mldl_cfg, | ||
1194 | slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE], | ||
1195 | slave_adapter[EXT_SLAVE_TYPE_ACCEL], | ||
1196 | slave_adapter[EXT_SLAVE_TYPE_COMPASS], | ||
1197 | slave_adapter[EXT_SLAVE_TYPE_PRESSURE]); | ||
1198 | |||
1199 | |||
1200 | if (client->irq) | ||
1201 | mpuirq_exit(); | ||
1202 | |||
1203 | misc_deregister(&mpu->dev); | ||
1204 | |||
1205 | unregister_pm_notifier(&mpu->nb); | ||
1206 | |||
1207 | kfree(mpu->mldl_cfg.mpu_ram->ram); | ||
1208 | kfree(mpu); | ||
1209 | |||
1210 | return 0; | ||
1211 | } | ||
1212 | |||
1213 | static struct i2c_driver mpu_driver = { | ||
1214 | .class = I2C_CLASS_HWMON, | ||
1215 | .probe = mpu_probe, | ||
1216 | .remove = mpu_remove, | ||
1217 | .id_table = mpu_id, | ||
1218 | .driver = { | ||
1219 | .owner = THIS_MODULE, | ||
1220 | .name = MPU_NAME, | ||
1221 | }, | ||
1222 | .address_list = normal_i2c, | ||
1223 | .shutdown = mpu_shutdown, /* optional */ | ||
1224 | .suspend = mpu_dev_suspend, /* optional */ | ||
1225 | .resume = mpu_dev_resume, /* optional */ | ||
1226 | |||
1227 | }; | ||
1228 | |||
1229 | static int __init mpu_init(void) | ||
1230 | { | ||
1231 | int res = i2c_add_driver(&mpu_driver); | ||
1232 | pr_info("%s: Probe name %s\n", __func__, MPU_NAME); | ||
1233 | if (res) | ||
1234 | pr_err("%s failed\n", __func__); | ||
1235 | return res; | ||
1236 | } | ||
1237 | |||
1238 | static void __exit mpu_exit(void) | ||
1239 | { | ||
1240 | pr_info("%s\n", __func__); | ||
1241 | i2c_del_driver(&mpu_driver); | ||
1242 | } | ||
1243 | |||
1244 | module_init(mpu_init); | ||
1245 | module_exit(mpu_exit); | ||
1246 | |||
1247 | MODULE_AUTHOR("Invensense Corporation"); | ||
1248 | MODULE_DESCRIPTION("User space character device interface for MPU"); | ||
1249 | MODULE_LICENSE("GPL"); | ||
1250 | MODULE_ALIAS(MPU_NAME); | ||