blob: ba00d5e36415227eb84e04c795ec9ed6bc8e469d [file] [log] [blame]
Mona Hossain2892b6b2012-02-17 13:53:11 -08001/* Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
2 *
3 * Copyright (c) 2012, Code Aurora Forum. 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 version 2 and
7 * only version 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__
16
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include <linux/module.h>
20#include <linux/fs.h>
21#include <linux/platform_device.h>
22#include <linux/debugfs.h>
23#include <linux/cdev.h>
24#include <linux/uaccess.h>
25#include <linux/sched.h>
26#include <linux/list.h>
27#include <linux/mutex.h>
28#include <linux/io.h>
29#include <linux/ion.h>
30#include <linux/types.h>
31#include <linux/clk.h>
32#include <linux/qseecom.h>
33#include <mach/msm_bus.h>
34#include <mach/msm_bus_board.h>
35#include <mach/scm.h>
36#include <mach/peripheral-loader.h>
37#include "qseecom_legacy.h"
38
39#define QSEECOM_DEV "qseecom"
40#define QSEOS_VERSION_13 0x13
41#define QSEOS_VERSION_14 0x14
42#define QSEOS_CHECK_VERSION_CMD 0x00001803;
43
44enum qseecom_command_scm_resp_type {
45 QSEOS_APP_ID = 0xEE01,
46 QSEOS_LISTENER_ID
47};
48
49enum qseecom_qceos_cmd_id {
50 QSEOS_APP_START_COMMAND = 0x01,
51 QSEOS_APP_SHUTDOWN_COMMAND,
52 QSEOS_APP_LOOKUP_COMMAND,
53 QSEOS_REGISTER_LISTENER,
54 QSEOS_DEREGISTER_LISTENER,
55 QSEOS_CLIENT_SEND_DATA_COMMAND,
56 QSEOS_LISTENER_DATA_RSP_COMMAND,
57 QSEOS_CMD_MAX = 0xEFFFFFFF
58};
59
60enum qseecom_qceos_cmd_status {
61 QSEOS_RESULT_SUCCESS = 0,
62 QSEOS_RESULT_INCOMPLETE,
63 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
64};
65
66__packed struct qseecom_check_app_ireq {
67 uint32_t qsee_cmd_id;
68 char app_name[MAX_APP_NAME_SIZE];
69};
70
71__packed struct qseecom_load_app_ireq {
72 uint32_t qsee_cmd_id;
73 uint32_t mdt_len; /* Length of the mdt file */
74 uint32_t img_len; /* Length of .bxx and .mdt files */
75 uint32_t phy_addr; /* phy addr of the start of image */
76 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
77};
78
79__packed struct qseecom_unload_app_ireq {
80 uint32_t qsee_cmd_id;
81 uint32_t app_id;
82};
83
84__packed struct qseecom_register_listener_ireq {
85 uint32_t qsee_cmd_id;
86 uint32_t listener_id;
87 void *sb_ptr;
88 uint32_t sb_len;
89};
90
91__packed struct qseecom_unregister_listener_ireq {
92 uint32_t qsee_cmd_id;
93 uint32_t listener_id;
94};
95
96__packed struct qseecom_client_send_data_ireq {
97 uint32_t qsee_cmd_id;
98 uint32_t app_id;
99 void *req_ptr;
100 uint32_t req_len;
101 void *rsp_ptr; /* First 4 bytes should always be the return status */
102 uint32_t rsp_len;
103};
104
105/* send_data resp */
106__packed struct qseecom_client_listener_data_irsp {
107 uint32_t qsee_cmd_id;
108 uint32_t listener_id;
109};
110
111/*
112 * struct qseecom_command_scm_resp - qseecom response buffer
113 * @cmd_status: value from enum tz_sched_cmd_status
114 * @sb_in_rsp_addr: points to physical location of response
115 * buffer
116 * @sb_in_rsp_len: length of command response
117 */
118__packed struct qseecom_command_scm_resp {
119 uint32_t result;
120 enum qseecom_command_scm_resp_type resp_type;
121 unsigned int data;
122};
123
124static struct class *driver_class;
125static dev_t qseecom_device_no;
126static struct cdev qseecom_cdev;
127
128/* Data structures used in legacy support */
129static void *pil;
130static uint32_t pil_ref_cnt;
131static DEFINE_MUTEX(pil_access_lock);
132
133static DEFINE_MUTEX(send_msg_lock);
134static DEFINE_MUTEX(qsee_bw_mutex);
135static DEFINE_MUTEX(app_access_lock);
136
137static int qsee_bw_count;
138static struct clk *qseecom_bus_clk;
139static uint32_t qsee_perf_client;
140
141struct qseecom_registered_listener_list {
142 struct list_head list;
143 struct qseecom_register_listener_req svc;
144 u8 *sb_reg_req;
145 u8 *sb_virt;
146 s32 sb_phys;
147 size_t sb_length;
148 struct ion_handle *ihandle; /* Retrieve phy addr */
149
150 wait_queue_head_t rcv_req_wq;
151 int rcv_req_flag;
152};
153
154struct qseecom_registered_app_list {
155 struct list_head list;
156 u32 app_id;
157 u32 ref_cnt;
158};
159
160struct qseecom_control {
161 struct ion_client *ion_clnt; /* Ion client */
162 struct list_head registered_listener_list_head;
163 spinlock_t registered_listener_list_lock;
164
165 struct list_head registered_app_list_head;
166 spinlock_t registered_app_list_lock;
167
168 wait_queue_head_t send_resp_wq;
169 int send_resp_flag;
170
171 uint32_t qseos_version;
172};
173
174struct qseecom_client_handle {
175 u32 app_id;
176 u8 *sb_virt;
177 s32 sb_phys;
178 uint32_t user_virt_sb_base;
179 size_t sb_length;
180 struct ion_handle *ihandle; /* Retrieve phy addr */
181};
182
183struct qseecom_listener_handle {
184 u32 id;
185};
186
187static struct qseecom_control qseecom;
188
189struct qseecom_dev_handle {
190 bool service;
191 union {
192 struct qseecom_client_handle client;
193 struct qseecom_listener_handle listener;
194 };
195 bool released;
196 int abort;
197 wait_queue_head_t abort_wq;
198 atomic_t ioctl_count;
199};
200
201static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800202 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800203{
204 struct qseecom_registered_listener_list *ptr;
205 int unique = 1;
206 unsigned long flags;
207
208 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
209 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800210 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800211 pr_err("Service id: %u is already registered\n",
212 ptr->svc.listener_id);
213 unique = 0;
214 break;
215 }
216 }
217 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
218 return unique;
219}
220
221static struct qseecom_registered_listener_list *__qseecom_find_svc(
222 int32_t listener_id)
223{
224 struct qseecom_registered_listener_list *entry = NULL;
225 unsigned long flags;
226
227 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
228 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
229 {
230 if (entry->svc.listener_id == listener_id)
231 break;
232 }
233 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
234 return entry;
235}
236
237static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
238 struct qseecom_dev_handle *handle,
239 struct qseecom_register_listener_req *listener)
240{
241 int ret = 0;
242 unsigned int flags = 0;
243 struct qseecom_register_listener_ireq req;
244 struct qseecom_command_scm_resp resp;
245 ion_phys_addr_t pa;
246
247 /* Get the handle of the shared fd */
248 svc->ihandle = ion_import_fd(qseecom.ion_clnt, listener->ifd_data_fd);
249 if (svc->ihandle == NULL) {
250 pr_err("Ion client could not retrieve the handle\n");
251 return -ENOMEM;
252 }
253
254 /* Get the physical address of the ION BUF */
255 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
256
257 /* Populate the structure for sending scm call to load image */
258 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
259 svc->ihandle, flags);
260 svc->sb_phys = pa;
261
262 if (qseecom.qseos_version == QSEOS_VERSION_14) {
263 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
264 req.listener_id = svc->svc.listener_id;
265 req.sb_len = svc->sb_length;
266 req.sb_ptr = (void *)svc->sb_phys;
267
268 resp.result = QSEOS_RESULT_INCOMPLETE;
269
270 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
271 sizeof(req), &resp, sizeof(resp));
272 if (ret) {
273 pr_err("qseecom_scm_call failed with err: %d\n", ret);
274 return -EINVAL;
275 }
276
277 if (resp.result != QSEOS_RESULT_SUCCESS) {
278 pr_err("Error SB registration req: resp.result = %d\n",
279 resp.result);
280 return -EPERM;
281 }
282 } else {
283 struct qseecom_command cmd;
284 struct qseecom_response resp;
285 struct qse_pr_init_sb_req_s sb_init_req;
286 struct qse_pr_init_sb_rsp_s sb_init_rsp;
287
288 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
289 sizeof(sb_init_rsp)), GFP_KERNEL);
290
291 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
292 sb_init_req.listener_id = svc->svc.listener_id;
293 sb_init_req.sb_len = svc->sb_length;
294 sb_init_req.sb_ptr = svc->sb_phys;
295
296 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
297
298 /* It will always be a new cmd from this method */
299 cmd.cmd_type = TZ_SCHED_CMD_NEW;
300 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
301 cmd.sb_in_cmd_len = sizeof(sb_init_req);
302
303 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
304
305 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
306 , &resp, sizeof(resp));
307
308 if (ret) {
309 pr_err("qseecom_scm_call failed with err: %d\n", ret);
310 return -EINVAL;
311 }
312
313 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
314 pr_err("SB registration fail resp.cmd_status %d\n",
315 resp.cmd_status);
316 return -EINVAL;
317 }
318 memset(svc->sb_virt, 0, svc->sb_length);
319 }
320 return 0;
321}
322
323static int qseecom_register_listener(struct qseecom_dev_handle *data,
324 void __user *argp)
325{
326 int ret = 0;
327 unsigned long flags;
328 struct qseecom_register_listener_req rcvd_lstnr;
329 struct qseecom_registered_listener_list *new_entry;
330
331 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
332 if (ret) {
333 pr_err("copy_from_user failed\n");
334 return ret;
335 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800336 data->listener.id = 0;
337 data->service = true;
338 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800339 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800340 data->released = true;
341 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800342 }
343
344 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
345 if (!new_entry) {
346 pr_err("kmalloc failed\n");
347 return -ENOMEM;
348 }
349 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
350 new_entry->rcv_req_flag = 0;
351
352 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
353 new_entry->sb_length = rcvd_lstnr.sb_size;
354 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
355 pr_err("qseecom_set_sb_memoryfailed\n");
356 kzfree(new_entry);
357 return -ENOMEM;
358 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800359
Mona Hossain2892b6b2012-02-17 13:53:11 -0800360 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800361 init_waitqueue_head(&new_entry->rcv_req_wq);
362
363 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
364 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
365 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800366
Mona Hossain2892b6b2012-02-17 13:53:11 -0800367 return ret;
368}
369
370static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
371{
372 int ret = 0;
373 unsigned long flags;
374 uint32_t unmap_mem = 0;
375 struct qseecom_register_listener_ireq req;
376 struct qseecom_registered_listener_list *ptr_svc = NULL;
377 struct qseecom_command_scm_resp resp;
378 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
379
380 if (qseecom.qseos_version == QSEOS_VERSION_14) {
381 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
382 req.listener_id = data->listener.id;
383 resp.result = QSEOS_RESULT_INCOMPLETE;
384
385 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
386 sizeof(req), &resp, sizeof(resp));
387 if (ret) {
388 pr_err("qseecom_scm_call failed with err: %d\n", ret);
389 return ret;
390 }
391
392 if (resp.result != QSEOS_RESULT_SUCCESS) {
393 pr_err("SB deregistartion: result=%d\n", resp.result);
394 return -EPERM;
395 }
396 } else {
397 struct qse_pr_init_sb_req_s sb_init_req;
398 struct qseecom_command cmd;
399 struct qseecom_response resp;
400 struct qseecom_registered_listener_list *svc;
401
402 svc = __qseecom_find_svc(data->listener.id);
403 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
404 sb_init_req.listener_id = data->listener.id;
405 sb_init_req.sb_len = 0;
406 sb_init_req.sb_ptr = 0;
407
408 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
409
410 /* It will always be a new cmd from this method */
411 cmd.cmd_type = TZ_SCHED_CMD_NEW;
412 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
413 cmd.sb_in_cmd_len = sizeof(sb_init_req);
414 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
415
416 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
417 &resp, sizeof(resp));
418 if (ret) {
419 pr_err("qseecom_scm_call failed with err: %d\n", ret);
420 return ret;
421 }
422 kzfree(svc->sb_reg_req);
423 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
424 pr_err("Error with SB initialization\n");
425 return -EPERM;
426 }
427 }
428 data->abort = 1;
429 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
430 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
431 list) {
432 if (ptr_svc->svc.listener_id == data->listener.id) {
433 wake_up_all(&ptr_svc->rcv_req_wq);
434 break;
435 }
436 }
437 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
438
439 while (atomic_read(&data->ioctl_count) > 1) {
440 if (wait_event_interruptible(data->abort_wq,
441 atomic_read(&data->ioctl_count) <= 1)) {
442 pr_err("Interrupted from abort\n");
443 ret = -ERESTARTSYS;
444 break;
445 }
446 }
447
448 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
449 list_for_each_entry(ptr_svc,
450 &qseecom.registered_listener_list_head,
451 list)
452 {
453 if (ptr_svc->svc.listener_id == data->listener.id) {
454 if (ptr_svc->sb_virt) {
455 unmap_mem = 1;
456 ihandle = ptr_svc->ihandle;
457 }
458 list_del(&ptr_svc->list);
459 kzfree(ptr_svc);
460 break;
461 }
462 }
463 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
464
465 /* Unmap the memory */
466 if (unmap_mem) {
467 if (!IS_ERR_OR_NULL(ihandle)) {
468 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
469 ion_free(qseecom.ion_clnt, ihandle);
470 }
471 }
472 data->released = true;
473 return ret;
474}
475
476static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
477 void __user *argp)
478{
479 ion_phys_addr_t pa;
480 int32_t ret;
481 unsigned int flags = 0;
482 struct qseecom_set_sb_mem_param_req req;
483 uint32_t len;
484
485 /* Copy the relevant information needed for loading the image */
486 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
487 return -EFAULT;
488
Mona Hossain2892b6b2012-02-17 13:53:11 -0800489 /* Get the handle of the shared fd */
490 data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
491 if (IS_ERR_OR_NULL(data->client.ihandle)) {
492 pr_err("Ion client could not retrieve the handle\n");
493 return -ENOMEM;
494 }
495 /* Get the physical address of the ION BUF */
496 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
497 /* Populate the structure for sending scm call to load image */
498 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
499 data->client.ihandle,
500 flags);
501 data->client.sb_phys = pa;
502 data->client.sb_length = req.sb_len;
503 data->client.user_virt_sb_base = req.virt_sb_base;
504 return 0;
505}
506
507
508static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
509{
510 int ret;
511 ret = (qseecom.send_resp_flag != 0);
512 return ret || data->abort;
513}
514
515static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
516 struct qseecom_command_scm_resp *resp)
517{
518 int ret = 0;
519 uint32_t lstnr;
520 unsigned long flags;
521 struct qseecom_client_listener_data_irsp send_data_rsp;
522 struct qseecom_registered_listener_list *ptr_svc = NULL;
523
524
525 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
526 lstnr = resp->data;
527 /*
528 * Wake up blocking lsitener service with the lstnr id
529 */
530 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
531 flags);
532 list_for_each_entry(ptr_svc,
533 &qseecom.registered_listener_list_head, list) {
534 if (ptr_svc->svc.listener_id == lstnr) {
535 ptr_svc->rcv_req_flag = 1;
536 wake_up_interruptible(&ptr_svc->rcv_req_wq);
537 break;
538 }
539 }
540 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
541 flags);
542 if (ptr_svc->svc.listener_id != lstnr) {
543 pr_warning("Service requested for does on exist\n");
544 return -ERESTARTSYS;
545 }
546 pr_debug("waking up rcv_req_wq and "
547 "waiting for send_resp_wq\n");
548 if (wait_event_interruptible(qseecom.send_resp_wq,
549 __qseecom_listener_has_sent_rsp(data))) {
550 pr_warning("Interrupted: exiting send_cmd loop\n");
551 return -ERESTARTSYS;
552 }
553
554 if (data->abort) {
555 pr_err("Aborting driver\n");
556 return -ENODEV;
557 }
558 qseecom.send_resp_flag = 0;
559 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
560 send_data_rsp.listener_id = lstnr ;
561
562 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
563 (const void *)&send_data_rsp,
564 sizeof(send_data_rsp), resp,
565 sizeof(*resp));
566 if (ret) {
567 pr_err("qseecom_scm_call failed with err: %d\n", ret);
568 return ret;
569 }
570 }
571 return ret;
572}
573
574static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
575{
576 struct qseecom_registered_app_list *entry = NULL;
577 unsigned long flags = 0;
578 u32 app_id = 0;
579 struct ion_handle *ihandle; /* Ion handle */
580 struct qseecom_load_img_req load_img_req;
581 int32_t ret;
582 ion_phys_addr_t pa = 0;
583 uint32_t len;
584 struct qseecom_command_scm_resp resp;
585 struct qseecom_check_app_ireq req;
586 /* Copy the relevant information needed for loading the image */
587 if (__copy_from_user(&load_img_req,
588 (void __user *)argp,
589 sizeof(struct qseecom_load_img_req))) {
590 pr_err("copy_from_user failed\n");
591 return -EFAULT;
592 }
593
594 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
595 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
596
597 /* SCM_CALL to check if app_id for the mentioned app exists */
598 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
599 sizeof(struct qseecom_check_app_ireq),
600 &resp, sizeof(resp));
601
602 if (resp.result == QSEOS_RESULT_FAILURE)
603 app_id = 0;
604 else
605 app_id = resp.data;
606
607 if (app_id) {
608 pr_warn("App id already exists\n");
609 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
610 list_for_each_entry(entry,
611 &qseecom.registered_app_list_head, list){
612 if (entry->app_id == app_id) {
613 entry->ref_cnt++;
614 break;
615 }
616 }
617 spin_unlock_irqrestore(
618 &qseecom.registered_app_list_lock, flags);
619 } else {
620 struct qseecom_load_app_ireq load_req;
621
622 pr_warn("App id does not exist\n");
623 /* Get the handle of the shared fd */
624 ihandle = ion_import_fd(qseecom.ion_clnt,
625 load_img_req.ifd_data_fd);
626 if (IS_ERR_OR_NULL(ihandle)) {
627 pr_err("Ion client could not retrieve the handle\n");
628 return -ENOMEM;
629 }
630
631 /* Get the physical address of the ION BUF */
632 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
633
634 /* Populate the structure for sending scm call to load image */
635 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
636 load_req.mdt_len = load_img_req.mdt_len;
637 load_req.img_len = load_img_req.img_len;
638 load_req.phy_addr = pa;
639
640 /* SCM_CALL to load the app and get the app_id back */
641 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
642 sizeof(struct qseecom_load_app_ireq),
643 &resp, sizeof(resp));
644
645 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
646 ret = __qseecom_process_incomplete_cmd(data, &resp);
647 if (ret) {
648 pr_err("process_incomplete_cmd failed err: %d\n",
649 ret);
650 if (!IS_ERR_OR_NULL(ihandle))
651 ion_free(qseecom.ion_clnt, ihandle);
652 return ret;
653 }
654 }
655 if (resp.result != QSEOS_RESULT_SUCCESS) {
656 pr_err("scm_call failed resp.result != QSEOS_RESULT_SUCCESS\n");
657 if (!IS_ERR_OR_NULL(ihandle))
658 ion_free(qseecom.ion_clnt, ihandle);
659 return -EFAULT;
660 }
661
662 app_id = resp.data;
663
664 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
665 if (!entry) {
666 pr_err("kmalloc failed\n");
667 return -ENOMEM;
668 }
669 entry->app_id = app_id;
670 entry->ref_cnt = 1;
671
672 /* Deallocate the handle */
673 if (!IS_ERR_OR_NULL(ihandle))
674 ion_free(qseecom.ion_clnt, ihandle);
675
676 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
677 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
678 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
679 flags);
680 }
681 data->client.app_id = app_id;
682 load_img_req.app_id = app_id;
683 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
684 pr_err("copy_to_user failed\n");
685 kzfree(entry);
686 return -EFAULT;
687 }
688 return 0;
689}
690
691static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
692{
693 wake_up_all(&qseecom.send_resp_wq);
694 while (atomic_read(&data->ioctl_count) > 1) {
695 if (wait_event_interruptible(data->abort_wq,
696 atomic_read(&data->ioctl_count) <= 1)) {
697 pr_err("Interrupted from abort\n");
698 return -ERESTARTSYS;
699 break;
700 }
701 }
702 /* Set unload app */
703 return 1;
704}
705
706static int qseecom_unload_app(struct qseecom_dev_handle *data)
707{
708 unsigned long flags;
709 int ret = 0;
710 struct qseecom_command_scm_resp resp;
711 struct qseecom_registered_app_list *ptr_app;
712 uint32_t unload = 0;
713
714 if (qseecom.qseos_version == QSEOS_VERSION_14) {
715 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
716 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
717 list) {
718 if (ptr_app->app_id == data->client.app_id) {
719 if (ptr_app->ref_cnt == 1) {
720 unload = __qseecom_cleanup_app(data);
721 list_del(&ptr_app->list);
722 kzfree(ptr_app);
723 break;
724 } else {
725 ptr_app->ref_cnt--;
Mona Hossain0af10ab2012-02-28 18:26:41 -0800726 data->released = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800727 break;
728 }
729 }
730 }
731 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
732 flags);
733 }
734 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
735 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
736 ion_free(qseecom.ion_clnt, data->client.ihandle);
737 }
738
739 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
740 struct qseecom_unload_app_ireq req;
741
742 /* Populate the structure for sending scm call to load image */
743 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
744 req.app_id = data->client.app_id;
745
746 /* SCM_CALL to unload the app */
747 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
748 sizeof(struct qseecom_unload_app_ireq),
749 &resp, sizeof(resp));
750 if (ret) {
751 pr_err("Fail to unload APP\n");
752 return -EFAULT;
753 }
754 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
755 ret = __qseecom_process_incomplete_cmd(data, &resp);
756 if (ret) {
757 pr_err("process_incomplete_cmd fail err: %d\n",
758 ret);
759 return ret;
760 }
761 }
762 }
763
764 if (qseecom.qseos_version == QSEOS_VERSION_13) {
765 data->abort = 1;
766 wake_up_all(&qseecom.send_resp_wq);
767 while (atomic_read(&data->ioctl_count) > 0) {
768 if (wait_event_interruptible(data->abort_wq,
769 atomic_read(&data->ioctl_count) <= 0)) {
770 pr_err("Interrupted from abort\n");
771 ret = -ERESTARTSYS;
772 break;
773 }
774 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800775 }
776 data->released = true;
777 return ret;
778}
779
780static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
781 uint32_t virt)
782{
783 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
784}
785
786static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
787 struct qseecom_send_cmd_req *req)
788{
789 int ret = 0;
790 unsigned long flags;
791 u32 reqd_len_sb_in = 0;
792 struct qseecom_command cmd;
793 struct qseecom_response resp;
794
795
796 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
797 pr_err("cmd buffer or response buffer is null\n");
798 return -EINVAL;
799 }
800
801 if (req->cmd_req_len <= 0 ||
802 req->resp_len <= 0 ||
803 req->cmd_req_len > data->client.sb_length ||
804 req->resp_len > data->client.sb_length) {
805 pr_err("cmd buffer length or "
806 "response buffer length not valid\n");
807 return -EINVAL;
808 }
809
810 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
811 if (reqd_len_sb_in > data->client.sb_length) {
812 pr_debug("Not enough memory to fit cmd_buf and "
813 "resp_buf. Required: %u, Available: %u\n",
814 reqd_len_sb_in, data->client.sb_length);
815 return -ENOMEM;
816 }
817 cmd.cmd_type = TZ_SCHED_CMD_NEW;
818 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
819 cmd.sb_in_cmd_len = req->cmd_req_len;
820
821 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
822 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
823 resp.sb_in_rsp_len = req->resp_len;
824
825 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
826 sizeof(cmd), &resp, sizeof(resp));
827
828 if (ret) {
829 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
830 return ret;
831 }
832
833 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
834 /*
835 * If cmd is incomplete, get the callback cmd out from SB out
836 * and put it on the list
837 */
838 struct qseecom_registered_listener_list *ptr_svc = NULL;
839 /*
840 * We don't know which service can handle the command. so we
841 * wake up all blocking services and let them figure out if
842 * they can handle the given command.
843 */
844 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
845 flags);
846 list_for_each_entry(ptr_svc,
847 &qseecom.registered_listener_list_head, list) {
848 ptr_svc->rcv_req_flag = 1;
849 wake_up_interruptible(&ptr_svc->rcv_req_wq);
850 }
851 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
852 flags);
853
854 pr_debug("waking up rcv_req_wq and "
855 "waiting for send_resp_wq\n");
856 if (wait_event_interruptible(qseecom.send_resp_wq,
857 __qseecom_listener_has_sent_rsp(data))) {
858 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
859 return -ERESTARTSYS;
860 }
861
862 if (data->abort) {
863 pr_err("Aborting driver\n");
864 return -ENODEV;
865 }
866 qseecom.send_resp_flag = 0;
867 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
868 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
869 sizeof(cmd), &resp, sizeof(resp));
870 if (ret) {
871 pr_err("qseecom_scm_call failed with err: %d\n", ret);
872 return ret;
873 }
874 }
875 return ret;
876}
877
878static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
879 struct qseecom_send_cmd_req *req)
880{
881 int ret = 0;
882 u32 reqd_len_sb_in = 0;
883 struct qseecom_client_send_data_ireq send_data_req;
884 struct qseecom_command_scm_resp resp;
885
886 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
887 pr_err("cmd buffer or response buffer is null\n");
888 return -EINVAL;
889 }
890
891 if (req->cmd_req_len <= 0 ||
892 req->resp_len <= 0 ||
893 req->cmd_req_len > data->client.sb_length ||
894 req->resp_len > data->client.sb_length) {
895 pr_err("cmd buffer length or "
896 "response buffer length not valid\n");
897 return -EINVAL;
898 }
899
900 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
901 if (reqd_len_sb_in > data->client.sb_length) {
902 pr_debug("Not enough memory to fit cmd_buf and "
903 "resp_buf. Required: %u, Available: %u\n",
904 reqd_len_sb_in, data->client.sb_length);
905 return -ENOMEM;
906 }
907
908 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
909 send_data_req.app_id = data->client.app_id;
910 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
911 (uint32_t)req->cmd_req_buf));
912 send_data_req.req_len = req->cmd_req_len;
913 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
914 (uint32_t)req->resp_buf));
915 send_data_req.rsp_len = req->resp_len;
916
917 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
918 sizeof(send_data_req),
919 &resp, sizeof(resp));
920 if (ret) {
921 pr_err("qseecom_scm_call failed with err: %d\n", ret);
922 return ret;
923 }
924
925 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
926 ret = __qseecom_process_incomplete_cmd(data, &resp);
927 if (ret) {
928 pr_err("process_incomplete_cmd failed err: %d\n", ret);
929 return ret;
930 }
931 }
932 return ret;
933}
934
935
936static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
937{
938 int ret = 0;
939 struct qseecom_send_cmd_req req;
940
941 ret = copy_from_user(&req, argp, sizeof(req));
942 if (ret) {
943 pr_err("copy_from_user failed\n");
944 return ret;
945 }
946 if (qseecom.qseos_version == QSEOS_VERSION_14)
947 ret = __qseecom_send_cmd(data, &req);
948 else
949 ret = __qseecom_send_cmd_legacy(data, &req);
950 if (ret)
951 return ret;
952
953 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
954 req.resp_len, req.resp_buf);
955 return ret;
956}
957
958static int __qseecom_send_cmd_req_clean_up(
959 struct qseecom_send_modfd_cmd_req *req)
960{
961 char *field;
962 uint32_t *update;
963 int ret = 0;
964 int i = 0;
965
966 for (i = 0; i < MAX_ION_FD; i++) {
967 if (req->ifd_data[i].fd != 0) {
968 field = (char *)req->cmd_req_buf +
969 req->ifd_data[i].cmd_buf_offset;
970 update = (uint32_t *) field;
971 *update = 0;
972 }
973 }
974 return ret;
975}
976
977static int __qseecom_update_with_phy_addr(
978 struct qseecom_send_modfd_cmd_req *req)
979{
980 struct ion_handle *ihandle;
981 char *field;
982 uint32_t *update;
983 ion_phys_addr_t pa;
984 int ret = 0;
985 int i = 0;
986 uint32_t length;
987
988 for (i = 0; i < MAX_ION_FD; i++) {
989 if (req->ifd_data[i].fd != 0) {
990 /* Get the handle of the shared fd */
991 ihandle = ion_import_fd(qseecom.ion_clnt,
992 req->ifd_data[i].fd);
993 if (IS_ERR_OR_NULL(ihandle)) {
994 pr_err("Ion client can't retrieve the handle\n");
995 return -ENOMEM;
996 }
997 field = (char *) req->cmd_req_buf +
998 req->ifd_data[i].cmd_buf_offset;
999 update = (uint32_t *) field;
1000
1001 /* Populate the cmd data structure with the phys_addr */
1002 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1003 if (ret)
1004 return -ENOMEM;
1005
1006 *update = (uint32_t)pa;
1007 /* Deallocate the handle */
1008 if (!IS_ERR_OR_NULL(ihandle))
1009 ion_free(qseecom.ion_clnt, ihandle);
1010 }
1011 }
1012 return ret;
1013}
1014
1015static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1016 void __user *argp)
1017{
1018 int ret = 0;
1019 struct qseecom_send_modfd_cmd_req req;
1020 struct qseecom_send_cmd_req send_cmd_req;
1021
1022 ret = copy_from_user(&req, argp, sizeof(req));
1023 if (ret) {
1024 pr_err("copy_from_user failed\n");
1025 return ret;
1026 }
1027 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1028 send_cmd_req.cmd_req_len = req.cmd_req_len;
1029 send_cmd_req.resp_buf = req.resp_buf;
1030 send_cmd_req.resp_len = req.resp_len;
1031
1032 ret = __qseecom_update_with_phy_addr(&req);
1033 if (ret)
1034 return ret;
1035 if (qseecom.qseos_version == QSEOS_VERSION_14)
1036 ret = __qseecom_send_cmd(data, &send_cmd_req);
1037 else
1038 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1039 __qseecom_send_cmd_req_clean_up(&req);
1040
1041 if (ret)
1042 return ret;
1043
1044 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1045 req.resp_len, req.resp_buf);
1046 return ret;
1047}
1048
1049static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1050 struct qseecom_registered_listener_list *svc)
1051{
1052 int ret;
1053 ret = (svc->rcv_req_flag != 0);
1054 return ret || data->abort;
1055}
1056
1057static int qseecom_receive_req(struct qseecom_dev_handle *data)
1058{
1059 int ret = 0;
1060 struct qseecom_registered_listener_list *this_lstnr;
1061
1062 this_lstnr = __qseecom_find_svc(data->listener.id);
1063 while (1) {
1064 if (wait_event_interruptible(this_lstnr->rcv_req_wq,
1065 __qseecom_listener_has_rcvd_req(data,
1066 this_lstnr))) {
1067 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1068 /* woken up for different reason */
1069 return -ERESTARTSYS;
1070 }
1071
1072 if (data->abort) {
1073 pr_err("Aborting driver!\n");
1074 return -ENODEV;
1075 }
1076 this_lstnr->rcv_req_flag = 0;
1077 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1078 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1079 break;
1080 } else {
1081 break;
1082 }
1083 }
1084 return ret;
1085}
1086
1087static int qseecom_send_resp(void)
1088{
1089 qseecom.send_resp_flag = 1;
1090 wake_up_interruptible(&qseecom.send_resp_wq);
1091 return 0;
1092}
1093
1094static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1095 void __user *argp)
1096{
1097 struct qseecom_qseos_version_req req;
1098
1099 if (copy_from_user(&req, argp, sizeof(req))) {
1100 pr_err("copy_from_user failed");
1101 return -EINVAL;
1102 }
1103 req.qseos_version = qseecom.qseos_version;
1104 if (copy_to_user(argp, &req, sizeof(req))) {
1105 pr_err("copy_to_user failed");
1106 return -EINVAL;
1107 }
1108 return 0;
1109}
1110
1111static int qsee_vote_for_clock(void)
1112{
1113 int ret = 0;
1114
1115 if (!qsee_perf_client)
1116 return -EINVAL;
1117
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001118 /* Check if the clk is valid */
1119 if (IS_ERR_OR_NULL(qseecom_bus_clk))
1120 return -EINVAL;
1121
Mona Hossain2892b6b2012-02-17 13:53:11 -08001122 mutex_lock(&qsee_bw_mutex);
1123 if (!qsee_bw_count) {
1124 ret = msm_bus_scale_client_update_request(
1125 qsee_perf_client, 1);
1126 if (ret) {
1127 pr_err("Bandwidth request failed (%d)\n", ret);
1128 } else {
1129 ret = clk_enable(qseecom_bus_clk);
1130 if (ret)
1131 pr_err("Clock enable failed\n");
1132 }
1133 }
1134 if (ret)
1135 msm_bus_scale_client_update_request(qsee_perf_client, 0);
1136 else
1137 qsee_bw_count++;
1138
1139 mutex_unlock(&qsee_bw_mutex);
1140 return ret;
1141}
1142
1143static void qsee_disable_clock_vote(void)
1144{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001145
Mona Hossain2892b6b2012-02-17 13:53:11 -08001146 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001147 return;
1148
1149 /* Check if the clk is valid */
1150 if (IS_ERR_OR_NULL(qseecom_bus_clk))
1151 return;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001152
1153 mutex_lock(&qsee_bw_mutex);
1154 if (qsee_bw_count > 0) {
1155 if (qsee_bw_count-- == 1) {
1156 msm_bus_scale_client_update_request(qsee_perf_client,
1157 0);
1158 clk_disable(qseecom_bus_clk);
1159 }
1160 }
1161 mutex_unlock(&qsee_bw_mutex);
1162}
1163
1164
1165static long qseecom_ioctl(struct file *file, unsigned cmd,
1166 unsigned long arg)
1167{
1168 int ret = 0;
1169 struct qseecom_dev_handle *data = file->private_data;
1170 void __user *argp = (void __user *) arg;
1171
1172 if (data->abort) {
1173 pr_err("Aborting qseecom driver\n");
1174 return -ENODEV;
1175 }
1176
1177 switch (cmd) {
1178 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1179 pr_debug("ioctl register_listener_req()\n");
1180 atomic_inc(&data->ioctl_count);
1181 ret = qseecom_register_listener(data, argp);
1182 atomic_dec(&data->ioctl_count);
1183 wake_up_all(&data->abort_wq);
1184 if (ret)
1185 pr_err("failed qseecom_register_listener: %d\n", ret);
1186 break;
1187 }
1188 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1189 pr_debug("ioctl unregister_listener_req()\n");
1190 atomic_inc(&data->ioctl_count);
1191 ret = qseecom_unregister_listener(data);
1192 atomic_dec(&data->ioctl_count);
1193 wake_up_all(&data->abort_wq);
1194 if (ret)
1195 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1196 break;
1197 }
1198 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1199 /* Only one client allowed here at a time */
1200 mutex_lock(&send_msg_lock);
1201 atomic_inc(&data->ioctl_count);
1202 ret = qseecom_send_cmd(data, argp);
1203 atomic_dec(&data->ioctl_count);
1204 wake_up_all(&data->abort_wq);
1205 mutex_unlock(&send_msg_lock);
1206 if (ret)
1207 pr_err("failed qseecom_send_cmd: %d\n", ret);
1208 break;
1209 }
1210 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1211 /* Only one client allowed here at a time */
1212 mutex_lock(&send_msg_lock);
1213 atomic_inc(&data->ioctl_count);
1214 ret = qseecom_send_modfd_cmd(data, argp);
1215 atomic_dec(&data->ioctl_count);
1216 wake_up_all(&data->abort_wq);
1217 mutex_unlock(&send_msg_lock);
1218 if (ret)
1219 pr_err("failed qseecom_send_cmd: %d\n", ret);
1220 break;
1221 }
1222 case QSEECOM_IOCTL_RECEIVE_REQ: {
1223 atomic_inc(&data->ioctl_count);
1224 ret = qseecom_receive_req(data);
1225 atomic_dec(&data->ioctl_count);
1226 wake_up_all(&data->abort_wq);
1227 if (ret)
1228 pr_err("failed qseecom_receive_req: %d\n", ret);
1229 break;
1230 }
1231 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1232 atomic_inc(&data->ioctl_count);
1233 ret = qseecom_send_resp();
1234 atomic_dec(&data->ioctl_count);
1235 wake_up_all(&data->abort_wq);
1236 if (ret)
1237 pr_err("failed qseecom_send_resp: %d\n", ret);
1238 break;
1239 }
1240 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1241 ret = qseecom_set_client_mem_param(data, argp);
1242 if (ret)
1243 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1244 ret);
1245 break;
1246 }
1247 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1248 mutex_lock(&app_access_lock);
1249 atomic_inc(&data->ioctl_count);
1250 ret = qseecom_load_app(data, argp);
1251 atomic_dec(&data->ioctl_count);
1252 mutex_unlock(&app_access_lock);
1253 if (ret)
1254 pr_err("failed load_app request: %d\n", ret);
1255 break;
1256 }
1257 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1258 mutex_lock(&app_access_lock);
1259 atomic_inc(&data->ioctl_count);
1260 ret = qseecom_unload_app(data);
1261 atomic_dec(&data->ioctl_count);
1262 mutex_unlock(&app_access_lock);
1263 if (ret)
1264 pr_err("failed unload_app request: %d\n", ret);
1265 break;
1266 }
1267 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1268 atomic_inc(&data->ioctl_count);
1269 ret = qseecom_get_qseos_version(data, argp);
1270 if (ret)
1271 pr_err("qseecom_get_qseos_version: %d\n", ret);
1272 atomic_dec(&data->ioctl_count);
1273 break;
1274 }
1275 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1276 atomic_inc(&data->ioctl_count);
1277 ret = qsee_vote_for_clock();
1278 if (ret)
1279 pr_err("Failed to vote for clock%d\n", ret);
1280 atomic_dec(&data->ioctl_count);
1281 break;
1282 }
1283 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1284 atomic_inc(&data->ioctl_count);
1285 qsee_disable_clock_vote();
1286 atomic_dec(&data->ioctl_count);
1287 break;
1288 }
1289 default:
1290 return -EINVAL;
1291 }
1292 return ret;
1293}
1294
1295static int qseecom_open(struct inode *inode, struct file *file)
1296{
1297 int ret = 0;
1298 struct qseecom_dev_handle *data;
1299
1300 data = kzalloc(sizeof(*data), GFP_KERNEL);
1301 if (!data) {
1302 pr_err("kmalloc failed\n");
1303 return -ENOMEM;
1304 }
1305 file->private_data = data;
1306 data->abort = 0;
1307 data->service = false;
1308 data->released = false;
1309 init_waitqueue_head(&data->abort_wq);
1310 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001311 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1312 int pil_error;
1313 mutex_lock(&pil_access_lock);
1314 if (pil_ref_cnt == 0) {
1315 pil = pil_get("tzapps");
1316 if (IS_ERR(pil)) {
1317 pr_err("Playready PIL image load failed\n");
1318 pil_error = PTR_ERR(pil);
1319 pil = NULL;
1320 pr_debug("tzapps image load FAILED\n");
1321 mutex_unlock(&pil_access_lock);
1322 return pil_error;
1323 }
1324 }
1325 pil_ref_cnt++;
1326 mutex_unlock(&pil_access_lock);
1327 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001328 return ret;
1329}
1330
1331static int qseecom_release(struct inode *inode, struct file *file)
1332{
1333 struct qseecom_dev_handle *data = file->private_data;
1334 int ret = 0;
1335
1336 if (data->released == false) {
1337 pr_warn("data->released == false\n");
1338 if (data->service)
1339 ret = qseecom_unregister_listener(data);
1340 else
1341 ret = qseecom_unload_app(data);
1342 if (ret) {
1343 pr_err("Close failed\n");
1344 return ret;
1345 }
1346 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001347 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1348 mutex_lock(&pil_access_lock);
1349 if (pil_ref_cnt == 1)
1350 pil_put(pil);
1351 pil_ref_cnt--;
1352 mutex_unlock(&pil_access_lock);
1353 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001354 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001355 qsee_disable_clock_vote();
1356
Mona Hossain2892b6b2012-02-17 13:53:11 -08001357 return ret;
1358}
1359
1360/* qseecom bus scaling */
1361static struct msm_bus_paths qsee_bw_table[] = {
1362 {
1363 .vectors = (struct msm_bus_vectors[]){
1364 {
1365 .src = MSM_BUS_MASTER_SPS,
1366 .dst = MSM_BUS_SLAVE_EBI_CH0,
1367 },
1368 },
1369 .num_paths = 1,
1370 },
1371 {
1372 .vectors = (struct msm_bus_vectors[]){
1373 {
1374 .src = MSM_BUS_MASTER_SPS,
1375 .dst = MSM_BUS_SLAVE_EBI_CH0,
1376 .ib = (492 * 8) * 1000000UL,
1377 .ab = (492 * 8) * 100000UL,
1378 },
1379 },
1380 .num_paths = 1,
1381 },
1382};
1383
1384static struct msm_bus_scale_pdata qsee_bus_pdata = {
1385 .usecase = qsee_bw_table,
1386 .num_usecases = ARRAY_SIZE(qsee_bw_table),
1387 .name = "qsee",
1388};
1389
1390static const struct file_operations qseecom_fops = {
1391 .owner = THIS_MODULE,
1392 .unlocked_ioctl = qseecom_ioctl,
1393 .open = qseecom_open,
1394 .release = qseecom_release
1395};
1396
1397static int __init qseecom_init(void)
1398{
1399 int rc;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001400 struct device *class_dev;
1401 char qsee_not_legacy = 0;
1402 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1403
1404 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1405 if (rc < 0) {
1406 pr_err("alloc_chrdev_region failed %d\n", rc);
1407 return rc;
1408 }
1409
1410 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1411 if (IS_ERR(driver_class)) {
1412 rc = -ENOMEM;
1413 pr_err("class_create failed %d\n", rc);
1414 goto unregister_chrdev_region;
1415 }
1416
1417 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1418 QSEECOM_DEV);
1419 if (!class_dev) {
1420 pr_err("class_device_create failed %d\n", rc);
1421 rc = -ENOMEM;
1422 goto class_destroy;
1423 }
1424
1425 cdev_init(&qseecom_cdev, &qseecom_fops);
1426 qseecom_cdev.owner = THIS_MODULE;
1427
1428 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1429 if (rc < 0) {
1430 pr_err("cdev_add failed %d\n", rc);
1431 goto err;
1432 }
1433
1434 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1435 spin_lock_init(&qseecom.registered_listener_list_lock);
1436 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1437 spin_lock_init(&qseecom.registered_app_list_lock);
1438 init_waitqueue_head(&qseecom.send_resp_wq);
1439 qseecom.send_resp_flag = 0;
1440
1441 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1442 &qsee_not_legacy, sizeof(qsee_not_legacy));
1443 if (rc) {
1444 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1445 goto err;
1446 }
1447 if (qsee_not_legacy)
1448 qseecom.qseos_version = QSEOS_VERSION_14;
1449 else {
1450 qseecom.qseos_version = QSEOS_VERSION_13;
1451 pil = NULL;
1452 pil_ref_cnt = 0;
1453 }
1454 /* Create ION msm client */
1455 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1456 if (qseecom.ion_clnt == NULL) {
1457 pr_err("Ion client cannot be created\n");
1458 rc = -ENOMEM;
1459 goto err;
1460 }
1461
1462 /* register client for bus scaling */
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001463 qsee_perf_client = msm_bus_scale_register_client(
1464 &qsee_bus_pdata);
1465 if (!qsee_perf_client) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001466 pr_err("Unable to register bus client\n");
1467
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001468 qseecom_bus_clk = clk_get(class_dev, "qseecom");
1469 if (IS_ERR(qseecom_bus_clk)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001470 qseecom_bus_clk = NULL;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001471 } else if (qseecom_bus_clk != NULL) {
1472 pr_debug("Enabled DFAB clock");
1473 clk_set_rate(qseecom_bus_clk, 64000000);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001474 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001475 }
1476 return 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001477
Mona Hossain2892b6b2012-02-17 13:53:11 -08001478err:
1479 device_destroy(driver_class, qseecom_device_no);
1480class_destroy:
1481 class_destroy(driver_class);
1482unregister_chrdev_region:
1483 unregister_chrdev_region(qseecom_device_no, 1);
1484 return rc;
1485}
1486
1487static void __exit qseecom_exit(void)
1488{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001489 clk_put(qseecom_bus_clk);
1490
Mona Hossain2892b6b2012-02-17 13:53:11 -08001491 device_destroy(driver_class, qseecom_device_no);
1492 class_destroy(driver_class);
1493 unregister_chrdev_region(qseecom_device_no, 1);
1494 ion_client_destroy(qseecom.ion_clnt);
1495}
1496
1497MODULE_LICENSE("GPL v2");
1498MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
1499
1500module_init(qseecom_init);
1501module_exit(qseecom_exit);