blob: 55ffb17622c6bf6eff5b0057f2d74370d445b385 [file] [log] [blame]
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001
Mona Hossainacea1022012-04-09 13:37:27 -07002
Mona Hossaind44a3842012-10-15 09:41:35 -07003/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
Mona Hossain2892b6b2012-02-17 13:53:11 -08004 *
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 Hossaind44a3842012-10-15 09:41:35 -070035#include <linux/elf.h>
36#include <linux/firmware.h>
Mona Hossainacea1022012-04-09 13:37:27 -070037#include <linux/freezer.h>
Mona Hossainf1f2ed62012-11-15 19:51:33 -080038#include <linux/scatterlist.h>
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070039#include <mach/board.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080040#include <mach/msm_bus.h>
41#include <mach/msm_bus_board.h>
42#include <mach/scm.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070043#include <mach/subsystem_restart.h>
Ramesh Masavarapuff377032012-09-14 12:11:32 -070044#include <mach/socinfo.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080045#include "qseecom_legacy.h"
Mona Hossaind44a3842012-10-15 09:41:35 -070046#include "qseecom_kernel.h"
Mona Hossain2892b6b2012-02-17 13:53:11 -080047
48#define QSEECOM_DEV "qseecom"
49#define QSEOS_VERSION_13 0x13
50#define QSEOS_VERSION_14 0x14
Mona Hossain05c73562012-10-29 17:49:01 -070051#define QSEEE_VERSION_00 0x400000
Mona Hossain5b76a622012-11-15 20:09:08 -080052#define QSEE_VERSION_01 0x401000
53#define QSEE_VERSION_02 0x402000
54
Mona Hossain05c73562012-10-29 17:49:01 -070055
56#define QSEOS_CHECK_VERSION_CMD 0x00001803
Mona Hossain2892b6b2012-02-17 13:53:11 -080057
Mona Hossaind39e33b2012-11-05 13:36:40 -080058#define QSEE_CE_CLK_100MHZ 100000000
59#define QSEE_CE_CLK_50MHZ 50000000
60
Mona Hossainf1f2ed62012-11-15 19:51:33 -080061#define QSEECOM_MAX_SG_ENTRY 10
62
Mona Hossain2892b6b2012-02-17 13:53:11 -080063enum qseecom_command_scm_resp_type {
64 QSEOS_APP_ID = 0xEE01,
65 QSEOS_LISTENER_ID
66};
67
68enum qseecom_qceos_cmd_id {
69 QSEOS_APP_START_COMMAND = 0x01,
70 QSEOS_APP_SHUTDOWN_COMMAND,
71 QSEOS_APP_LOOKUP_COMMAND,
72 QSEOS_REGISTER_LISTENER,
73 QSEOS_DEREGISTER_LISTENER,
74 QSEOS_CLIENT_SEND_DATA_COMMAND,
75 QSEOS_LISTENER_DATA_RSP_COMMAND,
Mona Hossain5ab9d772012-04-11 21:00:40 -070076 QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
77 QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
Mona Hossain05c73562012-10-29 17:49:01 -070078 QSEOS_GET_APP_STATE_COMMAND,
79 QSEOS_LOAD_SERV_IMAGE_COMMAND,
80 QSEOS_UNLOAD_SERV_IMAGE_COMMAND,
Mona Hossain5b76a622012-11-15 20:09:08 -080081 QSEOS_APP_REGION_NOTIFICATION,
Mona Hossain2892b6b2012-02-17 13:53:11 -080082 QSEOS_CMD_MAX = 0xEFFFFFFF
83};
84
85enum qseecom_qceos_cmd_status {
86 QSEOS_RESULT_SUCCESS = 0,
87 QSEOS_RESULT_INCOMPLETE,
88 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
89};
90
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070091enum qseecom_clk_definitions {
92 CLK_DFAB = 0,
93 CLK_SFPB,
94};
95
Mona Hossain5b76a622012-11-15 20:09:08 -080096__packed struct qsee_apps_region_info_ireq {
97 uint32_t qsee_cmd_id;
98 uint32_t addr;
99 uint32_t size;
100};
101
Mona Hossain2892b6b2012-02-17 13:53:11 -0800102__packed struct qseecom_check_app_ireq {
103 uint32_t qsee_cmd_id;
104 char app_name[MAX_APP_NAME_SIZE];
105};
106
107__packed struct qseecom_load_app_ireq {
108 uint32_t qsee_cmd_id;
109 uint32_t mdt_len; /* Length of the mdt file */
110 uint32_t img_len; /* Length of .bxx and .mdt files */
111 uint32_t phy_addr; /* phy addr of the start of image */
112 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
113};
114
115__packed struct qseecom_unload_app_ireq {
116 uint32_t qsee_cmd_id;
117 uint32_t app_id;
118};
119
Mona Hossain05c73562012-10-29 17:49:01 -0700120__packed struct qseecom_load_lib_image_ireq {
121 uint32_t qsee_cmd_id;
122 uint32_t mdt_len;
123 uint32_t img_len;
124 uint32_t phy_addr;
125};
126
127__packed struct qseecom_unload_lib_image_ireq {
128 uint32_t qsee_cmd_id;
129};
130
Mona Hossain2892b6b2012-02-17 13:53:11 -0800131__packed struct qseecom_register_listener_ireq {
132 uint32_t qsee_cmd_id;
133 uint32_t listener_id;
134 void *sb_ptr;
135 uint32_t sb_len;
136};
137
138__packed struct qseecom_unregister_listener_ireq {
139 uint32_t qsee_cmd_id;
140 uint32_t listener_id;
141};
142
143__packed struct qseecom_client_send_data_ireq {
144 uint32_t qsee_cmd_id;
145 uint32_t app_id;
146 void *req_ptr;
147 uint32_t req_len;
148 void *rsp_ptr; /* First 4 bytes should always be the return status */
149 uint32_t rsp_len;
150};
151
152/* send_data resp */
153__packed struct qseecom_client_listener_data_irsp {
154 uint32_t qsee_cmd_id;
155 uint32_t listener_id;
156};
157
158/*
159 * struct qseecom_command_scm_resp - qseecom response buffer
160 * @cmd_status: value from enum tz_sched_cmd_status
161 * @sb_in_rsp_addr: points to physical location of response
162 * buffer
163 * @sb_in_rsp_len: length of command response
164 */
165__packed struct qseecom_command_scm_resp {
166 uint32_t result;
167 enum qseecom_command_scm_resp_type resp_type;
168 unsigned int data;
169};
170
171static struct class *driver_class;
172static dev_t qseecom_device_no;
173static struct cdev qseecom_cdev;
174
175/* Data structures used in legacy support */
176static void *pil;
177static uint32_t pil_ref_cnt;
178static DEFINE_MUTEX(pil_access_lock);
179
Mona Hossain2892b6b2012-02-17 13:53:11 -0800180static DEFINE_MUTEX(qsee_bw_mutex);
181static DEFINE_MUTEX(app_access_lock);
182
183static int qsee_bw_count;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700184static int qsee_sfpb_bw_count;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800185static uint32_t qsee_perf_client;
186
187struct qseecom_registered_listener_list {
188 struct list_head list;
189 struct qseecom_register_listener_req svc;
190 u8 *sb_reg_req;
191 u8 *sb_virt;
192 s32 sb_phys;
193 size_t sb_length;
194 struct ion_handle *ihandle; /* Retrieve phy addr */
195
196 wait_queue_head_t rcv_req_wq;
197 int rcv_req_flag;
198};
199
200struct qseecom_registered_app_list {
201 struct list_head list;
202 u32 app_id;
203 u32 ref_cnt;
204};
205
Mona Hossaind44a3842012-10-15 09:41:35 -0700206struct qseecom_registered_kclient_list {
207 struct list_head list;
208 struct qseecom_handle *handle;
209};
210
Mona Hossain2892b6b2012-02-17 13:53:11 -0800211struct qseecom_control {
212 struct ion_client *ion_clnt; /* Ion client */
213 struct list_head registered_listener_list_head;
214 spinlock_t registered_listener_list_lock;
215
216 struct list_head registered_app_list_head;
217 spinlock_t registered_app_list_lock;
218
Mona Hossaind44a3842012-10-15 09:41:35 -0700219 struct list_head registered_kclient_list_head;
220 spinlock_t registered_kclient_list_lock;
221
Mona Hossain2892b6b2012-02-17 13:53:11 -0800222 wait_queue_head_t send_resp_wq;
223 int send_resp_flag;
224
225 uint32_t qseos_version;
Mona Hossain05c73562012-10-29 17:49:01 -0700226 uint32_t qsee_version;
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700227 struct device *pdev;
Mona Hossain05c73562012-10-29 17:49:01 -0700228 bool commonlib_loaded;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800229};
230
231struct qseecom_client_handle {
232 u32 app_id;
233 u8 *sb_virt;
234 s32 sb_phys;
235 uint32_t user_virt_sb_base;
236 size_t sb_length;
237 struct ion_handle *ihandle; /* Retrieve phy addr */
238};
239
240struct qseecom_listener_handle {
241 u32 id;
242};
243
244static struct qseecom_control qseecom;
245
246struct qseecom_dev_handle {
247 bool service;
248 union {
249 struct qseecom_client_handle client;
250 struct qseecom_listener_handle listener;
251 };
252 bool released;
253 int abort;
254 wait_queue_head_t abort_wq;
255 atomic_t ioctl_count;
256};
257
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700258struct clk *ce_core_clk;
259struct clk *ce_clk;
260struct clk *ce_core_src_clk;
261struct clk *ce_bus_clk;
262
Mona Hossainf1f2ed62012-11-15 19:51:33 -0800263struct qseecom_sg_entry {
264 uint32_t phys_addr;
265 uint32_t len;
266};
267
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700268/* Function proto types */
269static int qsee_vote_for_clock(int32_t);
270static void qsee_disable_clock_vote(int32_t);
271
Mona Hossain2892b6b2012-02-17 13:53:11 -0800272static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800273 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800274{
275 struct qseecom_registered_listener_list *ptr;
276 int unique = 1;
277 unsigned long flags;
278
279 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
280 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800281 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800282 pr_err("Service id: %u is already registered\n",
283 ptr->svc.listener_id);
284 unique = 0;
285 break;
286 }
287 }
288 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
289 return unique;
290}
291
292static struct qseecom_registered_listener_list *__qseecom_find_svc(
293 int32_t listener_id)
294{
295 struct qseecom_registered_listener_list *entry = NULL;
296 unsigned long flags;
297
298 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
299 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
300 {
301 if (entry->svc.listener_id == listener_id)
302 break;
303 }
304 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
305 return entry;
306}
307
308static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
309 struct qseecom_dev_handle *handle,
310 struct qseecom_register_listener_req *listener)
311{
312 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800313 struct qseecom_register_listener_ireq req;
314 struct qseecom_command_scm_resp resp;
315 ion_phys_addr_t pa;
316
317 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800318 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
319 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800320 if (svc->ihandle == NULL) {
321 pr_err("Ion client could not retrieve the handle\n");
322 return -ENOMEM;
323 }
324
325 /* Get the physical address of the ION BUF */
326 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
327
328 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700329 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800330 svc->sb_phys = pa;
331
332 if (qseecom.qseos_version == QSEOS_VERSION_14) {
333 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
334 req.listener_id = svc->svc.listener_id;
335 req.sb_len = svc->sb_length;
336 req.sb_ptr = (void *)svc->sb_phys;
337
338 resp.result = QSEOS_RESULT_INCOMPLETE;
339
340 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
341 sizeof(req), &resp, sizeof(resp));
342 if (ret) {
343 pr_err("qseecom_scm_call failed with err: %d\n", ret);
344 return -EINVAL;
345 }
346
347 if (resp.result != QSEOS_RESULT_SUCCESS) {
348 pr_err("Error SB registration req: resp.result = %d\n",
349 resp.result);
350 return -EPERM;
351 }
352 } else {
353 struct qseecom_command cmd;
354 struct qseecom_response resp;
355 struct qse_pr_init_sb_req_s sb_init_req;
356 struct qse_pr_init_sb_rsp_s sb_init_rsp;
357
358 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
359 sizeof(sb_init_rsp)), GFP_KERNEL);
360
361 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
362 sb_init_req.listener_id = svc->svc.listener_id;
363 sb_init_req.sb_len = svc->sb_length;
364 sb_init_req.sb_ptr = svc->sb_phys;
365
366 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
367
368 /* It will always be a new cmd from this method */
369 cmd.cmd_type = TZ_SCHED_CMD_NEW;
370 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
371 cmd.sb_in_cmd_len = sizeof(sb_init_req);
372
373 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
374
375 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
376 , &resp, sizeof(resp));
377
378 if (ret) {
379 pr_err("qseecom_scm_call failed with err: %d\n", ret);
380 return -EINVAL;
381 }
382
383 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
384 pr_err("SB registration fail resp.cmd_status %d\n",
385 resp.cmd_status);
386 return -EINVAL;
387 }
388 memset(svc->sb_virt, 0, svc->sb_length);
389 }
390 return 0;
391}
392
393static int qseecom_register_listener(struct qseecom_dev_handle *data,
394 void __user *argp)
395{
396 int ret = 0;
397 unsigned long flags;
398 struct qseecom_register_listener_req rcvd_lstnr;
399 struct qseecom_registered_listener_list *new_entry;
400
401 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
402 if (ret) {
403 pr_err("copy_from_user failed\n");
404 return ret;
405 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800406 data->listener.id = 0;
407 data->service = true;
408 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800409 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800410 data->released = true;
411 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800412 }
413
414 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
415 if (!new_entry) {
416 pr_err("kmalloc failed\n");
417 return -ENOMEM;
418 }
419 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
420 new_entry->rcv_req_flag = 0;
421
422 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
423 new_entry->sb_length = rcvd_lstnr.sb_size;
424 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
425 pr_err("qseecom_set_sb_memoryfailed\n");
426 kzfree(new_entry);
427 return -ENOMEM;
428 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800429
Mona Hossain2892b6b2012-02-17 13:53:11 -0800430 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800431 init_waitqueue_head(&new_entry->rcv_req_wq);
432
433 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
434 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
435 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800436
Mona Hossain2892b6b2012-02-17 13:53:11 -0800437 return ret;
438}
439
440static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
441{
442 int ret = 0;
443 unsigned long flags;
444 uint32_t unmap_mem = 0;
445 struct qseecom_register_listener_ireq req;
446 struct qseecom_registered_listener_list *ptr_svc = NULL;
447 struct qseecom_command_scm_resp resp;
448 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
449
450 if (qseecom.qseos_version == QSEOS_VERSION_14) {
451 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
452 req.listener_id = data->listener.id;
453 resp.result = QSEOS_RESULT_INCOMPLETE;
454
455 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
456 sizeof(req), &resp, sizeof(resp));
457 if (ret) {
458 pr_err("qseecom_scm_call failed with err: %d\n", ret);
459 return ret;
460 }
461
462 if (resp.result != QSEOS_RESULT_SUCCESS) {
463 pr_err("SB deregistartion: result=%d\n", resp.result);
464 return -EPERM;
465 }
466 } else {
467 struct qse_pr_init_sb_req_s sb_init_req;
468 struct qseecom_command cmd;
469 struct qseecom_response resp;
470 struct qseecom_registered_listener_list *svc;
471
472 svc = __qseecom_find_svc(data->listener.id);
473 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
474 sb_init_req.listener_id = data->listener.id;
475 sb_init_req.sb_len = 0;
476 sb_init_req.sb_ptr = 0;
477
478 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
479
480 /* It will always be a new cmd from this method */
481 cmd.cmd_type = TZ_SCHED_CMD_NEW;
482 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
483 cmd.sb_in_cmd_len = sizeof(sb_init_req);
484 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
485
486 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
487 &resp, sizeof(resp));
488 if (ret) {
489 pr_err("qseecom_scm_call failed with err: %d\n", ret);
490 return ret;
491 }
492 kzfree(svc->sb_reg_req);
493 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
494 pr_err("Error with SB initialization\n");
495 return -EPERM;
496 }
497 }
498 data->abort = 1;
499 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
500 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
501 list) {
502 if (ptr_svc->svc.listener_id == data->listener.id) {
503 wake_up_all(&ptr_svc->rcv_req_wq);
504 break;
505 }
506 }
507 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
508
509 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700510 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800511 atomic_read(&data->ioctl_count) <= 1)) {
512 pr_err("Interrupted from abort\n");
513 ret = -ERESTARTSYS;
514 break;
515 }
516 }
517
518 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
519 list_for_each_entry(ptr_svc,
520 &qseecom.registered_listener_list_head,
521 list)
522 {
523 if (ptr_svc->svc.listener_id == data->listener.id) {
524 if (ptr_svc->sb_virt) {
525 unmap_mem = 1;
526 ihandle = ptr_svc->ihandle;
527 }
528 list_del(&ptr_svc->list);
529 kzfree(ptr_svc);
530 break;
531 }
532 }
533 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
534
535 /* Unmap the memory */
536 if (unmap_mem) {
537 if (!IS_ERR_OR_NULL(ihandle)) {
538 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
539 ion_free(qseecom.ion_clnt, ihandle);
540 }
541 }
542 data->released = true;
543 return ret;
544}
545
546static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
547 void __user *argp)
548{
549 ion_phys_addr_t pa;
550 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800551 struct qseecom_set_sb_mem_param_req req;
552 uint32_t len;
553
554 /* Copy the relevant information needed for loading the image */
555 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
556 return -EFAULT;
557
Mona Hossain2892b6b2012-02-17 13:53:11 -0800558 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800559 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
560 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800561 if (IS_ERR_OR_NULL(data->client.ihandle)) {
562 pr_err("Ion client could not retrieve the handle\n");
563 return -ENOMEM;
564 }
565 /* Get the physical address of the ION BUF */
566 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
567 /* Populate the structure for sending scm call to load image */
568 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700569 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800570 data->client.sb_phys = pa;
571 data->client.sb_length = req.sb_len;
572 data->client.user_virt_sb_base = req.virt_sb_base;
573 return 0;
574}
575
Mona Hossain2892b6b2012-02-17 13:53:11 -0800576static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
577{
578 int ret;
579 ret = (qseecom.send_resp_flag != 0);
580 return ret || data->abort;
581}
582
583static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
584 struct qseecom_command_scm_resp *resp)
585{
586 int ret = 0;
587 uint32_t lstnr;
588 unsigned long flags;
589 struct qseecom_client_listener_data_irsp send_data_rsp;
590 struct qseecom_registered_listener_list *ptr_svc = NULL;
591
592
593 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
594 lstnr = resp->data;
595 /*
596 * Wake up blocking lsitener service with the lstnr id
597 */
598 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
599 flags);
600 list_for_each_entry(ptr_svc,
601 &qseecom.registered_listener_list_head, list) {
602 if (ptr_svc->svc.listener_id == lstnr) {
603 ptr_svc->rcv_req_flag = 1;
604 wake_up_interruptible(&ptr_svc->rcv_req_wq);
605 break;
606 }
607 }
608 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
609 flags);
610 if (ptr_svc->svc.listener_id != lstnr) {
611 pr_warning("Service requested for does on exist\n");
612 return -ERESTARTSYS;
613 }
614 pr_debug("waking up rcv_req_wq and "
615 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700616 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800617 __qseecom_listener_has_sent_rsp(data))) {
618 pr_warning("Interrupted: exiting send_cmd loop\n");
619 return -ERESTARTSYS;
620 }
621
622 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700623 pr_err("Aborting listener service %d\n",
624 data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800625 return -ENODEV;
626 }
627 qseecom.send_resp_flag = 0;
628 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
629 send_data_rsp.listener_id = lstnr ;
630
631 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
632 (const void *)&send_data_rsp,
633 sizeof(send_data_rsp), resp,
634 sizeof(*resp));
635 if (ret) {
636 pr_err("qseecom_scm_call failed with err: %d\n", ret);
637 return ret;
638 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700639 if (resp->result == QSEOS_RESULT_FAILURE) {
640 pr_err("Response result %d not supported\n",
641 resp->result);
642 return -EINVAL;
643 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800644 }
645 return ret;
646}
647
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700648static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
649{
650 int32_t ret;
651 struct qseecom_command_scm_resp resp;
652
653 /* SCM_CALL to check if app_id for the mentioned app exists */
654 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
655 sizeof(struct qseecom_check_app_ireq),
656 &resp, sizeof(resp));
657 if (ret) {
658 pr_err("scm_call to check if app is already loaded failed\n");
659 return -EINVAL;
660 }
661
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700662 if (resp.result == QSEOS_RESULT_FAILURE) {
663 return 0;
664 } else {
665 switch (resp.resp_type) {
666 /*qsee returned listener type response */
667 case QSEOS_LISTENER_ID:
668 pr_err("resp type is of listener type instead of app");
669 return -EINVAL;
670 break;
671 case QSEOS_APP_ID:
672 return resp.data;
673 default:
674 pr_err("invalid resp type (%d) from qsee",
675 resp.resp_type);
676 return -ENODEV;
677 break;
678 }
679 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700680}
681
Mona Hossain2892b6b2012-02-17 13:53:11 -0800682static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
683{
684 struct qseecom_registered_app_list *entry = NULL;
685 unsigned long flags = 0;
686 u32 app_id = 0;
687 struct ion_handle *ihandle; /* Ion handle */
688 struct qseecom_load_img_req load_img_req;
689 int32_t ret;
690 ion_phys_addr_t pa = 0;
691 uint32_t len;
692 struct qseecom_command_scm_resp resp;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700693 struct qseecom_load_app_ireq load_req;
694
Mona Hossain2892b6b2012-02-17 13:53:11 -0800695 /* Copy the relevant information needed for loading the image */
696 if (__copy_from_user(&load_img_req,
697 (void __user *)argp,
698 sizeof(struct qseecom_load_img_req))) {
699 pr_err("copy_from_user failed\n");
700 return -EFAULT;
701 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700702 /* Vote for the SFPB clock */
703 ret = qsee_vote_for_clock(CLK_SFPB);
704 if (ret)
705 pr_warning("Unable to vote for SFPB clock");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800706
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700707 pr_warn("App (%s) does not exist, loading apps for first time\n",
Mona Hossaind44a3842012-10-15 09:41:35 -0700708 (char *)(load_img_req.img_name));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700709 /* Get the handle of the shared fd */
710 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800711 load_img_req.ifd_data_fd);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700712 if (IS_ERR_OR_NULL(ihandle)) {
713 pr_err("Ion client could not retrieve the handle\n");
714 qsee_disable_clock_vote(CLK_SFPB);
715 return -ENOMEM;
716 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800717
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700718 /* Get the physical address of the ION BUF */
719 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800720
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700721 /* Populate the structure for sending scm call to load image */
Mona Hossaind44a3842012-10-15 09:41:35 -0700722 memcpy(load_req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700723 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
724 load_req.mdt_len = load_img_req.mdt_len;
725 load_req.img_len = load_img_req.img_len;
726 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800727
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700728 /* SCM_CALL to load the app and get the app_id back */
729 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
730 sizeof(struct qseecom_load_app_ireq),
731 &resp, sizeof(resp));
732 if (ret) {
733 pr_err("scm_call to load app failed\n");
734 return -EINVAL;
735 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800736
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700737 if (resp.result == QSEOS_RESULT_FAILURE) {
738 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800739 if (!IS_ERR_OR_NULL(ihandle))
740 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700741 qsee_disable_clock_vote(CLK_SFPB);
742 return -EFAULT;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800743 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700744
745 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
746 ret = __qseecom_process_incomplete_cmd(data, &resp);
747 if (ret) {
748 pr_err("process_incomplete_cmd failed err: %d\n",
749 ret);
750 if (!IS_ERR_OR_NULL(ihandle))
751 ion_free(qseecom.ion_clnt, ihandle);
752 qsee_disable_clock_vote(CLK_SFPB);
753 return ret;
754 }
755 }
756
757 if (resp.result != QSEOS_RESULT_SUCCESS) {
758 pr_err("scm_call failed resp.result unknown, %d\n",
759 resp.result);
760 if (!IS_ERR_OR_NULL(ihandle))
761 ion_free(qseecom.ion_clnt, ihandle);
762 qsee_disable_clock_vote(CLK_SFPB);
763 return -EFAULT;
764 }
765
766 app_id = resp.data;
767
768 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
769 if (!entry) {
770 pr_err("kmalloc failed\n");
771 qsee_disable_clock_vote(CLK_SFPB);
772 return -ENOMEM;
773 }
774 entry->app_id = app_id;
775 entry->ref_cnt = 1;
776
777 /* Deallocate the handle */
778 if (!IS_ERR_OR_NULL(ihandle))
779 ion_free(qseecom.ion_clnt, ihandle);
780
781 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
782 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
783 spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
784
785 pr_warn("App with id %d (%s) now loaded\n", app_id,
Mona Hossaind44a3842012-10-15 09:41:35 -0700786 (char *)(load_img_req.img_name));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700787
Mona Hossain2892b6b2012-02-17 13:53:11 -0800788 data->client.app_id = app_id;
789 load_img_req.app_id = app_id;
790 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
791 pr_err("copy_to_user failed\n");
792 kzfree(entry);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700793 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800794 return -EFAULT;
795 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700796 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800797 return 0;
798}
799
800static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
801{
802 wake_up_all(&qseecom.send_resp_wq);
803 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700804 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800805 atomic_read(&data->ioctl_count) <= 1)) {
806 pr_err("Interrupted from abort\n");
807 return -ERESTARTSYS;
808 break;
809 }
810 }
811 /* Set unload app */
812 return 1;
813}
814
815static int qseecom_unload_app(struct qseecom_dev_handle *data)
816{
817 unsigned long flags;
818 int ret = 0;
819 struct qseecom_command_scm_resp resp;
820 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700821 bool unload = false;
822 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800823
Mona Hossain1fb538f2012-08-30 16:19:38 -0700824 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
825 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800826 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
827 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
828 list) {
829 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700830 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800831 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700832 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800833 break;
834 } else {
835 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700836 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700837 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800838 break;
839 }
840 }
841 }
842 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
843 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700844 if (found_app == false) {
845 pr_err("Cannot find app with id = %d\n",
846 data->client.app_id);
847 return -EINVAL;
848 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800849 }
850
851 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
852 struct qseecom_unload_app_ireq req;
853
Mona Hossain340dba82012-08-07 19:54:46 -0700854 __qseecom_cleanup_app(data);
855 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
856 list_del(&ptr_app->list);
857 kzfree(ptr_app);
858 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
859 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800860 /* Populate the structure for sending scm call to load image */
861 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
862 req.app_id = data->client.app_id;
863
864 /* SCM_CALL to unload the app */
865 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
866 sizeof(struct qseecom_unload_app_ireq),
867 &resp, sizeof(resp));
868 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700869 pr_err("scm_call to unload app (id = %d) failed\n",
870 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800871 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700872 } else {
873 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800874 }
875 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
876 ret = __qseecom_process_incomplete_cmd(data, &resp);
877 if (ret) {
878 pr_err("process_incomplete_cmd fail err: %d\n",
879 ret);
880 return ret;
881 }
882 }
883 }
884
885 if (qseecom.qseos_version == QSEOS_VERSION_13) {
886 data->abort = 1;
887 wake_up_all(&qseecom.send_resp_wq);
888 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700889 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800890 atomic_read(&data->ioctl_count) <= 0)) {
891 pr_err("Interrupted from abort\n");
892 ret = -ERESTARTSYS;
893 break;
894 }
895 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800896 }
Mona Hossain340dba82012-08-07 19:54:46 -0700897 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
898 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
899 ion_free(qseecom.ion_clnt, data->client.ihandle);
900 data->client.ihandle = NULL;
901 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800902 data->released = true;
903 return ret;
904}
905
906static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
907 uint32_t virt)
908{
909 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
910}
911
912static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
913 struct qseecom_send_cmd_req *req)
914{
915 int ret = 0;
916 unsigned long flags;
917 u32 reqd_len_sb_in = 0;
918 struct qseecom_command cmd;
919 struct qseecom_response resp;
920
921
922 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
923 pr_err("cmd buffer or response buffer is null\n");
924 return -EINVAL;
925 }
926
927 if (req->cmd_req_len <= 0 ||
928 req->resp_len <= 0 ||
929 req->cmd_req_len > data->client.sb_length ||
930 req->resp_len > data->client.sb_length) {
931 pr_err("cmd buffer length or "
932 "response buffer length not valid\n");
933 return -EINVAL;
934 }
935
936 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
937 if (reqd_len_sb_in > data->client.sb_length) {
938 pr_debug("Not enough memory to fit cmd_buf and "
939 "resp_buf. Required: %u, Available: %u\n",
940 reqd_len_sb_in, data->client.sb_length);
941 return -ENOMEM;
942 }
943 cmd.cmd_type = TZ_SCHED_CMD_NEW;
944 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
945 cmd.sb_in_cmd_len = req->cmd_req_len;
946
947 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
948 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
949 resp.sb_in_rsp_len = req->resp_len;
950
951 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
952 sizeof(cmd), &resp, sizeof(resp));
953
954 if (ret) {
955 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
956 return ret;
957 }
958
959 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
960 /*
961 * If cmd is incomplete, get the callback cmd out from SB out
962 * and put it on the list
963 */
964 struct qseecom_registered_listener_list *ptr_svc = NULL;
965 /*
966 * We don't know which service can handle the command. so we
967 * wake up all blocking services and let them figure out if
968 * they can handle the given command.
969 */
970 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
971 flags);
972 list_for_each_entry(ptr_svc,
973 &qseecom.registered_listener_list_head, list) {
974 ptr_svc->rcv_req_flag = 1;
975 wake_up_interruptible(&ptr_svc->rcv_req_wq);
976 }
977 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
978 flags);
979
980 pr_debug("waking up rcv_req_wq and "
981 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700982 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800983 __qseecom_listener_has_sent_rsp(data))) {
984 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
985 return -ERESTARTSYS;
986 }
987
988 if (data->abort) {
989 pr_err("Aborting driver\n");
990 return -ENODEV;
991 }
992 qseecom.send_resp_flag = 0;
993 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
994 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
995 sizeof(cmd), &resp, sizeof(resp));
996 if (ret) {
997 pr_err("qseecom_scm_call failed with err: %d\n", ret);
998 return ret;
999 }
1000 }
1001 return ret;
1002}
1003
1004static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
1005 struct qseecom_send_cmd_req *req)
1006{
1007 int ret = 0;
1008 u32 reqd_len_sb_in = 0;
1009 struct qseecom_client_send_data_ireq send_data_req;
1010 struct qseecom_command_scm_resp resp;
1011
1012 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
1013 pr_err("cmd buffer or response buffer is null\n");
1014 return -EINVAL;
1015 }
1016
1017 if (req->cmd_req_len <= 0 ||
1018 req->resp_len <= 0 ||
1019 req->cmd_req_len > data->client.sb_length ||
1020 req->resp_len > data->client.sb_length) {
1021 pr_err("cmd buffer length or "
1022 "response buffer length not valid\n");
1023 return -EINVAL;
1024 }
1025
1026 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
1027 if (reqd_len_sb_in > data->client.sb_length) {
1028 pr_debug("Not enough memory to fit cmd_buf and "
1029 "resp_buf. Required: %u, Available: %u\n",
1030 reqd_len_sb_in, data->client.sb_length);
1031 return -ENOMEM;
1032 }
1033
1034 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
1035 send_data_req.app_id = data->client.app_id;
1036 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
1037 (uint32_t)req->cmd_req_buf));
1038 send_data_req.req_len = req->cmd_req_len;
1039 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
1040 (uint32_t)req->resp_buf));
1041 send_data_req.rsp_len = req->resp_len;
1042
1043 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
1044 sizeof(send_data_req),
1045 &resp, sizeof(resp));
1046 if (ret) {
1047 pr_err("qseecom_scm_call failed with err: %d\n", ret);
1048 return ret;
1049 }
1050
1051 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1052 ret = __qseecom_process_incomplete_cmd(data, &resp);
1053 if (ret) {
1054 pr_err("process_incomplete_cmd failed err: %d\n", ret);
1055 return ret;
1056 }
Mona Hossainbb0bca12012-04-12 11:47:45 -07001057 } else {
1058 if (resp.result != QSEOS_RESULT_SUCCESS) {
1059 pr_err("Response result %d not supported\n",
1060 resp.result);
1061 ret = -EINVAL;
1062 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001063 }
1064 return ret;
1065}
1066
1067
1068static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1069{
1070 int ret = 0;
1071 struct qseecom_send_cmd_req req;
1072
1073 ret = copy_from_user(&req, argp, sizeof(req));
1074 if (ret) {
1075 pr_err("copy_from_user failed\n");
1076 return ret;
1077 }
1078 if (qseecom.qseos_version == QSEOS_VERSION_14)
1079 ret = __qseecom_send_cmd(data, &req);
1080 else
1081 ret = __qseecom_send_cmd_legacy(data, &req);
1082 if (ret)
1083 return ret;
1084
1085 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1086 req.resp_len, req.resp_buf);
1087 return ret;
1088}
1089
1090static int __qseecom_send_cmd_req_clean_up(
1091 struct qseecom_send_modfd_cmd_req *req)
1092{
1093 char *field;
1094 uint32_t *update;
1095 int ret = 0;
1096 int i = 0;
1097
1098 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001099 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001100 field = (char *)req->cmd_req_buf +
1101 req->ifd_data[i].cmd_buf_offset;
1102 update = (uint32_t *) field;
1103 *update = 0;
1104 }
1105 }
1106 return ret;
1107}
1108
1109static int __qseecom_update_with_phy_addr(
1110 struct qseecom_send_modfd_cmd_req *req)
1111{
1112 struct ion_handle *ihandle;
1113 char *field;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001114 int ret = 0;
1115 int i = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001116
1117 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001118 struct sg_table *sg_ptr = NULL;
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001119 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001120 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001121 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001122 req->ifd_data[i].fd);
1123 if (IS_ERR_OR_NULL(ihandle)) {
1124 pr_err("Ion client can't retrieve the handle\n");
1125 return -ENOMEM;
1126 }
1127 field = (char *) req->cmd_req_buf +
1128 req->ifd_data[i].cmd_buf_offset;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001129
1130 /* Populate the cmd data structure with the phys_addr */
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001131 sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
1132 if (sg_ptr == NULL) {
1133 pr_err("IOn client could not retrieve sg table\n");
1134 goto err;
1135 }
1136 if (sg_ptr->nents == 0) {
1137 pr_err("Num of scattered entries is 0\n");
1138 goto err;
1139 }
1140 if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
1141 pr_err("Num of scattered entries");
1142 pr_err(" (%d) is greater than max supported %d\n",
1143 sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
1144 goto err;
1145 }
1146 if (sg_ptr->nents == 1) {
1147 uint32_t *update;
1148 update = (uint32_t *) field;
1149 *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
1150 } else {
1151 struct qseecom_sg_entry *update;
1152 struct scatterlist *sg;
1153 int j = 0;
1154 update = (struct qseecom_sg_entry *) field;
1155 sg = sg_ptr->sgl;
1156 for (j = 0; j < sg_ptr->nents; j++) {
1157 update->phys_addr = (uint32_t)
1158 sg_dma_address(sg);
1159 update->len = (uint32_t)sg->length;
1160 update++;
1161 sg = sg_next(sg);
1162 }
1163 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001164 /* Deallocate the handle */
1165 if (!IS_ERR_OR_NULL(ihandle))
1166 ion_free(qseecom.ion_clnt, ihandle);
1167 }
1168 }
1169 return ret;
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001170err:
1171 if (!IS_ERR_OR_NULL(ihandle))
1172 ion_free(qseecom.ion_clnt, ihandle);
1173 return -ENOMEM;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001174}
1175
1176static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1177 void __user *argp)
1178{
1179 int ret = 0;
1180 struct qseecom_send_modfd_cmd_req req;
1181 struct qseecom_send_cmd_req send_cmd_req;
1182
1183 ret = copy_from_user(&req, argp, sizeof(req));
1184 if (ret) {
1185 pr_err("copy_from_user failed\n");
1186 return ret;
1187 }
1188 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1189 send_cmd_req.cmd_req_len = req.cmd_req_len;
1190 send_cmd_req.resp_buf = req.resp_buf;
1191 send_cmd_req.resp_len = req.resp_len;
1192
1193 ret = __qseecom_update_with_phy_addr(&req);
1194 if (ret)
1195 return ret;
1196 if (qseecom.qseos_version == QSEOS_VERSION_14)
1197 ret = __qseecom_send_cmd(data, &send_cmd_req);
1198 else
1199 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1200 __qseecom_send_cmd_req_clean_up(&req);
1201
1202 if (ret)
1203 return ret;
1204
1205 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1206 req.resp_len, req.resp_buf);
1207 return ret;
1208}
1209
1210static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1211 struct qseecom_registered_listener_list *svc)
1212{
1213 int ret;
1214 ret = (svc->rcv_req_flag != 0);
1215 return ret || data->abort;
1216}
1217
1218static int qseecom_receive_req(struct qseecom_dev_handle *data)
1219{
1220 int ret = 0;
1221 struct qseecom_registered_listener_list *this_lstnr;
1222
1223 this_lstnr = __qseecom_find_svc(data->listener.id);
1224 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001225 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001226 __qseecom_listener_has_rcvd_req(data,
1227 this_lstnr))) {
1228 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1229 /* woken up for different reason */
1230 return -ERESTARTSYS;
1231 }
1232
1233 if (data->abort) {
1234 pr_err("Aborting driver!\n");
1235 return -ENODEV;
1236 }
1237 this_lstnr->rcv_req_flag = 0;
1238 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1239 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1240 break;
1241 } else {
1242 break;
1243 }
1244 }
1245 return ret;
1246}
1247
Mona Hossaind44a3842012-10-15 09:41:35 -07001248static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
1249{
1250 struct elf32_hdr *ehdr;
1251
1252 if (fw_entry->size < sizeof(*ehdr)) {
1253 pr_err("%s: Not big enough to be an elf header\n",
1254 qseecom.pdev->init_name);
1255 return false;
1256 }
1257 ehdr = (struct elf32_hdr *)fw_entry->data;
1258 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
1259 pr_err("%s: Not an elf header\n",
1260 qseecom.pdev->init_name);
1261 return false;
1262 }
1263
1264 if (ehdr->e_phnum == 0) {
1265 pr_err("%s: No loadable segments\n",
1266 qseecom.pdev->init_name);
1267 return false;
1268 }
1269 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
1270 sizeof(struct elf32_hdr) > fw_entry->size) {
1271 pr_err("%s: Program headers not within mdt\n",
1272 qseecom.pdev->init_name);
1273 return false;
1274 }
1275 return true;
1276}
1277
1278static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size)
1279{
1280 int ret = -1;
1281 int i = 0, rc = 0;
1282 const struct firmware *fw_entry = NULL;
1283 struct elf32_phdr *phdr;
1284 char fw_name[MAX_APP_NAME_SIZE];
1285 struct elf32_hdr *ehdr;
1286 int num_images = 0;
1287
1288 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1289 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1290 if (rc) {
1291 pr_err("error with request_firmware\n");
1292 ret = -EIO;
1293 goto err;
1294 }
1295 if (!__qseecom_is_fw_image_valid(fw_entry)) {
1296 ret = -EIO;
1297 goto err;
1298 }
1299 *fw_size = fw_entry->size;
1300 phdr = (struct elf32_phdr *)(fw_entry->data + sizeof(struct elf32_hdr));
1301 ehdr = (struct elf32_hdr *)fw_entry->data;
1302 num_images = ehdr->e_phnum;
1303 release_firmware(fw_entry);
1304 for (i = 0; i < num_images; i++, phdr++) {
1305 memset(fw_name, 0, sizeof(fw_name));
1306 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1307 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1308 if (ret)
1309 goto err;
1310 *fw_size += fw_entry->size;
1311 release_firmware(fw_entry);
1312 }
1313 return ret;
1314err:
1315 if (fw_entry)
1316 release_firmware(fw_entry);
1317 *fw_size = 0;
1318 return ret;
1319}
1320
1321static int __qseecom_get_fw_data(char *appname, u8 *img_data,
1322 struct qseecom_load_app_ireq *load_req)
1323{
1324 int ret = -1;
1325 int i = 0, rc = 0;
1326 const struct firmware *fw_entry = NULL;
1327 char fw_name[MAX_APP_NAME_SIZE];
1328 u8 *img_data_ptr = img_data;
1329 struct elf32_hdr *ehdr;
1330 int num_images = 0;
1331
1332 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1333 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1334 if (rc) {
1335 ret = -EIO;
1336 goto err;
1337 }
1338 load_req->img_len = fw_entry->size;
1339 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1340 img_data_ptr = img_data_ptr + fw_entry->size;
1341 load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
1342 ehdr = (struct elf32_hdr *)fw_entry->data;
1343 num_images = ehdr->e_phnum;
1344 release_firmware(fw_entry);
1345 for (i = 0; i < num_images; i++) {
1346 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1347 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1348 if (ret) {
1349 pr_err("Failed to locate blob %s\n", fw_name);
1350 goto err;
1351 }
1352 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1353 img_data_ptr = img_data_ptr + fw_entry->size;
1354 load_req->img_len += fw_entry->size;
1355 release_firmware(fw_entry);
1356 }
1357 load_req->phy_addr = virt_to_phys(img_data);
1358 return ret;
1359err:
1360 release_firmware(fw_entry);
1361 return ret;
1362}
1363
1364static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
1365{
1366 int ret = -1;
1367 uint32_t fw_size = 0;
1368 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1369 struct qseecom_command_scm_resp resp;
1370 u8 *img_data = NULL;
1371
1372 if (__qseecom_get_fw_size(appname, &fw_size))
1373 return -EIO;
1374
1375 img_data = kzalloc(fw_size, GFP_KERNEL);
1376 if (!img_data) {
1377 pr_err("Failied to allocate memory for copying image data\n");
1378 return -ENOMEM;
1379 }
1380 ret = __qseecom_get_fw_data(appname, img_data, &load_req);
1381 if (ret) {
1382 kzfree(img_data);
1383 return -EIO;
1384 }
1385
1386 /* Populate the remaining parameters */
1387 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
1388 memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001389 ret = qsee_vote_for_clock(CLK_SFPB);
1390 if (ret) {
1391 kzfree(img_data);
1392 pr_warning("Unable to vote for SFPB clock");
Mona Hossain60f9fb02012-11-05 13:51:50 -08001393 return -EIO;
1394 }
1395
Mona Hossaind44a3842012-10-15 09:41:35 -07001396 /* SCM_CALL to load the image */
1397 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1398 sizeof(struct qseecom_load_app_ireq),
1399 &resp, sizeof(resp));
1400 kzfree(img_data);
1401 if (ret) {
1402 pr_err("scm_call to load failed : ret %d\n", ret);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001403 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001404 return -EIO;
1405 }
1406
1407 switch (resp.result) {
1408 case QSEOS_RESULT_SUCCESS:
1409 ret = resp.data;
1410 break;
1411 case QSEOS_RESULT_INCOMPLETE:
1412 ret = __qseecom_process_incomplete_cmd(data, &resp);
1413 if (ret)
1414 pr_err("process_incomplete_cmd FAILED\n");
1415 else
1416 ret = resp.data;
1417 break;
1418 case QSEOS_RESULT_FAILURE:
1419 pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
1420 break;
1421 default:
1422 pr_err("scm call return unknown response %d\n", resp.result);
1423 ret = -EINVAL;
1424 break;
1425 }
Mona Hossain60f9fb02012-11-05 13:51:50 -08001426 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001427
Mona Hossaind44a3842012-10-15 09:41:35 -07001428 return ret;
1429}
1430
Mona Hossain05c73562012-10-29 17:49:01 -07001431static int qseecom_load_commonlib_image(void)
1432{
1433 int32_t ret = 0;
1434 uint32_t fw_size = 0;
1435 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1436 struct qseecom_command_scm_resp resp;
1437 u8 *img_data = NULL;
1438
1439 if (__qseecom_get_fw_size("commonlib", &fw_size))
1440 return -EIO;
1441
1442 img_data = kzalloc(fw_size, GFP_KERNEL);
1443 if (!img_data) {
1444 pr_err("Mem allocation for lib image data failed\n");
1445 return -ENOMEM;
1446 }
1447 ret = __qseecom_get_fw_data("commonlib", img_data, &load_req);
1448 if (ret) {
1449 kzfree(img_data);
1450 return -EIO;
1451 }
1452 /* Populate the remaining parameters */
1453 load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
1454 /* SCM_CALL to load the image */
1455 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1456 sizeof(struct qseecom_load_lib_image_ireq),
1457 &resp, sizeof(resp));
1458 kzfree(img_data);
1459 if (ret) {
1460 pr_err("scm_call to load failed : ret %d\n", ret);
1461 ret = -EIO;
1462 } else {
1463 switch (resp.result) {
1464 case QSEOS_RESULT_SUCCESS:
1465 break;
1466 case QSEOS_RESULT_FAILURE:
1467 pr_err("scm call failed w/response result%d\n",
1468 resp.result);
1469 ret = -EINVAL;
1470 break;
1471 default:
1472 pr_err("scm call return unknown response %d\n",
1473 resp.result);
1474 ret = -EINVAL;
1475 break;
1476 }
1477 }
1478 return ret;
1479}
1480
1481static int qseecom_unload_commonlib_image(void)
1482{
1483 int ret = -EINVAL;
1484 struct qseecom_unload_lib_image_ireq unload_req = {0};
1485 struct qseecom_command_scm_resp resp;
1486
1487 /* Populate the remaining parameters */
1488 unload_req.qsee_cmd_id = QSEOS_UNLOAD_SERV_IMAGE_COMMAND;
1489 /* SCM_CALL to load the image */
1490 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &unload_req,
1491 sizeof(struct qseecom_unload_lib_image_ireq),
1492 &resp, sizeof(resp));
1493 if (ret) {
1494 pr_err("scm_call to unload lib failed : ret %d\n", ret);
1495 ret = -EIO;
1496 } else {
1497 switch (resp.result) {
1498 case QSEOS_RESULT_SUCCESS:
1499 break;
1500 case QSEOS_RESULT_FAILURE:
1501 pr_err("scm fail resp.result QSEOS_RESULT FAILURE\n");
1502 break;
1503 default:
1504 pr_err("scm call return unknown response %d\n",
1505 resp.result);
1506 ret = -EINVAL;
1507 break;
1508 }
1509 }
1510 return ret;
1511}
1512
Mona Hossaind44a3842012-10-15 09:41:35 -07001513int qseecom_start_app(struct qseecom_handle **handle,
1514 char *app_name, uint32_t size)
1515{
Mona Hossain05c73562012-10-29 17:49:01 -07001516 int32_t ret = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07001517 unsigned long flags = 0;
1518 struct qseecom_dev_handle *data = NULL;
1519 struct qseecom_check_app_ireq app_ireq;
1520 struct qseecom_registered_app_list *entry = NULL;
1521 struct qseecom_registered_kclient_list *kclient_entry = NULL;
1522 bool found_app = false;
1523 uint32_t len;
1524 ion_phys_addr_t pa;
1525
1526 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1527 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1528 return -EINVAL;
1529 }
1530
Mona Hossain05c73562012-10-29 17:49:01 -07001531 if (qseecom.qsee_version > QSEEE_VERSION_00) {
1532 mutex_lock(&app_access_lock);
1533 if (qseecom.commonlib_loaded == false) {
1534 ret = qseecom_load_commonlib_image();
1535 if (ret == 0)
1536 qseecom.commonlib_loaded = true;
1537 }
1538 mutex_unlock(&app_access_lock);
1539 }
1540
1541 if (ret)
1542 return -EIO;
1543
Mona Hossaind44a3842012-10-15 09:41:35 -07001544 *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
1545 if (!(*handle)) {
1546 pr_err("failed to allocate memory for kernel client handle\n");
1547 return -ENOMEM;
1548 }
1549
1550 app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1551 memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
1552 ret = __qseecom_check_app_exists(app_ireq);
1553 if (ret < 0)
1554 return -EINVAL;
1555
1556 data = kzalloc(sizeof(*data), GFP_KERNEL);
1557 if (!data) {
1558 pr_err("kmalloc failed\n");
1559 if (ret == 0) {
1560 kfree(*handle);
1561 *handle = NULL;
1562 }
1563 return -ENOMEM;
1564 }
1565 data->abort = 0;
1566 data->service = false;
1567 data->released = false;
1568 data->client.app_id = ret;
1569 data->client.sb_length = size;
1570 data->client.user_virt_sb_base = 0;
1571 data->client.ihandle = NULL;
1572
1573 init_waitqueue_head(&data->abort_wq);
1574 atomic_set(&data->ioctl_count, 0);
1575
1576 data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
1577 ION_HEAP(ION_QSECOM_HEAP_ID), 0);
1578 if (IS_ERR_OR_NULL(data->client.ihandle)) {
1579 pr_err("Ion client could not retrieve the handle\n");
1580 kfree(data);
1581 kfree(*handle);
1582 *handle = NULL;
1583 return -EINVAL;
1584 }
1585
1586 if (ret > 0) {
1587 pr_warn("App id %d for [%s] app exists\n", ret,
1588 (char *)app_ireq.app_name);
1589 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1590 list_for_each_entry(entry,
1591 &qseecom.registered_app_list_head, list){
1592 if (entry->app_id == ret) {
1593 entry->ref_cnt++;
1594 found_app = true;
1595 break;
1596 }
1597 }
1598 spin_unlock_irqrestore(
1599 &qseecom.registered_app_list_lock, flags);
1600 if (!found_app)
1601 pr_warn("App_id %d [%s] was loaded but not registered\n",
1602 ret, (char *)app_ireq.app_name);
1603 } else {
1604 /* load the app and get the app_id */
1605 pr_debug("%s: Loading app for the first time'\n",
1606 qseecom.pdev->init_name);
1607 mutex_lock(&app_access_lock);
1608 ret = __qseecom_load_fw(data, app_name);
1609 mutex_unlock(&app_access_lock);
1610
1611 if (ret < 0) {
1612 kfree(*handle);
1613 *handle = NULL;
1614 return ret;
1615 }
1616 data->client.app_id = ret;
1617 }
1618 if (!found_app) {
1619 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1620 if (!entry) {
1621 pr_err("kmalloc failed\n");
1622 return -ENOMEM;
1623 }
1624 entry->app_id = ret;
1625 entry->ref_cnt = 1;
1626
1627 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1628 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
1629 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
1630 flags);
1631 }
1632
1633 /* Get the physical address of the ION BUF */
1634 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
1635 /* Populate the structure for sending scm call to load image */
1636 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
1637 data->client.ihandle);
1638 data->client.sb_phys = pa;
1639 (*handle)->dev = (void *)data;
1640 (*handle)->sbuf = (unsigned char *)data->client.sb_virt;
1641 (*handle)->sbuf_len = data->client.sb_length;
1642
1643 kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
1644 if (!kclient_entry) {
1645 pr_err("kmalloc failed\n");
1646 return -ENOMEM;
1647 }
1648 kclient_entry->handle = *handle;
1649
1650 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1651 list_add_tail(&kclient_entry->list,
1652 &qseecom.registered_kclient_list_head);
1653 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1654
1655 return 0;
1656}
1657EXPORT_SYMBOL(qseecom_start_app);
1658
1659int qseecom_shutdown_app(struct qseecom_handle **handle)
1660{
1661 int ret = -EINVAL;
1662 struct qseecom_dev_handle *data =
1663 (struct qseecom_dev_handle *) ((*handle)->dev);
1664 struct qseecom_registered_kclient_list *kclient = NULL;
1665 unsigned long flags = 0;
1666 bool found_handle = false;
1667
1668 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1669 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1670 return -EINVAL;
1671 }
1672 if (*handle == NULL) {
1673 pr_err("Handle is not initialized\n");
1674 return -EINVAL;
1675 }
1676
1677 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1678 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
1679 list) {
1680 if (kclient->handle == (*handle)) {
1681 list_del(&kclient->list);
1682 found_handle = true;
1683 break;
1684 }
1685 }
1686 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1687 if (!found_handle)
1688 pr_err("Unable to find the handle, exiting\n");
1689 else
1690 ret = qseecom_unload_app(data);
1691 if (ret == 0) {
1692 kzfree(data);
1693 kzfree(*handle);
1694 kzfree(kclient);
1695 *handle = NULL;
1696 }
1697 return ret;
1698}
1699EXPORT_SYMBOL(qseecom_shutdown_app);
1700
1701int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
1702 uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
1703{
1704 int ret = 0;
1705 struct qseecom_send_cmd_req req = {0, 0, 0, 0};
1706 struct qseecom_dev_handle *data;
1707
1708 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1709 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1710 return -EINVAL;
1711 }
1712
1713 if (handle == NULL) {
1714 pr_err("Handle is not initialized\n");
1715 return -EINVAL;
1716 }
1717 data = handle->dev;
1718
1719 req.cmd_req_len = sbuf_len;
1720 req.resp_len = rbuf_len;
1721 req.cmd_req_buf = send_buf;
1722 req.resp_buf = resp_buf;
1723
1724 mutex_lock(&app_access_lock);
1725 atomic_inc(&data->ioctl_count);
1726
1727 ret = __qseecom_send_cmd(data, &req);
1728
1729 atomic_dec(&data->ioctl_count);
1730 mutex_unlock(&app_access_lock);
1731
1732 if (ret)
1733 return ret;
1734
1735 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1736 req.resp_len, req.resp_buf);
1737 return ret;
1738}
1739EXPORT_SYMBOL(qseecom_send_command);
1740
Mona Hossain91a8fc92012-11-07 19:58:30 -08001741int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
1742{
1743 if ((handle == NULL) || (handle->dev == NULL)) {
1744 pr_err("No valid kernel client\n");
1745 return -EINVAL;
1746 }
1747 if (high)
1748 return qsee_vote_for_clock(CLK_DFAB);
1749 else {
1750 qsee_disable_clock_vote(CLK_DFAB);
1751 return 0;
1752 }
1753}
1754EXPORT_SYMBOL(qseecom_set_bandwidth);
1755
Mona Hossain2892b6b2012-02-17 13:53:11 -08001756static int qseecom_send_resp(void)
1757{
1758 qseecom.send_resp_flag = 1;
1759 wake_up_interruptible(&qseecom.send_resp_wq);
1760 return 0;
1761}
1762
1763static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1764 void __user *argp)
1765{
1766 struct qseecom_qseos_version_req req;
1767
1768 if (copy_from_user(&req, argp, sizeof(req))) {
1769 pr_err("copy_from_user failed");
1770 return -EINVAL;
1771 }
1772 req.qseos_version = qseecom.qseos_version;
1773 if (copy_to_user(argp, &req, sizeof(req))) {
1774 pr_err("copy_to_user failed");
1775 return -EINVAL;
1776 }
1777 return 0;
1778}
1779
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001780static int qsee_vote_for_clock(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001781{
1782 int ret = 0;
1783
1784 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001785 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001786
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001787 switch (clk_type) {
1788 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001789 mutex_lock(&qsee_bw_mutex);
1790 if (!qsee_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001791 if (qsee_sfpb_bw_count > 0)
1792 ret = msm_bus_scale_client_update_request(
1793 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001794 else {
1795 if (ce_core_src_clk != NULL)
1796 clk_set_rate(ce_core_src_clk,
1797 QSEE_CE_CLK_100MHZ);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001798 ret = msm_bus_scale_client_update_request(
1799 qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001800 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001801 if (ret)
1802 pr_err("DFAB Bandwidth req failed (%d)\n",
1803 ret);
1804 else
1805 qsee_bw_count++;
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001806 } else {
1807 qsee_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001808 }
1809 mutex_unlock(&qsee_bw_mutex);
1810 break;
1811 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001812 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001813 if (!qsee_sfpb_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001814 if (qsee_bw_count > 0)
1815 ret = msm_bus_scale_client_update_request(
1816 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001817 else {
1818 if (ce_core_src_clk != NULL)
1819 clk_set_rate(ce_core_src_clk,
1820 QSEE_CE_CLK_100MHZ);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001821 ret = msm_bus_scale_client_update_request(
1822 qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001823 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001824
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001825 if (ret)
1826 pr_err("SFPB Bandwidth req failed (%d)\n",
1827 ret);
1828 else
1829 qsee_sfpb_bw_count++;
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001830 } else {
1831 qsee_sfpb_bw_count++;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001832 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001833 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001834 break;
1835 default:
1836 pr_err("Clock type not defined\n");
1837 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001838 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001839 return ret;
1840}
1841
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001842static void qsee_disable_clock_vote(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001843{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001844 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001845
Mona Hossain2892b6b2012-02-17 13:53:11 -08001846 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001847 return;
1848
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001849 switch (clk_type) {
1850 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001851 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001852 if (qsee_bw_count == 0) {
1853 pr_err("Client error.Extra call to disable DFAB clk\n");
1854 mutex_unlock(&qsee_bw_mutex);
1855 return;
1856 }
1857
1858 if ((qsee_bw_count > 0) && (qsee_bw_count-- == 1)) {
1859 if (qsee_sfpb_bw_count > 0)
1860 ret = msm_bus_scale_client_update_request(
1861 qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001862 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001863 ret = msm_bus_scale_client_update_request(
1864 qsee_perf_client, 0);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001865 if (ce_core_src_clk != NULL)
1866 clk_set_rate(ce_core_src_clk,
1867 QSEE_CE_CLK_50MHZ);
1868 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001869 if (ret)
1870 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001871 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001872 }
1873 mutex_unlock(&qsee_bw_mutex);
1874 break;
1875 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001876 mutex_lock(&qsee_bw_mutex);
1877 if (qsee_sfpb_bw_count == 0) {
1878 pr_err("Client error.Extra call to disable SFPB clk\n");
1879 mutex_unlock(&qsee_bw_mutex);
1880 return;
1881 }
1882 if ((qsee_sfpb_bw_count > 0) && (qsee_sfpb_bw_count-- == 1)) {
1883 if (qsee_bw_count > 0)
1884 ret = msm_bus_scale_client_update_request(
1885 qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001886 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001887 ret = msm_bus_scale_client_update_request(
1888 qsee_perf_client, 0);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001889 if (ce_core_src_clk != NULL)
1890 clk_set_rate(ce_core_src_clk,
1891 QSEE_CE_CLK_50MHZ);
1892 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001893 if (ret)
1894 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001895 ret);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001896 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001897 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001898 break;
1899 default:
1900 pr_err("Clock type not defined\n");
1901 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001902 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001903
Mona Hossain2892b6b2012-02-17 13:53:11 -08001904}
1905
Mona Hossain5ab9d772012-04-11 21:00:40 -07001906static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1907 void __user *argp)
1908{
1909 struct ion_handle *ihandle; /* Ion handle */
1910 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001911 int ret;
1912 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001913 ion_phys_addr_t pa = 0;
1914 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001915 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001916 struct qseecom_load_app_ireq load_req;
1917 struct qseecom_command_scm_resp resp;
1918
1919 /* Copy the relevant information needed for loading the image */
1920 if (__copy_from_user(&load_img_req,
1921 (void __user *)argp,
1922 sizeof(struct qseecom_load_img_req))) {
1923 pr_err("copy_from_user failed\n");
1924 return -EFAULT;
1925 }
1926
1927 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001928 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001929 load_img_req.ifd_data_fd);
1930 if (IS_ERR_OR_NULL(ihandle)) {
1931 pr_err("Ion client could not retrieve the handle\n");
1932 return -ENOMEM;
1933 }
1934
1935 /* Get the physical address of the ION BUF */
1936 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1937
1938 /* Populate the structure for sending scm call to load image */
1939 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1940 load_req.mdt_len = load_img_req.mdt_len;
1941 load_req.img_len = load_img_req.img_len;
1942 load_req.phy_addr = pa;
1943
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001944 /* SCM_CALL tied to Core0 */
1945 mask = CPU_MASK_CPU0;
1946 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1947 if (set_cpu_ret) {
1948 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1949 set_cpu_ret);
1950 ret = -EFAULT;
1951 goto qseecom_load_external_elf_set_cpu_err;
1952 }
1953
Mona Hossain5ab9d772012-04-11 21:00:40 -07001954 /* SCM_CALL to load the external elf */
1955 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1956 sizeof(struct qseecom_load_app_ireq),
1957 &resp, sizeof(resp));
1958 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001959 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07001960 ret);
1961 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001962 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001963 }
1964
1965 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1966 ret = __qseecom_process_incomplete_cmd(data, &resp);
1967 if (ret)
1968 pr_err("process_incomplete_cmd failed err: %d\n",
1969 ret);
1970 } else {
1971 if (resp.result != QSEOS_RESULT_SUCCESS) {
1972 pr_err("scm_call to load image failed resp.result =%d\n",
1973 resp.result);
1974 ret = -EFAULT;
1975 }
1976 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001977
1978qseecom_load_external_elf_scm_err:
1979 /* Restore the CPU mask */
1980 mask = CPU_MASK_ALL;
1981 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1982 if (set_cpu_ret) {
1983 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1984 set_cpu_ret);
1985 ret = -EFAULT;
1986 }
1987
1988qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07001989 /* Deallocate the handle */
1990 if (!IS_ERR_OR_NULL(ihandle))
1991 ion_free(qseecom.ion_clnt, ihandle);
1992
1993 return ret;
1994}
1995
1996static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1997{
1998 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001999 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002000 struct qseecom_command_scm_resp resp;
2001 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002002 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002003
2004 /* Populate the structure for sending scm call to unload image */
2005 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002006
2007 /* SCM_CALL tied to Core0 */
2008 mask = CPU_MASK_CPU0;
2009 ret = set_cpus_allowed_ptr(current, &mask);
2010 if (ret) {
2011 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
2012 ret);
2013 return -EFAULT;
2014 }
2015
Mona Hossain5ab9d772012-04-11 21:00:40 -07002016 /* SCM_CALL to unload the external elf */
2017 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
2018 sizeof(struct qseecom_unload_app_ireq),
2019 &resp, sizeof(resp));
2020 if (ret) {
2021 pr_err("scm_call to unload failed : ret %d\n",
2022 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002023 ret = -EFAULT;
2024 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002025 }
2026 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2027 ret = __qseecom_process_incomplete_cmd(data, &resp);
2028 if (ret)
2029 pr_err("process_incomplete_cmd fail err: %d\n",
2030 ret);
2031 } else {
2032 if (resp.result != QSEOS_RESULT_SUCCESS) {
2033 pr_err("scm_call to unload image failed resp.result =%d\n",
2034 resp.result);
2035 ret = -EFAULT;
2036 }
2037 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002038
2039qseecom_unload_external_elf_scm_err:
2040 /* Restore the CPU mask */
2041 mask = CPU_MASK_ALL;
2042 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2043 if (set_cpu_ret) {
2044 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2045 set_cpu_ret);
2046 ret = -EFAULT;
2047 }
2048
Mona Hossain5ab9d772012-04-11 21:00:40 -07002049 return ret;
2050}
Mona Hossain2892b6b2012-02-17 13:53:11 -08002051
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002052static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
2053 void __user *argp)
2054{
2055
2056 int32_t ret;
2057 struct qseecom_qseos_app_load_query query_req;
2058 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002059 struct qseecom_registered_app_list *entry = NULL;
2060 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002061
2062 /* Copy the relevant information needed for loading the image */
2063 if (__copy_from_user(&query_req,
2064 (void __user *)argp,
2065 sizeof(struct qseecom_qseos_app_load_query))) {
2066 pr_err("copy_from_user failed\n");
2067 return -EFAULT;
2068 }
2069
2070 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
2071 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
2072
2073 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002074
2075 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002076 pr_err(" scm call to check if app is loaded failed");
2077 return ret; /* scm call failed */
2078 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002079 pr_warn("App id %d (%s) already exists\n", ret,
2080 (char *)(req.app_name));
2081 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
2082 list_for_each_entry(entry,
2083 &qseecom.registered_app_list_head, list){
2084 if (entry->app_id == ret) {
2085 entry->ref_cnt++;
2086 break;
2087 }
2088 }
2089 spin_unlock_irqrestore(
2090 &qseecom.registered_app_list_lock, flags);
2091 data->client.app_id = ret;
2092 query_req.app_id = ret;
2093
2094 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
2095 pr_err("copy_to_user failed\n");
2096 return -EFAULT;
2097 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002098 return -EEXIST; /* app already loaded */
2099 } else {
2100 return 0; /* app not loaded */
2101 }
2102}
2103
Mona Hossain2892b6b2012-02-17 13:53:11 -08002104static long qseecom_ioctl(struct file *file, unsigned cmd,
2105 unsigned long arg)
2106{
2107 int ret = 0;
2108 struct qseecom_dev_handle *data = file->private_data;
2109 void __user *argp = (void __user *) arg;
2110
2111 if (data->abort) {
2112 pr_err("Aborting qseecom driver\n");
2113 return -ENODEV;
2114 }
2115
2116 switch (cmd) {
2117 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
2118 pr_debug("ioctl register_listener_req()\n");
2119 atomic_inc(&data->ioctl_count);
2120 ret = qseecom_register_listener(data, argp);
2121 atomic_dec(&data->ioctl_count);
2122 wake_up_all(&data->abort_wq);
2123 if (ret)
2124 pr_err("failed qseecom_register_listener: %d\n", ret);
2125 break;
2126 }
2127 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
2128 pr_debug("ioctl unregister_listener_req()\n");
2129 atomic_inc(&data->ioctl_count);
2130 ret = qseecom_unregister_listener(data);
2131 atomic_dec(&data->ioctl_count);
2132 wake_up_all(&data->abort_wq);
2133 if (ret)
2134 pr_err("failed qseecom_unregister_listener: %d\n", ret);
2135 break;
2136 }
2137 case QSEECOM_IOCTL_SEND_CMD_REQ: {
2138 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002139 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002140 atomic_inc(&data->ioctl_count);
2141 ret = qseecom_send_cmd(data, argp);
2142 atomic_dec(&data->ioctl_count);
2143 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002144 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002145 if (ret)
2146 pr_err("failed qseecom_send_cmd: %d\n", ret);
2147 break;
2148 }
2149 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
2150 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002151 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002152 atomic_inc(&data->ioctl_count);
2153 ret = qseecom_send_modfd_cmd(data, argp);
2154 atomic_dec(&data->ioctl_count);
2155 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002156 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002157 if (ret)
2158 pr_err("failed qseecom_send_cmd: %d\n", ret);
2159 break;
2160 }
2161 case QSEECOM_IOCTL_RECEIVE_REQ: {
2162 atomic_inc(&data->ioctl_count);
2163 ret = qseecom_receive_req(data);
2164 atomic_dec(&data->ioctl_count);
2165 wake_up_all(&data->abort_wq);
2166 if (ret)
2167 pr_err("failed qseecom_receive_req: %d\n", ret);
2168 break;
2169 }
2170 case QSEECOM_IOCTL_SEND_RESP_REQ: {
2171 atomic_inc(&data->ioctl_count);
2172 ret = qseecom_send_resp();
2173 atomic_dec(&data->ioctl_count);
2174 wake_up_all(&data->abort_wq);
2175 if (ret)
2176 pr_err("failed qseecom_send_resp: %d\n", ret);
2177 break;
2178 }
2179 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
2180 ret = qseecom_set_client_mem_param(data, argp);
2181 if (ret)
2182 pr_err("failed Qqseecom_set_mem_param request: %d\n",
2183 ret);
2184 break;
2185 }
2186 case QSEECOM_IOCTL_LOAD_APP_REQ: {
2187 mutex_lock(&app_access_lock);
2188 atomic_inc(&data->ioctl_count);
Mona Hossain05c73562012-10-29 17:49:01 -07002189 if (qseecom.qsee_version > QSEEE_VERSION_00) {
2190 if (qseecom.commonlib_loaded == false) {
2191 ret = qseecom_load_commonlib_image();
2192 if (ret == 0)
2193 qseecom.commonlib_loaded = true;
2194 }
2195 }
2196 if (ret == 0)
2197 ret = qseecom_load_app(data, argp);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002198 atomic_dec(&data->ioctl_count);
2199 mutex_unlock(&app_access_lock);
2200 if (ret)
2201 pr_err("failed load_app request: %d\n", ret);
2202 break;
2203 }
2204 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
2205 mutex_lock(&app_access_lock);
2206 atomic_inc(&data->ioctl_count);
2207 ret = qseecom_unload_app(data);
2208 atomic_dec(&data->ioctl_count);
2209 mutex_unlock(&app_access_lock);
2210 if (ret)
2211 pr_err("failed unload_app request: %d\n", ret);
2212 break;
2213 }
2214 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
2215 atomic_inc(&data->ioctl_count);
2216 ret = qseecom_get_qseos_version(data, argp);
2217 if (ret)
2218 pr_err("qseecom_get_qseos_version: %d\n", ret);
2219 atomic_dec(&data->ioctl_count);
2220 break;
2221 }
2222 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
2223 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002224 ret = qsee_vote_for_clock(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002225 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002226 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002227 atomic_dec(&data->ioctl_count);
2228 break;
2229 }
2230 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
2231 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002232 qsee_disable_clock_vote(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002233 atomic_dec(&data->ioctl_count);
2234 break;
2235 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07002236 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
2237 data->released = true;
2238 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2239 pr_err("Loading External elf image unsupported in rev 0x13\n");
2240 ret = -EINVAL;
2241 break;
2242 }
2243 mutex_lock(&app_access_lock);
2244 atomic_inc(&data->ioctl_count);
2245 ret = qseecom_load_external_elf(data, argp);
2246 atomic_dec(&data->ioctl_count);
2247 mutex_unlock(&app_access_lock);
2248 if (ret)
2249 pr_err("failed load_external_elf request: %d\n", ret);
2250 break;
2251 }
2252 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
2253 data->released = true;
2254 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2255 pr_err("Unloading External elf image unsupported in rev 0x13\n");
2256 ret = -EINVAL;
2257 break;
2258 }
2259 mutex_lock(&app_access_lock);
2260 atomic_inc(&data->ioctl_count);
2261 ret = qseecom_unload_external_elf(data);
2262 atomic_dec(&data->ioctl_count);
2263 mutex_unlock(&app_access_lock);
2264 if (ret)
2265 pr_err("failed unload_app request: %d\n", ret);
2266 break;
2267 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002268 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
2269 mutex_lock(&app_access_lock);
2270 atomic_inc(&data->ioctl_count);
2271 ret = qseecom_query_app_loaded(data, argp);
2272 atomic_dec(&data->ioctl_count);
2273 mutex_unlock(&app_access_lock);
2274 break;
2275 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002276 default:
2277 return -EINVAL;
2278 }
2279 return ret;
2280}
2281
2282static int qseecom_open(struct inode *inode, struct file *file)
2283{
2284 int ret = 0;
2285 struct qseecom_dev_handle *data;
2286
2287 data = kzalloc(sizeof(*data), GFP_KERNEL);
2288 if (!data) {
2289 pr_err("kmalloc failed\n");
2290 return -ENOMEM;
2291 }
2292 file->private_data = data;
2293 data->abort = 0;
2294 data->service = false;
2295 data->released = false;
2296 init_waitqueue_head(&data->abort_wq);
2297 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002298 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2299 int pil_error;
2300 mutex_lock(&pil_access_lock);
2301 if (pil_ref_cnt == 0) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002302 pil = subsystem_get("tzapps");
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002303 if (IS_ERR(pil)) {
2304 pr_err("Playready PIL image load failed\n");
2305 pil_error = PTR_ERR(pil);
2306 pil = NULL;
2307 pr_debug("tzapps image load FAILED\n");
2308 mutex_unlock(&pil_access_lock);
2309 return pil_error;
2310 }
2311 }
2312 pil_ref_cnt++;
2313 mutex_unlock(&pil_access_lock);
2314 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002315 return ret;
2316}
2317
2318static int qseecom_release(struct inode *inode, struct file *file)
2319{
2320 struct qseecom_dev_handle *data = file->private_data;
2321 int ret = 0;
2322
2323 if (data->released == false) {
2324 pr_warn("data->released == false\n");
2325 if (data->service)
2326 ret = qseecom_unregister_listener(data);
2327 else
2328 ret = qseecom_unload_app(data);
2329 if (ret) {
2330 pr_err("Close failed\n");
2331 return ret;
2332 }
2333 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002334 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2335 mutex_lock(&pil_access_lock);
2336 if (pil_ref_cnt == 1)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002337 subsystem_put(pil);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002338 pil_ref_cnt--;
2339 mutex_unlock(&pil_access_lock);
2340 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002341 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002342
Mona Hossain2892b6b2012-02-17 13:53:11 -08002343 return ret;
2344}
2345
Mona Hossain2892b6b2012-02-17 13:53:11 -08002346static const struct file_operations qseecom_fops = {
2347 .owner = THIS_MODULE,
2348 .unlocked_ioctl = qseecom_ioctl,
2349 .open = qseecom_open,
2350 .release = qseecom_release
2351};
2352
Mona Hossaind39e33b2012-11-05 13:36:40 -08002353static int __qseecom_enable_clk(void)
2354{
2355 int rc = 0;
2356
2357 /* Enable CE core clk */
2358 rc = clk_prepare_enable(ce_core_clk);
2359 if (rc) {
2360 pr_err("Unable to enable/prepare CE core clk\n");
2361 return -EIO;
2362 } else {
2363 /* Enable CE clk */
2364 rc = clk_prepare_enable(ce_clk);
2365 if (rc) {
2366 pr_err("Unable to enable/prepare CE iface clk\n");
2367 clk_disable_unprepare(ce_core_clk);
2368 return -EIO;
2369 } else {
2370 /* Enable AXI clk */
2371 rc = clk_prepare_enable(ce_bus_clk);
2372 if (rc) {
2373 pr_err("Unable to enable/prepare CE iface clk\n");
2374 clk_disable_unprepare(ce_core_clk);
2375 clk_disable_unprepare(ce_clk);
2376 return -EIO;
2377 }
2378 }
2379 }
2380 return rc;
2381}
2382
2383static void __qseecom_disable_clk(void)
2384{
2385 if (ce_clk != NULL)
2386 clk_disable_unprepare(ce_clk);
2387 if (ce_core_clk != NULL)
2388 clk_disable_unprepare(ce_core_clk);
2389 if (ce_bus_clk != NULL)
2390 clk_disable_unprepare(ce_bus_clk);
2391}
2392
2393static int __qseecom_init_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002394{
2395 int rc = 0;
2396 struct device *pdev;
2397
2398 pdev = qseecom.pdev;
2399 /* Get CE3 src core clk. */
2400 ce_core_src_clk = clk_get(pdev, "core_clk_src");
2401 if (!IS_ERR(ce_core_src_clk)) {
Mona Hossaind39e33b2012-11-05 13:36:40 -08002402 /* Set the core src clk @50Mhz */
2403 rc = clk_set_rate(ce_core_src_clk, QSEE_CE_CLK_50MHZ);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002404 if (rc) {
2405 clk_put(ce_core_src_clk);
2406 pr_err("Unable to set the core src clk @100Mhz.\n");
Mona Hossaind39e33b2012-11-05 13:36:40 -08002407 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002408 }
2409 } else {
2410 pr_warn("Unable to get CE core src clk, set to NULL\n");
2411 ce_core_src_clk = NULL;
2412 }
2413
2414 /* Get CE core clk */
2415 ce_core_clk = clk_get(pdev, "core_clk");
2416 if (IS_ERR(ce_core_clk)) {
2417 rc = PTR_ERR(ce_core_clk);
2418 pr_err("Unable to get CE core clk\n");
2419 if (ce_core_src_clk != NULL)
2420 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002421 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002422 }
2423
2424 /* Get CE Interface clk */
2425 ce_clk = clk_get(pdev, "iface_clk");
2426 if (IS_ERR(ce_clk)) {
2427 rc = PTR_ERR(ce_clk);
2428 pr_err("Unable to get CE interface clk\n");
2429 if (ce_core_src_clk != NULL)
2430 clk_put(ce_core_src_clk);
2431 clk_put(ce_core_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002432 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002433 }
2434
2435 /* Get CE AXI clk */
2436 ce_bus_clk = clk_get(pdev, "bus_clk");
2437 if (IS_ERR(ce_bus_clk)) {
2438 rc = PTR_ERR(ce_bus_clk);
2439 pr_err("Unable to get CE BUS interface clk\n");
2440 if (ce_core_src_clk != NULL)
2441 clk_put(ce_core_src_clk);
2442 clk_put(ce_core_clk);
2443 clk_put(ce_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002444 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002445 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002446 return rc;
2447}
2448
Mona Hossaind39e33b2012-11-05 13:36:40 -08002449static void __qseecom_deinit_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002450{
Mona Hossaind39e33b2012-11-05 13:36:40 -08002451 if (ce_clk != NULL) {
2452 clk_put(ce_clk);
2453 ce_clk = NULL;
2454 }
2455 if (ce_core_clk != NULL) {
2456 clk_put(ce_core_clk);
2457 ce_clk = NULL;
2458 }
2459 if (ce_bus_clk != NULL) {
2460 clk_put(ce_bus_clk);
2461 ce_clk = NULL;
2462 }
2463 if (ce_core_src_clk != NULL) {
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002464 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002465 ce_core_src_clk = NULL;
2466 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002467}
2468
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002469static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002470{
2471 int rc;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002472 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002473 struct device *class_dev;
2474 char qsee_not_legacy = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07002475 struct msm_bus_scale_pdata *qseecom_platform_support = NULL;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002476 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
2477
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002478 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002479 qsee_perf_client = 0;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002480 qsee_sfpb_bw_count = 0;
2481
2482 ce_core_clk = NULL;
2483 ce_clk = NULL;
2484 ce_core_src_clk = NULL;
2485 ce_bus_clk = NULL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002486
Mona Hossain2892b6b2012-02-17 13:53:11 -08002487 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
2488 if (rc < 0) {
2489 pr_err("alloc_chrdev_region failed %d\n", rc);
2490 return rc;
2491 }
2492
2493 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
2494 if (IS_ERR(driver_class)) {
2495 rc = -ENOMEM;
2496 pr_err("class_create failed %d\n", rc);
2497 goto unregister_chrdev_region;
2498 }
2499
2500 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
2501 QSEECOM_DEV);
2502 if (!class_dev) {
2503 pr_err("class_device_create failed %d\n", rc);
2504 rc = -ENOMEM;
2505 goto class_destroy;
2506 }
2507
2508 cdev_init(&qseecom_cdev, &qseecom_fops);
2509 qseecom_cdev.owner = THIS_MODULE;
2510
2511 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
2512 if (rc < 0) {
2513 pr_err("cdev_add failed %d\n", rc);
2514 goto err;
2515 }
2516
2517 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
2518 spin_lock_init(&qseecom.registered_listener_list_lock);
2519 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
2520 spin_lock_init(&qseecom.registered_app_list_lock);
Mona Hossaind44a3842012-10-15 09:41:35 -07002521 INIT_LIST_HEAD(&qseecom.registered_kclient_list_head);
2522 spin_lock_init(&qseecom.registered_kclient_list_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002523 init_waitqueue_head(&qseecom.send_resp_wq);
2524 qseecom.send_resp_flag = 0;
2525
2526 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
2527 &qsee_not_legacy, sizeof(qsee_not_legacy));
2528 if (rc) {
Mona Hossain05c73562012-10-29 17:49:01 -07002529 pr_err("Failed to retrieve QSEOS version information %d\n", rc);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002530 goto err;
2531 }
Mona Hossain05c73562012-10-29 17:49:01 -07002532 if (qsee_not_legacy) {
2533 uint32_t feature = 10;
2534
2535 qseecom.qsee_version = QSEEE_VERSION_00;
2536 rc = scm_call(6, 3, &feature, sizeof(feature),
2537 &qseecom.qsee_version, sizeof(qseecom.qsee_version));
2538 if (rc) {
2539 pr_err("Failed to get QSEE version info %d\n", rc);
2540 goto err;
2541 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002542 qseecom.qseos_version = QSEOS_VERSION_14;
Mona Hossain05c73562012-10-29 17:49:01 -07002543 } else {
Mona Hossain2892b6b2012-02-17 13:53:11 -08002544 qseecom.qseos_version = QSEOS_VERSION_13;
Mona Hossain05c73562012-10-29 17:49:01 -07002545 qseecom.qsee_version = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002546 pil = NULL;
2547 pil_ref_cnt = 0;
2548 }
Mona Hossain05c73562012-10-29 17:49:01 -07002549 qseecom.commonlib_loaded = false;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002550 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002551 /* Create ION msm client */
Mona Hossaind44a3842012-10-15 09:41:35 -07002552 qseecom.ion_clnt = msm_ion_client_create(-1, "qseecom-kernel");
Mona Hossain2892b6b2012-02-17 13:53:11 -08002553 if (qseecom.ion_clnt == NULL) {
2554 pr_err("Ion client cannot be created\n");
2555 rc = -ENOMEM;
2556 goto err;
2557 }
2558
2559 /* register client for bus scaling */
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002560 if (pdev->dev.of_node) {
2561 ret = __qseecom_init_clk();
2562 if (ret)
2563 goto err;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002564 ret = __qseecom_enable_clk();
2565 if (ret) {
2566 __qseecom_deinit_clk();
2567 goto err;
2568 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002569 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2570 msm_bus_cl_get_pdata(pdev);
Mona Hossain5b76a622012-11-15 20:09:08 -08002571 if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
2572 struct resource *resource = NULL;
2573 struct qsee_apps_region_info_ireq req;
2574 struct qseecom_command_scm_resp resp;
2575
2576 resource = platform_get_resource_byname(pdev,
2577 IORESOURCE_MEM, "secapp-region");
2578 if (resource) {
2579 req.qsee_cmd_id = QSEOS_APP_REGION_NOTIFICATION;
2580 req.addr = resource->start;
2581 req.size = resource_size(resource);
2582 pr_warn("secure app region addr=0x%x size=0x%x",
2583 req.addr, req.size);
2584 } else {
2585 pr_err("Fail to get secure app region info\n");
2586 rc = -EINVAL;
2587 goto err;
2588 }
2589 rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
2590 &resp, sizeof(resp));
2591 if (rc) {
2592 pr_err("Failed to send secapp region info %d\n",
2593 rc);
2594 goto err;
2595 }
2596 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002597 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002598 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2599 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002600 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002601
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002602 qsee_perf_client = msm_bus_scale_register_client(
2603 qseecom_platform_support);
2604
2605 if (!qsee_perf_client)
2606 pr_err("Unable to register bus client\n");
2607 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002608err:
2609 device_destroy(driver_class, qseecom_device_no);
2610class_destroy:
2611 class_destroy(driver_class);
2612unregister_chrdev_region:
2613 unregister_chrdev_region(qseecom_device_no, 1);
2614 return rc;
2615}
2616
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002617static int __devinit qseecom_remove(struct platform_device *pdev)
2618{
Mona Hossaind44a3842012-10-15 09:41:35 -07002619 struct qseecom_registered_kclient_list *kclient = NULL;
2620 unsigned long flags = 0;
2621 int ret = 0;
2622
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002623 if (pdev->dev.platform_data != NULL)
2624 msm_bus_scale_unregister_client(qsee_perf_client);
Mona Hossaind44a3842012-10-15 09:41:35 -07002625
2626 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2627 kclient = list_entry((&qseecom.registered_kclient_list_head)->next,
2628 struct qseecom_registered_kclient_list, list);
2629 if (list_empty(&kclient->list)) {
2630 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2631 flags);
2632 return 0;
2633 }
2634 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
2635 list) {
2636 if (kclient)
2637 list_del(&kclient->list);
2638 break;
2639 }
2640 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
2641
2642
2643 while (kclient->handle != NULL) {
2644 ret = qseecom_unload_app(kclient->handle->dev);
2645 if (ret == 0) {
2646 kzfree(kclient->handle->dev);
2647 kzfree(kclient->handle);
2648 kzfree(kclient);
2649 }
2650 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2651 kclient = list_entry(
2652 (&qseecom.registered_kclient_list_head)->next,
2653 struct qseecom_registered_kclient_list, list);
2654 if (list_empty(&kclient->list)) {
2655 spin_unlock_irqrestore(
2656 &qseecom.registered_kclient_list_lock, flags);
2657 return 0;
2658 }
2659 list_for_each_entry(kclient,
2660 &qseecom.registered_kclient_list_head, list) {
2661 if (kclient)
2662 list_del(&kclient->list);
2663 break;
2664 }
2665 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2666 flags);
2667 if (!kclient) {
2668 ret = 0;
2669 break;
2670 }
2671 }
Mona Hossain05c73562012-10-29 17:49:01 -07002672 if (qseecom.qseos_version > QSEEE_VERSION_00)
2673 qseecom_unload_commonlib_image();
Mona Hossaind39e33b2012-11-05 13:36:40 -08002674
2675 if (qsee_perf_client)
2676 msm_bus_scale_client_update_request(qsee_perf_client, 0);
2677 /* register client for bus scaling */
2678 if (pdev->dev.of_node) {
2679 __qseecom_disable_clk();
2680 __qseecom_deinit_clk();
2681 }
Mona Hossaind44a3842012-10-15 09:41:35 -07002682 return ret;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002683};
2684
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002685static struct of_device_id qseecom_match[] = {
2686 {
2687 .compatible = "qcom,qseecom",
2688 },
2689 {}
2690};
2691
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002692static struct platform_driver qseecom_plat_driver = {
2693 .probe = qseecom_probe,
2694 .remove = qseecom_remove,
2695 .driver = {
2696 .name = "qseecom",
2697 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002698 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002699 },
2700};
2701
2702static int __devinit qseecom_init(void)
2703{
2704 return platform_driver_register(&qseecom_plat_driver);
2705}
2706
2707static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002708{
Mona Hossain2892b6b2012-02-17 13:53:11 -08002709 device_destroy(driver_class, qseecom_device_no);
2710 class_destroy(driver_class);
2711 unregister_chrdev_region(qseecom_device_no, 1);
2712 ion_client_destroy(qseecom.ion_clnt);
2713}
2714
2715MODULE_LICENSE("GPL v2");
2716MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2717
2718module_init(qseecom_init);
2719module_exit(qseecom_exit);