blob: 12f896e11a4a50b8215ea3c71eccc74ade41e07b [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>
Mitchel Humpherys4f8be2e2012-09-06 10:41:41 -070031#include <linux/msm_ion.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080032#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>
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070036#include <mach/board.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080037#include <mach/msm_bus.h>
38#include <mach/msm_bus_board.h>
39#include <mach/scm.h>
40#include <mach/peripheral-loader.h>
Ramesh Masavarapuff377032012-09-14 12:11:32 -070041#include <mach/socinfo.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080042#include "qseecom_legacy.h"
43
44#define QSEECOM_DEV "qseecom"
45#define QSEOS_VERSION_13 0x13
46#define QSEOS_VERSION_14 0x14
47#define QSEOS_CHECK_VERSION_CMD 0x00001803;
48
49enum qseecom_command_scm_resp_type {
50 QSEOS_APP_ID = 0xEE01,
51 QSEOS_LISTENER_ID
52};
53
54enum qseecom_qceos_cmd_id {
55 QSEOS_APP_START_COMMAND = 0x01,
56 QSEOS_APP_SHUTDOWN_COMMAND,
57 QSEOS_APP_LOOKUP_COMMAND,
58 QSEOS_REGISTER_LISTENER,
59 QSEOS_DEREGISTER_LISTENER,
60 QSEOS_CLIENT_SEND_DATA_COMMAND,
61 QSEOS_LISTENER_DATA_RSP_COMMAND,
Mona Hossain5ab9d772012-04-11 21:00:40 -070062 QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
63 QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
Mona Hossain2892b6b2012-02-17 13:53:11 -080064 QSEOS_CMD_MAX = 0xEFFFFFFF
65};
66
67enum qseecom_qceos_cmd_status {
68 QSEOS_RESULT_SUCCESS = 0,
69 QSEOS_RESULT_INCOMPLETE,
70 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
71};
72
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070073enum qseecom_clk_definitions {
74 CLK_DFAB = 0,
75 CLK_SFPB,
76};
77
Mona Hossain2892b6b2012-02-17 13:53:11 -080078__packed struct qseecom_check_app_ireq {
79 uint32_t qsee_cmd_id;
80 char app_name[MAX_APP_NAME_SIZE];
81};
82
83__packed struct qseecom_load_app_ireq {
84 uint32_t qsee_cmd_id;
85 uint32_t mdt_len; /* Length of the mdt file */
86 uint32_t img_len; /* Length of .bxx and .mdt files */
87 uint32_t phy_addr; /* phy addr of the start of image */
88 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
89};
90
91__packed struct qseecom_unload_app_ireq {
92 uint32_t qsee_cmd_id;
93 uint32_t app_id;
94};
95
96__packed struct qseecom_register_listener_ireq {
97 uint32_t qsee_cmd_id;
98 uint32_t listener_id;
99 void *sb_ptr;
100 uint32_t sb_len;
101};
102
103__packed struct qseecom_unregister_listener_ireq {
104 uint32_t qsee_cmd_id;
105 uint32_t listener_id;
106};
107
108__packed struct qseecom_client_send_data_ireq {
109 uint32_t qsee_cmd_id;
110 uint32_t app_id;
111 void *req_ptr;
112 uint32_t req_len;
113 void *rsp_ptr; /* First 4 bytes should always be the return status */
114 uint32_t rsp_len;
115};
116
117/* send_data resp */
118__packed struct qseecom_client_listener_data_irsp {
119 uint32_t qsee_cmd_id;
120 uint32_t listener_id;
121};
122
123/*
124 * struct qseecom_command_scm_resp - qseecom response buffer
125 * @cmd_status: value from enum tz_sched_cmd_status
126 * @sb_in_rsp_addr: points to physical location of response
127 * buffer
128 * @sb_in_rsp_len: length of command response
129 */
130__packed struct qseecom_command_scm_resp {
131 uint32_t result;
132 enum qseecom_command_scm_resp_type resp_type;
133 unsigned int data;
134};
135
136static struct class *driver_class;
137static dev_t qseecom_device_no;
138static struct cdev qseecom_cdev;
139
140/* Data structures used in legacy support */
141static void *pil;
142static uint32_t pil_ref_cnt;
143static DEFINE_MUTEX(pil_access_lock);
144
Mona Hossain2892b6b2012-02-17 13:53:11 -0800145static DEFINE_MUTEX(qsee_bw_mutex);
146static DEFINE_MUTEX(app_access_lock);
147
148static int qsee_bw_count;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700149static int qsee_sfpb_bw_count;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800150static uint32_t qsee_perf_client;
151
152struct qseecom_registered_listener_list {
153 struct list_head list;
154 struct qseecom_register_listener_req svc;
155 u8 *sb_reg_req;
156 u8 *sb_virt;
157 s32 sb_phys;
158 size_t sb_length;
159 struct ion_handle *ihandle; /* Retrieve phy addr */
160
161 wait_queue_head_t rcv_req_wq;
162 int rcv_req_flag;
163};
164
165struct qseecom_registered_app_list {
166 struct list_head list;
167 u32 app_id;
168 u32 ref_cnt;
169};
170
171struct qseecom_control {
172 struct ion_client *ion_clnt; /* Ion client */
173 struct list_head registered_listener_list_head;
174 spinlock_t registered_listener_list_lock;
175
176 struct list_head registered_app_list_head;
177 spinlock_t registered_app_list_lock;
178
179 wait_queue_head_t send_resp_wq;
180 int send_resp_flag;
181
182 uint32_t qseos_version;
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700183 struct device *pdev;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800184};
185
186struct qseecom_client_handle {
187 u32 app_id;
188 u8 *sb_virt;
189 s32 sb_phys;
190 uint32_t user_virt_sb_base;
191 size_t sb_length;
192 struct ion_handle *ihandle; /* Retrieve phy addr */
193};
194
195struct qseecom_listener_handle {
196 u32 id;
197};
198
199static struct qseecom_control qseecom;
200
201struct qseecom_dev_handle {
202 bool service;
203 union {
204 struct qseecom_client_handle client;
205 struct qseecom_listener_handle listener;
206 };
207 bool released;
208 int abort;
209 wait_queue_head_t abort_wq;
210 atomic_t ioctl_count;
211};
212
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700213struct clk *ce_core_clk;
214struct clk *ce_clk;
215struct clk *ce_core_src_clk;
216struct clk *ce_bus_clk;
217
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700218/* Function proto types */
219static int qsee_vote_for_clock(int32_t);
220static void qsee_disable_clock_vote(int32_t);
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700221static int __qseecom_init_clk(void);
222static void __qseecom_disable_clk(void);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700223
Mona Hossain2892b6b2012-02-17 13:53:11 -0800224static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800225 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800226{
227 struct qseecom_registered_listener_list *ptr;
228 int unique = 1;
229 unsigned long flags;
230
231 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
232 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800233 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800234 pr_err("Service id: %u is already registered\n",
235 ptr->svc.listener_id);
236 unique = 0;
237 break;
238 }
239 }
240 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
241 return unique;
242}
243
244static struct qseecom_registered_listener_list *__qseecom_find_svc(
245 int32_t listener_id)
246{
247 struct qseecom_registered_listener_list *entry = NULL;
248 unsigned long flags;
249
250 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
251 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
252 {
253 if (entry->svc.listener_id == listener_id)
254 break;
255 }
256 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
257 return entry;
258}
259
260static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
261 struct qseecom_dev_handle *handle,
262 struct qseecom_register_listener_req *listener)
263{
264 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800265 struct qseecom_register_listener_ireq req;
266 struct qseecom_command_scm_resp resp;
267 ion_phys_addr_t pa;
268
269 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800270 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
271 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800272 if (svc->ihandle == NULL) {
273 pr_err("Ion client could not retrieve the handle\n");
274 return -ENOMEM;
275 }
276
277 /* Get the physical address of the ION BUF */
278 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
279
280 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700281 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800282 svc->sb_phys = pa;
283
284 if (qseecom.qseos_version == QSEOS_VERSION_14) {
285 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
286 req.listener_id = svc->svc.listener_id;
287 req.sb_len = svc->sb_length;
288 req.sb_ptr = (void *)svc->sb_phys;
289
290 resp.result = QSEOS_RESULT_INCOMPLETE;
291
292 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
293 sizeof(req), &resp, sizeof(resp));
294 if (ret) {
295 pr_err("qseecom_scm_call failed with err: %d\n", ret);
296 return -EINVAL;
297 }
298
299 if (resp.result != QSEOS_RESULT_SUCCESS) {
300 pr_err("Error SB registration req: resp.result = %d\n",
301 resp.result);
302 return -EPERM;
303 }
304 } else {
305 struct qseecom_command cmd;
306 struct qseecom_response resp;
307 struct qse_pr_init_sb_req_s sb_init_req;
308 struct qse_pr_init_sb_rsp_s sb_init_rsp;
309
310 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
311 sizeof(sb_init_rsp)), GFP_KERNEL);
312
313 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
314 sb_init_req.listener_id = svc->svc.listener_id;
315 sb_init_req.sb_len = svc->sb_length;
316 sb_init_req.sb_ptr = svc->sb_phys;
317
318 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
319
320 /* It will always be a new cmd from this method */
321 cmd.cmd_type = TZ_SCHED_CMD_NEW;
322 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
323 cmd.sb_in_cmd_len = sizeof(sb_init_req);
324
325 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
326
327 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
328 , &resp, sizeof(resp));
329
330 if (ret) {
331 pr_err("qseecom_scm_call failed with err: %d\n", ret);
332 return -EINVAL;
333 }
334
335 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
336 pr_err("SB registration fail resp.cmd_status %d\n",
337 resp.cmd_status);
338 return -EINVAL;
339 }
340 memset(svc->sb_virt, 0, svc->sb_length);
341 }
342 return 0;
343}
344
345static int qseecom_register_listener(struct qseecom_dev_handle *data,
346 void __user *argp)
347{
348 int ret = 0;
349 unsigned long flags;
350 struct qseecom_register_listener_req rcvd_lstnr;
351 struct qseecom_registered_listener_list *new_entry;
352
353 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
354 if (ret) {
355 pr_err("copy_from_user failed\n");
356 return ret;
357 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800358 data->listener.id = 0;
359 data->service = true;
360 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800361 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800362 data->released = true;
363 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800364 }
365
366 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
367 if (!new_entry) {
368 pr_err("kmalloc failed\n");
369 return -ENOMEM;
370 }
371 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
372 new_entry->rcv_req_flag = 0;
373
374 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
375 new_entry->sb_length = rcvd_lstnr.sb_size;
376 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
377 pr_err("qseecom_set_sb_memoryfailed\n");
378 kzfree(new_entry);
379 return -ENOMEM;
380 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800381
Mona Hossain2892b6b2012-02-17 13:53:11 -0800382 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800383 init_waitqueue_head(&new_entry->rcv_req_wq);
384
385 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
386 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
387 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800388
Mona Hossain2892b6b2012-02-17 13:53:11 -0800389 return ret;
390}
391
392static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
393{
394 int ret = 0;
395 unsigned long flags;
396 uint32_t unmap_mem = 0;
397 struct qseecom_register_listener_ireq req;
398 struct qseecom_registered_listener_list *ptr_svc = NULL;
399 struct qseecom_command_scm_resp resp;
400 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
401
402 if (qseecom.qseos_version == QSEOS_VERSION_14) {
403 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
404 req.listener_id = data->listener.id;
405 resp.result = QSEOS_RESULT_INCOMPLETE;
406
407 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
408 sizeof(req), &resp, sizeof(resp));
409 if (ret) {
410 pr_err("qseecom_scm_call failed with err: %d\n", ret);
411 return ret;
412 }
413
414 if (resp.result != QSEOS_RESULT_SUCCESS) {
415 pr_err("SB deregistartion: result=%d\n", resp.result);
416 return -EPERM;
417 }
418 } else {
419 struct qse_pr_init_sb_req_s sb_init_req;
420 struct qseecom_command cmd;
421 struct qseecom_response resp;
422 struct qseecom_registered_listener_list *svc;
423
424 svc = __qseecom_find_svc(data->listener.id);
425 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
426 sb_init_req.listener_id = data->listener.id;
427 sb_init_req.sb_len = 0;
428 sb_init_req.sb_ptr = 0;
429
430 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
431
432 /* It will always be a new cmd from this method */
433 cmd.cmd_type = TZ_SCHED_CMD_NEW;
434 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
435 cmd.sb_in_cmd_len = sizeof(sb_init_req);
436 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
437
438 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
439 &resp, sizeof(resp));
440 if (ret) {
441 pr_err("qseecom_scm_call failed with err: %d\n", ret);
442 return ret;
443 }
444 kzfree(svc->sb_reg_req);
445 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
446 pr_err("Error with SB initialization\n");
447 return -EPERM;
448 }
449 }
450 data->abort = 1;
451 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
452 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
453 list) {
454 if (ptr_svc->svc.listener_id == data->listener.id) {
455 wake_up_all(&ptr_svc->rcv_req_wq);
456 break;
457 }
458 }
459 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
460
461 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700462 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800463 atomic_read(&data->ioctl_count) <= 1)) {
464 pr_err("Interrupted from abort\n");
465 ret = -ERESTARTSYS;
466 break;
467 }
468 }
469
470 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
471 list_for_each_entry(ptr_svc,
472 &qseecom.registered_listener_list_head,
473 list)
474 {
475 if (ptr_svc->svc.listener_id == data->listener.id) {
476 if (ptr_svc->sb_virt) {
477 unmap_mem = 1;
478 ihandle = ptr_svc->ihandle;
479 }
480 list_del(&ptr_svc->list);
481 kzfree(ptr_svc);
482 break;
483 }
484 }
485 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
486
487 /* Unmap the memory */
488 if (unmap_mem) {
489 if (!IS_ERR_OR_NULL(ihandle)) {
490 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
491 ion_free(qseecom.ion_clnt, ihandle);
492 }
493 }
494 data->released = true;
495 return ret;
496}
497
498static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
499 void __user *argp)
500{
501 ion_phys_addr_t pa;
502 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800503 struct qseecom_set_sb_mem_param_req req;
504 uint32_t len;
505
506 /* Copy the relevant information needed for loading the image */
507 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
508 return -EFAULT;
509
Mona Hossain2892b6b2012-02-17 13:53:11 -0800510 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800511 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
512 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800513 if (IS_ERR_OR_NULL(data->client.ihandle)) {
514 pr_err("Ion client could not retrieve the handle\n");
515 return -ENOMEM;
516 }
517 /* Get the physical address of the ION BUF */
518 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
519 /* Populate the structure for sending scm call to load image */
520 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700521 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800522 data->client.sb_phys = pa;
523 data->client.sb_length = req.sb_len;
524 data->client.user_virt_sb_base = req.virt_sb_base;
525 return 0;
526}
527
Mona Hossain2892b6b2012-02-17 13:53:11 -0800528static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
529{
530 int ret;
531 ret = (qseecom.send_resp_flag != 0);
532 return ret || data->abort;
533}
534
535static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
536 struct qseecom_command_scm_resp *resp)
537{
538 int ret = 0;
539 uint32_t lstnr;
540 unsigned long flags;
541 struct qseecom_client_listener_data_irsp send_data_rsp;
542 struct qseecom_registered_listener_list *ptr_svc = NULL;
543
544
545 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
546 lstnr = resp->data;
547 /*
548 * Wake up blocking lsitener service with the lstnr id
549 */
550 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
551 flags);
552 list_for_each_entry(ptr_svc,
553 &qseecom.registered_listener_list_head, list) {
554 if (ptr_svc->svc.listener_id == lstnr) {
555 ptr_svc->rcv_req_flag = 1;
556 wake_up_interruptible(&ptr_svc->rcv_req_wq);
557 break;
558 }
559 }
560 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
561 flags);
562 if (ptr_svc->svc.listener_id != lstnr) {
563 pr_warning("Service requested for does on exist\n");
564 return -ERESTARTSYS;
565 }
566 pr_debug("waking up rcv_req_wq and "
567 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700568 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800569 __qseecom_listener_has_sent_rsp(data))) {
570 pr_warning("Interrupted: exiting send_cmd loop\n");
571 return -ERESTARTSYS;
572 }
573
574 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700575 pr_err("Aborting listener service %d\n",
576 data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800577 return -ENODEV;
578 }
579 qseecom.send_resp_flag = 0;
580 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
581 send_data_rsp.listener_id = lstnr ;
582
583 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
584 (const void *)&send_data_rsp,
585 sizeof(send_data_rsp), resp,
586 sizeof(*resp));
587 if (ret) {
588 pr_err("qseecom_scm_call failed with err: %d\n", ret);
589 return ret;
590 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700591 if (resp->result == QSEOS_RESULT_FAILURE) {
592 pr_err("Response result %d not supported\n",
593 resp->result);
594 return -EINVAL;
595 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800596 }
597 return ret;
598}
599
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700600static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
601{
602 int32_t ret;
603 struct qseecom_command_scm_resp resp;
604
605 /* SCM_CALL to check if app_id for the mentioned app exists */
606 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
607 sizeof(struct qseecom_check_app_ireq),
608 &resp, sizeof(resp));
609 if (ret) {
610 pr_err("scm_call to check if app is already loaded failed\n");
611 return -EINVAL;
612 }
613
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700614 if (resp.result == QSEOS_RESULT_FAILURE) {
615 return 0;
616 } else {
617 switch (resp.resp_type) {
618 /*qsee returned listener type response */
619 case QSEOS_LISTENER_ID:
620 pr_err("resp type is of listener type instead of app");
621 return -EINVAL;
622 break;
623 case QSEOS_APP_ID:
624 return resp.data;
625 default:
626 pr_err("invalid resp type (%d) from qsee",
627 resp.resp_type);
628 return -ENODEV;
629 break;
630 }
631 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700632}
633
Mona Hossain2892b6b2012-02-17 13:53:11 -0800634static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
635{
636 struct qseecom_registered_app_list *entry = NULL;
637 unsigned long flags = 0;
638 u32 app_id = 0;
639 struct ion_handle *ihandle; /* Ion handle */
640 struct qseecom_load_img_req load_img_req;
641 int32_t ret;
642 ion_phys_addr_t pa = 0;
643 uint32_t len;
644 struct qseecom_command_scm_resp resp;
645 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700646 struct qseecom_load_app_ireq load_req;
647
Mona Hossain2892b6b2012-02-17 13:53:11 -0800648 /* Copy the relevant information needed for loading the image */
649 if (__copy_from_user(&load_img_req,
650 (void __user *)argp,
651 sizeof(struct qseecom_load_img_req))) {
652 pr_err("copy_from_user failed\n");
653 return -EFAULT;
654 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700655 /* Vote for the SFPB clock */
656 ret = qsee_vote_for_clock(CLK_SFPB);
657 if (ret)
658 pr_warning("Unable to vote for SFPB clock");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800659
660 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
661 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
662
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700663 pr_warn("App (%s) does not exist, loading apps for first time\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700664 (char *)(req.app_name));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700665 /* Get the handle of the shared fd */
666 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800667 load_img_req.ifd_data_fd);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700668 if (IS_ERR_OR_NULL(ihandle)) {
669 pr_err("Ion client could not retrieve the handle\n");
670 qsee_disable_clock_vote(CLK_SFPB);
671 return -ENOMEM;
672 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800673
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700674 /* Get the physical address of the ION BUF */
675 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800676
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700677 /* Populate the structure for sending scm call to load image */
678 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
679 load_req.mdt_len = load_img_req.mdt_len;
680 load_req.img_len = load_img_req.img_len;
681 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800682
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700683 /* SCM_CALL to load the app and get the app_id back */
684 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
685 sizeof(struct qseecom_load_app_ireq),
686 &resp, sizeof(resp));
687 if (ret) {
688 pr_err("scm_call to load app failed\n");
689 return -EINVAL;
690 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800691
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700692 if (resp.result == QSEOS_RESULT_FAILURE) {
693 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800694 if (!IS_ERR_OR_NULL(ihandle))
695 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700696 qsee_disable_clock_vote(CLK_SFPB);
697 return -EFAULT;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800698 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700699
700 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
701 ret = __qseecom_process_incomplete_cmd(data, &resp);
702 if (ret) {
703 pr_err("process_incomplete_cmd failed err: %d\n",
704 ret);
705 if (!IS_ERR_OR_NULL(ihandle))
706 ion_free(qseecom.ion_clnt, ihandle);
707 qsee_disable_clock_vote(CLK_SFPB);
708 return ret;
709 }
710 }
711
712 if (resp.result != QSEOS_RESULT_SUCCESS) {
713 pr_err("scm_call failed resp.result unknown, %d\n",
714 resp.result);
715 if (!IS_ERR_OR_NULL(ihandle))
716 ion_free(qseecom.ion_clnt, ihandle);
717 qsee_disable_clock_vote(CLK_SFPB);
718 return -EFAULT;
719 }
720
721 app_id = resp.data;
722
723 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
724 if (!entry) {
725 pr_err("kmalloc failed\n");
726 qsee_disable_clock_vote(CLK_SFPB);
727 return -ENOMEM;
728 }
729 entry->app_id = app_id;
730 entry->ref_cnt = 1;
731
732 /* Deallocate the handle */
733 if (!IS_ERR_OR_NULL(ihandle))
734 ion_free(qseecom.ion_clnt, ihandle);
735
736 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
737 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
738 spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
739
740 pr_warn("App with id %d (%s) now loaded\n", app_id,
741 (char *)(req.app_name));
742
Mona Hossain2892b6b2012-02-17 13:53:11 -0800743 data->client.app_id = app_id;
744 load_img_req.app_id = app_id;
745 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
746 pr_err("copy_to_user failed\n");
747 kzfree(entry);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700748 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800749 return -EFAULT;
750 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700751 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800752 return 0;
753}
754
755static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
756{
757 wake_up_all(&qseecom.send_resp_wq);
758 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700759 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800760 atomic_read(&data->ioctl_count) <= 1)) {
761 pr_err("Interrupted from abort\n");
762 return -ERESTARTSYS;
763 break;
764 }
765 }
766 /* Set unload app */
767 return 1;
768}
769
770static int qseecom_unload_app(struct qseecom_dev_handle *data)
771{
772 unsigned long flags;
773 int ret = 0;
774 struct qseecom_command_scm_resp resp;
775 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700776 bool unload = false;
777 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800778
Mona Hossain1fb538f2012-08-30 16:19:38 -0700779 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
780 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800781 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
782 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
783 list) {
784 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700785 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800786 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700787 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800788 break;
789 } else {
790 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700791 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700792 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800793 break;
794 }
795 }
796 }
797 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
798 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700799 if (found_app == false) {
800 pr_err("Cannot find app with id = %d\n",
801 data->client.app_id);
802 return -EINVAL;
803 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800804 }
805
806 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
807 struct qseecom_unload_app_ireq req;
808
Mona Hossain340dba82012-08-07 19:54:46 -0700809 __qseecom_cleanup_app(data);
810 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
811 list_del(&ptr_app->list);
812 kzfree(ptr_app);
813 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
814 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800815 /* Populate the structure for sending scm call to load image */
816 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
817 req.app_id = data->client.app_id;
818
819 /* SCM_CALL to unload the app */
820 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
821 sizeof(struct qseecom_unload_app_ireq),
822 &resp, sizeof(resp));
823 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700824 pr_err("scm_call to unload app (id = %d) failed\n",
825 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800826 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700827 } else {
828 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800829 }
830 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
831 ret = __qseecom_process_incomplete_cmd(data, &resp);
832 if (ret) {
833 pr_err("process_incomplete_cmd fail err: %d\n",
834 ret);
835 return ret;
836 }
837 }
838 }
839
840 if (qseecom.qseos_version == QSEOS_VERSION_13) {
841 data->abort = 1;
842 wake_up_all(&qseecom.send_resp_wq);
843 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700844 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800845 atomic_read(&data->ioctl_count) <= 0)) {
846 pr_err("Interrupted from abort\n");
847 ret = -ERESTARTSYS;
848 break;
849 }
850 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800851 }
Mona Hossain340dba82012-08-07 19:54:46 -0700852 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
853 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
854 ion_free(qseecom.ion_clnt, data->client.ihandle);
855 data->client.ihandle = NULL;
856 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800857 data->released = true;
858 return ret;
859}
860
861static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
862 uint32_t virt)
863{
864 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
865}
866
867static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
868 struct qseecom_send_cmd_req *req)
869{
870 int ret = 0;
871 unsigned long flags;
872 u32 reqd_len_sb_in = 0;
873 struct qseecom_command cmd;
874 struct qseecom_response resp;
875
876
877 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
878 pr_err("cmd buffer or response buffer is null\n");
879 return -EINVAL;
880 }
881
882 if (req->cmd_req_len <= 0 ||
883 req->resp_len <= 0 ||
884 req->cmd_req_len > data->client.sb_length ||
885 req->resp_len > data->client.sb_length) {
886 pr_err("cmd buffer length or "
887 "response buffer length not valid\n");
888 return -EINVAL;
889 }
890
891 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
892 if (reqd_len_sb_in > data->client.sb_length) {
893 pr_debug("Not enough memory to fit cmd_buf and "
894 "resp_buf. Required: %u, Available: %u\n",
895 reqd_len_sb_in, data->client.sb_length);
896 return -ENOMEM;
897 }
898 cmd.cmd_type = TZ_SCHED_CMD_NEW;
899 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
900 cmd.sb_in_cmd_len = req->cmd_req_len;
901
902 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
903 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
904 resp.sb_in_rsp_len = req->resp_len;
905
906 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
907 sizeof(cmd), &resp, sizeof(resp));
908
909 if (ret) {
910 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
911 return ret;
912 }
913
914 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
915 /*
916 * If cmd is incomplete, get the callback cmd out from SB out
917 * and put it on the list
918 */
919 struct qseecom_registered_listener_list *ptr_svc = NULL;
920 /*
921 * We don't know which service can handle the command. so we
922 * wake up all blocking services and let them figure out if
923 * they can handle the given command.
924 */
925 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
926 flags);
927 list_for_each_entry(ptr_svc,
928 &qseecom.registered_listener_list_head, list) {
929 ptr_svc->rcv_req_flag = 1;
930 wake_up_interruptible(&ptr_svc->rcv_req_wq);
931 }
932 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
933 flags);
934
935 pr_debug("waking up rcv_req_wq and "
936 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700937 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800938 __qseecom_listener_has_sent_rsp(data))) {
939 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
940 return -ERESTARTSYS;
941 }
942
943 if (data->abort) {
944 pr_err("Aborting driver\n");
945 return -ENODEV;
946 }
947 qseecom.send_resp_flag = 0;
948 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
949 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
950 sizeof(cmd), &resp, sizeof(resp));
951 if (ret) {
952 pr_err("qseecom_scm_call failed with err: %d\n", ret);
953 return ret;
954 }
955 }
956 return ret;
957}
958
959static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
960 struct qseecom_send_cmd_req *req)
961{
962 int ret = 0;
963 u32 reqd_len_sb_in = 0;
964 struct qseecom_client_send_data_ireq send_data_req;
965 struct qseecom_command_scm_resp resp;
966
967 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
968 pr_err("cmd buffer or response buffer is null\n");
969 return -EINVAL;
970 }
971
972 if (req->cmd_req_len <= 0 ||
973 req->resp_len <= 0 ||
974 req->cmd_req_len > data->client.sb_length ||
975 req->resp_len > data->client.sb_length) {
976 pr_err("cmd buffer length or "
977 "response buffer length not valid\n");
978 return -EINVAL;
979 }
980
981 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
982 if (reqd_len_sb_in > data->client.sb_length) {
983 pr_debug("Not enough memory to fit cmd_buf and "
984 "resp_buf. Required: %u, Available: %u\n",
985 reqd_len_sb_in, data->client.sb_length);
986 return -ENOMEM;
987 }
988
989 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
990 send_data_req.app_id = data->client.app_id;
991 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
992 (uint32_t)req->cmd_req_buf));
993 send_data_req.req_len = req->cmd_req_len;
994 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
995 (uint32_t)req->resp_buf));
996 send_data_req.rsp_len = req->resp_len;
997
998 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
999 sizeof(send_data_req),
1000 &resp, sizeof(resp));
1001 if (ret) {
1002 pr_err("qseecom_scm_call failed with err: %d\n", ret);
1003 return ret;
1004 }
1005
1006 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1007 ret = __qseecom_process_incomplete_cmd(data, &resp);
1008 if (ret) {
1009 pr_err("process_incomplete_cmd failed err: %d\n", ret);
1010 return ret;
1011 }
Mona Hossainbb0bca12012-04-12 11:47:45 -07001012 } else {
1013 if (resp.result != QSEOS_RESULT_SUCCESS) {
1014 pr_err("Response result %d not supported\n",
1015 resp.result);
1016 ret = -EINVAL;
1017 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001018 }
1019 return ret;
1020}
1021
1022
1023static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1024{
1025 int ret = 0;
1026 struct qseecom_send_cmd_req req;
1027
1028 ret = copy_from_user(&req, argp, sizeof(req));
1029 if (ret) {
1030 pr_err("copy_from_user failed\n");
1031 return ret;
1032 }
1033 if (qseecom.qseos_version == QSEOS_VERSION_14)
1034 ret = __qseecom_send_cmd(data, &req);
1035 else
1036 ret = __qseecom_send_cmd_legacy(data, &req);
1037 if (ret)
1038 return ret;
1039
1040 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1041 req.resp_len, req.resp_buf);
1042 return ret;
1043}
1044
1045static int __qseecom_send_cmd_req_clean_up(
1046 struct qseecom_send_modfd_cmd_req *req)
1047{
1048 char *field;
1049 uint32_t *update;
1050 int ret = 0;
1051 int i = 0;
1052
1053 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001054 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001055 field = (char *)req->cmd_req_buf +
1056 req->ifd_data[i].cmd_buf_offset;
1057 update = (uint32_t *) field;
1058 *update = 0;
1059 }
1060 }
1061 return ret;
1062}
1063
1064static int __qseecom_update_with_phy_addr(
1065 struct qseecom_send_modfd_cmd_req *req)
1066{
1067 struct ion_handle *ihandle;
1068 char *field;
1069 uint32_t *update;
1070 ion_phys_addr_t pa;
1071 int ret = 0;
1072 int i = 0;
1073 uint32_t length;
1074
1075 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001076 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001077 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001078 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001079 req->ifd_data[i].fd);
1080 if (IS_ERR_OR_NULL(ihandle)) {
1081 pr_err("Ion client can't retrieve the handle\n");
1082 return -ENOMEM;
1083 }
1084 field = (char *) req->cmd_req_buf +
1085 req->ifd_data[i].cmd_buf_offset;
1086 update = (uint32_t *) field;
1087
1088 /* Populate the cmd data structure with the phys_addr */
1089 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1090 if (ret)
1091 return -ENOMEM;
1092
1093 *update = (uint32_t)pa;
1094 /* Deallocate the handle */
1095 if (!IS_ERR_OR_NULL(ihandle))
1096 ion_free(qseecom.ion_clnt, ihandle);
1097 }
1098 }
1099 return ret;
1100}
1101
1102static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1103 void __user *argp)
1104{
1105 int ret = 0;
1106 struct qseecom_send_modfd_cmd_req req;
1107 struct qseecom_send_cmd_req send_cmd_req;
1108
1109 ret = copy_from_user(&req, argp, sizeof(req));
1110 if (ret) {
1111 pr_err("copy_from_user failed\n");
1112 return ret;
1113 }
1114 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1115 send_cmd_req.cmd_req_len = req.cmd_req_len;
1116 send_cmd_req.resp_buf = req.resp_buf;
1117 send_cmd_req.resp_len = req.resp_len;
1118
1119 ret = __qseecom_update_with_phy_addr(&req);
1120 if (ret)
1121 return ret;
1122 if (qseecom.qseos_version == QSEOS_VERSION_14)
1123 ret = __qseecom_send_cmd(data, &send_cmd_req);
1124 else
1125 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1126 __qseecom_send_cmd_req_clean_up(&req);
1127
1128 if (ret)
1129 return ret;
1130
1131 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1132 req.resp_len, req.resp_buf);
1133 return ret;
1134}
1135
1136static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1137 struct qseecom_registered_listener_list *svc)
1138{
1139 int ret;
1140 ret = (svc->rcv_req_flag != 0);
1141 return ret || data->abort;
1142}
1143
1144static int qseecom_receive_req(struct qseecom_dev_handle *data)
1145{
1146 int ret = 0;
1147 struct qseecom_registered_listener_list *this_lstnr;
1148
1149 this_lstnr = __qseecom_find_svc(data->listener.id);
1150 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001151 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001152 __qseecom_listener_has_rcvd_req(data,
1153 this_lstnr))) {
1154 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1155 /* woken up for different reason */
1156 return -ERESTARTSYS;
1157 }
1158
1159 if (data->abort) {
1160 pr_err("Aborting driver!\n");
1161 return -ENODEV;
1162 }
1163 this_lstnr->rcv_req_flag = 0;
1164 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1165 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1166 break;
1167 } else {
1168 break;
1169 }
1170 }
1171 return ret;
1172}
1173
1174static int qseecom_send_resp(void)
1175{
1176 qseecom.send_resp_flag = 1;
1177 wake_up_interruptible(&qseecom.send_resp_wq);
1178 return 0;
1179}
1180
1181static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1182 void __user *argp)
1183{
1184 struct qseecom_qseos_version_req req;
1185
1186 if (copy_from_user(&req, argp, sizeof(req))) {
1187 pr_err("copy_from_user failed");
1188 return -EINVAL;
1189 }
1190 req.qseos_version = qseecom.qseos_version;
1191 if (copy_to_user(argp, &req, sizeof(req))) {
1192 pr_err("copy_to_user failed");
1193 return -EINVAL;
1194 }
1195 return 0;
1196}
1197
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001198static int qsee_vote_for_clock(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001199{
1200 int ret = 0;
1201
1202 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001203 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001204
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001205 switch (clk_type) {
1206 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001207 mutex_lock(&qsee_bw_mutex);
1208 if (!qsee_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001209 if (qsee_sfpb_bw_count > 0)
1210 ret = msm_bus_scale_client_update_request(
1211 qsee_perf_client, 3);
1212 else
1213 ret = msm_bus_scale_client_update_request(
1214 qsee_perf_client, 1);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001215 if (ret)
1216 pr_err("DFAB Bandwidth req failed (%d)\n",
1217 ret);
1218 else
1219 qsee_bw_count++;
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001220 } else {
1221 qsee_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001222 }
1223 mutex_unlock(&qsee_bw_mutex);
1224 break;
1225 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001226 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001227 if (!qsee_sfpb_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001228 if (qsee_bw_count > 0)
1229 ret = msm_bus_scale_client_update_request(
1230 qsee_perf_client, 3);
1231 else
1232 ret = msm_bus_scale_client_update_request(
1233 qsee_perf_client, 2);
1234
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001235 if (ret)
1236 pr_err("SFPB Bandwidth req failed (%d)\n",
1237 ret);
1238 else
1239 qsee_sfpb_bw_count++;
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001240 } else {
1241 qsee_sfpb_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001242 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001243 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001244 break;
1245 default:
1246 pr_err("Clock type not defined\n");
1247 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001248 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001249 return ret;
1250}
1251
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001252static void qsee_disable_clock_vote(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001253{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001254 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001255
Mona Hossain2892b6b2012-02-17 13:53:11 -08001256 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001257 return;
1258
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001259 switch (clk_type) {
1260 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001261 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001262 if (qsee_bw_count == 0) {
1263 pr_err("Client error.Extra call to disable DFAB clk\n");
1264 mutex_unlock(&qsee_bw_mutex);
1265 return;
1266 }
1267
1268 if ((qsee_bw_count > 0) && (qsee_bw_count-- == 1)) {
1269 if (qsee_sfpb_bw_count > 0)
1270 ret = msm_bus_scale_client_update_request(
1271 qsee_perf_client, 2);
1272 else
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001273 ret = msm_bus_scale_client_update_request(
1274 qsee_perf_client, 0);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001275 if (ret)
1276 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001277 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001278 }
1279 mutex_unlock(&qsee_bw_mutex);
1280 break;
1281 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001282 mutex_lock(&qsee_bw_mutex);
1283 if (qsee_sfpb_bw_count == 0) {
1284 pr_err("Client error.Extra call to disable SFPB clk\n");
1285 mutex_unlock(&qsee_bw_mutex);
1286 return;
1287 }
1288 if ((qsee_sfpb_bw_count > 0) && (qsee_sfpb_bw_count-- == 1)) {
1289 if (qsee_bw_count > 0)
1290 ret = msm_bus_scale_client_update_request(
1291 qsee_perf_client, 1);
1292 else
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001293 ret = msm_bus_scale_client_update_request(
1294 qsee_perf_client, 0);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001295 if (ret)
1296 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001297 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001298 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001299 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001300 break;
1301 default:
1302 pr_err("Clock type not defined\n");
1303 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001304 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001305
Mona Hossain2892b6b2012-02-17 13:53:11 -08001306}
1307
Mona Hossain5ab9d772012-04-11 21:00:40 -07001308static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1309 void __user *argp)
1310{
1311 struct ion_handle *ihandle; /* Ion handle */
1312 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001313 int ret;
1314 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001315 ion_phys_addr_t pa = 0;
1316 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001317 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001318 struct qseecom_load_app_ireq load_req;
1319 struct qseecom_command_scm_resp resp;
1320
1321 /* Copy the relevant information needed for loading the image */
1322 if (__copy_from_user(&load_img_req,
1323 (void __user *)argp,
1324 sizeof(struct qseecom_load_img_req))) {
1325 pr_err("copy_from_user failed\n");
1326 return -EFAULT;
1327 }
1328
1329 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001330 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001331 load_img_req.ifd_data_fd);
1332 if (IS_ERR_OR_NULL(ihandle)) {
1333 pr_err("Ion client could not retrieve the handle\n");
1334 return -ENOMEM;
1335 }
1336
1337 /* Get the physical address of the ION BUF */
1338 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1339
1340 /* Populate the structure for sending scm call to load image */
1341 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1342 load_req.mdt_len = load_img_req.mdt_len;
1343 load_req.img_len = load_img_req.img_len;
1344 load_req.phy_addr = pa;
1345
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001346 /* SCM_CALL tied to Core0 */
1347 mask = CPU_MASK_CPU0;
1348 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1349 if (set_cpu_ret) {
1350 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1351 set_cpu_ret);
1352 ret = -EFAULT;
1353 goto qseecom_load_external_elf_set_cpu_err;
1354 }
1355
Mona Hossain5ab9d772012-04-11 21:00:40 -07001356 /* SCM_CALL to load the external elf */
1357 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1358 sizeof(struct qseecom_load_app_ireq),
1359 &resp, sizeof(resp));
1360 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001361 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07001362 ret);
1363 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001364 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001365 }
1366
1367 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1368 ret = __qseecom_process_incomplete_cmd(data, &resp);
1369 if (ret)
1370 pr_err("process_incomplete_cmd failed err: %d\n",
1371 ret);
1372 } else {
1373 if (resp.result != QSEOS_RESULT_SUCCESS) {
1374 pr_err("scm_call to load image failed resp.result =%d\n",
1375 resp.result);
1376 ret = -EFAULT;
1377 }
1378 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001379
1380qseecom_load_external_elf_scm_err:
1381 /* Restore the CPU mask */
1382 mask = CPU_MASK_ALL;
1383 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1384 if (set_cpu_ret) {
1385 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1386 set_cpu_ret);
1387 ret = -EFAULT;
1388 }
1389
1390qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07001391 /* Deallocate the handle */
1392 if (!IS_ERR_OR_NULL(ihandle))
1393 ion_free(qseecom.ion_clnt, ihandle);
1394
1395 return ret;
1396}
1397
1398static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1399{
1400 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001401 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001402 struct qseecom_command_scm_resp resp;
1403 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001404 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001405
1406 /* Populate the structure for sending scm call to unload image */
1407 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001408
1409 /* SCM_CALL tied to Core0 */
1410 mask = CPU_MASK_CPU0;
1411 ret = set_cpus_allowed_ptr(current, &mask);
1412 if (ret) {
1413 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1414 ret);
1415 return -EFAULT;
1416 }
1417
Mona Hossain5ab9d772012-04-11 21:00:40 -07001418 /* SCM_CALL to unload the external elf */
1419 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
1420 sizeof(struct qseecom_unload_app_ireq),
1421 &resp, sizeof(resp));
1422 if (ret) {
1423 pr_err("scm_call to unload failed : ret %d\n",
1424 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001425 ret = -EFAULT;
1426 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001427 }
1428 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1429 ret = __qseecom_process_incomplete_cmd(data, &resp);
1430 if (ret)
1431 pr_err("process_incomplete_cmd fail err: %d\n",
1432 ret);
1433 } else {
1434 if (resp.result != QSEOS_RESULT_SUCCESS) {
1435 pr_err("scm_call to unload image failed resp.result =%d\n",
1436 resp.result);
1437 ret = -EFAULT;
1438 }
1439 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001440
1441qseecom_unload_external_elf_scm_err:
1442 /* Restore the CPU mask */
1443 mask = CPU_MASK_ALL;
1444 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1445 if (set_cpu_ret) {
1446 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1447 set_cpu_ret);
1448 ret = -EFAULT;
1449 }
1450
Mona Hossain5ab9d772012-04-11 21:00:40 -07001451 return ret;
1452}
Mona Hossain2892b6b2012-02-17 13:53:11 -08001453
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001454static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
1455 void __user *argp)
1456{
1457
1458 int32_t ret;
1459 struct qseecom_qseos_app_load_query query_req;
1460 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001461 struct qseecom_registered_app_list *entry = NULL;
1462 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001463
1464 /* Copy the relevant information needed for loading the image */
1465 if (__copy_from_user(&query_req,
1466 (void __user *)argp,
1467 sizeof(struct qseecom_qseos_app_load_query))) {
1468 pr_err("copy_from_user failed\n");
1469 return -EFAULT;
1470 }
1471
1472 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1473 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
1474
1475 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001476
1477 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001478 pr_err(" scm call to check if app is loaded failed");
1479 return ret; /* scm call failed */
1480 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001481 pr_warn("App id %d (%s) already exists\n", ret,
1482 (char *)(req.app_name));
1483 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1484 list_for_each_entry(entry,
1485 &qseecom.registered_app_list_head, list){
1486 if (entry->app_id == ret) {
1487 entry->ref_cnt++;
1488 break;
1489 }
1490 }
1491 spin_unlock_irqrestore(
1492 &qseecom.registered_app_list_lock, flags);
1493 data->client.app_id = ret;
1494 query_req.app_id = ret;
1495
1496 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
1497 pr_err("copy_to_user failed\n");
1498 return -EFAULT;
1499 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001500 return -EEXIST; /* app already loaded */
1501 } else {
1502 return 0; /* app not loaded */
1503 }
1504}
1505
Mona Hossain2892b6b2012-02-17 13:53:11 -08001506static long qseecom_ioctl(struct file *file, unsigned cmd,
1507 unsigned long arg)
1508{
1509 int ret = 0;
1510 struct qseecom_dev_handle *data = file->private_data;
1511 void __user *argp = (void __user *) arg;
1512
1513 if (data->abort) {
1514 pr_err("Aborting qseecom driver\n");
1515 return -ENODEV;
1516 }
1517
1518 switch (cmd) {
1519 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1520 pr_debug("ioctl register_listener_req()\n");
1521 atomic_inc(&data->ioctl_count);
1522 ret = qseecom_register_listener(data, argp);
1523 atomic_dec(&data->ioctl_count);
1524 wake_up_all(&data->abort_wq);
1525 if (ret)
1526 pr_err("failed qseecom_register_listener: %d\n", ret);
1527 break;
1528 }
1529 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1530 pr_debug("ioctl unregister_listener_req()\n");
1531 atomic_inc(&data->ioctl_count);
1532 ret = qseecom_unregister_listener(data);
1533 atomic_dec(&data->ioctl_count);
1534 wake_up_all(&data->abort_wq);
1535 if (ret)
1536 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1537 break;
1538 }
1539 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1540 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001541 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001542 atomic_inc(&data->ioctl_count);
1543 ret = qseecom_send_cmd(data, argp);
1544 atomic_dec(&data->ioctl_count);
1545 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001546 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001547 if (ret)
1548 pr_err("failed qseecom_send_cmd: %d\n", ret);
1549 break;
1550 }
1551 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1552 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001553 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001554 atomic_inc(&data->ioctl_count);
1555 ret = qseecom_send_modfd_cmd(data, argp);
1556 atomic_dec(&data->ioctl_count);
1557 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001558 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001559 if (ret)
1560 pr_err("failed qseecom_send_cmd: %d\n", ret);
1561 break;
1562 }
1563 case QSEECOM_IOCTL_RECEIVE_REQ: {
1564 atomic_inc(&data->ioctl_count);
1565 ret = qseecom_receive_req(data);
1566 atomic_dec(&data->ioctl_count);
1567 wake_up_all(&data->abort_wq);
1568 if (ret)
1569 pr_err("failed qseecom_receive_req: %d\n", ret);
1570 break;
1571 }
1572 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1573 atomic_inc(&data->ioctl_count);
1574 ret = qseecom_send_resp();
1575 atomic_dec(&data->ioctl_count);
1576 wake_up_all(&data->abort_wq);
1577 if (ret)
1578 pr_err("failed qseecom_send_resp: %d\n", ret);
1579 break;
1580 }
1581 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1582 ret = qseecom_set_client_mem_param(data, argp);
1583 if (ret)
1584 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1585 ret);
1586 break;
1587 }
1588 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1589 mutex_lock(&app_access_lock);
1590 atomic_inc(&data->ioctl_count);
1591 ret = qseecom_load_app(data, argp);
1592 atomic_dec(&data->ioctl_count);
1593 mutex_unlock(&app_access_lock);
1594 if (ret)
1595 pr_err("failed load_app request: %d\n", ret);
1596 break;
1597 }
1598 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1599 mutex_lock(&app_access_lock);
1600 atomic_inc(&data->ioctl_count);
1601 ret = qseecom_unload_app(data);
1602 atomic_dec(&data->ioctl_count);
1603 mutex_unlock(&app_access_lock);
1604 if (ret)
1605 pr_err("failed unload_app request: %d\n", ret);
1606 break;
1607 }
1608 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1609 atomic_inc(&data->ioctl_count);
1610 ret = qseecom_get_qseos_version(data, argp);
1611 if (ret)
1612 pr_err("qseecom_get_qseos_version: %d\n", ret);
1613 atomic_dec(&data->ioctl_count);
1614 break;
1615 }
1616 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1617 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001618 ret = qsee_vote_for_clock(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001619 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001620 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001621 atomic_dec(&data->ioctl_count);
1622 break;
1623 }
1624 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1625 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001626 qsee_disable_clock_vote(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001627 atomic_dec(&data->ioctl_count);
1628 break;
1629 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07001630 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
1631 data->released = true;
1632 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1633 pr_err("Loading External elf image unsupported in rev 0x13\n");
1634 ret = -EINVAL;
1635 break;
1636 }
1637 mutex_lock(&app_access_lock);
1638 atomic_inc(&data->ioctl_count);
1639 ret = qseecom_load_external_elf(data, argp);
1640 atomic_dec(&data->ioctl_count);
1641 mutex_unlock(&app_access_lock);
1642 if (ret)
1643 pr_err("failed load_external_elf request: %d\n", ret);
1644 break;
1645 }
1646 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
1647 data->released = true;
1648 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1649 pr_err("Unloading External elf image unsupported in rev 0x13\n");
1650 ret = -EINVAL;
1651 break;
1652 }
1653 mutex_lock(&app_access_lock);
1654 atomic_inc(&data->ioctl_count);
1655 ret = qseecom_unload_external_elf(data);
1656 atomic_dec(&data->ioctl_count);
1657 mutex_unlock(&app_access_lock);
1658 if (ret)
1659 pr_err("failed unload_app request: %d\n", ret);
1660 break;
1661 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001662 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
1663 mutex_lock(&app_access_lock);
1664 atomic_inc(&data->ioctl_count);
1665 ret = qseecom_query_app_loaded(data, argp);
1666 atomic_dec(&data->ioctl_count);
1667 mutex_unlock(&app_access_lock);
1668 break;
1669 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001670 default:
1671 return -EINVAL;
1672 }
1673 return ret;
1674}
1675
1676static int qseecom_open(struct inode *inode, struct file *file)
1677{
1678 int ret = 0;
1679 struct qseecom_dev_handle *data;
1680
1681 data = kzalloc(sizeof(*data), GFP_KERNEL);
1682 if (!data) {
1683 pr_err("kmalloc failed\n");
1684 return -ENOMEM;
1685 }
1686 file->private_data = data;
1687 data->abort = 0;
1688 data->service = false;
1689 data->released = false;
1690 init_waitqueue_head(&data->abort_wq);
1691 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001692 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1693 int pil_error;
1694 mutex_lock(&pil_access_lock);
1695 if (pil_ref_cnt == 0) {
1696 pil = pil_get("tzapps");
1697 if (IS_ERR(pil)) {
1698 pr_err("Playready PIL image load failed\n");
1699 pil_error = PTR_ERR(pil);
1700 pil = NULL;
1701 pr_debug("tzapps image load FAILED\n");
1702 mutex_unlock(&pil_access_lock);
1703 return pil_error;
1704 }
1705 }
1706 pil_ref_cnt++;
1707 mutex_unlock(&pil_access_lock);
1708 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001709 return ret;
1710}
1711
1712static int qseecom_release(struct inode *inode, struct file *file)
1713{
1714 struct qseecom_dev_handle *data = file->private_data;
1715 int ret = 0;
1716
1717 if (data->released == false) {
1718 pr_warn("data->released == false\n");
1719 if (data->service)
1720 ret = qseecom_unregister_listener(data);
1721 else
1722 ret = qseecom_unload_app(data);
1723 if (ret) {
1724 pr_err("Close failed\n");
1725 return ret;
1726 }
1727 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001728 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1729 mutex_lock(&pil_access_lock);
1730 if (pil_ref_cnt == 1)
1731 pil_put(pil);
1732 pil_ref_cnt--;
1733 mutex_unlock(&pil_access_lock);
1734 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001735 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001736
Mona Hossain2892b6b2012-02-17 13:53:11 -08001737 return ret;
1738}
1739
Mona Hossain2892b6b2012-02-17 13:53:11 -08001740static const struct file_operations qseecom_fops = {
1741 .owner = THIS_MODULE,
1742 .unlocked_ioctl = qseecom_ioctl,
1743 .open = qseecom_open,
1744 .release = qseecom_release
1745};
1746
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001747static int __qseecom_init_clk()
1748{
1749 int rc = 0;
1750 struct device *pdev;
1751
1752 pdev = qseecom.pdev;
1753 /* Get CE3 src core clk. */
1754 ce_core_src_clk = clk_get(pdev, "core_clk_src");
1755 if (!IS_ERR(ce_core_src_clk)) {
1756 ce_core_src_clk = ce_core_src_clk;
1757
1758 /* Set the core src clk @100Mhz */
1759 rc = clk_set_rate(ce_core_src_clk, 100000000);
1760 if (rc) {
1761 clk_put(ce_core_src_clk);
1762 pr_err("Unable to set the core src clk @100Mhz.\n");
1763 goto err_clk;
1764 }
1765 } else {
1766 pr_warn("Unable to get CE core src clk, set to NULL\n");
1767 ce_core_src_clk = NULL;
1768 }
1769
1770 /* Get CE core clk */
1771 ce_core_clk = clk_get(pdev, "core_clk");
1772 if (IS_ERR(ce_core_clk)) {
1773 rc = PTR_ERR(ce_core_clk);
1774 pr_err("Unable to get CE core clk\n");
1775 if (ce_core_src_clk != NULL)
1776 clk_put(ce_core_src_clk);
1777 goto err_clk;
1778 }
1779
1780 /* Get CE Interface clk */
1781 ce_clk = clk_get(pdev, "iface_clk");
1782 if (IS_ERR(ce_clk)) {
1783 rc = PTR_ERR(ce_clk);
1784 pr_err("Unable to get CE interface clk\n");
1785 if (ce_core_src_clk != NULL)
1786 clk_put(ce_core_src_clk);
1787 clk_put(ce_core_clk);
1788 goto err_clk;
1789 }
1790
1791 /* Get CE AXI clk */
1792 ce_bus_clk = clk_get(pdev, "bus_clk");
1793 if (IS_ERR(ce_bus_clk)) {
1794 rc = PTR_ERR(ce_bus_clk);
1795 pr_err("Unable to get CE BUS interface clk\n");
1796 if (ce_core_src_clk != NULL)
1797 clk_put(ce_core_src_clk);
1798 clk_put(ce_core_clk);
1799 clk_put(ce_clk);
1800 goto err_clk;
1801 }
1802
1803 /* Enable CE core clk */
1804 rc = clk_prepare_enable(ce_core_clk);
1805 if (rc) {
1806 pr_err("Unable to enable/prepare CE core clk\n");
1807 if (ce_core_src_clk != NULL)
1808 clk_put(ce_core_src_clk);
1809 clk_put(ce_core_clk);
1810 clk_put(ce_clk);
1811 goto err_clk;
1812 } else {
1813 /* Enable CE clk */
1814 rc = clk_prepare_enable(ce_clk);
1815 if (rc) {
1816 pr_err("Unable to enable/prepare CE iface clk\n");
1817 clk_disable_unprepare(ce_core_clk);
1818 if (ce_core_src_clk != NULL)
1819 clk_put(ce_core_src_clk);
1820 clk_put(ce_core_clk);
1821 clk_put(ce_clk);
1822 goto err_clk;
1823 } else {
1824 /* Enable AXI clk */
1825 rc = clk_prepare_enable(ce_bus_clk);
1826 if (rc) {
1827 pr_err("Unable to enable/prepare CE iface clk\n");
1828 clk_disable_unprepare(ce_core_clk);
1829 clk_disable_unprepare(ce_clk);
1830 if (ce_core_src_clk != NULL)
1831 clk_put(ce_core_src_clk);
1832 clk_put(ce_core_clk);
1833 clk_put(ce_clk);
1834 goto err_clk;
1835 }
1836 }
1837 }
1838 return rc;
1839
1840err_clk:
1841 if (rc)
1842 pr_err("Unable to init CE clks, rc = %d\n", rc);
1843 clk_disable_unprepare(ce_clk);
1844 clk_disable_unprepare(ce_core_clk);
1845 clk_disable_unprepare(ce_bus_clk);
1846 if (ce_core_src_clk != NULL)
1847 clk_put(ce_core_src_clk);
1848 clk_put(ce_clk);
1849 clk_put(ce_core_clk);
1850 clk_put(ce_bus_clk);
1851 return rc;
1852}
1853
1854
1855
1856static void __qseecom_disable_clk()
1857{
1858 clk_disable_unprepare(ce_clk);
1859 clk_disable_unprepare(ce_core_clk);
1860 clk_disable_unprepare(ce_bus_clk);
1861 if (ce_core_src_clk != NULL)
1862 clk_put(ce_core_src_clk);
1863 clk_put(ce_clk);
1864 clk_put(ce_core_clk);
1865 clk_put(ce_bus_clk);
1866}
1867
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001868static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001869{
1870 int rc;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001871 int ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001872 struct device *class_dev;
1873 char qsee_not_legacy = 0;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001874 struct msm_bus_scale_pdata *qseecom_platform_support;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001875 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1876
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001877 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001878 qsee_perf_client = 0;
1879
Mona Hossain2892b6b2012-02-17 13:53:11 -08001880 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1881 if (rc < 0) {
1882 pr_err("alloc_chrdev_region failed %d\n", rc);
1883 return rc;
1884 }
1885
1886 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1887 if (IS_ERR(driver_class)) {
1888 rc = -ENOMEM;
1889 pr_err("class_create failed %d\n", rc);
1890 goto unregister_chrdev_region;
1891 }
1892
1893 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1894 QSEECOM_DEV);
1895 if (!class_dev) {
1896 pr_err("class_device_create failed %d\n", rc);
1897 rc = -ENOMEM;
1898 goto class_destroy;
1899 }
1900
1901 cdev_init(&qseecom_cdev, &qseecom_fops);
1902 qseecom_cdev.owner = THIS_MODULE;
1903
1904 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1905 if (rc < 0) {
1906 pr_err("cdev_add failed %d\n", rc);
1907 goto err;
1908 }
1909
1910 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1911 spin_lock_init(&qseecom.registered_listener_list_lock);
1912 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1913 spin_lock_init(&qseecom.registered_app_list_lock);
1914 init_waitqueue_head(&qseecom.send_resp_wq);
1915 qseecom.send_resp_flag = 0;
1916
1917 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1918 &qsee_not_legacy, sizeof(qsee_not_legacy));
1919 if (rc) {
1920 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1921 goto err;
1922 }
1923 if (qsee_not_legacy)
1924 qseecom.qseos_version = QSEOS_VERSION_14;
1925 else {
1926 qseecom.qseos_version = QSEOS_VERSION_13;
1927 pil = NULL;
1928 pil_ref_cnt = 0;
1929 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001930
1931 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001932 /* Create ION msm client */
1933 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1934 if (qseecom.ion_clnt == NULL) {
1935 pr_err("Ion client cannot be created\n");
1936 rc = -ENOMEM;
1937 goto err;
1938 }
1939
1940 /* register client for bus scaling */
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001941 if (pdev->dev.of_node) {
1942 ret = __qseecom_init_clk();
1943 if (ret)
1944 goto err;
1945 qseecom_platform_support = (struct msm_bus_scale_pdata *)
1946 msm_bus_cl_get_pdata(pdev);
1947 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001948 qseecom_platform_support = (struct msm_bus_scale_pdata *)
1949 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001950 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001951
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001952 qsee_perf_client = msm_bus_scale_register_client(
1953 qseecom_platform_support);
1954
1955 if (!qsee_perf_client)
1956 pr_err("Unable to register bus client\n");
1957 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001958err:
1959 device_destroy(driver_class, qseecom_device_no);
1960class_destroy:
1961 class_destroy(driver_class);
1962unregister_chrdev_region:
1963 unregister_chrdev_region(qseecom_device_no, 1);
1964 return rc;
1965}
1966
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001967static int __devinit qseecom_remove(struct platform_device *pdev)
1968{
1969 if (pdev->dev.platform_data != NULL)
1970 msm_bus_scale_unregister_client(qsee_perf_client);
1971 return 0;
1972};
1973
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001974static struct of_device_id qseecom_match[] = {
1975 {
1976 .compatible = "qcom,qseecom",
1977 },
1978 {}
1979};
1980
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001981static struct platform_driver qseecom_plat_driver = {
1982 .probe = qseecom_probe,
1983 .remove = qseecom_remove,
1984 .driver = {
1985 .name = "qseecom",
1986 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001987 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001988 },
1989};
1990
1991static int __devinit qseecom_init(void)
1992{
1993 return platform_driver_register(&qseecom_plat_driver);
1994}
1995
1996static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001997{
Ramesh Masavarapuff377032012-09-14 12:11:32 -07001998
1999 __qseecom_disable_clk();
2000
Mona Hossain2892b6b2012-02-17 13:53:11 -08002001 device_destroy(driver_class, qseecom_device_no);
2002 class_destroy(driver_class);
2003 unregister_chrdev_region(qseecom_device_no, 1);
2004 ion_client_destroy(qseecom.ion_clnt);
2005}
2006
2007MODULE_LICENSE("GPL v2");
2008MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2009
2010module_init(qseecom_init);
2011module_exit(qseecom_exit);