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