blob: af535432014fd121f8928c791098a380a8206c4d [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14/* Qualcomm Over the Air (OTA) Crypto driver */
15
16#include <linux/types.h>
17#include <linux/platform_device.h>
18#include <linux/dma-mapping.h>
19#include <linux/kernel.h>
20#include <linux/dmapool.h>
21#include <linux/interrupt.h>
22#include <linux/spinlock.h>
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/fs.h>
26#include <linux/miscdevice.h>
27#include <linux/uaccess.h>
28#include <linux/debugfs.h>
29
30
31#include <linux/qcota.h>
Mona Hossain5c8ea1f2011-07-28 15:11:29 -070032#include "qce.h"
33#include "qce_ota.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
35enum qce_ota_oper_enum {
36 QCE_OTA_F8_OPER = 0,
37 QCE_OTA_MPKT_F8_OPER = 1,
38 QCE_OTA_F9_OPER = 2,
39 QCE_OTA_OPER_LAST
40};
41
42struct ota_dev_control;
43
44struct ota_async_req {
45 struct list_head list;
46 struct completion complete;
47 int err;
48 enum qce_ota_oper_enum op;
49 union {
50 struct qce_f9_req f9_req;
51 struct qce_f8_req f8_req;
52 struct qce_f8_multi_pkt_req f8_mp_req;
53 } req;
54
55 struct ota_dev_control *podev;
56};
57
58/*
59 * Register ourselves as a misc device to be able to access the ota
60 * from userspace.
61 */
62
63
64#define QCOTA_DEV "qcota"
65
66
67struct ota_dev_control {
68
69 /* misc device */
70 struct miscdevice miscdevice;
71
72 /* qce handle */
73 void *qce;
74
75 /* platform device */
76 struct platform_device *pdev;
77
78 unsigned magic;
79
80 struct list_head ready_commands;
81 struct ota_async_req *active_command;
82 spinlock_t lock;
83 struct tasklet_struct done_tasklet;
84};
85
86#define OTA_MAGIC 0x4f544143
87
88static long qcota_ioctl(struct file *file,
89 unsigned cmd, unsigned long arg);
90static int qcota_open(struct inode *inode, struct file *file);
91static int qcota_release(struct inode *inode, struct file *file);
92static int start_req(struct ota_dev_control *podev);
93
94static const struct file_operations qcota_fops = {
95 .owner = THIS_MODULE,
96 .unlocked_ioctl = qcota_ioctl,
97 .open = qcota_open,
98 .release = qcota_release,
99};
100
101static struct ota_dev_control qcota_dev[] = {
102 {
103 .miscdevice = {
104 .minor = MISC_DYNAMIC_MINOR,
105 .name = "qcota0",
106 .fops = &qcota_fops,
107 },
108 .magic = OTA_MAGIC,
109 },
110 {
111 .miscdevice = {
112 .minor = MISC_DYNAMIC_MINOR,
113 .name = "qcota1",
114 .fops = &qcota_fops,
115 },
116 .magic = OTA_MAGIC,
117 },
118 {
119 .miscdevice = {
120 .minor = MISC_DYNAMIC_MINOR,
121 .name = "qcota2",
122 .fops = &qcota_fops,
123 },
124 .magic = OTA_MAGIC,
125 }
126};
127
128#define MAX_OTA_DEVICE ARRAY_SIZE(qcota_dev)
129
130#define DEBUG_MAX_FNAME 16
131#define DEBUG_MAX_RW_BUF 1024
132
133struct qcota_stat {
134 u32 f8_req;
135 u32 f8_mp_req;
136 u32 f9_req;
137 u32 f8_op_success;
138 u32 f8_op_fail;
139 u32 f8_mp_op_success;
140 u32 f8_mp_op_fail;
141 u32 f9_op_success;
142 u32 f9_op_fail;
143};
144static struct qcota_stat _qcota_stat[MAX_OTA_DEVICE];
145static struct dentry *_debug_dent;
146static char _debug_read_buf[DEBUG_MAX_RW_BUF];
147static int _debug_qcota[MAX_OTA_DEVICE];
148
149static struct ota_dev_control *qcota_minor_to_control(unsigned n)
150{
151 int i;
152
153 for (i = 0; i < MAX_OTA_DEVICE; i++) {
154 if (qcota_dev[i].miscdevice.minor == n)
155 return &qcota_dev[i];
156 }
157 return NULL;
158}
159
160static int qcota_open(struct inode *inode, struct file *file)
161{
162 struct ota_dev_control *podev;
163
164 podev = qcota_minor_to_control(MINOR(inode->i_rdev));
165 if (podev == NULL) {
166 pr_err("%s: no such device %d\n", __func__,
167 MINOR(inode->i_rdev));
168 return -ENOENT;
169 }
170
171 file->private_data = podev;
172
173 return 0;
174}
175
176static int qcota_release(struct inode *inode, struct file *file)
177{
178 struct ota_dev_control *podev;
179
180 podev = file->private_data;
181
182 if (podev != NULL && podev->magic != OTA_MAGIC) {
183 pr_err("%s: invalid handle %p\n",
184 __func__, podev);
185 }
186
187 file->private_data = NULL;
188
189 return 0;
190}
191
192static void req_done(unsigned long data)
193{
194 struct ota_dev_control *podev = (struct ota_dev_control *)data;
195 struct ota_async_req *areq;
196 unsigned long flags;
197 struct ota_async_req *new_req = NULL;
198 int ret = 0;
199
200 spin_lock_irqsave(&podev->lock, flags);
201 areq = podev->active_command;
202 podev->active_command = NULL;
203
204again:
205 if (!list_empty(&podev->ready_commands)) {
206 new_req = container_of(podev->ready_commands.next,
207 struct ota_async_req, list);
208 list_del(&new_req->list);
209 podev->active_command = new_req;
210 new_req->err = 0;
211 ret = start_req(podev);
212 }
213
214 spin_unlock_irqrestore(&podev->lock, flags);
215
216 if (areq)
217 complete(&areq->complete);
218
219 if (new_req && ret) {
220 complete(&new_req->complete);
221 spin_lock_irqsave(&podev->lock, flags);
222 podev->active_command = NULL;
223 areq = NULL;
224 ret = 0;
225 new_req = NULL;
226 goto again;
227 }
228
229 return;
230}
231
232static void f9_cb(void *cookie, unsigned char *icv, unsigned char *iv,
233 int ret)
234{
235 struct ota_async_req *areq = (struct ota_async_req *) cookie;
236 struct ota_dev_control *podev;
237 struct qcota_stat *pstat;
238
239 podev = areq->podev;
240 pstat = &_qcota_stat[podev->pdev->id];
241 areq->req.f9_req.mac_i = (uint32_t) icv;
242
243 if (ret)
244 areq->err = -ENXIO;
245 else
246 areq->err = 0;
247
248 tasklet_schedule(&podev->done_tasklet);
249};
250
251static void f8_cb(void *cookie, unsigned char *icv, unsigned char *iv,
252 int ret)
253{
254 struct ota_async_req *areq = (struct ota_async_req *) cookie;
255 struct ota_dev_control *podev;
256 struct qcota_stat *pstat;
257
258 podev = areq->podev;
259 pstat = &_qcota_stat[podev->pdev->id];
260
261 if (ret)
262 areq->err = -ENXIO;
263 else
264 areq->err = 0;
265
266 tasklet_schedule(&podev->done_tasklet);
267};
268
269static int start_req(struct ota_dev_control *podev)
270{
271 struct ota_async_req *areq;
272 struct qce_f9_req *pf9;
273 struct qce_f8_multi_pkt_req *p_mp_f8;
274 struct qce_f8_req *pf8;
275 int ret = 0;
276
277 /* start the command on the podev->active_command */
278 areq = podev->active_command;
279 areq->podev = podev;
280
281 switch (areq->op) {
282 case QCE_OTA_F8_OPER:
283 pf8 = &areq->req.f8_req;
284 ret = qce_f8_req(podev->qce, pf8, areq, f8_cb);
285 break;
286 case QCE_OTA_MPKT_F8_OPER:
287 p_mp_f8 = &areq->req.f8_mp_req;
288 ret = qce_f8_multi_pkt_req(podev->qce, p_mp_f8, areq, f8_cb);
289 break;
290
291 case QCE_OTA_F9_OPER:
292 pf9 = &areq->req.f9_req;
293 ret = qce_f9_req(podev->qce, pf9, areq, f9_cb);
294 break;
295
296 default:
297 ret = -ENOTSUPP;
298 break;
299 };
300 areq->err = ret;
301 return ret;
302};
303
304static int submit_req(struct ota_async_req *areq, struct ota_dev_control *podev)
305{
306 unsigned long flags;
307 int ret = 0;
308 struct qcota_stat *pstat;
309
310 areq->err = 0;
311 spin_lock_irqsave(&podev->lock, flags);
312 if (podev->active_command == NULL) {
313 podev->active_command = areq;
314 ret = start_req(podev);
315 } else {
316 list_add_tail(&areq->list, &podev->ready_commands);
317 }
318
319 if (ret != 0)
320 podev->active_command = NULL;
321 spin_unlock_irqrestore(&podev->lock, flags);
322
323 if (ret == 0)
324 wait_for_completion(&areq->complete);
325
326 pstat = &_qcota_stat[podev->pdev->id];
327 switch (areq->op) {
328 case QCE_OTA_F8_OPER:
329 if (areq->err)
330 pstat->f8_op_fail++;
331 else
332 pstat->f8_op_success++;
333 break;
334
335 case QCE_OTA_MPKT_F8_OPER:
336
337 if (areq->err)
338 pstat->f8_mp_op_fail++;
339 else
340 pstat->f8_mp_op_success++;
341 break;
342
343 case QCE_OTA_F9_OPER:
344 default:
345 if (areq->err)
346 pstat->f9_op_fail++;
347 else
348 pstat->f9_op_success++;
349 break;
350 };
351
352 return areq->err;
353};
354
355static long qcota_ioctl(struct file *file,
356 unsigned cmd, unsigned long arg)
357{
358 int err = 0;
359 struct ota_dev_control *podev;
360 uint8_t *user_src;
361 uint8_t *user_dst;
362 uint8_t *k_buf = NULL;
363 struct ota_async_req areq;
364 uint32_t total;
365 struct qcota_stat *pstat;
366
367 podev = file->private_data;
368 if (podev == NULL || podev->magic != OTA_MAGIC) {
369 pr_err("%s: invalid handle %p\n",
370 __func__, podev);
371 return -ENOENT;
372 }
373
374 /* Verify user arguments. */
375 if (_IOC_TYPE(cmd) != QCOTA_IOC_MAGIC)
376 return -ENOTTY;
377
378 init_completion(&areq.complete);
379
380 pstat = &_qcota_stat[podev->pdev->id];
381
382 switch (cmd) {
383 case QCOTA_F9_REQ:
384 if (!access_ok(VERIFY_WRITE, (void __user *)arg,
385 sizeof(struct qce_f9_req)))
386 return -EFAULT;
387 if (__copy_from_user(&areq.req.f9_req, (void __user *)arg,
388 sizeof(struct qce_f9_req)))
389 return -EFAULT;
390
391 user_src = areq.req.f9_req.message;
392 if (!access_ok(VERIFY_READ, (void __user *)user_src,
393 areq.req.f9_req.msize))
394 return -EFAULT;
395
396 k_buf = kmalloc(areq.req.f9_req.msize, GFP_KERNEL);
397 if (k_buf == NULL)
398 return -ENOMEM;
399
400 if (__copy_from_user(k_buf, (void __user *)user_src,
401 areq.req.f9_req.msize)) {
402 kfree(k_buf);
403 return -EFAULT;
404 }
405
406 areq.req.f9_req.message = k_buf;
407 areq.op = QCE_OTA_F9_OPER;
408
409 pstat->f9_req++;
410 err = submit_req(&areq, podev);
411
412 areq.req.f9_req.message = user_src;
413 if (err == 0 && __copy_to_user((void __user *)arg,
414 &areq.req.f9_req, sizeof(struct qce_f9_req))) {
415 err = -EFAULT;
416 }
417 kfree(k_buf);
418 break;
419
420 case QCOTA_F8_REQ:
421 if (!access_ok(VERIFY_WRITE, (void __user *)arg,
422 sizeof(struct qce_f8_req)))
423 return -EFAULT;
424 if (__copy_from_user(&areq.req.f8_req, (void __user *)arg,
425 sizeof(struct qce_f8_req)))
426 return -EFAULT;
427 total = areq.req.f8_req.data_len;
428 user_src = areq.req.f8_req.data_in;
429 if (user_src != NULL) {
430 if (!access_ok(VERIFY_READ, (void __user *)
431 user_src, total))
432 return -EFAULT;
433
434 };
435
436 user_dst = areq.req.f8_req.data_out;
437 if (!access_ok(VERIFY_WRITE, (void __user *)
438 user_dst, total))
439 return -EFAULT;
440
441 k_buf = kmalloc(total, GFP_KERNEL);
442 if (k_buf == NULL)
443 return -ENOMEM;
444
445 /* k_buf returned from kmalloc should be cache line aligned */
446 if (user_src && __copy_from_user(k_buf,
447 (void __user *)user_src, total)) {
448 kfree(k_buf);
449 return -EFAULT;
450 }
451
452 if (user_src)
453 areq.req.f8_req.data_in = k_buf;
454 else
455 areq.req.f8_req.data_in = NULL;
456 areq.req.f8_req.data_out = k_buf;
457
458 areq.op = QCE_OTA_F8_OPER;
459
460 pstat->f8_req++;
461 err = submit_req(&areq, podev);
462
463 if (err == 0 && __copy_to_user(user_dst, k_buf, total))
464 err = -EFAULT;
465 kfree(k_buf);
466
467 break;
468
469 case QCOTA_F8_MPKT_REQ:
470 if (!access_ok(VERIFY_WRITE, (void __user *)arg,
471 sizeof(struct qce_f8_multi_pkt_req)))
472 return -EFAULT;
473 if (__copy_from_user(&areq.req.f8_mp_req, (void __user *)arg,
474 sizeof(struct qce_f8_multi_pkt_req)))
475 return -EFAULT;
476
477 total = areq.req.f8_mp_req.num_pkt *
478 areq.req.f8_mp_req.qce_f8_req.data_len;
479
480 user_src = areq.req.f8_mp_req.qce_f8_req.data_in;
481 if (!access_ok(VERIFY_READ, (void __user *)
482 user_src, total))
483 return -EFAULT;
484
485 user_dst = areq.req.f8_mp_req.qce_f8_req.data_out;
486 if (!access_ok(VERIFY_WRITE, (void __user *)
487 user_dst, total))
488 return -EFAULT;
489
490 k_buf = kmalloc(total, GFP_KERNEL);
491 if (k_buf == NULL)
492 return -ENOMEM;
493 /* k_buf returned from kmalloc should be cache line aligned */
494 if (__copy_from_user(k_buf, (void __user *)user_src, total)) {
495 kfree(k_buf);
496
497 return -EFAULT;
498 }
499
500 areq.req.f8_mp_req.qce_f8_req.data_out = k_buf;
501 areq.req.f8_mp_req.qce_f8_req.data_in = k_buf;
502
503 areq.op = QCE_OTA_MPKT_F8_OPER;
504
505 pstat->f8_mp_req++;
506 err = submit_req(&areq, podev);
507
508 if (err == 0 && __copy_to_user(user_dst, k_buf, total))
509 err = -EFAULT;
510 kfree(k_buf);
511 break;
512
513 default:
514 return -ENOTTY;
515 }
516
517 return err;
518}
519
520static int qcota_probe(struct platform_device *pdev)
521{
522 void *handle = NULL;
523 int rc = 0;
524 struct ota_dev_control *podev;
525 struct ce_hw_support ce_support;
526
527 if (pdev->id >= MAX_OTA_DEVICE) {
528 pr_err("%s: device id %d exceeds allowed %d\n",
529 __func__, pdev->id, MAX_OTA_DEVICE);
530 return -ENOENT;
531 }
532
533 podev = &qcota_dev[pdev->id];
534
535 INIT_LIST_HEAD(&podev->ready_commands);
536 podev->active_command = NULL;
537 spin_lock_init(&podev->lock);
538 tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
539
540 /* open qce */
541 handle = qce_open(pdev, &rc);
542 if (handle == NULL) {
543 pr_err("%s: device id %d, can not open qce\n",
544 __func__, pdev->id);
545 platform_set_drvdata(pdev, NULL);
546 return rc;
547 }
548 if (qce_hw_support(handle, &ce_support) < 0 ||
549 ce_support.ota == false) {
550 pr_err("%s: device id %d, qce does not support ota capability\n",
551 __func__, pdev->id);
552 rc = -ENODEV;
553 goto err;
554 }
555 podev->qce = handle;
556 podev->pdev = pdev;
557 platform_set_drvdata(pdev, podev);
558
559 rc = misc_register(&podev->miscdevice);
560 if (rc < 0)
561 goto err;
562
563 return 0;
564err:
565 if (handle)
566 qce_close(handle);
567 platform_set_drvdata(pdev, NULL);
568 podev->qce = NULL;
569 podev->pdev = NULL;
570 return rc;
571};
572
573static int qcota_remove(struct platform_device *pdev)
574{
575 struct ota_dev_control *podev;
576
577 podev = platform_get_drvdata(pdev);
578 if (!podev)
579 return 0;
580 if (podev->qce)
581 qce_close(podev->qce);
582
583 if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
584 misc_deregister(&podev->miscdevice);
585 tasklet_kill(&podev->done_tasklet);
586 return 0;
587};
588
589static struct platform_driver qcota_plat_driver = {
590 .probe = qcota_probe,
591 .remove = qcota_remove,
592 .driver = {
593 .name = "qcota",
594 .owner = THIS_MODULE,
595 },
596};
597
598static int _disp_stats(int id)
599{
600 struct qcota_stat *pstat;
601 int len = 0;
602
603 pstat = &_qcota_stat[id];
604 len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
605 "\nQualcomm OTA crypto accelerator %d Statistics:\n",
606 id + 1);
607
608 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
609 " F8 request : %d\n",
610 pstat->f8_req);
611 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
612 " F8 operation success : %d\n",
613 pstat->f8_op_success);
614 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
615 " F8 operation fail : %d\n",
616 pstat->f8_op_fail);
617
618 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
619 " F8 MP request : %d\n",
620 pstat->f8_mp_req);
621 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
622 " F8 MP operation success: %d\n",
623 pstat->f8_mp_op_success);
624 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
625 " F8 MP operation fail : %d\n",
626 pstat->f8_mp_op_fail);
627
628 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
629 " F9 request : %d\n",
630 pstat->f9_req);
631 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
632 " F9 operation success : %d\n",
633 pstat->f9_op_success);
634 len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
635 " F9 operation fail : %d\n",
636 pstat->f9_op_fail);
637
638 return len;
639}
640
641static int _debug_stats_open(struct inode *inode, struct file *file)
642{
643 file->private_data = inode->i_private;
644 return 0;
645}
646
647static ssize_t _debug_stats_read(struct file *file, char __user *buf,
648 size_t count, loff_t *ppos)
649{
650 int rc = -EINVAL;
651 int qcota = *((int *) file->private_data);
652 int len;
653
654 len = _disp_stats(qcota);
655
656 rc = simple_read_from_buffer((void __user *) buf, len,
657 ppos, (void *) _debug_read_buf, len);
658
659 return rc;
660}
661
662static ssize_t _debug_stats_write(struct file *file, const char __user *buf,
663 size_t count, loff_t *ppos)
664{
665
666 int qcota = *((int *) file->private_data);
667
668 memset((char *)&_qcota_stat[qcota], 0, sizeof(struct qcota_stat));
669 return count;
670};
671
672static const struct file_operations _debug_stats_ops = {
673 .open = _debug_stats_open,
674 .read = _debug_stats_read,
675 .write = _debug_stats_write,
676};
677
678static int _qcota_debug_init(void)
679{
680 int rc;
681 char name[DEBUG_MAX_FNAME];
682 int i;
683 struct dentry *dent;
684
685 _debug_dent = debugfs_create_dir("qcota", NULL);
686 if (IS_ERR(_debug_dent)) {
687 pr_err("qcota debugfs_create_dir fail, error %ld\n",
688 PTR_ERR(_debug_dent));
689 return PTR_ERR(_debug_dent);
690 }
691
692 for (i = 0; i < MAX_OTA_DEVICE; i++) {
693 snprintf(name, DEBUG_MAX_FNAME-1, "stats-%d", i+1);
694 _debug_qcota[i] = i;
695 dent = debugfs_create_file(name, 0644, _debug_dent,
696 &_debug_qcota[i], &_debug_stats_ops);
697 if (dent == NULL) {
698 pr_err("qcota debugfs_create_file fail, error %ld\n",
699 PTR_ERR(dent));
700 rc = PTR_ERR(dent);
701 goto err;
702 }
703 }
704 return 0;
705err:
706 debugfs_remove_recursive(_debug_dent);
707 return rc;
708}
709
710static int __init qcota_init(void)
711{
712 int rc;
713
714 rc = _qcota_debug_init();
715 if (rc)
716 return rc;
717 return platform_driver_register(&qcota_plat_driver);
718}
719static void __exit qcota_exit(void)
720{
721 debugfs_remove_recursive(_debug_dent);
722 platform_driver_unregister(&qcota_plat_driver);
723}
724
725MODULE_LICENSE("GPL v2");
726MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
727MODULE_DESCRIPTION("Qualcomm Ota Crypto driver");
728MODULE_VERSION("1.01");
729
730module_init(qcota_init);
731module_exit(qcota_exit);