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