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