blob: 77a412f18705d16055b4dec26326b2606b5af96b [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 *
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08005 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Mona Hossain2892b6b2012-02-17 13:53:11 -08006 *
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 Hossain803c3d92012-11-21 13:33:42 -080045#include <mach/qseecomi.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080046#include "qseecom_legacy.h"
Mona Hossaind44a3842012-10-15 09:41:35 -070047#include "qseecom_kernel.h"
Mona Hossain2892b6b2012-02-17 13:53:11 -080048
49#define QSEECOM_DEV "qseecom"
50#define QSEOS_VERSION_13 0x13
51#define QSEOS_VERSION_14 0x14
Mona Hossain05c73562012-10-29 17:49:01 -070052#define QSEEE_VERSION_00 0x400000
Mona Hossain5b76a622012-11-15 20:09:08 -080053#define QSEE_VERSION_01 0x401000
54#define QSEE_VERSION_02 0x402000
55
Mona Hossain05c73562012-10-29 17:49:01 -070056
57#define QSEOS_CHECK_VERSION_CMD 0x00001803
Mona Hossain2892b6b2012-02-17 13:53:11 -080058
Mona Hossaind39e33b2012-11-05 13:36:40 -080059#define QSEE_CE_CLK_100MHZ 100000000
Mona Hossaind39e33b2012-11-05 13:36:40 -080060
Mona Hossain13dd8922013-01-03 06:11:09 -080061#define QSEECOM_MAX_SG_ENTRY 512
Mona Hossainf1f2ed62012-11-15 19:51:33 -080062
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070063enum qseecom_clk_definitions {
64 CLK_DFAB = 0,
65 CLK_SFPB,
66};
67
Mona Hossain2892b6b2012-02-17 13:53:11 -080068static struct class *driver_class;
69static dev_t qseecom_device_no;
70static struct cdev qseecom_cdev;
71
72/* Data structures used in legacy support */
73static void *pil;
74static uint32_t pil_ref_cnt;
75static DEFINE_MUTEX(pil_access_lock);
76
Mona Hossain2892b6b2012-02-17 13:53:11 -080077static DEFINE_MUTEX(qsee_bw_mutex);
78static DEFINE_MUTEX(app_access_lock);
79
80static int qsee_bw_count;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070081static int qsee_sfpb_bw_count;
Mona Hossain2892b6b2012-02-17 13:53:11 -080082static uint32_t qsee_perf_client;
83
84struct qseecom_registered_listener_list {
85 struct list_head list;
86 struct qseecom_register_listener_req svc;
87 u8 *sb_reg_req;
88 u8 *sb_virt;
89 s32 sb_phys;
90 size_t sb_length;
91 struct ion_handle *ihandle; /* Retrieve phy addr */
92
93 wait_queue_head_t rcv_req_wq;
94 int rcv_req_flag;
95};
96
97struct qseecom_registered_app_list {
98 struct list_head list;
99 u32 app_id;
100 u32 ref_cnt;
101};
102
Mona Hossaind44a3842012-10-15 09:41:35 -0700103struct qseecom_registered_kclient_list {
104 struct list_head list;
105 struct qseecom_handle *handle;
106};
107
Mona Hossain2892b6b2012-02-17 13:53:11 -0800108struct qseecom_control {
109 struct ion_client *ion_clnt; /* Ion client */
110 struct list_head registered_listener_list_head;
111 spinlock_t registered_listener_list_lock;
112
113 struct list_head registered_app_list_head;
114 spinlock_t registered_app_list_lock;
115
Mona Hossaind44a3842012-10-15 09:41:35 -0700116 struct list_head registered_kclient_list_head;
117 spinlock_t registered_kclient_list_lock;
118
Mona Hossain2892b6b2012-02-17 13:53:11 -0800119 wait_queue_head_t send_resp_wq;
120 int send_resp_flag;
121
122 uint32_t qseos_version;
Mona Hossain05c73562012-10-29 17:49:01 -0700123 uint32_t qsee_version;
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700124 struct device *pdev;
Mona Hossain05c73562012-10-29 17:49:01 -0700125 bool commonlib_loaded;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800126};
127
128struct qseecom_client_handle {
129 u32 app_id;
130 u8 *sb_virt;
131 s32 sb_phys;
132 uint32_t user_virt_sb_base;
133 size_t sb_length;
134 struct ion_handle *ihandle; /* Retrieve phy addr */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800135 bool perf_enabled;
136 bool fast_load_enabled;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800137};
138
139struct qseecom_listener_handle {
140 u32 id;
141};
142
143static struct qseecom_control qseecom;
144
145struct qseecom_dev_handle {
146 bool service;
147 union {
148 struct qseecom_client_handle client;
149 struct qseecom_listener_handle listener;
150 };
151 bool released;
152 int abort;
153 wait_queue_head_t abort_wq;
154 atomic_t ioctl_count;
155};
156
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700157struct clk *ce_core_clk;
158struct clk *ce_clk;
159struct clk *ce_core_src_clk;
160struct clk *ce_bus_clk;
161
Mona Hossainf1f2ed62012-11-15 19:51:33 -0800162struct qseecom_sg_entry {
163 uint32_t phys_addr;
164 uint32_t len;
165};
166
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700167/* Function proto types */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800168static int qsee_vote_for_clock(struct qseecom_dev_handle *, int32_t);
169static void qsee_disable_clock_vote(struct qseecom_dev_handle *, int32_t);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700170
Mona Hossain2892b6b2012-02-17 13:53:11 -0800171static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800172 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800173{
174 struct qseecom_registered_listener_list *ptr;
175 int unique = 1;
176 unsigned long flags;
177
178 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
179 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800180 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800181 pr_err("Service id: %u is already registered\n",
182 ptr->svc.listener_id);
183 unique = 0;
184 break;
185 }
186 }
187 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
188 return unique;
189}
190
191static struct qseecom_registered_listener_list *__qseecom_find_svc(
192 int32_t listener_id)
193{
194 struct qseecom_registered_listener_list *entry = NULL;
195 unsigned long flags;
196
197 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
198 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
199 {
200 if (entry->svc.listener_id == listener_id)
201 break;
202 }
203 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
204 return entry;
205}
206
207static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
208 struct qseecom_dev_handle *handle,
209 struct qseecom_register_listener_req *listener)
210{
211 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800212 struct qseecom_register_listener_ireq req;
213 struct qseecom_command_scm_resp resp;
214 ion_phys_addr_t pa;
215
216 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800217 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
218 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800219 if (svc->ihandle == NULL) {
220 pr_err("Ion client could not retrieve the handle\n");
221 return -ENOMEM;
222 }
223
224 /* Get the physical address of the ION BUF */
225 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
226
227 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700228 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800229 svc->sb_phys = pa;
230
231 if (qseecom.qseos_version == QSEOS_VERSION_14) {
232 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
233 req.listener_id = svc->svc.listener_id;
234 req.sb_len = svc->sb_length;
235 req.sb_ptr = (void *)svc->sb_phys;
236
237 resp.result = QSEOS_RESULT_INCOMPLETE;
238
239 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
240 sizeof(req), &resp, sizeof(resp));
241 if (ret) {
242 pr_err("qseecom_scm_call failed with err: %d\n", ret);
243 return -EINVAL;
244 }
245
246 if (resp.result != QSEOS_RESULT_SUCCESS) {
247 pr_err("Error SB registration req: resp.result = %d\n",
248 resp.result);
249 return -EPERM;
250 }
251 } else {
252 struct qseecom_command cmd;
253 struct qseecom_response resp;
254 struct qse_pr_init_sb_req_s sb_init_req;
255 struct qse_pr_init_sb_rsp_s sb_init_rsp;
256
257 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
258 sizeof(sb_init_rsp)), GFP_KERNEL);
259
260 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
261 sb_init_req.listener_id = svc->svc.listener_id;
262 sb_init_req.sb_len = svc->sb_length;
263 sb_init_req.sb_ptr = svc->sb_phys;
264
265 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
266
267 /* It will always be a new cmd from this method */
268 cmd.cmd_type = TZ_SCHED_CMD_NEW;
269 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
270 cmd.sb_in_cmd_len = sizeof(sb_init_req);
271
272 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
273
274 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
275 , &resp, sizeof(resp));
276
277 if (ret) {
278 pr_err("qseecom_scm_call failed with err: %d\n", ret);
279 return -EINVAL;
280 }
281
282 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
283 pr_err("SB registration fail resp.cmd_status %d\n",
284 resp.cmd_status);
285 return -EINVAL;
286 }
287 memset(svc->sb_virt, 0, svc->sb_length);
288 }
289 return 0;
290}
291
292static int qseecom_register_listener(struct qseecom_dev_handle *data,
293 void __user *argp)
294{
295 int ret = 0;
296 unsigned long flags;
297 struct qseecom_register_listener_req rcvd_lstnr;
298 struct qseecom_registered_listener_list *new_entry;
299
300 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
301 if (ret) {
302 pr_err("copy_from_user failed\n");
303 return ret;
304 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800305 data->listener.id = 0;
306 data->service = true;
307 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800308 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800309 data->released = true;
310 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800311 }
312
313 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
314 if (!new_entry) {
315 pr_err("kmalloc failed\n");
316 return -ENOMEM;
317 }
318 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
319 new_entry->rcv_req_flag = 0;
320
321 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
322 new_entry->sb_length = rcvd_lstnr.sb_size;
323 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
324 pr_err("qseecom_set_sb_memoryfailed\n");
325 kzfree(new_entry);
326 return -ENOMEM;
327 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800328
Mona Hossain2892b6b2012-02-17 13:53:11 -0800329 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800330 init_waitqueue_head(&new_entry->rcv_req_wq);
331
332 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
333 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
334 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800335
Mona Hossain2892b6b2012-02-17 13:53:11 -0800336 return ret;
337}
338
339static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
340{
341 int ret = 0;
342 unsigned long flags;
343 uint32_t unmap_mem = 0;
344 struct qseecom_register_listener_ireq req;
345 struct qseecom_registered_listener_list *ptr_svc = NULL;
346 struct qseecom_command_scm_resp resp;
347 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
348
349 if (qseecom.qseos_version == QSEOS_VERSION_14) {
350 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
351 req.listener_id = data->listener.id;
352 resp.result = QSEOS_RESULT_INCOMPLETE;
353
354 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
355 sizeof(req), &resp, sizeof(resp));
356 if (ret) {
357 pr_err("qseecom_scm_call failed with err: %d\n", ret);
358 return ret;
359 }
360
361 if (resp.result != QSEOS_RESULT_SUCCESS) {
362 pr_err("SB deregistartion: result=%d\n", resp.result);
363 return -EPERM;
364 }
365 } else {
366 struct qse_pr_init_sb_req_s sb_init_req;
367 struct qseecom_command cmd;
368 struct qseecom_response resp;
369 struct qseecom_registered_listener_list *svc;
370
371 svc = __qseecom_find_svc(data->listener.id);
372 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
373 sb_init_req.listener_id = data->listener.id;
374 sb_init_req.sb_len = 0;
375 sb_init_req.sb_ptr = 0;
376
377 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
378
379 /* It will always be a new cmd from this method */
380 cmd.cmd_type = TZ_SCHED_CMD_NEW;
381 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
382 cmd.sb_in_cmd_len = sizeof(sb_init_req);
383 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
384
385 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
386 &resp, sizeof(resp));
387 if (ret) {
388 pr_err("qseecom_scm_call failed with err: %d\n", ret);
389 return ret;
390 }
391 kzfree(svc->sb_reg_req);
392 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
393 pr_err("Error with SB initialization\n");
394 return -EPERM;
395 }
396 }
397 data->abort = 1;
398 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
399 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
400 list) {
401 if (ptr_svc->svc.listener_id == data->listener.id) {
402 wake_up_all(&ptr_svc->rcv_req_wq);
403 break;
404 }
405 }
406 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
407
408 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700409 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800410 atomic_read(&data->ioctl_count) <= 1)) {
411 pr_err("Interrupted from abort\n");
412 ret = -ERESTARTSYS;
413 break;
414 }
415 }
416
417 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
418 list_for_each_entry(ptr_svc,
419 &qseecom.registered_listener_list_head,
420 list)
421 {
422 if (ptr_svc->svc.listener_id == data->listener.id) {
423 if (ptr_svc->sb_virt) {
424 unmap_mem = 1;
425 ihandle = ptr_svc->ihandle;
426 }
427 list_del(&ptr_svc->list);
428 kzfree(ptr_svc);
429 break;
430 }
431 }
432 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
433
434 /* Unmap the memory */
435 if (unmap_mem) {
436 if (!IS_ERR_OR_NULL(ihandle)) {
437 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
438 ion_free(qseecom.ion_clnt, ihandle);
439 }
440 }
441 data->released = true;
442 return ret;
443}
444
445static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
446 void __user *argp)
447{
448 ion_phys_addr_t pa;
449 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800450 struct qseecom_set_sb_mem_param_req req;
451 uint32_t len;
452
453 /* Copy the relevant information needed for loading the image */
454 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
455 return -EFAULT;
456
Mona Hossain2892b6b2012-02-17 13:53:11 -0800457 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800458 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
459 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800460 if (IS_ERR_OR_NULL(data->client.ihandle)) {
461 pr_err("Ion client could not retrieve the handle\n");
462 return -ENOMEM;
463 }
464 /* Get the physical address of the ION BUF */
465 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
466 /* Populate the structure for sending scm call to load image */
467 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700468 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800469 data->client.sb_phys = pa;
470 data->client.sb_length = req.sb_len;
471 data->client.user_virt_sb_base = req.virt_sb_base;
472 return 0;
473}
474
Mona Hossain2892b6b2012-02-17 13:53:11 -0800475static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
476{
477 int ret;
478 ret = (qseecom.send_resp_flag != 0);
479 return ret || data->abort;
480}
481
482static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
483 struct qseecom_command_scm_resp *resp)
484{
485 int ret = 0;
Mona Hossain0b3a2792013-02-05 17:38:55 -0800486 int rc = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800487 uint32_t lstnr;
488 unsigned long flags;
489 struct qseecom_client_listener_data_irsp send_data_rsp;
490 struct qseecom_registered_listener_list *ptr_svc = NULL;
491
492
493 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
494 lstnr = resp->data;
495 /*
496 * Wake up blocking lsitener service with the lstnr id
497 */
498 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
499 flags);
500 list_for_each_entry(ptr_svc,
501 &qseecom.registered_listener_list_head, list) {
502 if (ptr_svc->svc.listener_id == lstnr) {
503 ptr_svc->rcv_req_flag = 1;
504 wake_up_interruptible(&ptr_svc->rcv_req_wq);
505 break;
506 }
507 }
508 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
509 flags);
510 if (ptr_svc->svc.listener_id != lstnr) {
511 pr_warning("Service requested for does on exist\n");
512 return -ERESTARTSYS;
513 }
514 pr_debug("waking up rcv_req_wq and "
515 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700516 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800517 __qseecom_listener_has_sent_rsp(data))) {
518 pr_warning("Interrupted: exiting send_cmd loop\n");
519 return -ERESTARTSYS;
520 }
521
522 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700523 pr_err("Aborting listener service %d\n",
524 data->listener.id);
Mona Hossain0b3a2792013-02-05 17:38:55 -0800525 rc = -ENODEV;
526 send_data_rsp.status = QSEOS_RESULT_FAILURE;
527 } else {
528 send_data_rsp.status = QSEOS_RESULT_SUCCESS;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800529 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800530
Mona Hossain2892b6b2012-02-17 13:53:11 -0800531 qseecom.send_resp_flag = 0;
532 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
533 send_data_rsp.listener_id = lstnr ;
534
535 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
536 (const void *)&send_data_rsp,
537 sizeof(send_data_rsp), resp,
538 sizeof(*resp));
539 if (ret) {
540 pr_err("qseecom_scm_call failed with err: %d\n", ret);
541 return ret;
542 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700543 if (resp->result == QSEOS_RESULT_FAILURE) {
544 pr_err("Response result %d not supported\n",
545 resp->result);
546 return -EINVAL;
547 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800548 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800549 if (rc)
550 return rc;
551
Mona Hossain2892b6b2012-02-17 13:53:11 -0800552 return ret;
553}
554
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700555static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
556{
557 int32_t ret;
558 struct qseecom_command_scm_resp resp;
559
560 /* SCM_CALL to check if app_id for the mentioned app exists */
561 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
562 sizeof(struct qseecom_check_app_ireq),
563 &resp, sizeof(resp));
564 if (ret) {
565 pr_err("scm_call to check if app is already loaded failed\n");
566 return -EINVAL;
567 }
568
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700569 if (resp.result == QSEOS_RESULT_FAILURE) {
570 return 0;
571 } else {
572 switch (resp.resp_type) {
573 /*qsee returned listener type response */
574 case QSEOS_LISTENER_ID:
575 pr_err("resp type is of listener type instead of app");
576 return -EINVAL;
577 break;
578 case QSEOS_APP_ID:
579 return resp.data;
580 default:
581 pr_err("invalid resp type (%d) from qsee",
582 resp.resp_type);
583 return -ENODEV;
584 break;
585 }
586 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700587}
588
Mona Hossain2892b6b2012-02-17 13:53:11 -0800589static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
590{
591 struct qseecom_registered_app_list *entry = NULL;
592 unsigned long flags = 0;
593 u32 app_id = 0;
594 struct ion_handle *ihandle; /* Ion handle */
595 struct qseecom_load_img_req load_img_req;
596 int32_t ret;
597 ion_phys_addr_t pa = 0;
598 uint32_t len;
599 struct qseecom_command_scm_resp resp;
Mona Hossain436b75f2012-11-20 17:10:40 -0800600 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700601 struct qseecom_load_app_ireq load_req;
602
Mona Hossain2892b6b2012-02-17 13:53:11 -0800603 /* Copy the relevant information needed for loading the image */
604 if (__copy_from_user(&load_img_req,
605 (void __user *)argp,
606 sizeof(struct qseecom_load_img_req))) {
607 pr_err("copy_from_user failed\n");
608 return -EFAULT;
609 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700610 /* Vote for the SFPB clock */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800611 ret = qsee_vote_for_clock(data, CLK_SFPB);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700612 if (ret)
613 pr_warning("Unable to vote for SFPB clock");
Mona Hossain436b75f2012-11-20 17:10:40 -0800614 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
615 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800616
Mona Hossain436b75f2012-11-20 17:10:40 -0800617 ret = __qseecom_check_app_exists(req);
618 if (ret < 0)
619 return ret;
620 else
621 app_id = ret;
622
623 if (app_id) {
624 pr_warn("App id %d (%s) already exists\n", app_id,
625 (char *)(req.app_name));
626 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
627 list_for_each_entry(entry,
628 &qseecom.registered_app_list_head, list){
629 if (entry->app_id == app_id) {
630 entry->ref_cnt++;
631 break;
632 }
633 }
634 spin_unlock_irqrestore(
635 &qseecom.registered_app_list_lock, flags);
636 } else {
637 pr_warn("App (%s) does'nt exist, loading apps for first time\n",
Mona Hossaind44a3842012-10-15 09:41:35 -0700638 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800639 /* Get the handle of the shared fd */
640 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800641 load_img_req.ifd_data_fd);
Mona Hossain436b75f2012-11-20 17:10:40 -0800642 if (IS_ERR_OR_NULL(ihandle)) {
643 pr_err("Ion client could not retrieve the handle\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800644 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800645 return -ENOMEM;
646 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800647
Mona Hossain436b75f2012-11-20 17:10:40 -0800648 /* Get the physical address of the ION BUF */
649 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800650
Mona Hossain436b75f2012-11-20 17:10:40 -0800651 /* Populate the structure for sending scm call to load image */
652 memcpy(load_req.app_name, load_img_req.img_name,
653 MAX_APP_NAME_SIZE);
654 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
655 load_req.mdt_len = load_img_req.mdt_len;
656 load_req.img_len = load_img_req.img_len;
657 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800658
Mona Hossain436b75f2012-11-20 17:10:40 -0800659 /* SCM_CALL to load the app and get the app_id back */
660 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700661 sizeof(struct qseecom_load_app_ireq),
662 &resp, sizeof(resp));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700663 if (ret) {
Mona Hossain436b75f2012-11-20 17:10:40 -0800664 pr_err("scm_call to load app failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -0800665 if (!IS_ERR_OR_NULL(ihandle))
666 ion_free(qseecom.ion_clnt, ihandle);
667 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800668 return -EINVAL;
669 }
670
671 if (resp.result == QSEOS_RESULT_FAILURE) {
672 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700673 if (!IS_ERR_OR_NULL(ihandle))
674 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800675 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800676 return -EFAULT;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700677 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700678
Mona Hossain436b75f2012-11-20 17:10:40 -0800679 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
680 ret = __qseecom_process_incomplete_cmd(data, &resp);
681 if (ret) {
682 pr_err("process_incomplete_cmd failed err: %d\n",
683 ret);
684 if (!IS_ERR_OR_NULL(ihandle))
685 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800686 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800687 return ret;
688 }
689 }
690
691 if (resp.result != QSEOS_RESULT_SUCCESS) {
692 pr_err("scm_call failed resp.result unknown, %d\n",
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700693 resp.result);
Mona Hossain436b75f2012-11-20 17:10:40 -0800694 if (!IS_ERR_OR_NULL(ihandle))
695 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800696 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800697 return -EFAULT;
698 }
699
700 app_id = resp.data;
701
702 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
703 if (!entry) {
704 pr_err("kmalloc failed\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800705 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800706 return -ENOMEM;
707 }
708 entry->app_id = app_id;
709 entry->ref_cnt = 1;
710
711 /* Deallocate the handle */
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700712 if (!IS_ERR_OR_NULL(ihandle))
713 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700714
Mona Hossain436b75f2012-11-20 17:10:40 -0800715 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
716 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
717 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
718 flags);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700719
Mona Hossain436b75f2012-11-20 17:10:40 -0800720 pr_warn("App with id %d (%s) now loaded\n", app_id,
Mona Hossaind44a3842012-10-15 09:41:35 -0700721 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800722 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800723 data->client.app_id = app_id;
724 load_img_req.app_id = app_id;
725 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
726 pr_err("copy_to_user failed\n");
727 kzfree(entry);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800728 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800729 return -EFAULT;
730 }
Mona Hossain04d3fac2012-12-03 10:10:37 -0800731 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800732 return 0;
733}
734
735static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
736{
737 wake_up_all(&qseecom.send_resp_wq);
738 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700739 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800740 atomic_read(&data->ioctl_count) <= 1)) {
741 pr_err("Interrupted from abort\n");
742 return -ERESTARTSYS;
743 break;
744 }
745 }
746 /* Set unload app */
747 return 1;
748}
749
750static int qseecom_unload_app(struct qseecom_dev_handle *data)
751{
752 unsigned long flags;
753 int ret = 0;
754 struct qseecom_command_scm_resp resp;
755 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700756 bool unload = false;
757 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800758
Mona Hossain1fb538f2012-08-30 16:19:38 -0700759 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
760 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800761 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
762 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
763 list) {
764 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700765 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800766 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700767 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800768 break;
769 } else {
770 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700771 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700772 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800773 break;
774 }
775 }
776 }
777 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
778 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700779 if (found_app == false) {
780 pr_err("Cannot find app with id = %d\n",
781 data->client.app_id);
782 return -EINVAL;
783 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800784 }
785
786 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
787 struct qseecom_unload_app_ireq req;
788
Mona Hossain340dba82012-08-07 19:54:46 -0700789 __qseecom_cleanup_app(data);
790 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
791 list_del(&ptr_app->list);
792 kzfree(ptr_app);
793 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
794 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800795 /* Populate the structure for sending scm call to load image */
796 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
797 req.app_id = data->client.app_id;
798
799 /* SCM_CALL to unload the app */
800 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
801 sizeof(struct qseecom_unload_app_ireq),
802 &resp, sizeof(resp));
803 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700804 pr_err("scm_call to unload app (id = %d) failed\n",
805 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800806 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700807 } else {
808 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800809 }
810 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
811 ret = __qseecom_process_incomplete_cmd(data, &resp);
812 if (ret) {
813 pr_err("process_incomplete_cmd fail err: %d\n",
814 ret);
815 return ret;
816 }
817 }
818 }
819
820 if (qseecom.qseos_version == QSEOS_VERSION_13) {
821 data->abort = 1;
822 wake_up_all(&qseecom.send_resp_wq);
823 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700824 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800825 atomic_read(&data->ioctl_count) <= 0)) {
826 pr_err("Interrupted from abort\n");
827 ret = -ERESTARTSYS;
828 break;
829 }
830 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800831 }
Mona Hossain340dba82012-08-07 19:54:46 -0700832 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
833 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
834 ion_free(qseecom.ion_clnt, data->client.ihandle);
835 data->client.ihandle = NULL;
836 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800837 data->released = true;
838 return ret;
839}
840
841static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
842 uint32_t virt)
843{
844 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
845}
846
847static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
848 struct qseecom_send_cmd_req *req)
849{
850 int ret = 0;
851 unsigned long flags;
852 u32 reqd_len_sb_in = 0;
853 struct qseecom_command cmd;
854 struct qseecom_response resp;
855
856
857 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
858 pr_err("cmd buffer or response buffer is null\n");
859 return -EINVAL;
860 }
861
862 if (req->cmd_req_len <= 0 ||
863 req->resp_len <= 0 ||
864 req->cmd_req_len > data->client.sb_length ||
865 req->resp_len > data->client.sb_length) {
866 pr_err("cmd buffer length or "
867 "response buffer length not valid\n");
868 return -EINVAL;
869 }
870
871 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
872 if (reqd_len_sb_in > data->client.sb_length) {
873 pr_debug("Not enough memory to fit cmd_buf and "
874 "resp_buf. Required: %u, Available: %u\n",
875 reqd_len_sb_in, data->client.sb_length);
876 return -ENOMEM;
877 }
878 cmd.cmd_type = TZ_SCHED_CMD_NEW;
879 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
880 cmd.sb_in_cmd_len = req->cmd_req_len;
881
882 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
883 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
884 resp.sb_in_rsp_len = req->resp_len;
885
886 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
887 sizeof(cmd), &resp, sizeof(resp));
888
889 if (ret) {
890 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
891 return ret;
892 }
893
894 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
895 /*
896 * If cmd is incomplete, get the callback cmd out from SB out
897 * and put it on the list
898 */
899 struct qseecom_registered_listener_list *ptr_svc = NULL;
900 /*
901 * We don't know which service can handle the command. so we
902 * wake up all blocking services and let them figure out if
903 * they can handle the given command.
904 */
905 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
906 flags);
907 list_for_each_entry(ptr_svc,
908 &qseecom.registered_listener_list_head, list) {
909 ptr_svc->rcv_req_flag = 1;
910 wake_up_interruptible(&ptr_svc->rcv_req_wq);
911 }
912 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
913 flags);
914
915 pr_debug("waking up rcv_req_wq and "
916 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700917 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800918 __qseecom_listener_has_sent_rsp(data))) {
919 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
920 return -ERESTARTSYS;
921 }
922
923 if (data->abort) {
924 pr_err("Aborting driver\n");
925 return -ENODEV;
926 }
927 qseecom.send_resp_flag = 0;
928 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
929 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
930 sizeof(cmd), &resp, sizeof(resp));
931 if (ret) {
932 pr_err("qseecom_scm_call failed with err: %d\n", ret);
933 return ret;
934 }
935 }
936 return ret;
937}
938
939static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
940 struct qseecom_send_cmd_req *req)
941{
942 int ret = 0;
943 u32 reqd_len_sb_in = 0;
944 struct qseecom_client_send_data_ireq send_data_req;
945 struct qseecom_command_scm_resp resp;
946
947 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
948 pr_err("cmd buffer or response buffer is null\n");
949 return -EINVAL;
950 }
951
952 if (req->cmd_req_len <= 0 ||
953 req->resp_len <= 0 ||
954 req->cmd_req_len > data->client.sb_length ||
955 req->resp_len > data->client.sb_length) {
956 pr_err("cmd buffer length or "
957 "response buffer length not valid\n");
958 return -EINVAL;
959 }
960
961 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
962 if (reqd_len_sb_in > data->client.sb_length) {
963 pr_debug("Not enough memory to fit cmd_buf and "
964 "resp_buf. Required: %u, Available: %u\n",
965 reqd_len_sb_in, data->client.sb_length);
966 return -ENOMEM;
967 }
968
969 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
970 send_data_req.app_id = data->client.app_id;
971 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
972 (uint32_t)req->cmd_req_buf));
973 send_data_req.req_len = req->cmd_req_len;
974 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
975 (uint32_t)req->resp_buf));
976 send_data_req.rsp_len = req->resp_len;
977
978 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
979 sizeof(send_data_req),
980 &resp, sizeof(resp));
981 if (ret) {
982 pr_err("qseecom_scm_call failed with err: %d\n", ret);
983 return ret;
984 }
985
986 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
987 ret = __qseecom_process_incomplete_cmd(data, &resp);
988 if (ret) {
989 pr_err("process_incomplete_cmd failed err: %d\n", ret);
990 return ret;
991 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700992 } else {
993 if (resp.result != QSEOS_RESULT_SUCCESS) {
994 pr_err("Response result %d not supported\n",
995 resp.result);
996 ret = -EINVAL;
997 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800998 }
999 return ret;
1000}
1001
1002
1003static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1004{
1005 int ret = 0;
1006 struct qseecom_send_cmd_req req;
1007
1008 ret = copy_from_user(&req, argp, sizeof(req));
1009 if (ret) {
1010 pr_err("copy_from_user failed\n");
1011 return ret;
1012 }
1013 if (qseecom.qseos_version == QSEOS_VERSION_14)
1014 ret = __qseecom_send_cmd(data, &req);
1015 else
1016 ret = __qseecom_send_cmd_legacy(data, &req);
1017 if (ret)
1018 return ret;
1019
1020 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1021 req.resp_len, req.resp_buf);
1022 return ret;
1023}
1024
1025static int __qseecom_send_cmd_req_clean_up(
1026 struct qseecom_send_modfd_cmd_req *req)
1027{
1028 char *field;
1029 uint32_t *update;
1030 int ret = 0;
1031 int i = 0;
1032
1033 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001034 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001035 field = (char *)req->cmd_req_buf +
1036 req->ifd_data[i].cmd_buf_offset;
1037 update = (uint32_t *) field;
1038 *update = 0;
1039 }
1040 }
1041 return ret;
1042}
1043
1044static int __qseecom_update_with_phy_addr(
1045 struct qseecom_send_modfd_cmd_req *req)
1046{
1047 struct ion_handle *ihandle;
1048 char *field;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001049 int ret = 0;
1050 int i = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001051
1052 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001053 struct sg_table *sg_ptr = NULL;
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001054 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001055 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001056 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001057 req->ifd_data[i].fd);
1058 if (IS_ERR_OR_NULL(ihandle)) {
1059 pr_err("Ion client can't retrieve the handle\n");
1060 return -ENOMEM;
1061 }
1062 field = (char *) req->cmd_req_buf +
1063 req->ifd_data[i].cmd_buf_offset;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001064
1065 /* Populate the cmd data structure with the phys_addr */
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001066 sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
1067 if (sg_ptr == NULL) {
1068 pr_err("IOn client could not retrieve sg table\n");
1069 goto err;
1070 }
1071 if (sg_ptr->nents == 0) {
1072 pr_err("Num of scattered entries is 0\n");
1073 goto err;
1074 }
1075 if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
1076 pr_err("Num of scattered entries");
1077 pr_err(" (%d) is greater than max supported %d\n",
1078 sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
1079 goto err;
1080 }
1081 if (sg_ptr->nents == 1) {
1082 uint32_t *update;
1083 update = (uint32_t *) field;
1084 *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
1085 } else {
1086 struct qseecom_sg_entry *update;
1087 struct scatterlist *sg;
1088 int j = 0;
1089 update = (struct qseecom_sg_entry *) field;
1090 sg = sg_ptr->sgl;
1091 for (j = 0; j < sg_ptr->nents; j++) {
1092 update->phys_addr = (uint32_t)
1093 sg_dma_address(sg);
1094 update->len = (uint32_t)sg->length;
1095 update++;
1096 sg = sg_next(sg);
1097 }
1098 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001099 /* Deallocate the handle */
1100 if (!IS_ERR_OR_NULL(ihandle))
1101 ion_free(qseecom.ion_clnt, ihandle);
1102 }
1103 }
1104 return ret;
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001105err:
1106 if (!IS_ERR_OR_NULL(ihandle))
1107 ion_free(qseecom.ion_clnt, ihandle);
1108 return -ENOMEM;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001109}
1110
1111static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1112 void __user *argp)
1113{
1114 int ret = 0;
1115 struct qseecom_send_modfd_cmd_req req;
1116 struct qseecom_send_cmd_req send_cmd_req;
1117
1118 ret = copy_from_user(&req, argp, sizeof(req));
1119 if (ret) {
1120 pr_err("copy_from_user failed\n");
1121 return ret;
1122 }
1123 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1124 send_cmd_req.cmd_req_len = req.cmd_req_len;
1125 send_cmd_req.resp_buf = req.resp_buf;
1126 send_cmd_req.resp_len = req.resp_len;
1127
1128 ret = __qseecom_update_with_phy_addr(&req);
1129 if (ret)
1130 return ret;
1131 if (qseecom.qseos_version == QSEOS_VERSION_14)
1132 ret = __qseecom_send_cmd(data, &send_cmd_req);
1133 else
1134 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1135 __qseecom_send_cmd_req_clean_up(&req);
1136
1137 if (ret)
1138 return ret;
1139
1140 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1141 req.resp_len, req.resp_buf);
1142 return ret;
1143}
1144
1145static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1146 struct qseecom_registered_listener_list *svc)
1147{
1148 int ret;
1149 ret = (svc->rcv_req_flag != 0);
1150 return ret || data->abort;
1151}
1152
1153static int qseecom_receive_req(struct qseecom_dev_handle *data)
1154{
1155 int ret = 0;
1156 struct qseecom_registered_listener_list *this_lstnr;
1157
1158 this_lstnr = __qseecom_find_svc(data->listener.id);
1159 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001160 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001161 __qseecom_listener_has_rcvd_req(data,
1162 this_lstnr))) {
1163 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1164 /* woken up for different reason */
1165 return -ERESTARTSYS;
1166 }
1167
1168 if (data->abort) {
1169 pr_err("Aborting driver!\n");
1170 return -ENODEV;
1171 }
1172 this_lstnr->rcv_req_flag = 0;
1173 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1174 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1175 break;
1176 } else {
1177 break;
1178 }
1179 }
1180 return ret;
1181}
1182
Mona Hossaind44a3842012-10-15 09:41:35 -07001183static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
1184{
1185 struct elf32_hdr *ehdr;
1186
1187 if (fw_entry->size < sizeof(*ehdr)) {
1188 pr_err("%s: Not big enough to be an elf header\n",
1189 qseecom.pdev->init_name);
1190 return false;
1191 }
1192 ehdr = (struct elf32_hdr *)fw_entry->data;
1193 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
1194 pr_err("%s: Not an elf header\n",
1195 qseecom.pdev->init_name);
1196 return false;
1197 }
1198
1199 if (ehdr->e_phnum == 0) {
1200 pr_err("%s: No loadable segments\n",
1201 qseecom.pdev->init_name);
1202 return false;
1203 }
1204 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
1205 sizeof(struct elf32_hdr) > fw_entry->size) {
1206 pr_err("%s: Program headers not within mdt\n",
1207 qseecom.pdev->init_name);
1208 return false;
1209 }
1210 return true;
1211}
1212
1213static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size)
1214{
1215 int ret = -1;
1216 int i = 0, rc = 0;
1217 const struct firmware *fw_entry = NULL;
1218 struct elf32_phdr *phdr;
1219 char fw_name[MAX_APP_NAME_SIZE];
1220 struct elf32_hdr *ehdr;
1221 int num_images = 0;
1222
1223 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1224 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1225 if (rc) {
1226 pr_err("error with request_firmware\n");
1227 ret = -EIO;
1228 goto err;
1229 }
1230 if (!__qseecom_is_fw_image_valid(fw_entry)) {
1231 ret = -EIO;
1232 goto err;
1233 }
1234 *fw_size = fw_entry->size;
1235 phdr = (struct elf32_phdr *)(fw_entry->data + sizeof(struct elf32_hdr));
1236 ehdr = (struct elf32_hdr *)fw_entry->data;
1237 num_images = ehdr->e_phnum;
1238 release_firmware(fw_entry);
1239 for (i = 0; i < num_images; i++, phdr++) {
1240 memset(fw_name, 0, sizeof(fw_name));
1241 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1242 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1243 if (ret)
1244 goto err;
1245 *fw_size += fw_entry->size;
1246 release_firmware(fw_entry);
1247 }
1248 return ret;
1249err:
1250 if (fw_entry)
1251 release_firmware(fw_entry);
1252 *fw_size = 0;
1253 return ret;
1254}
1255
1256static int __qseecom_get_fw_data(char *appname, u8 *img_data,
1257 struct qseecom_load_app_ireq *load_req)
1258{
1259 int ret = -1;
1260 int i = 0, rc = 0;
1261 const struct firmware *fw_entry = NULL;
1262 char fw_name[MAX_APP_NAME_SIZE];
1263 u8 *img_data_ptr = img_data;
1264 struct elf32_hdr *ehdr;
1265 int num_images = 0;
1266
1267 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1268 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1269 if (rc) {
1270 ret = -EIO;
1271 goto err;
1272 }
1273 load_req->img_len = fw_entry->size;
1274 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1275 img_data_ptr = img_data_ptr + fw_entry->size;
1276 load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
1277 ehdr = (struct elf32_hdr *)fw_entry->data;
1278 num_images = ehdr->e_phnum;
1279 release_firmware(fw_entry);
1280 for (i = 0; i < num_images; i++) {
1281 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1282 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1283 if (ret) {
1284 pr_err("Failed to locate blob %s\n", fw_name);
1285 goto err;
1286 }
1287 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1288 img_data_ptr = img_data_ptr + fw_entry->size;
1289 load_req->img_len += fw_entry->size;
1290 release_firmware(fw_entry);
1291 }
1292 load_req->phy_addr = virt_to_phys(img_data);
1293 return ret;
1294err:
1295 release_firmware(fw_entry);
1296 return ret;
1297}
1298
1299static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
1300{
1301 int ret = -1;
1302 uint32_t fw_size = 0;
1303 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1304 struct qseecom_command_scm_resp resp;
1305 u8 *img_data = NULL;
1306
1307 if (__qseecom_get_fw_size(appname, &fw_size))
1308 return -EIO;
1309
1310 img_data = kzalloc(fw_size, GFP_KERNEL);
1311 if (!img_data) {
1312 pr_err("Failied to allocate memory for copying image data\n");
1313 return -ENOMEM;
1314 }
1315 ret = __qseecom_get_fw_data(appname, img_data, &load_req);
1316 if (ret) {
1317 kzfree(img_data);
1318 return -EIO;
1319 }
1320
1321 /* Populate the remaining parameters */
1322 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
1323 memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001324 ret = qsee_vote_for_clock(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001325 if (ret) {
1326 kzfree(img_data);
1327 pr_warning("Unable to vote for SFPB clock");
Mona Hossain60f9fb02012-11-05 13:51:50 -08001328 return -EIO;
1329 }
1330
Mona Hossaind44a3842012-10-15 09:41:35 -07001331 /* SCM_CALL to load the image */
1332 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1333 sizeof(struct qseecom_load_app_ireq),
1334 &resp, sizeof(resp));
1335 kzfree(img_data);
1336 if (ret) {
1337 pr_err("scm_call to load failed : ret %d\n", ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001338 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001339 return -EIO;
1340 }
1341
1342 switch (resp.result) {
1343 case QSEOS_RESULT_SUCCESS:
1344 ret = resp.data;
1345 break;
1346 case QSEOS_RESULT_INCOMPLETE:
1347 ret = __qseecom_process_incomplete_cmd(data, &resp);
1348 if (ret)
1349 pr_err("process_incomplete_cmd FAILED\n");
1350 else
1351 ret = resp.data;
1352 break;
1353 case QSEOS_RESULT_FAILURE:
1354 pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
1355 break;
1356 default:
1357 pr_err("scm call return unknown response %d\n", resp.result);
1358 ret = -EINVAL;
1359 break;
1360 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08001361 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001362
Mona Hossaind44a3842012-10-15 09:41:35 -07001363 return ret;
1364}
1365
Mona Hossain9498f5e2013-01-23 18:08:45 -08001366static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
Mona Hossain05c73562012-10-29 17:49:01 -07001367{
1368 int32_t ret = 0;
1369 uint32_t fw_size = 0;
1370 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1371 struct qseecom_command_scm_resp resp;
1372 u8 *img_data = NULL;
1373
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001374 if (__qseecom_get_fw_size("cmnlib", &fw_size))
Mona Hossain05c73562012-10-29 17:49:01 -07001375 return -EIO;
1376
1377 img_data = kzalloc(fw_size, GFP_KERNEL);
1378 if (!img_data) {
1379 pr_err("Mem allocation for lib image data failed\n");
1380 return -ENOMEM;
1381 }
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001382 ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
Mona Hossain05c73562012-10-29 17:49:01 -07001383 if (ret) {
1384 kzfree(img_data);
1385 return -EIO;
1386 }
1387 /* Populate the remaining parameters */
1388 load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
Mona Hossain6311d572013-03-01 15:54:02 -08001389 /* Vote for the SFPB clock */
1390 ret = qsee_vote_for_clock(data, CLK_SFPB);
1391 if (ret) {
1392 pr_err("Unable to vote for SFPB clock: ret = %d", ret);
1393 kzfree(img_data);
1394 return -EIO;
1395 }
1396
Mona Hossain05c73562012-10-29 17:49:01 -07001397 /* SCM_CALL to load the image */
1398 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1399 sizeof(struct qseecom_load_lib_image_ireq),
1400 &resp, sizeof(resp));
Mona Hossain05c73562012-10-29 17:49:01 -07001401 if (ret) {
1402 pr_err("scm_call to load failed : ret %d\n", ret);
1403 ret = -EIO;
1404 } else {
1405 switch (resp.result) {
1406 case QSEOS_RESULT_SUCCESS:
1407 break;
1408 case QSEOS_RESULT_FAILURE:
1409 pr_err("scm call failed w/response result%d\n",
1410 resp.result);
1411 ret = -EINVAL;
1412 break;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001413 case QSEOS_RESULT_INCOMPLETE:
1414 ret = __qseecom_process_incomplete_cmd(data, &resp);
1415 if (ret)
1416 pr_err("process_incomplete_cmd failed err: %d\n",
1417 ret);
1418 break;
Mona Hossain05c73562012-10-29 17:49:01 -07001419 default:
1420 pr_err("scm call return unknown response %d\n",
1421 resp.result);
1422 ret = -EINVAL;
1423 break;
1424 }
1425 }
Hariprasad Dhalinarasimha1a81ca32013-01-31 18:32:32 -08001426 kzfree(img_data);
Mona Hossain6311d572013-03-01 15:54:02 -08001427 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain05c73562012-10-29 17:49:01 -07001428 return ret;
1429}
1430
1431static int qseecom_unload_commonlib_image(void)
1432{
1433 int ret = -EINVAL;
1434 struct qseecom_unload_lib_image_ireq unload_req = {0};
1435 struct qseecom_command_scm_resp resp;
1436
1437 /* Populate the remaining parameters */
1438 unload_req.qsee_cmd_id = QSEOS_UNLOAD_SERV_IMAGE_COMMAND;
1439 /* SCM_CALL to load the image */
1440 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &unload_req,
1441 sizeof(struct qseecom_unload_lib_image_ireq),
1442 &resp, sizeof(resp));
1443 if (ret) {
1444 pr_err("scm_call to unload lib failed : ret %d\n", ret);
1445 ret = -EIO;
1446 } else {
1447 switch (resp.result) {
1448 case QSEOS_RESULT_SUCCESS:
1449 break;
1450 case QSEOS_RESULT_FAILURE:
1451 pr_err("scm fail resp.result QSEOS_RESULT FAILURE\n");
1452 break;
1453 default:
1454 pr_err("scm call return unknown response %d\n",
1455 resp.result);
1456 ret = -EINVAL;
1457 break;
1458 }
1459 }
1460 return ret;
1461}
1462
Mona Hossaind44a3842012-10-15 09:41:35 -07001463int qseecom_start_app(struct qseecom_handle **handle,
1464 char *app_name, uint32_t size)
1465{
Mona Hossain05c73562012-10-29 17:49:01 -07001466 int32_t ret = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07001467 unsigned long flags = 0;
1468 struct qseecom_dev_handle *data = NULL;
1469 struct qseecom_check_app_ireq app_ireq;
1470 struct qseecom_registered_app_list *entry = NULL;
1471 struct qseecom_registered_kclient_list *kclient_entry = NULL;
1472 bool found_app = false;
1473 uint32_t len;
1474 ion_phys_addr_t pa;
1475
1476 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1477 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1478 return -EINVAL;
1479 }
1480
Mona Hossain823f9882012-11-23 14:42:20 -08001481 *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
1482 if (!(*handle)) {
1483 pr_err("failed to allocate memory for kernel client handle\n");
1484 return -ENOMEM;
1485 }
1486
Mona Hossaind44a3842012-10-15 09:41:35 -07001487 data = kzalloc(sizeof(*data), GFP_KERNEL);
1488 if (!data) {
1489 pr_err("kmalloc failed\n");
1490 if (ret == 0) {
1491 kfree(*handle);
1492 *handle = NULL;
1493 }
1494 return -ENOMEM;
1495 }
1496 data->abort = 0;
1497 data->service = false;
1498 data->released = false;
1499 data->client.app_id = ret;
1500 data->client.sb_length = size;
1501 data->client.user_virt_sb_base = 0;
1502 data->client.ihandle = NULL;
1503
1504 init_waitqueue_head(&data->abort_wq);
1505 atomic_set(&data->ioctl_count, 0);
1506
1507 data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
1508 ION_HEAP(ION_QSECOM_HEAP_ID), 0);
1509 if (IS_ERR_OR_NULL(data->client.ihandle)) {
1510 pr_err("Ion client could not retrieve the handle\n");
1511 kfree(data);
1512 kfree(*handle);
1513 *handle = NULL;
1514 return -EINVAL;
1515 }
1516
Mona Hossain9498f5e2013-01-23 18:08:45 -08001517 if (qseecom.qsee_version > QSEEE_VERSION_00) {
1518 mutex_lock(&app_access_lock);
1519 if (qseecom.commonlib_loaded == false) {
1520 ret = qseecom_load_commonlib_image(data);
1521 if (ret == 0)
1522 qseecom.commonlib_loaded = true;
1523 }
1524 mutex_unlock(&app_access_lock);
1525 }
1526
1527 if (ret) {
1528 pr_err("Failed to loadd commonlib image\n");
1529 kfree(data);
1530 kfree(*handle);
1531 *handle = NULL;
1532 return -EIO;
1533 }
1534
1535 app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1536 memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
1537 ret = __qseecom_check_app_exists(app_ireq);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001538 if (ret < 0) {
1539 kzfree(data);
1540 kfree(*handle);
1541 *handle = NULL;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001542 return -EINVAL;
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001543 }
Mona Hossain9498f5e2013-01-23 18:08:45 -08001544
Mona Hossaind44a3842012-10-15 09:41:35 -07001545 if (ret > 0) {
1546 pr_warn("App id %d for [%s] app exists\n", ret,
1547 (char *)app_ireq.app_name);
1548 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1549 list_for_each_entry(entry,
1550 &qseecom.registered_app_list_head, list){
1551 if (entry->app_id == ret) {
1552 entry->ref_cnt++;
1553 found_app = true;
1554 break;
1555 }
1556 }
1557 spin_unlock_irqrestore(
1558 &qseecom.registered_app_list_lock, flags);
1559 if (!found_app)
1560 pr_warn("App_id %d [%s] was loaded but not registered\n",
1561 ret, (char *)app_ireq.app_name);
1562 } else {
1563 /* load the app and get the app_id */
1564 pr_debug("%s: Loading app for the first time'\n",
1565 qseecom.pdev->init_name);
1566 mutex_lock(&app_access_lock);
1567 ret = __qseecom_load_fw(data, app_name);
1568 mutex_unlock(&app_access_lock);
1569
1570 if (ret < 0) {
1571 kfree(*handle);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001572 kfree(data);
Mona Hossaind44a3842012-10-15 09:41:35 -07001573 *handle = NULL;
1574 return ret;
1575 }
1576 data->client.app_id = ret;
1577 }
1578 if (!found_app) {
1579 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1580 if (!entry) {
1581 pr_err("kmalloc failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001582 kfree(data);
1583 kfree(*handle);
1584 *handle = NULL;
Mona Hossaind44a3842012-10-15 09:41:35 -07001585 return -ENOMEM;
1586 }
1587 entry->app_id = ret;
1588 entry->ref_cnt = 1;
1589
1590 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1591 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
1592 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
1593 flags);
1594 }
1595
1596 /* Get the physical address of the ION BUF */
1597 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
1598 /* Populate the structure for sending scm call to load image */
1599 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
1600 data->client.ihandle);
Hariprasad Dhalinarasimhaacfb09c2013-01-10 13:16:15 -08001601 data->client.user_virt_sb_base = (uint32_t)data->client.sb_virt;
Mona Hossaind44a3842012-10-15 09:41:35 -07001602 data->client.sb_phys = pa;
1603 (*handle)->dev = (void *)data;
1604 (*handle)->sbuf = (unsigned char *)data->client.sb_virt;
1605 (*handle)->sbuf_len = data->client.sb_length;
1606
1607 kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
1608 if (!kclient_entry) {
1609 pr_err("kmalloc failed\n");
1610 return -ENOMEM;
1611 }
1612 kclient_entry->handle = *handle;
1613
1614 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1615 list_add_tail(&kclient_entry->list,
1616 &qseecom.registered_kclient_list_head);
1617 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1618
1619 return 0;
1620}
1621EXPORT_SYMBOL(qseecom_start_app);
1622
1623int qseecom_shutdown_app(struct qseecom_handle **handle)
1624{
1625 int ret = -EINVAL;
Mona Hossain33824022013-02-25 09:32:33 -08001626 struct qseecom_dev_handle *data;
1627
Mona Hossaind44a3842012-10-15 09:41:35 -07001628 struct qseecom_registered_kclient_list *kclient = NULL;
1629 unsigned long flags = 0;
1630 bool found_handle = false;
1631
1632 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1633 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1634 return -EINVAL;
1635 }
Mona Hossain33824022013-02-25 09:32:33 -08001636 if ((handle == NULL) || (*handle == NULL)) {
Mona Hossaind44a3842012-10-15 09:41:35 -07001637 pr_err("Handle is not initialized\n");
1638 return -EINVAL;
1639 }
Mona Hossain33824022013-02-25 09:32:33 -08001640 data = (struct qseecom_dev_handle *) ((*handle)->dev);
Mona Hossaind44a3842012-10-15 09:41:35 -07001641 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1642 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
1643 list) {
1644 if (kclient->handle == (*handle)) {
1645 list_del(&kclient->list);
1646 found_handle = true;
1647 break;
1648 }
1649 }
1650 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1651 if (!found_handle)
1652 pr_err("Unable to find the handle, exiting\n");
1653 else
1654 ret = qseecom_unload_app(data);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001655 if (data->client.fast_load_enabled == true)
1656 qsee_disable_clock_vote(data, CLK_SFPB);
1657 if (data->client.perf_enabled == true)
1658 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001659 if (ret == 0) {
1660 kzfree(data);
1661 kzfree(*handle);
1662 kzfree(kclient);
1663 *handle = NULL;
1664 }
1665 return ret;
1666}
1667EXPORT_SYMBOL(qseecom_shutdown_app);
1668
1669int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
1670 uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
1671{
1672 int ret = 0;
1673 struct qseecom_send_cmd_req req = {0, 0, 0, 0};
1674 struct qseecom_dev_handle *data;
1675
1676 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1677 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1678 return -EINVAL;
1679 }
1680
1681 if (handle == NULL) {
1682 pr_err("Handle is not initialized\n");
1683 return -EINVAL;
1684 }
1685 data = handle->dev;
1686
1687 req.cmd_req_len = sbuf_len;
1688 req.resp_len = rbuf_len;
1689 req.cmd_req_buf = send_buf;
1690 req.resp_buf = resp_buf;
1691
1692 mutex_lock(&app_access_lock);
1693 atomic_inc(&data->ioctl_count);
1694
1695 ret = __qseecom_send_cmd(data, &req);
1696
1697 atomic_dec(&data->ioctl_count);
1698 mutex_unlock(&app_access_lock);
1699
1700 if (ret)
1701 return ret;
1702
1703 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1704 req.resp_len, req.resp_buf);
1705 return ret;
1706}
1707EXPORT_SYMBOL(qseecom_send_command);
1708
Mona Hossain91a8fc92012-11-07 19:58:30 -08001709int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
1710{
Mona Hossainfca6f422013-01-12 13:00:35 -08001711 int ret = 0;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001712 if ((handle == NULL) || (handle->dev == NULL)) {
1713 pr_err("No valid kernel client\n");
1714 return -EINVAL;
1715 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001716 if (high) {
1717 ret = qsee_vote_for_clock(handle->dev, CLK_DFAB);
1718 if (ret)
1719 pr_err("Failed to vote for DFAB clock%d\n", ret);
1720 ret = qsee_vote_for_clock(handle->dev, CLK_SFPB);
1721 if (ret) {
1722 pr_err("Failed to vote for SFPB clock%d\n", ret);
1723 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
1724 }
1725 } else {
Mona Hossain04d3fac2012-12-03 10:10:37 -08001726 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
Mona Hossainfca6f422013-01-12 13:00:35 -08001727 qsee_disable_clock_vote(handle->dev, CLK_SFPB);
Mona Hossain91a8fc92012-11-07 19:58:30 -08001728 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001729 return ret;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001730}
1731EXPORT_SYMBOL(qseecom_set_bandwidth);
1732
Mona Hossain2892b6b2012-02-17 13:53:11 -08001733static int qseecom_send_resp(void)
1734{
1735 qseecom.send_resp_flag = 1;
1736 wake_up_interruptible(&qseecom.send_resp_wq);
1737 return 0;
1738}
1739
1740static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1741 void __user *argp)
1742{
1743 struct qseecom_qseos_version_req req;
1744
1745 if (copy_from_user(&req, argp, sizeof(req))) {
1746 pr_err("copy_from_user failed");
1747 return -EINVAL;
1748 }
1749 req.qseos_version = qseecom.qseos_version;
1750 if (copy_to_user(argp, &req, sizeof(req))) {
1751 pr_err("copy_to_user failed");
1752 return -EINVAL;
1753 }
1754 return 0;
1755}
1756
Mona Hossain6311d572013-03-01 15:54:02 -08001757static int __qseecom_enable_clk(void)
1758{
1759 int rc = 0;
1760
1761 /* Enable CE core clk */
1762 rc = clk_prepare_enable(ce_core_clk);
1763 if (rc) {
1764 pr_err("Unable to enable/prepare CE core clk\n");
1765 goto err;
1766 } else {
1767 /* Enable CE clk */
1768 rc = clk_prepare_enable(ce_clk);
1769 if (rc) {
1770 pr_err("Unable to enable/prepare CE iface clk\n");
1771 goto ce_clk_err;
1772 } else {
1773 /* Enable AXI clk */
1774 rc = clk_prepare_enable(ce_bus_clk);
1775 if (rc) {
1776 pr_err("Unable to enable/prepare CE bus clk\n");
1777 goto ce_bus_clk_err;
1778 }
1779 }
1780 }
1781 return 0;
1782
1783ce_bus_clk_err:
1784 clk_disable_unprepare(ce_clk);
1785ce_clk_err:
1786 clk_disable_unprepare(ce_core_clk);
1787err:
1788 return -EIO;
1789}
1790
1791static void __qseecom_disable_clk(void)
1792{
1793 if (ce_clk != NULL)
1794 clk_disable_unprepare(ce_clk);
1795 if (ce_core_clk != NULL)
1796 clk_disable_unprepare(ce_core_clk);
1797 if (ce_bus_clk != NULL)
1798 clk_disable_unprepare(ce_bus_clk);
1799}
1800
Mona Hossain04d3fac2012-12-03 10:10:37 -08001801static int qsee_vote_for_clock(struct qseecom_dev_handle *data,
1802 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001803{
1804 int ret = 0;
1805
1806 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001807 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001808
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001809 switch (clk_type) {
1810 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001811 mutex_lock(&qsee_bw_mutex);
1812 if (!qsee_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001813 if (qsee_sfpb_bw_count > 0)
1814 ret = msm_bus_scale_client_update_request(
1815 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001816 else {
1817 if (ce_core_src_clk != NULL)
Mona Hossain6311d572013-03-01 15:54:02 -08001818 ret = __qseecom_enable_clk();
1819 if (!ret) {
1820 ret =
1821 msm_bus_scale_client_update_request(
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001822 qsee_perf_client, 1);
Mona Hossain6311d572013-03-01 15:54:02 -08001823 if ((ret) && (ce_core_src_clk != NULL))
1824 __qseecom_disable_clk();
1825 }
Mona Hossaind39e33b2012-11-05 13:36:40 -08001826 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001827 if (ret)
1828 pr_err("DFAB Bandwidth req failed (%d)\n",
1829 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001830 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001831 qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001832 data->client.perf_enabled = true;
1833 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001834 } else {
1835 qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001836 data->client.perf_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001837 }
1838 mutex_unlock(&qsee_bw_mutex);
1839 break;
1840 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001841 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001842 if (!qsee_sfpb_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001843 if (qsee_bw_count > 0)
1844 ret = msm_bus_scale_client_update_request(
1845 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001846 else {
1847 if (ce_core_src_clk != NULL)
Mona Hossain6311d572013-03-01 15:54:02 -08001848 ret = __qseecom_enable_clk();
1849 if (!ret) {
1850 ret =
1851 msm_bus_scale_client_update_request(
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001852 qsee_perf_client, 2);
Mona Hossain6311d572013-03-01 15:54:02 -08001853 if ((ret) && (ce_core_src_clk != NULL))
1854 __qseecom_disable_clk();
1855 }
Mona Hossaind39e33b2012-11-05 13:36:40 -08001856 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001857
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001858 if (ret)
1859 pr_err("SFPB Bandwidth req failed (%d)\n",
1860 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001861 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001862 qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001863 data->client.fast_load_enabled = true;
1864 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001865 } else {
1866 qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001867 data->client.fast_load_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001868 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001869 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001870 break;
1871 default:
1872 pr_err("Clock type not defined\n");
1873 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001874 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001875 return ret;
1876}
1877
Mona Hossain04d3fac2012-12-03 10:10:37 -08001878static void qsee_disable_clock_vote(struct qseecom_dev_handle *data,
1879 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001880{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001881 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001882
Mona Hossain2892b6b2012-02-17 13:53:11 -08001883 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001884 return;
1885
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001886 switch (clk_type) {
1887 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001888 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001889 if (qsee_bw_count == 0) {
1890 pr_err("Client error.Extra call to disable DFAB clk\n");
1891 mutex_unlock(&qsee_bw_mutex);
1892 return;
1893 }
1894
Mona Hossain04d3fac2012-12-03 10:10:37 -08001895 if (qsee_bw_count == 1) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001896 if (qsee_sfpb_bw_count > 0)
1897 ret = msm_bus_scale_client_update_request(
1898 qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001899 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001900 ret = msm_bus_scale_client_update_request(
1901 qsee_perf_client, 0);
Mona Hossain6311d572013-03-01 15:54:02 -08001902 if ((!ret) && (ce_core_src_clk != NULL))
1903 __qseecom_disable_clk();
Mona Hossaind39e33b2012-11-05 13:36:40 -08001904 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001905 if (ret)
1906 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001907 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001908 else {
1909 qsee_bw_count--;
1910 data->client.perf_enabled = false;
1911 }
1912 } else {
1913 qsee_bw_count--;
1914 data->client.perf_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001915 }
1916 mutex_unlock(&qsee_bw_mutex);
1917 break;
1918 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001919 mutex_lock(&qsee_bw_mutex);
1920 if (qsee_sfpb_bw_count == 0) {
1921 pr_err("Client error.Extra call to disable SFPB clk\n");
1922 mutex_unlock(&qsee_bw_mutex);
1923 return;
1924 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08001925 if (qsee_sfpb_bw_count == 1) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001926 if (qsee_bw_count > 0)
1927 ret = msm_bus_scale_client_update_request(
1928 qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001929 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001930 ret = msm_bus_scale_client_update_request(
1931 qsee_perf_client, 0);
Mona Hossain6311d572013-03-01 15:54:02 -08001932 if ((!ret) && (ce_core_src_clk != NULL))
1933 __qseecom_disable_clk();
Mona Hossaind39e33b2012-11-05 13:36:40 -08001934 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001935 if (ret)
1936 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001937 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001938 else {
1939 qsee_sfpb_bw_count--;
1940 data->client.fast_load_enabled = false;
1941 }
1942 } else {
1943 qsee_sfpb_bw_count--;
1944 data->client.fast_load_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001945 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001946 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001947 break;
1948 default:
1949 pr_err("Clock type not defined\n");
1950 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001951 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001952
Mona Hossain2892b6b2012-02-17 13:53:11 -08001953}
1954
Mona Hossain5ab9d772012-04-11 21:00:40 -07001955static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1956 void __user *argp)
1957{
1958 struct ion_handle *ihandle; /* Ion handle */
1959 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001960 int ret;
1961 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001962 ion_phys_addr_t pa = 0;
1963 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001964 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001965 struct qseecom_load_app_ireq load_req;
1966 struct qseecom_command_scm_resp resp;
1967
1968 /* Copy the relevant information needed for loading the image */
1969 if (__copy_from_user(&load_img_req,
1970 (void __user *)argp,
1971 sizeof(struct qseecom_load_img_req))) {
1972 pr_err("copy_from_user failed\n");
1973 return -EFAULT;
1974 }
1975
1976 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001977 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001978 load_img_req.ifd_data_fd);
1979 if (IS_ERR_OR_NULL(ihandle)) {
1980 pr_err("Ion client could not retrieve the handle\n");
1981 return -ENOMEM;
1982 }
1983
1984 /* Get the physical address of the ION BUF */
1985 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1986
1987 /* Populate the structure for sending scm call to load image */
1988 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1989 load_req.mdt_len = load_img_req.mdt_len;
1990 load_req.img_len = load_img_req.img_len;
1991 load_req.phy_addr = pa;
1992
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001993 /* SCM_CALL tied to Core0 */
1994 mask = CPU_MASK_CPU0;
1995 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1996 if (set_cpu_ret) {
1997 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1998 set_cpu_ret);
1999 ret = -EFAULT;
2000 goto qseecom_load_external_elf_set_cpu_err;
2001 }
Mona Hossain6311d572013-03-01 15:54:02 -08002002 /* Vote for the SFPB clock */
2003 ret = qsee_vote_for_clock(data, CLK_SFPB);
2004 if (ret) {
2005 pr_err("Unable to vote for SFPB clock: ret = %d", ret);
2006 ret = -EIO;
2007 goto qseecom_load_external_elf_set_cpu_err;
2008 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002009
Mona Hossain5ab9d772012-04-11 21:00:40 -07002010 /* SCM_CALL to load the external elf */
2011 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
2012 sizeof(struct qseecom_load_app_ireq),
2013 &resp, sizeof(resp));
2014 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002015 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07002016 ret);
2017 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002018 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002019 }
2020
2021 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2022 ret = __qseecom_process_incomplete_cmd(data, &resp);
2023 if (ret)
2024 pr_err("process_incomplete_cmd failed err: %d\n",
2025 ret);
2026 } else {
2027 if (resp.result != QSEOS_RESULT_SUCCESS) {
2028 pr_err("scm_call to load image failed resp.result =%d\n",
2029 resp.result);
2030 ret = -EFAULT;
2031 }
2032 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002033
2034qseecom_load_external_elf_scm_err:
2035 /* Restore the CPU mask */
2036 mask = CPU_MASK_ALL;
2037 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2038 if (set_cpu_ret) {
2039 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2040 set_cpu_ret);
2041 ret = -EFAULT;
2042 }
2043
2044qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07002045 /* Deallocate the handle */
2046 if (!IS_ERR_OR_NULL(ihandle))
2047 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain6311d572013-03-01 15:54:02 -08002048 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain5ab9d772012-04-11 21:00:40 -07002049 return ret;
2050}
2051
2052static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
2053{
2054 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002055 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002056 struct qseecom_command_scm_resp resp;
2057 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002058 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002059
2060 /* Populate the structure for sending scm call to unload image */
2061 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002062
2063 /* SCM_CALL tied to Core0 */
2064 mask = CPU_MASK_CPU0;
2065 ret = set_cpus_allowed_ptr(current, &mask);
2066 if (ret) {
2067 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
2068 ret);
2069 return -EFAULT;
2070 }
2071
Mona Hossain5ab9d772012-04-11 21:00:40 -07002072 /* SCM_CALL to unload the external elf */
2073 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
2074 sizeof(struct qseecom_unload_app_ireq),
2075 &resp, sizeof(resp));
2076 if (ret) {
2077 pr_err("scm_call to unload failed : ret %d\n",
2078 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002079 ret = -EFAULT;
2080 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002081 }
2082 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2083 ret = __qseecom_process_incomplete_cmd(data, &resp);
2084 if (ret)
2085 pr_err("process_incomplete_cmd fail err: %d\n",
2086 ret);
2087 } else {
2088 if (resp.result != QSEOS_RESULT_SUCCESS) {
2089 pr_err("scm_call to unload image failed resp.result =%d\n",
2090 resp.result);
2091 ret = -EFAULT;
2092 }
2093 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002094
2095qseecom_unload_external_elf_scm_err:
2096 /* Restore the CPU mask */
2097 mask = CPU_MASK_ALL;
2098 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2099 if (set_cpu_ret) {
2100 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2101 set_cpu_ret);
2102 ret = -EFAULT;
2103 }
2104
Mona Hossain5ab9d772012-04-11 21:00:40 -07002105 return ret;
2106}
Mona Hossain2892b6b2012-02-17 13:53:11 -08002107
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002108static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
2109 void __user *argp)
2110{
2111
2112 int32_t ret;
2113 struct qseecom_qseos_app_load_query query_req;
2114 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002115 struct qseecom_registered_app_list *entry = NULL;
2116 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002117
2118 /* Copy the relevant information needed for loading the image */
2119 if (__copy_from_user(&query_req,
2120 (void __user *)argp,
2121 sizeof(struct qseecom_qseos_app_load_query))) {
2122 pr_err("copy_from_user failed\n");
2123 return -EFAULT;
2124 }
2125
2126 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
2127 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
2128
2129 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002130
2131 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002132 pr_err(" scm call to check if app is loaded failed");
2133 return ret; /* scm call failed */
2134 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002135 pr_warn("App id %d (%s) already exists\n", ret,
2136 (char *)(req.app_name));
2137 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
2138 list_for_each_entry(entry,
2139 &qseecom.registered_app_list_head, list){
2140 if (entry->app_id == ret) {
2141 entry->ref_cnt++;
2142 break;
2143 }
2144 }
2145 spin_unlock_irqrestore(
2146 &qseecom.registered_app_list_lock, flags);
2147 data->client.app_id = ret;
2148 query_req.app_id = ret;
2149
2150 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
2151 pr_err("copy_to_user failed\n");
2152 return -EFAULT;
2153 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002154 return -EEXIST; /* app already loaded */
2155 } else {
2156 return 0; /* app not loaded */
2157 }
2158}
2159
Mona Hossain2892b6b2012-02-17 13:53:11 -08002160static long qseecom_ioctl(struct file *file, unsigned cmd,
2161 unsigned long arg)
2162{
2163 int ret = 0;
2164 struct qseecom_dev_handle *data = file->private_data;
2165 void __user *argp = (void __user *) arg;
2166
2167 if (data->abort) {
2168 pr_err("Aborting qseecom driver\n");
2169 return -ENODEV;
2170 }
2171
2172 switch (cmd) {
2173 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
2174 pr_debug("ioctl register_listener_req()\n");
2175 atomic_inc(&data->ioctl_count);
2176 ret = qseecom_register_listener(data, argp);
2177 atomic_dec(&data->ioctl_count);
2178 wake_up_all(&data->abort_wq);
2179 if (ret)
2180 pr_err("failed qseecom_register_listener: %d\n", ret);
2181 break;
2182 }
2183 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
2184 pr_debug("ioctl unregister_listener_req()\n");
2185 atomic_inc(&data->ioctl_count);
2186 ret = qseecom_unregister_listener(data);
2187 atomic_dec(&data->ioctl_count);
2188 wake_up_all(&data->abort_wq);
2189 if (ret)
2190 pr_err("failed qseecom_unregister_listener: %d\n", ret);
2191 break;
2192 }
2193 case QSEECOM_IOCTL_SEND_CMD_REQ: {
2194 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002195 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002196 atomic_inc(&data->ioctl_count);
2197 ret = qseecom_send_cmd(data, argp);
2198 atomic_dec(&data->ioctl_count);
2199 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002200 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002201 if (ret)
2202 pr_err("failed qseecom_send_cmd: %d\n", ret);
2203 break;
2204 }
2205 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
2206 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002207 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002208 atomic_inc(&data->ioctl_count);
2209 ret = qseecom_send_modfd_cmd(data, argp);
2210 atomic_dec(&data->ioctl_count);
2211 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002212 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002213 if (ret)
2214 pr_err("failed qseecom_send_cmd: %d\n", ret);
2215 break;
2216 }
2217 case QSEECOM_IOCTL_RECEIVE_REQ: {
2218 atomic_inc(&data->ioctl_count);
2219 ret = qseecom_receive_req(data);
2220 atomic_dec(&data->ioctl_count);
2221 wake_up_all(&data->abort_wq);
2222 if (ret)
2223 pr_err("failed qseecom_receive_req: %d\n", ret);
2224 break;
2225 }
2226 case QSEECOM_IOCTL_SEND_RESP_REQ: {
2227 atomic_inc(&data->ioctl_count);
2228 ret = qseecom_send_resp();
2229 atomic_dec(&data->ioctl_count);
2230 wake_up_all(&data->abort_wq);
2231 if (ret)
2232 pr_err("failed qseecom_send_resp: %d\n", ret);
2233 break;
2234 }
2235 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
2236 ret = qseecom_set_client_mem_param(data, argp);
2237 if (ret)
2238 pr_err("failed Qqseecom_set_mem_param request: %d\n",
2239 ret);
2240 break;
2241 }
2242 case QSEECOM_IOCTL_LOAD_APP_REQ: {
2243 mutex_lock(&app_access_lock);
2244 atomic_inc(&data->ioctl_count);
Mona Hossain05c73562012-10-29 17:49:01 -07002245 if (qseecom.qsee_version > QSEEE_VERSION_00) {
2246 if (qseecom.commonlib_loaded == false) {
Mona Hossain9498f5e2013-01-23 18:08:45 -08002247 ret = qseecom_load_commonlib_image(data);
Mona Hossain05c73562012-10-29 17:49:01 -07002248 if (ret == 0)
2249 qseecom.commonlib_loaded = true;
2250 }
2251 }
2252 if (ret == 0)
2253 ret = qseecom_load_app(data, argp);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002254 atomic_dec(&data->ioctl_count);
2255 mutex_unlock(&app_access_lock);
2256 if (ret)
2257 pr_err("failed load_app request: %d\n", ret);
2258 break;
2259 }
2260 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
2261 mutex_lock(&app_access_lock);
2262 atomic_inc(&data->ioctl_count);
2263 ret = qseecom_unload_app(data);
2264 atomic_dec(&data->ioctl_count);
2265 mutex_unlock(&app_access_lock);
2266 if (ret)
2267 pr_err("failed unload_app request: %d\n", ret);
2268 break;
2269 }
2270 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
2271 atomic_inc(&data->ioctl_count);
2272 ret = qseecom_get_qseos_version(data, argp);
2273 if (ret)
2274 pr_err("qseecom_get_qseos_version: %d\n", ret);
2275 atomic_dec(&data->ioctl_count);
2276 break;
2277 }
2278 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
2279 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002280 ret = qsee_vote_for_clock(data, CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002281 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002282 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002283 ret = qsee_vote_for_clock(data, CLK_SFPB);
2284 if (ret)
2285 pr_err("Failed to vote for SFPB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002286 atomic_dec(&data->ioctl_count);
2287 break;
2288 }
2289 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
2290 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002291 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002292 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002293 atomic_dec(&data->ioctl_count);
2294 break;
2295 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07002296 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
2297 data->released = true;
2298 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2299 pr_err("Loading External elf image unsupported in rev 0x13\n");
2300 ret = -EINVAL;
2301 break;
2302 }
2303 mutex_lock(&app_access_lock);
2304 atomic_inc(&data->ioctl_count);
2305 ret = qseecom_load_external_elf(data, argp);
2306 atomic_dec(&data->ioctl_count);
2307 mutex_unlock(&app_access_lock);
2308 if (ret)
2309 pr_err("failed load_external_elf request: %d\n", ret);
2310 break;
2311 }
2312 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
2313 data->released = true;
2314 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2315 pr_err("Unloading External elf image unsupported in rev 0x13\n");
2316 ret = -EINVAL;
2317 break;
2318 }
2319 mutex_lock(&app_access_lock);
2320 atomic_inc(&data->ioctl_count);
2321 ret = qseecom_unload_external_elf(data);
2322 atomic_dec(&data->ioctl_count);
2323 mutex_unlock(&app_access_lock);
2324 if (ret)
2325 pr_err("failed unload_app request: %d\n", ret);
2326 break;
2327 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002328 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
2329 mutex_lock(&app_access_lock);
2330 atomic_inc(&data->ioctl_count);
2331 ret = qseecom_query_app_loaded(data, argp);
2332 atomic_dec(&data->ioctl_count);
2333 mutex_unlock(&app_access_lock);
2334 break;
2335 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002336 default:
2337 return -EINVAL;
2338 }
2339 return ret;
2340}
2341
2342static int qseecom_open(struct inode *inode, struct file *file)
2343{
2344 int ret = 0;
2345 struct qseecom_dev_handle *data;
2346
2347 data = kzalloc(sizeof(*data), GFP_KERNEL);
2348 if (!data) {
2349 pr_err("kmalloc failed\n");
2350 return -ENOMEM;
2351 }
2352 file->private_data = data;
2353 data->abort = 0;
2354 data->service = false;
2355 data->released = false;
2356 init_waitqueue_head(&data->abort_wq);
2357 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002358 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2359 int pil_error;
2360 mutex_lock(&pil_access_lock);
2361 if (pil_ref_cnt == 0) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002362 pil = subsystem_get("tzapps");
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002363 if (IS_ERR(pil)) {
2364 pr_err("Playready PIL image load failed\n");
2365 pil_error = PTR_ERR(pil);
2366 pil = NULL;
2367 pr_debug("tzapps image load FAILED\n");
2368 mutex_unlock(&pil_access_lock);
2369 return pil_error;
2370 }
2371 }
2372 pil_ref_cnt++;
2373 mutex_unlock(&pil_access_lock);
2374 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002375 return ret;
2376}
2377
2378static int qseecom_release(struct inode *inode, struct file *file)
2379{
2380 struct qseecom_dev_handle *data = file->private_data;
2381 int ret = 0;
2382
2383 if (data->released == false) {
2384 pr_warn("data->released == false\n");
2385 if (data->service)
2386 ret = qseecom_unregister_listener(data);
2387 else
2388 ret = qseecom_unload_app(data);
2389 if (ret) {
2390 pr_err("Close failed\n");
2391 return ret;
2392 }
2393 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08002394 if (data->client.fast_load_enabled == true)
2395 qsee_disable_clock_vote(data, CLK_SFPB);
2396 if (data->client.perf_enabled == true)
2397 qsee_disable_clock_vote(data, CLK_DFAB);
2398
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002399 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2400 mutex_lock(&pil_access_lock);
2401 if (pil_ref_cnt == 1)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002402 subsystem_put(pil);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002403 pil_ref_cnt--;
2404 mutex_unlock(&pil_access_lock);
2405 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002406 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002407
Mona Hossain2892b6b2012-02-17 13:53:11 -08002408 return ret;
2409}
2410
Mona Hossain2892b6b2012-02-17 13:53:11 -08002411static const struct file_operations qseecom_fops = {
2412 .owner = THIS_MODULE,
2413 .unlocked_ioctl = qseecom_ioctl,
2414 .open = qseecom_open,
2415 .release = qseecom_release
2416};
2417
Mona Hossaind39e33b2012-11-05 13:36:40 -08002418static int __qseecom_init_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002419{
2420 int rc = 0;
2421 struct device *pdev;
2422
2423 pdev = qseecom.pdev;
2424 /* Get CE3 src core clk. */
2425 ce_core_src_clk = clk_get(pdev, "core_clk_src");
2426 if (!IS_ERR(ce_core_src_clk)) {
Mona Hossain6311d572013-03-01 15:54:02 -08002427 /* Set the core src clk @100Mhz */
2428 rc = clk_set_rate(ce_core_src_clk, QSEE_CE_CLK_100MHZ);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002429 if (rc) {
2430 clk_put(ce_core_src_clk);
2431 pr_err("Unable to set the core src clk @100Mhz.\n");
Mona Hossaind39e33b2012-11-05 13:36:40 -08002432 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002433 }
2434 } else {
2435 pr_warn("Unable to get CE core src clk, set to NULL\n");
2436 ce_core_src_clk = NULL;
2437 }
2438
2439 /* Get CE core clk */
2440 ce_core_clk = clk_get(pdev, "core_clk");
2441 if (IS_ERR(ce_core_clk)) {
2442 rc = PTR_ERR(ce_core_clk);
2443 pr_err("Unable to get CE core clk\n");
2444 if (ce_core_src_clk != NULL)
2445 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002446 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002447 }
2448
2449 /* Get CE Interface clk */
2450 ce_clk = clk_get(pdev, "iface_clk");
2451 if (IS_ERR(ce_clk)) {
2452 rc = PTR_ERR(ce_clk);
2453 pr_err("Unable to get CE interface clk\n");
2454 if (ce_core_src_clk != NULL)
2455 clk_put(ce_core_src_clk);
2456 clk_put(ce_core_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002457 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002458 }
2459
2460 /* Get CE AXI clk */
2461 ce_bus_clk = clk_get(pdev, "bus_clk");
2462 if (IS_ERR(ce_bus_clk)) {
2463 rc = PTR_ERR(ce_bus_clk);
2464 pr_err("Unable to get CE BUS interface clk\n");
2465 if (ce_core_src_clk != NULL)
2466 clk_put(ce_core_src_clk);
2467 clk_put(ce_core_clk);
2468 clk_put(ce_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002469 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002470 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002471 return rc;
2472}
2473
Mona Hossaind39e33b2012-11-05 13:36:40 -08002474static void __qseecom_deinit_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002475{
Mona Hossaind39e33b2012-11-05 13:36:40 -08002476 if (ce_clk != NULL) {
2477 clk_put(ce_clk);
2478 ce_clk = NULL;
2479 }
2480 if (ce_core_clk != NULL) {
2481 clk_put(ce_core_clk);
2482 ce_clk = NULL;
2483 }
2484 if (ce_bus_clk != NULL) {
2485 clk_put(ce_bus_clk);
2486 ce_clk = NULL;
2487 }
2488 if (ce_core_src_clk != NULL) {
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002489 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002490 ce_core_src_clk = NULL;
2491 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002492}
2493
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002494static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002495{
2496 int rc;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002497 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002498 struct device *class_dev;
2499 char qsee_not_legacy = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07002500 struct msm_bus_scale_pdata *qseecom_platform_support = NULL;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002501 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
2502
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002503 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002504 qsee_perf_client = 0;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002505 qsee_sfpb_bw_count = 0;
2506
2507 ce_core_clk = NULL;
2508 ce_clk = NULL;
2509 ce_core_src_clk = NULL;
2510 ce_bus_clk = NULL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002511
Mona Hossain2892b6b2012-02-17 13:53:11 -08002512 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
2513 if (rc < 0) {
2514 pr_err("alloc_chrdev_region failed %d\n", rc);
2515 return rc;
2516 }
2517
2518 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
2519 if (IS_ERR(driver_class)) {
2520 rc = -ENOMEM;
2521 pr_err("class_create failed %d\n", rc);
2522 goto unregister_chrdev_region;
2523 }
2524
2525 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
2526 QSEECOM_DEV);
2527 if (!class_dev) {
2528 pr_err("class_device_create failed %d\n", rc);
2529 rc = -ENOMEM;
2530 goto class_destroy;
2531 }
2532
2533 cdev_init(&qseecom_cdev, &qseecom_fops);
2534 qseecom_cdev.owner = THIS_MODULE;
2535
2536 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
2537 if (rc < 0) {
2538 pr_err("cdev_add failed %d\n", rc);
2539 goto err;
2540 }
2541
2542 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
2543 spin_lock_init(&qseecom.registered_listener_list_lock);
2544 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
2545 spin_lock_init(&qseecom.registered_app_list_lock);
Mona Hossaind44a3842012-10-15 09:41:35 -07002546 INIT_LIST_HEAD(&qseecom.registered_kclient_list_head);
2547 spin_lock_init(&qseecom.registered_kclient_list_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002548 init_waitqueue_head(&qseecom.send_resp_wq);
2549 qseecom.send_resp_flag = 0;
2550
2551 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
2552 &qsee_not_legacy, sizeof(qsee_not_legacy));
2553 if (rc) {
Mona Hossain05c73562012-10-29 17:49:01 -07002554 pr_err("Failed to retrieve QSEOS version information %d\n", rc);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002555 goto err;
2556 }
Mona Hossain05c73562012-10-29 17:49:01 -07002557 if (qsee_not_legacy) {
2558 uint32_t feature = 10;
2559
2560 qseecom.qsee_version = QSEEE_VERSION_00;
2561 rc = scm_call(6, 3, &feature, sizeof(feature),
2562 &qseecom.qsee_version, sizeof(qseecom.qsee_version));
2563 if (rc) {
2564 pr_err("Failed to get QSEE version info %d\n", rc);
2565 goto err;
2566 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002567 qseecom.qseos_version = QSEOS_VERSION_14;
Mona Hossain05c73562012-10-29 17:49:01 -07002568 } else {
Mona Hossain2892b6b2012-02-17 13:53:11 -08002569 qseecom.qseos_version = QSEOS_VERSION_13;
Mona Hossain05c73562012-10-29 17:49:01 -07002570 qseecom.qsee_version = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002571 pil = NULL;
2572 pil_ref_cnt = 0;
2573 }
Mona Hossain05c73562012-10-29 17:49:01 -07002574 qseecom.commonlib_loaded = false;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002575 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002576 /* Create ION msm client */
Mona Hossaind44a3842012-10-15 09:41:35 -07002577 qseecom.ion_clnt = msm_ion_client_create(-1, "qseecom-kernel");
Mona Hossain2892b6b2012-02-17 13:53:11 -08002578 if (qseecom.ion_clnt == NULL) {
2579 pr_err("Ion client cannot be created\n");
2580 rc = -ENOMEM;
2581 goto err;
2582 }
2583
2584 /* register client for bus scaling */
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002585 if (pdev->dev.of_node) {
2586 ret = __qseecom_init_clk();
2587 if (ret)
2588 goto err;
Mona Hossain6311d572013-03-01 15:54:02 -08002589
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002590 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2591 msm_bus_cl_get_pdata(pdev);
Mona Hossain5b76a622012-11-15 20:09:08 -08002592 if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
2593 struct resource *resource = NULL;
2594 struct qsee_apps_region_info_ireq req;
2595 struct qseecom_command_scm_resp resp;
2596
2597 resource = platform_get_resource_byname(pdev,
2598 IORESOURCE_MEM, "secapp-region");
2599 if (resource) {
2600 req.qsee_cmd_id = QSEOS_APP_REGION_NOTIFICATION;
2601 req.addr = resource->start;
2602 req.size = resource_size(resource);
2603 pr_warn("secure app region addr=0x%x size=0x%x",
2604 req.addr, req.size);
2605 } else {
2606 pr_err("Fail to get secure app region info\n");
2607 rc = -EINVAL;
2608 goto err;
2609 }
2610 rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
2611 &resp, sizeof(resp));
2612 if (rc) {
2613 pr_err("Failed to send secapp region info %d\n",
2614 rc);
2615 goto err;
2616 }
2617 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002618 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002619 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2620 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002621 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002622
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002623 qsee_perf_client = msm_bus_scale_register_client(
2624 qseecom_platform_support);
2625
2626 if (!qsee_perf_client)
2627 pr_err("Unable to register bus client\n");
2628 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002629err:
2630 device_destroy(driver_class, qseecom_device_no);
2631class_destroy:
2632 class_destroy(driver_class);
2633unregister_chrdev_region:
2634 unregister_chrdev_region(qseecom_device_no, 1);
2635 return rc;
2636}
2637
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002638static int __devinit qseecom_remove(struct platform_device *pdev)
2639{
Mona Hossaind44a3842012-10-15 09:41:35 -07002640 struct qseecom_registered_kclient_list *kclient = NULL;
2641 unsigned long flags = 0;
2642 int ret = 0;
2643
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002644 if (pdev->dev.platform_data != NULL)
2645 msm_bus_scale_unregister_client(qsee_perf_client);
Mona Hossaind44a3842012-10-15 09:41:35 -07002646
2647 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2648 kclient = list_entry((&qseecom.registered_kclient_list_head)->next,
2649 struct qseecom_registered_kclient_list, list);
2650 if (list_empty(&kclient->list)) {
2651 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2652 flags);
2653 return 0;
2654 }
2655 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
2656 list) {
2657 if (kclient)
2658 list_del(&kclient->list);
2659 break;
2660 }
2661 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
2662
2663
2664 while (kclient->handle != NULL) {
2665 ret = qseecom_unload_app(kclient->handle->dev);
2666 if (ret == 0) {
2667 kzfree(kclient->handle->dev);
2668 kzfree(kclient->handle);
2669 kzfree(kclient);
2670 }
2671 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2672 kclient = list_entry(
2673 (&qseecom.registered_kclient_list_head)->next,
2674 struct qseecom_registered_kclient_list, list);
2675 if (list_empty(&kclient->list)) {
2676 spin_unlock_irqrestore(
2677 &qseecom.registered_kclient_list_lock, flags);
2678 return 0;
2679 }
2680 list_for_each_entry(kclient,
2681 &qseecom.registered_kclient_list_head, list) {
2682 if (kclient)
2683 list_del(&kclient->list);
2684 break;
2685 }
2686 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2687 flags);
2688 if (!kclient) {
2689 ret = 0;
2690 break;
2691 }
2692 }
Mona Hossain05c73562012-10-29 17:49:01 -07002693 if (qseecom.qseos_version > QSEEE_VERSION_00)
2694 qseecom_unload_commonlib_image();
Mona Hossaind39e33b2012-11-05 13:36:40 -08002695
2696 if (qsee_perf_client)
2697 msm_bus_scale_client_update_request(qsee_perf_client, 0);
2698 /* register client for bus scaling */
Mona Hossain6311d572013-03-01 15:54:02 -08002699 if (pdev->dev.of_node)
Mona Hossaind39e33b2012-11-05 13:36:40 -08002700 __qseecom_deinit_clk();
Mona Hossain6311d572013-03-01 15:54:02 -08002701
Mona Hossaind44a3842012-10-15 09:41:35 -07002702 return ret;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002703};
2704
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002705static struct of_device_id qseecom_match[] = {
2706 {
2707 .compatible = "qcom,qseecom",
2708 },
2709 {}
2710};
2711
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002712static struct platform_driver qseecom_plat_driver = {
2713 .probe = qseecom_probe,
2714 .remove = qseecom_remove,
2715 .driver = {
2716 .name = "qseecom",
2717 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002718 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002719 },
2720};
2721
2722static int __devinit qseecom_init(void)
2723{
2724 return platform_driver_register(&qseecom_plat_driver);
2725}
2726
2727static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002728{
Mona Hossain2892b6b2012-02-17 13:53:11 -08002729 device_destroy(driver_class, qseecom_device_no);
2730 class_destroy(driver_class);
2731 unregister_chrdev_region(qseecom_device_no, 1);
2732 ion_client_destroy(qseecom.ion_clnt);
2733}
2734
2735MODULE_LICENSE("GPL v2");
2736MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2737
2738module_init(qseecom_init);
2739module_exit(qseecom_exit);