blob: 3e387efb5c531f409668820cce9e91eec1cd540b [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 Humpherys683564f2012-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 Masavarapua6301812012-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 Masavarapua6301812012-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 Masavarapua6301812012-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 Masavarapua6301812012-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 Humpherys456e2682012-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 Humpherys456e2682012-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
779 if (qseecom.qseos_version == QSEOS_VERSION_14) {
780 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
781 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
782 list) {
783 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700784 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800785 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700786 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800787 break;
788 } else {
789 ptr_app->ref_cnt--;
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700790 pr_warn("Can't unload app with id %d (it is inuse)\n",
791 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800792 break;
793 }
794 }
795 }
796 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
797 flags);
798 }
Mona Hossain340dba82012-08-07 19:54:46 -0700799 if (found_app == false) {
800 pr_err("Cannot find app with id = %d\n", data->client.app_id);
801 return -EINVAL;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800802 }
803
804 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
805 struct qseecom_unload_app_ireq req;
806
Mona Hossain340dba82012-08-07 19:54:46 -0700807 __qseecom_cleanup_app(data);
808 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
809 list_del(&ptr_app->list);
810 kzfree(ptr_app);
811 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
812 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800813 /* Populate the structure for sending scm call to load image */
814 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
815 req.app_id = data->client.app_id;
816
817 /* SCM_CALL to unload the app */
818 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
819 sizeof(struct qseecom_unload_app_ireq),
820 &resp, sizeof(resp));
821 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700822 pr_err("scm_call to unload app (id = %d) failed\n",
823 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800824 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700825 } else {
826 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800827 }
828 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
829 ret = __qseecom_process_incomplete_cmd(data, &resp);
830 if (ret) {
831 pr_err("process_incomplete_cmd fail err: %d\n",
832 ret);
833 return ret;
834 }
835 }
836 }
837
838 if (qseecom.qseos_version == QSEOS_VERSION_13) {
839 data->abort = 1;
840 wake_up_all(&qseecom.send_resp_wq);
841 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700842 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800843 atomic_read(&data->ioctl_count) <= 0)) {
844 pr_err("Interrupted from abort\n");
845 ret = -ERESTARTSYS;
846 break;
847 }
848 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800849 }
Mona Hossain340dba82012-08-07 19:54:46 -0700850 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
851 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
852 ion_free(qseecom.ion_clnt, data->client.ihandle);
853 data->client.ihandle = NULL;
854 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800855 data->released = true;
856 return ret;
857}
858
859static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
860 uint32_t virt)
861{
862 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
863}
864
865static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
866 struct qseecom_send_cmd_req *req)
867{
868 int ret = 0;
869 unsigned long flags;
870 u32 reqd_len_sb_in = 0;
871 struct qseecom_command cmd;
872 struct qseecom_response resp;
873
874
875 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
876 pr_err("cmd buffer or response buffer is null\n");
877 return -EINVAL;
878 }
879
880 if (req->cmd_req_len <= 0 ||
881 req->resp_len <= 0 ||
882 req->cmd_req_len > data->client.sb_length ||
883 req->resp_len > data->client.sb_length) {
884 pr_err("cmd buffer length or "
885 "response buffer length not valid\n");
886 return -EINVAL;
887 }
888
889 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
890 if (reqd_len_sb_in > data->client.sb_length) {
891 pr_debug("Not enough memory to fit cmd_buf and "
892 "resp_buf. Required: %u, Available: %u\n",
893 reqd_len_sb_in, data->client.sb_length);
894 return -ENOMEM;
895 }
896 cmd.cmd_type = TZ_SCHED_CMD_NEW;
897 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
898 cmd.sb_in_cmd_len = req->cmd_req_len;
899
900 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
901 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
902 resp.sb_in_rsp_len = req->resp_len;
903
904 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
905 sizeof(cmd), &resp, sizeof(resp));
906
907 if (ret) {
908 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
909 return ret;
910 }
911
912 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
913 /*
914 * If cmd is incomplete, get the callback cmd out from SB out
915 * and put it on the list
916 */
917 struct qseecom_registered_listener_list *ptr_svc = NULL;
918 /*
919 * We don't know which service can handle the command. so we
920 * wake up all blocking services and let them figure out if
921 * they can handle the given command.
922 */
923 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
924 flags);
925 list_for_each_entry(ptr_svc,
926 &qseecom.registered_listener_list_head, list) {
927 ptr_svc->rcv_req_flag = 1;
928 wake_up_interruptible(&ptr_svc->rcv_req_wq);
929 }
930 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
931 flags);
932
933 pr_debug("waking up rcv_req_wq and "
934 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700935 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800936 __qseecom_listener_has_sent_rsp(data))) {
937 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
938 return -ERESTARTSYS;
939 }
940
941 if (data->abort) {
942 pr_err("Aborting driver\n");
943 return -ENODEV;
944 }
945 qseecom.send_resp_flag = 0;
946 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
947 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
948 sizeof(cmd), &resp, sizeof(resp));
949 if (ret) {
950 pr_err("qseecom_scm_call failed with err: %d\n", ret);
951 return ret;
952 }
953 }
954 return ret;
955}
956
957static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
958 struct qseecom_send_cmd_req *req)
959{
960 int ret = 0;
961 u32 reqd_len_sb_in = 0;
962 struct qseecom_client_send_data_ireq send_data_req;
963 struct qseecom_command_scm_resp resp;
964
965 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
966 pr_err("cmd buffer or response buffer is null\n");
967 return -EINVAL;
968 }
969
970 if (req->cmd_req_len <= 0 ||
971 req->resp_len <= 0 ||
972 req->cmd_req_len > data->client.sb_length ||
973 req->resp_len > data->client.sb_length) {
974 pr_err("cmd buffer length or "
975 "response buffer length not valid\n");
976 return -EINVAL;
977 }
978
979 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
980 if (reqd_len_sb_in > data->client.sb_length) {
981 pr_debug("Not enough memory to fit cmd_buf and "
982 "resp_buf. Required: %u, Available: %u\n",
983 reqd_len_sb_in, data->client.sb_length);
984 return -ENOMEM;
985 }
986
987 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
988 send_data_req.app_id = data->client.app_id;
989 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
990 (uint32_t)req->cmd_req_buf));
991 send_data_req.req_len = req->cmd_req_len;
992 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
993 (uint32_t)req->resp_buf));
994 send_data_req.rsp_len = req->resp_len;
995
996 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
997 sizeof(send_data_req),
998 &resp, sizeof(resp));
999 if (ret) {
1000 pr_err("qseecom_scm_call failed with err: %d\n", ret);
1001 return ret;
1002 }
1003
1004 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1005 ret = __qseecom_process_incomplete_cmd(data, &resp);
1006 if (ret) {
1007 pr_err("process_incomplete_cmd failed err: %d\n", ret);
1008 return ret;
1009 }
Mona Hossainbb0bca12012-04-12 11:47:45 -07001010 } else {
1011 if (resp.result != QSEOS_RESULT_SUCCESS) {
1012 pr_err("Response result %d not supported\n",
1013 resp.result);
1014 ret = -EINVAL;
1015 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001016 }
1017 return ret;
1018}
1019
1020
1021static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1022{
1023 int ret = 0;
1024 struct qseecom_send_cmd_req req;
1025
1026 ret = copy_from_user(&req, argp, sizeof(req));
1027 if (ret) {
1028 pr_err("copy_from_user failed\n");
1029 return ret;
1030 }
1031 if (qseecom.qseos_version == QSEOS_VERSION_14)
1032 ret = __qseecom_send_cmd(data, &req);
1033 else
1034 ret = __qseecom_send_cmd_legacy(data, &req);
1035 if (ret)
1036 return ret;
1037
1038 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1039 req.resp_len, req.resp_buf);
1040 return ret;
1041}
1042
1043static int __qseecom_send_cmd_req_clean_up(
1044 struct qseecom_send_modfd_cmd_req *req)
1045{
1046 char *field;
1047 uint32_t *update;
1048 int ret = 0;
1049 int i = 0;
1050
1051 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001052 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001053 field = (char *)req->cmd_req_buf +
1054 req->ifd_data[i].cmd_buf_offset;
1055 update = (uint32_t *) field;
1056 *update = 0;
1057 }
1058 }
1059 return ret;
1060}
1061
1062static int __qseecom_update_with_phy_addr(
1063 struct qseecom_send_modfd_cmd_req *req)
1064{
1065 struct ion_handle *ihandle;
1066 char *field;
1067 uint32_t *update;
1068 ion_phys_addr_t pa;
1069 int ret = 0;
1070 int i = 0;
1071 uint32_t length;
1072
1073 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001074 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001075 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001076 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001077 req->ifd_data[i].fd);
1078 if (IS_ERR_OR_NULL(ihandle)) {
1079 pr_err("Ion client can't retrieve the handle\n");
1080 return -ENOMEM;
1081 }
1082 field = (char *) req->cmd_req_buf +
1083 req->ifd_data[i].cmd_buf_offset;
1084 update = (uint32_t *) field;
1085
1086 /* Populate the cmd data structure with the phys_addr */
1087 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1088 if (ret)
1089 return -ENOMEM;
1090
1091 *update = (uint32_t)pa;
1092 /* Deallocate the handle */
1093 if (!IS_ERR_OR_NULL(ihandle))
1094 ion_free(qseecom.ion_clnt, ihandle);
1095 }
1096 }
1097 return ret;
1098}
1099
1100static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1101 void __user *argp)
1102{
1103 int ret = 0;
1104 struct qseecom_send_modfd_cmd_req req;
1105 struct qseecom_send_cmd_req send_cmd_req;
1106
1107 ret = copy_from_user(&req, argp, sizeof(req));
1108 if (ret) {
1109 pr_err("copy_from_user failed\n");
1110 return ret;
1111 }
1112 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1113 send_cmd_req.cmd_req_len = req.cmd_req_len;
1114 send_cmd_req.resp_buf = req.resp_buf;
1115 send_cmd_req.resp_len = req.resp_len;
1116
1117 ret = __qseecom_update_with_phy_addr(&req);
1118 if (ret)
1119 return ret;
1120 if (qseecom.qseos_version == QSEOS_VERSION_14)
1121 ret = __qseecom_send_cmd(data, &send_cmd_req);
1122 else
1123 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1124 __qseecom_send_cmd_req_clean_up(&req);
1125
1126 if (ret)
1127 return ret;
1128
1129 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1130 req.resp_len, req.resp_buf);
1131 return ret;
1132}
1133
1134static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1135 struct qseecom_registered_listener_list *svc)
1136{
1137 int ret;
1138 ret = (svc->rcv_req_flag != 0);
1139 return ret || data->abort;
1140}
1141
1142static int qseecom_receive_req(struct qseecom_dev_handle *data)
1143{
1144 int ret = 0;
1145 struct qseecom_registered_listener_list *this_lstnr;
1146
1147 this_lstnr = __qseecom_find_svc(data->listener.id);
1148 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001149 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001150 __qseecom_listener_has_rcvd_req(data,
1151 this_lstnr))) {
1152 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1153 /* woken up for different reason */
1154 return -ERESTARTSYS;
1155 }
1156
1157 if (data->abort) {
1158 pr_err("Aborting driver!\n");
1159 return -ENODEV;
1160 }
1161 this_lstnr->rcv_req_flag = 0;
1162 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1163 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1164 break;
1165 } else {
1166 break;
1167 }
1168 }
1169 return ret;
1170}
1171
1172static int qseecom_send_resp(void)
1173{
1174 qseecom.send_resp_flag = 1;
1175 wake_up_interruptible(&qseecom.send_resp_wq);
1176 return 0;
1177}
1178
1179static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1180 void __user *argp)
1181{
1182 struct qseecom_qseos_version_req req;
1183
1184 if (copy_from_user(&req, argp, sizeof(req))) {
1185 pr_err("copy_from_user failed");
1186 return -EINVAL;
1187 }
1188 req.qseos_version = qseecom.qseos_version;
1189 if (copy_to_user(argp, &req, sizeof(req))) {
1190 pr_err("copy_to_user failed");
1191 return -EINVAL;
1192 }
1193 return 0;
1194}
1195
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001196static int qsee_vote_for_clock(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001197{
1198 int ret = 0;
1199
1200 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001201 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001202
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001203 switch (clk_type) {
1204 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001205 mutex_lock(&qsee_bw_mutex);
1206 if (!qsee_bw_count) {
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001207 if (qsee_sfpb_bw_count > 0)
1208 ret = msm_bus_scale_client_update_request(
1209 qsee_perf_client, 3);
1210 else
1211 ret = msm_bus_scale_client_update_request(
1212 qsee_perf_client, 1);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001213 if (ret)
1214 pr_err("DFAB Bandwidth req failed (%d)\n",
1215 ret);
1216 else
1217 qsee_bw_count++;
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001218 } else {
1219 qsee_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001220 }
1221 mutex_unlock(&qsee_bw_mutex);
1222 break;
1223 case CLK_SFPB:
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001224 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001225 if (!qsee_sfpb_bw_count) {
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001226 if (qsee_bw_count > 0)
1227 ret = msm_bus_scale_client_update_request(
1228 qsee_perf_client, 3);
1229 else
1230 ret = msm_bus_scale_client_update_request(
1231 qsee_perf_client, 2);
1232
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001233 if (ret)
1234 pr_err("SFPB Bandwidth req failed (%d)\n",
1235 ret);
1236 else
1237 qsee_sfpb_bw_count++;
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001238 } else {
1239 qsee_sfpb_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001240 }
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001241 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001242 break;
1243 default:
1244 pr_err("Clock type not defined\n");
1245 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001246 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001247 return ret;
1248}
1249
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001250static void qsee_disable_clock_vote(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001251{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001252 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001253
Mona Hossain2892b6b2012-02-17 13:53:11 -08001254 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001255 return;
1256
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001257 switch (clk_type) {
1258 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001259 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001260 if (qsee_bw_count == 0) {
1261 pr_err("Client error.Extra call to disable DFAB clk\n");
1262 mutex_unlock(&qsee_bw_mutex);
1263 return;
1264 }
1265
1266 if ((qsee_bw_count > 0) && (qsee_bw_count-- == 1)) {
1267 if (qsee_sfpb_bw_count > 0)
1268 ret = msm_bus_scale_client_update_request(
1269 qsee_perf_client, 2);
1270 else
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001271 ret = msm_bus_scale_client_update_request(
1272 qsee_perf_client, 0);
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001273 if (ret)
1274 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001275 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001276 }
1277 mutex_unlock(&qsee_bw_mutex);
1278 break;
1279 case CLK_SFPB:
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001280 mutex_lock(&qsee_bw_mutex);
1281 if (qsee_sfpb_bw_count == 0) {
1282 pr_err("Client error.Extra call to disable SFPB clk\n");
1283 mutex_unlock(&qsee_bw_mutex);
1284 return;
1285 }
1286 if ((qsee_sfpb_bw_count > 0) && (qsee_sfpb_bw_count-- == 1)) {
1287 if (qsee_bw_count > 0)
1288 ret = msm_bus_scale_client_update_request(
1289 qsee_perf_client, 1);
1290 else
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001291 ret = msm_bus_scale_client_update_request(
1292 qsee_perf_client, 0);
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001293 if (ret)
1294 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001295 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001296 }
Ramesh Masavarapu4f091cb2012-10-03 10:18:06 -07001297 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001298 break;
1299 default:
1300 pr_err("Clock type not defined\n");
1301 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001302 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001303
Mona Hossain2892b6b2012-02-17 13:53:11 -08001304}
1305
Mona Hossain5ab9d772012-04-11 21:00:40 -07001306static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1307 void __user *argp)
1308{
1309 struct ion_handle *ihandle; /* Ion handle */
1310 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001311 int ret;
1312 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001313 ion_phys_addr_t pa = 0;
1314 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001315 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001316 struct qseecom_load_app_ireq load_req;
1317 struct qseecom_command_scm_resp resp;
1318
1319 /* Copy the relevant information needed for loading the image */
1320 if (__copy_from_user(&load_img_req,
1321 (void __user *)argp,
1322 sizeof(struct qseecom_load_img_req))) {
1323 pr_err("copy_from_user failed\n");
1324 return -EFAULT;
1325 }
1326
1327 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001328 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001329 load_img_req.ifd_data_fd);
1330 if (IS_ERR_OR_NULL(ihandle)) {
1331 pr_err("Ion client could not retrieve the handle\n");
1332 return -ENOMEM;
1333 }
1334
1335 /* Get the physical address of the ION BUF */
1336 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1337
1338 /* Populate the structure for sending scm call to load image */
1339 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1340 load_req.mdt_len = load_img_req.mdt_len;
1341 load_req.img_len = load_img_req.img_len;
1342 load_req.phy_addr = pa;
1343
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001344 /* SCM_CALL tied to Core0 */
1345 mask = CPU_MASK_CPU0;
1346 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1347 if (set_cpu_ret) {
1348 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1349 set_cpu_ret);
1350 ret = -EFAULT;
1351 goto qseecom_load_external_elf_set_cpu_err;
1352 }
1353
Mona Hossain5ab9d772012-04-11 21:00:40 -07001354 /* SCM_CALL to load the external elf */
1355 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1356 sizeof(struct qseecom_load_app_ireq),
1357 &resp, sizeof(resp));
1358 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001359 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07001360 ret);
1361 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001362 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001363 }
1364
1365 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1366 ret = __qseecom_process_incomplete_cmd(data, &resp);
1367 if (ret)
1368 pr_err("process_incomplete_cmd failed err: %d\n",
1369 ret);
1370 } else {
1371 if (resp.result != QSEOS_RESULT_SUCCESS) {
1372 pr_err("scm_call to load image failed resp.result =%d\n",
1373 resp.result);
1374 ret = -EFAULT;
1375 }
1376 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001377
1378qseecom_load_external_elf_scm_err:
1379 /* Restore the CPU mask */
1380 mask = CPU_MASK_ALL;
1381 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1382 if (set_cpu_ret) {
1383 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1384 set_cpu_ret);
1385 ret = -EFAULT;
1386 }
1387
1388qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07001389 /* Deallocate the handle */
1390 if (!IS_ERR_OR_NULL(ihandle))
1391 ion_free(qseecom.ion_clnt, ihandle);
1392
1393 return ret;
1394}
1395
1396static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1397{
1398 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001399 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001400 struct qseecom_command_scm_resp resp;
1401 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001402 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001403
1404 /* Populate the structure for sending scm call to unload image */
1405 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001406
1407 /* SCM_CALL tied to Core0 */
1408 mask = CPU_MASK_CPU0;
1409 ret = set_cpus_allowed_ptr(current, &mask);
1410 if (ret) {
1411 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1412 ret);
1413 return -EFAULT;
1414 }
1415
Mona Hossain5ab9d772012-04-11 21:00:40 -07001416 /* SCM_CALL to unload the external elf */
1417 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
1418 sizeof(struct qseecom_unload_app_ireq),
1419 &resp, sizeof(resp));
1420 if (ret) {
1421 pr_err("scm_call to unload failed : ret %d\n",
1422 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001423 ret = -EFAULT;
1424 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001425 }
1426 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1427 ret = __qseecom_process_incomplete_cmd(data, &resp);
1428 if (ret)
1429 pr_err("process_incomplete_cmd fail err: %d\n",
1430 ret);
1431 } else {
1432 if (resp.result != QSEOS_RESULT_SUCCESS) {
1433 pr_err("scm_call to unload image failed resp.result =%d\n",
1434 resp.result);
1435 ret = -EFAULT;
1436 }
1437 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001438
1439qseecom_unload_external_elf_scm_err:
1440 /* Restore the CPU mask */
1441 mask = CPU_MASK_ALL;
1442 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1443 if (set_cpu_ret) {
1444 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1445 set_cpu_ret);
1446 ret = -EFAULT;
1447 }
1448
Mona Hossain5ab9d772012-04-11 21:00:40 -07001449 return ret;
1450}
Mona Hossain2892b6b2012-02-17 13:53:11 -08001451
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001452static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
1453 void __user *argp)
1454{
1455
1456 int32_t ret;
1457 struct qseecom_qseos_app_load_query query_req;
1458 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001459 struct qseecom_registered_app_list *entry = NULL;
1460 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001461
1462 /* Copy the relevant information needed for loading the image */
1463 if (__copy_from_user(&query_req,
1464 (void __user *)argp,
1465 sizeof(struct qseecom_qseos_app_load_query))) {
1466 pr_err("copy_from_user failed\n");
1467 return -EFAULT;
1468 }
1469
1470 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1471 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
1472
1473 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001474
1475 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001476 pr_err(" scm call to check if app is loaded failed");
1477 return ret; /* scm call failed */
1478 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001479 pr_warn("App id %d (%s) already exists\n", ret,
1480 (char *)(req.app_name));
1481 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1482 list_for_each_entry(entry,
1483 &qseecom.registered_app_list_head, list){
1484 if (entry->app_id == ret) {
1485 entry->ref_cnt++;
1486 break;
1487 }
1488 }
1489 spin_unlock_irqrestore(
1490 &qseecom.registered_app_list_lock, flags);
1491 data->client.app_id = ret;
1492 query_req.app_id = ret;
1493
1494 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
1495 pr_err("copy_to_user failed\n");
1496 return -EFAULT;
1497 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001498 return -EEXIST; /* app already loaded */
1499 } else {
1500 return 0; /* app not loaded */
1501 }
1502}
1503
Mona Hossain2892b6b2012-02-17 13:53:11 -08001504static long qseecom_ioctl(struct file *file, unsigned cmd,
1505 unsigned long arg)
1506{
1507 int ret = 0;
1508 struct qseecom_dev_handle *data = file->private_data;
1509 void __user *argp = (void __user *) arg;
1510
1511 if (data->abort) {
1512 pr_err("Aborting qseecom driver\n");
1513 return -ENODEV;
1514 }
1515
1516 switch (cmd) {
1517 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1518 pr_debug("ioctl register_listener_req()\n");
1519 atomic_inc(&data->ioctl_count);
1520 ret = qseecom_register_listener(data, argp);
1521 atomic_dec(&data->ioctl_count);
1522 wake_up_all(&data->abort_wq);
1523 if (ret)
1524 pr_err("failed qseecom_register_listener: %d\n", ret);
1525 break;
1526 }
1527 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1528 pr_debug("ioctl unregister_listener_req()\n");
1529 atomic_inc(&data->ioctl_count);
1530 ret = qseecom_unregister_listener(data);
1531 atomic_dec(&data->ioctl_count);
1532 wake_up_all(&data->abort_wq);
1533 if (ret)
1534 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1535 break;
1536 }
1537 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1538 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001539 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001540 atomic_inc(&data->ioctl_count);
1541 ret = qseecom_send_cmd(data, argp);
1542 atomic_dec(&data->ioctl_count);
1543 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001544 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001545 if (ret)
1546 pr_err("failed qseecom_send_cmd: %d\n", ret);
1547 break;
1548 }
1549 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1550 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001551 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001552 atomic_inc(&data->ioctl_count);
1553 ret = qseecom_send_modfd_cmd(data, argp);
1554 atomic_dec(&data->ioctl_count);
1555 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001556 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001557 if (ret)
1558 pr_err("failed qseecom_send_cmd: %d\n", ret);
1559 break;
1560 }
1561 case QSEECOM_IOCTL_RECEIVE_REQ: {
1562 atomic_inc(&data->ioctl_count);
1563 ret = qseecom_receive_req(data);
1564 atomic_dec(&data->ioctl_count);
1565 wake_up_all(&data->abort_wq);
1566 if (ret)
1567 pr_err("failed qseecom_receive_req: %d\n", ret);
1568 break;
1569 }
1570 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1571 atomic_inc(&data->ioctl_count);
1572 ret = qseecom_send_resp();
1573 atomic_dec(&data->ioctl_count);
1574 wake_up_all(&data->abort_wq);
1575 if (ret)
1576 pr_err("failed qseecom_send_resp: %d\n", ret);
1577 break;
1578 }
1579 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1580 ret = qseecom_set_client_mem_param(data, argp);
1581 if (ret)
1582 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1583 ret);
1584 break;
1585 }
1586 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1587 mutex_lock(&app_access_lock);
1588 atomic_inc(&data->ioctl_count);
1589 ret = qseecom_load_app(data, argp);
1590 atomic_dec(&data->ioctl_count);
1591 mutex_unlock(&app_access_lock);
1592 if (ret)
1593 pr_err("failed load_app request: %d\n", ret);
1594 break;
1595 }
1596 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1597 mutex_lock(&app_access_lock);
1598 atomic_inc(&data->ioctl_count);
1599 ret = qseecom_unload_app(data);
1600 atomic_dec(&data->ioctl_count);
1601 mutex_unlock(&app_access_lock);
1602 if (ret)
1603 pr_err("failed unload_app request: %d\n", ret);
1604 break;
1605 }
1606 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1607 atomic_inc(&data->ioctl_count);
1608 ret = qseecom_get_qseos_version(data, argp);
1609 if (ret)
1610 pr_err("qseecom_get_qseos_version: %d\n", ret);
1611 atomic_dec(&data->ioctl_count);
1612 break;
1613 }
1614 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1615 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001616 ret = qsee_vote_for_clock(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001617 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001618 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossaind240c162013-01-10 04:30:04 -08001619 ret = qsee_vote_for_clock(CLK_SFPB);
1620 if (ret)
1621 pr_err("Failed to vote for SFPB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001622 atomic_dec(&data->ioctl_count);
1623 break;
1624 }
1625 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1626 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001627 qsee_disable_clock_vote(CLK_DFAB);
Mona Hossaind240c162013-01-10 04:30:04 -08001628 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001629 atomic_dec(&data->ioctl_count);
1630 break;
1631 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07001632 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
1633 data->released = true;
1634 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1635 pr_err("Loading External elf image unsupported in rev 0x13\n");
1636 ret = -EINVAL;
1637 break;
1638 }
1639 mutex_lock(&app_access_lock);
1640 atomic_inc(&data->ioctl_count);
1641 ret = qseecom_load_external_elf(data, argp);
1642 atomic_dec(&data->ioctl_count);
1643 mutex_unlock(&app_access_lock);
1644 if (ret)
1645 pr_err("failed load_external_elf request: %d\n", ret);
1646 break;
1647 }
1648 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
1649 data->released = true;
1650 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1651 pr_err("Unloading External elf image unsupported in rev 0x13\n");
1652 ret = -EINVAL;
1653 break;
1654 }
1655 mutex_lock(&app_access_lock);
1656 atomic_inc(&data->ioctl_count);
1657 ret = qseecom_unload_external_elf(data);
1658 atomic_dec(&data->ioctl_count);
1659 mutex_unlock(&app_access_lock);
1660 if (ret)
1661 pr_err("failed unload_app request: %d\n", ret);
1662 break;
1663 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001664 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
1665 mutex_lock(&app_access_lock);
1666 atomic_inc(&data->ioctl_count);
1667 ret = qseecom_query_app_loaded(data, argp);
1668 atomic_dec(&data->ioctl_count);
1669 mutex_unlock(&app_access_lock);
1670 break;
1671 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001672 default:
1673 return -EINVAL;
1674 }
1675 return ret;
1676}
1677
1678static int qseecom_open(struct inode *inode, struct file *file)
1679{
1680 int ret = 0;
1681 struct qseecom_dev_handle *data;
1682
1683 data = kzalloc(sizeof(*data), GFP_KERNEL);
1684 if (!data) {
1685 pr_err("kmalloc failed\n");
1686 return -ENOMEM;
1687 }
1688 file->private_data = data;
1689 data->abort = 0;
1690 data->service = false;
1691 data->released = false;
1692 init_waitqueue_head(&data->abort_wq);
1693 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001694 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1695 int pil_error;
1696 mutex_lock(&pil_access_lock);
1697 if (pil_ref_cnt == 0) {
1698 pil = pil_get("tzapps");
1699 if (IS_ERR(pil)) {
1700 pr_err("Playready PIL image load failed\n");
1701 pil_error = PTR_ERR(pil);
1702 pil = NULL;
1703 pr_debug("tzapps image load FAILED\n");
1704 mutex_unlock(&pil_access_lock);
1705 return pil_error;
1706 }
1707 }
1708 pil_ref_cnt++;
1709 mutex_unlock(&pil_access_lock);
1710 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001711 return ret;
1712}
1713
1714static int qseecom_release(struct inode *inode, struct file *file)
1715{
1716 struct qseecom_dev_handle *data = file->private_data;
1717 int ret = 0;
1718
1719 if (data->released == false) {
1720 pr_warn("data->released == false\n");
1721 if (data->service)
1722 ret = qseecom_unregister_listener(data);
1723 else
1724 ret = qseecom_unload_app(data);
1725 if (ret) {
1726 pr_err("Close failed\n");
1727 return ret;
1728 }
1729 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001730 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1731 mutex_lock(&pil_access_lock);
1732 if (pil_ref_cnt == 1)
1733 pil_put(pil);
1734 pil_ref_cnt--;
1735 mutex_unlock(&pil_access_lock);
1736 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001737 kfree(data);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001738 qsee_disable_clock_vote(CLK_DFAB);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001739
Mona Hossain2892b6b2012-02-17 13:53:11 -08001740 return ret;
1741}
1742
Mona Hossain2892b6b2012-02-17 13:53:11 -08001743static const struct file_operations qseecom_fops = {
1744 .owner = THIS_MODULE,
1745 .unlocked_ioctl = qseecom_ioctl,
1746 .open = qseecom_open,
1747 .release = qseecom_release
1748};
1749
Ramesh Masavarapua6301812012-09-14 12:11:32 -07001750static int __qseecom_init_clk()
1751{
1752 int rc = 0;
1753 struct device *pdev;
1754
1755 pdev = qseecom.pdev;
1756 /* Get CE3 src core clk. */
1757 ce_core_src_clk = clk_get(pdev, "core_clk_src");
1758 if (!IS_ERR(ce_core_src_clk)) {
1759 ce_core_src_clk = ce_core_src_clk;
1760
1761 /* Set the core src clk @100Mhz */
1762 rc = clk_set_rate(ce_core_src_clk, 100000000);
1763 if (rc) {
1764 clk_put(ce_core_src_clk);
1765 pr_err("Unable to set the core src clk @100Mhz.\n");
1766 goto err_clk;
1767 }
1768 } else {
1769 pr_warn("Unable to get CE core src clk, set to NULL\n");
1770 ce_core_src_clk = NULL;
1771 }
1772
1773 /* Get CE core clk */
1774 ce_core_clk = clk_get(pdev, "core_clk");
1775 if (IS_ERR(ce_core_clk)) {
1776 rc = PTR_ERR(ce_core_clk);
1777 pr_err("Unable to get CE core clk\n");
1778 if (ce_core_src_clk != NULL)
1779 clk_put(ce_core_src_clk);
1780 goto err_clk;
1781 }
1782
1783 /* Get CE Interface clk */
1784 ce_clk = clk_get(pdev, "iface_clk");
1785 if (IS_ERR(ce_clk)) {
1786 rc = PTR_ERR(ce_clk);
1787 pr_err("Unable to get CE interface clk\n");
1788 if (ce_core_src_clk != NULL)
1789 clk_put(ce_core_src_clk);
1790 clk_put(ce_core_clk);
1791 goto err_clk;
1792 }
1793
1794 /* Get CE AXI clk */
1795 ce_bus_clk = clk_get(pdev, "bus_clk");
1796 if (IS_ERR(ce_bus_clk)) {
1797 rc = PTR_ERR(ce_bus_clk);
1798 pr_err("Unable to get CE BUS interface clk\n");
1799 if (ce_core_src_clk != NULL)
1800 clk_put(ce_core_src_clk);
1801 clk_put(ce_core_clk);
1802 clk_put(ce_clk);
1803 goto err_clk;
1804 }
1805
1806 /* Enable CE core clk */
1807 rc = clk_prepare_enable(ce_core_clk);
1808 if (rc) {
1809 pr_err("Unable to enable/prepare CE core clk\n");
1810 if (ce_core_src_clk != NULL)
1811 clk_put(ce_core_src_clk);
1812 clk_put(ce_core_clk);
1813 clk_put(ce_clk);
1814 goto err_clk;
1815 } else {
1816 /* Enable CE clk */
1817 rc = clk_prepare_enable(ce_clk);
1818 if (rc) {
1819 pr_err("Unable to enable/prepare CE iface clk\n");
1820 clk_disable_unprepare(ce_core_clk);
1821 if (ce_core_src_clk != NULL)
1822 clk_put(ce_core_src_clk);
1823 clk_put(ce_core_clk);
1824 clk_put(ce_clk);
1825 goto err_clk;
1826 } else {
1827 /* Enable AXI clk */
1828 rc = clk_prepare_enable(ce_bus_clk);
1829 if (rc) {
1830 pr_err("Unable to enable/prepare CE iface clk\n");
1831 clk_disable_unprepare(ce_core_clk);
1832 clk_disable_unprepare(ce_clk);
1833 if (ce_core_src_clk != NULL)
1834 clk_put(ce_core_src_clk);
1835 clk_put(ce_core_clk);
1836 clk_put(ce_clk);
1837 goto err_clk;
1838 }
1839 }
1840 }
1841 return rc;
1842
1843err_clk:
1844 if (rc)
1845 pr_err("Unable to init CE clks, rc = %d\n", rc);
1846 clk_disable_unprepare(ce_clk);
1847 clk_disable_unprepare(ce_core_clk);
1848 clk_disable_unprepare(ce_bus_clk);
1849 if (ce_core_src_clk != NULL)
1850 clk_put(ce_core_src_clk);
1851 clk_put(ce_clk);
1852 clk_put(ce_core_clk);
1853 clk_put(ce_bus_clk);
1854 return rc;
1855}
1856
1857
1858
1859static void __qseecom_disable_clk()
1860{
1861 clk_disable_unprepare(ce_clk);
1862 clk_disable_unprepare(ce_core_clk);
1863 clk_disable_unprepare(ce_bus_clk);
1864 if (ce_core_src_clk != NULL)
1865 clk_put(ce_core_src_clk);
1866 clk_put(ce_clk);
1867 clk_put(ce_core_clk);
1868 clk_put(ce_bus_clk);
1869}
1870
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001871static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001872{
1873 int rc;
Ramesh Masavarapua6301812012-09-14 12:11:32 -07001874 int ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001875 struct device *class_dev;
1876 char qsee_not_legacy = 0;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001877 struct msm_bus_scale_pdata *qseecom_platform_support;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001878 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1879
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001880 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001881 qsee_perf_client = 0;
1882
Mona Hossain2892b6b2012-02-17 13:53:11 -08001883 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1884 if (rc < 0) {
1885 pr_err("alloc_chrdev_region failed %d\n", rc);
1886 return rc;
1887 }
1888
1889 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1890 if (IS_ERR(driver_class)) {
1891 rc = -ENOMEM;
1892 pr_err("class_create failed %d\n", rc);
1893 goto unregister_chrdev_region;
1894 }
1895
1896 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1897 QSEECOM_DEV);
1898 if (!class_dev) {
1899 pr_err("class_device_create failed %d\n", rc);
1900 rc = -ENOMEM;
1901 goto class_destroy;
1902 }
1903
1904 cdev_init(&qseecom_cdev, &qseecom_fops);
1905 qseecom_cdev.owner = THIS_MODULE;
1906
1907 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1908 if (rc < 0) {
1909 pr_err("cdev_add failed %d\n", rc);
1910 goto err;
1911 }
1912
1913 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1914 spin_lock_init(&qseecom.registered_listener_list_lock);
1915 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1916 spin_lock_init(&qseecom.registered_app_list_lock);
1917 init_waitqueue_head(&qseecom.send_resp_wq);
1918 qseecom.send_resp_flag = 0;
1919
1920 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1921 &qsee_not_legacy, sizeof(qsee_not_legacy));
1922 if (rc) {
1923 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1924 goto err;
1925 }
1926 if (qsee_not_legacy)
1927 qseecom.qseos_version = QSEOS_VERSION_14;
1928 else {
1929 qseecom.qseos_version = QSEOS_VERSION_13;
1930 pil = NULL;
1931 pil_ref_cnt = 0;
1932 }
Ramesh Masavarapua6301812012-09-14 12:11:32 -07001933
1934 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001935 /* Create ION msm client */
1936 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1937 if (qseecom.ion_clnt == NULL) {
1938 pr_err("Ion client cannot be created\n");
1939 rc = -ENOMEM;
1940 goto err;
1941 }
1942
1943 /* register client for bus scaling */
Ramesh Masavarapua6301812012-09-14 12:11:32 -07001944 if (pdev->dev.of_node) {
1945 ret = __qseecom_init_clk();
1946 if (ret)
1947 goto err;
1948 qseecom_platform_support = (struct msm_bus_scale_pdata *)
1949 msm_bus_cl_get_pdata(pdev);
1950 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001951 qseecom_platform_support = (struct msm_bus_scale_pdata *)
1952 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001953 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001954
Ramesh Masavarapua6301812012-09-14 12:11:32 -07001955 qsee_perf_client = msm_bus_scale_register_client(
1956 qseecom_platform_support);
1957
1958 if (!qsee_perf_client)
1959 pr_err("Unable to register bus client\n");
1960 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001961err:
1962 device_destroy(driver_class, qseecom_device_no);
1963class_destroy:
1964 class_destroy(driver_class);
1965unregister_chrdev_region:
1966 unregister_chrdev_region(qseecom_device_no, 1);
1967 return rc;
1968}
1969
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001970static int __devinit qseecom_remove(struct platform_device *pdev)
1971{
1972 if (pdev->dev.platform_data != NULL)
1973 msm_bus_scale_unregister_client(qsee_perf_client);
1974 return 0;
1975};
1976
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001977static struct of_device_id qseecom_match[] = {
1978 {
1979 .compatible = "qcom,qseecom",
1980 },
1981 {}
1982};
1983
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001984static struct platform_driver qseecom_plat_driver = {
1985 .probe = qseecom_probe,
1986 .remove = qseecom_remove,
1987 .driver = {
1988 .name = "qseecom",
1989 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001990 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001991 },
1992};
1993
1994static int __devinit qseecom_init(void)
1995{
1996 return platform_driver_register(&qseecom_plat_driver);
1997}
1998
1999static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002000{
Ramesh Masavarapua6301812012-09-14 12:11:32 -07002001
2002 __qseecom_disable_clk();
2003
Mona Hossain2892b6b2012-02-17 13:53:11 -08002004 device_destroy(driver_class, qseecom_device_no);
2005 class_destroy(driver_class);
2006 unregister_chrdev_region(qseecom_device_no, 1);
2007 ion_client_destroy(qseecom.ion_clnt);
2008}
2009
2010MODULE_LICENSE("GPL v2");
2011MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2012
2013module_init(qseecom_init);
2014module_exit(qseecom_exit);