blob: 9e22ffbd27a1c118a7f56505f296a810f4681226 [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
60#define QSEE_CE_CLK_50MHZ 50000000
61
Mona Hossain13dd8922013-01-03 06:11:09 -080062#define QSEECOM_MAX_SG_ENTRY 512
Mona Hossainf1f2ed62012-11-15 19:51:33 -080063
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070064enum qseecom_clk_definitions {
65 CLK_DFAB = 0,
66 CLK_SFPB,
67};
68
Mona Hossain2892b6b2012-02-17 13:53:11 -080069static struct class *driver_class;
70static dev_t qseecom_device_no;
71static struct cdev qseecom_cdev;
72
73/* Data structures used in legacy support */
74static void *pil;
75static uint32_t pil_ref_cnt;
76static DEFINE_MUTEX(pil_access_lock);
77
Mona Hossain2892b6b2012-02-17 13:53:11 -080078static DEFINE_MUTEX(qsee_bw_mutex);
79static DEFINE_MUTEX(app_access_lock);
80
81static int qsee_bw_count;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070082static int qsee_sfpb_bw_count;
Mona Hossain2892b6b2012-02-17 13:53:11 -080083static uint32_t qsee_perf_client;
84
85struct qseecom_registered_listener_list {
86 struct list_head list;
87 struct qseecom_register_listener_req svc;
88 u8 *sb_reg_req;
89 u8 *sb_virt;
90 s32 sb_phys;
91 size_t sb_length;
92 struct ion_handle *ihandle; /* Retrieve phy addr */
93
94 wait_queue_head_t rcv_req_wq;
95 int rcv_req_flag;
96};
97
98struct qseecom_registered_app_list {
99 struct list_head list;
100 u32 app_id;
101 u32 ref_cnt;
102};
103
Mona Hossaind44a3842012-10-15 09:41:35 -0700104struct qseecom_registered_kclient_list {
105 struct list_head list;
106 struct qseecom_handle *handle;
107};
108
Mona Hossain2892b6b2012-02-17 13:53:11 -0800109struct qseecom_control {
110 struct ion_client *ion_clnt; /* Ion client */
111 struct list_head registered_listener_list_head;
112 spinlock_t registered_listener_list_lock;
113
114 struct list_head registered_app_list_head;
115 spinlock_t registered_app_list_lock;
116
Mona Hossaind44a3842012-10-15 09:41:35 -0700117 struct list_head registered_kclient_list_head;
118 spinlock_t registered_kclient_list_lock;
119
Mona Hossain2892b6b2012-02-17 13:53:11 -0800120 wait_queue_head_t send_resp_wq;
121 int send_resp_flag;
122
123 uint32_t qseos_version;
Mona Hossain05c73562012-10-29 17:49:01 -0700124 uint32_t qsee_version;
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700125 struct device *pdev;
Mona Hossain05c73562012-10-29 17:49:01 -0700126 bool commonlib_loaded;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800127};
128
129struct qseecom_client_handle {
130 u32 app_id;
131 u8 *sb_virt;
132 s32 sb_phys;
133 uint32_t user_virt_sb_base;
134 size_t sb_length;
135 struct ion_handle *ihandle; /* Retrieve phy addr */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800136 bool perf_enabled;
137 bool fast_load_enabled;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800138};
139
140struct qseecom_listener_handle {
141 u32 id;
142};
143
144static struct qseecom_control qseecom;
145
146struct qseecom_dev_handle {
147 bool service;
148 union {
149 struct qseecom_client_handle client;
150 struct qseecom_listener_handle listener;
151 };
152 bool released;
153 int abort;
154 wait_queue_head_t abort_wq;
155 atomic_t ioctl_count;
156};
157
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700158struct clk *ce_core_clk;
159struct clk *ce_clk;
160struct clk *ce_core_src_clk;
161struct clk *ce_bus_clk;
162
Mona Hossainf1f2ed62012-11-15 19:51:33 -0800163struct qseecom_sg_entry {
164 uint32_t phys_addr;
165 uint32_t len;
166};
167
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700168/* Function proto types */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800169static int qsee_vote_for_clock(struct qseecom_dev_handle *, int32_t);
170static void qsee_disable_clock_vote(struct qseecom_dev_handle *, int32_t);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700171
Mona Hossain2892b6b2012-02-17 13:53:11 -0800172static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800173 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800174{
175 struct qseecom_registered_listener_list *ptr;
176 int unique = 1;
177 unsigned long flags;
178
179 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
180 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800181 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800182 pr_err("Service id: %u is already registered\n",
183 ptr->svc.listener_id);
184 unique = 0;
185 break;
186 }
187 }
188 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
189 return unique;
190}
191
192static struct qseecom_registered_listener_list *__qseecom_find_svc(
193 int32_t listener_id)
194{
195 struct qseecom_registered_listener_list *entry = NULL;
196 unsigned long flags;
197
198 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
199 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
200 {
201 if (entry->svc.listener_id == listener_id)
202 break;
203 }
204 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
205 return entry;
206}
207
208static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
209 struct qseecom_dev_handle *handle,
210 struct qseecom_register_listener_req *listener)
211{
212 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800213 struct qseecom_register_listener_ireq req;
214 struct qseecom_command_scm_resp resp;
215 ion_phys_addr_t pa;
216
217 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800218 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
219 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800220 if (svc->ihandle == NULL) {
221 pr_err("Ion client could not retrieve the handle\n");
222 return -ENOMEM;
223 }
224
225 /* Get the physical address of the ION BUF */
226 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
227
228 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700229 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800230 svc->sb_phys = pa;
231
232 if (qseecom.qseos_version == QSEOS_VERSION_14) {
233 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
234 req.listener_id = svc->svc.listener_id;
235 req.sb_len = svc->sb_length;
236 req.sb_ptr = (void *)svc->sb_phys;
237
238 resp.result = QSEOS_RESULT_INCOMPLETE;
239
240 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
241 sizeof(req), &resp, sizeof(resp));
242 if (ret) {
243 pr_err("qseecom_scm_call failed with err: %d\n", ret);
244 return -EINVAL;
245 }
246
247 if (resp.result != QSEOS_RESULT_SUCCESS) {
248 pr_err("Error SB registration req: resp.result = %d\n",
249 resp.result);
250 return -EPERM;
251 }
252 } else {
253 struct qseecom_command cmd;
254 struct qseecom_response resp;
255 struct qse_pr_init_sb_req_s sb_init_req;
256 struct qse_pr_init_sb_rsp_s sb_init_rsp;
257
258 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
259 sizeof(sb_init_rsp)), GFP_KERNEL);
260
261 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
262 sb_init_req.listener_id = svc->svc.listener_id;
263 sb_init_req.sb_len = svc->sb_length;
264 sb_init_req.sb_ptr = svc->sb_phys;
265
266 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
267
268 /* It will always be a new cmd from this method */
269 cmd.cmd_type = TZ_SCHED_CMD_NEW;
270 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
271 cmd.sb_in_cmd_len = sizeof(sb_init_req);
272
273 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
274
275 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
276 , &resp, sizeof(resp));
277
278 if (ret) {
279 pr_err("qseecom_scm_call failed with err: %d\n", ret);
280 return -EINVAL;
281 }
282
283 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
284 pr_err("SB registration fail resp.cmd_status %d\n",
285 resp.cmd_status);
286 return -EINVAL;
287 }
288 memset(svc->sb_virt, 0, svc->sb_length);
289 }
290 return 0;
291}
292
293static int qseecom_register_listener(struct qseecom_dev_handle *data,
294 void __user *argp)
295{
296 int ret = 0;
297 unsigned long flags;
298 struct qseecom_register_listener_req rcvd_lstnr;
299 struct qseecom_registered_listener_list *new_entry;
300
301 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
302 if (ret) {
303 pr_err("copy_from_user failed\n");
304 return ret;
305 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800306 data->listener.id = 0;
307 data->service = true;
308 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800309 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800310 data->released = true;
311 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800312 }
313
314 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
315 if (!new_entry) {
316 pr_err("kmalloc failed\n");
317 return -ENOMEM;
318 }
319 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
320 new_entry->rcv_req_flag = 0;
321
322 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
323 new_entry->sb_length = rcvd_lstnr.sb_size;
324 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
325 pr_err("qseecom_set_sb_memoryfailed\n");
326 kzfree(new_entry);
327 return -ENOMEM;
328 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800329
Mona Hossain2892b6b2012-02-17 13:53:11 -0800330 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800331 init_waitqueue_head(&new_entry->rcv_req_wq);
332
333 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
334 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
335 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800336
Mona Hossain2892b6b2012-02-17 13:53:11 -0800337 return ret;
338}
339
340static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
341{
342 int ret = 0;
343 unsigned long flags;
344 uint32_t unmap_mem = 0;
345 struct qseecom_register_listener_ireq req;
346 struct qseecom_registered_listener_list *ptr_svc = NULL;
347 struct qseecom_command_scm_resp resp;
348 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
349
350 if (qseecom.qseos_version == QSEOS_VERSION_14) {
351 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
352 req.listener_id = data->listener.id;
353 resp.result = QSEOS_RESULT_INCOMPLETE;
354
355 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
356 sizeof(req), &resp, sizeof(resp));
357 if (ret) {
358 pr_err("qseecom_scm_call failed with err: %d\n", ret);
359 return ret;
360 }
361
362 if (resp.result != QSEOS_RESULT_SUCCESS) {
363 pr_err("SB deregistartion: result=%d\n", resp.result);
364 return -EPERM;
365 }
366 } else {
367 struct qse_pr_init_sb_req_s sb_init_req;
368 struct qseecom_command cmd;
369 struct qseecom_response resp;
370 struct qseecom_registered_listener_list *svc;
371
372 svc = __qseecom_find_svc(data->listener.id);
373 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
374 sb_init_req.listener_id = data->listener.id;
375 sb_init_req.sb_len = 0;
376 sb_init_req.sb_ptr = 0;
377
378 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
379
380 /* It will always be a new cmd from this method */
381 cmd.cmd_type = TZ_SCHED_CMD_NEW;
382 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
383 cmd.sb_in_cmd_len = sizeof(sb_init_req);
384 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
385
386 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
387 &resp, sizeof(resp));
388 if (ret) {
389 pr_err("qseecom_scm_call failed with err: %d\n", ret);
390 return ret;
391 }
392 kzfree(svc->sb_reg_req);
393 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
394 pr_err("Error with SB initialization\n");
395 return -EPERM;
396 }
397 }
398 data->abort = 1;
399 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
400 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
401 list) {
402 if (ptr_svc->svc.listener_id == data->listener.id) {
403 wake_up_all(&ptr_svc->rcv_req_wq);
404 break;
405 }
406 }
407 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
408
409 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700410 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800411 atomic_read(&data->ioctl_count) <= 1)) {
412 pr_err("Interrupted from abort\n");
413 ret = -ERESTARTSYS;
414 break;
415 }
416 }
417
418 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
419 list_for_each_entry(ptr_svc,
420 &qseecom.registered_listener_list_head,
421 list)
422 {
423 if (ptr_svc->svc.listener_id == data->listener.id) {
424 if (ptr_svc->sb_virt) {
425 unmap_mem = 1;
426 ihandle = ptr_svc->ihandle;
427 }
428 list_del(&ptr_svc->list);
429 kzfree(ptr_svc);
430 break;
431 }
432 }
433 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
434
435 /* Unmap the memory */
436 if (unmap_mem) {
437 if (!IS_ERR_OR_NULL(ihandle)) {
438 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
439 ion_free(qseecom.ion_clnt, ihandle);
440 }
441 }
442 data->released = true;
443 return ret;
444}
445
446static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
447 void __user *argp)
448{
449 ion_phys_addr_t pa;
450 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800451 struct qseecom_set_sb_mem_param_req req;
452 uint32_t len;
453
454 /* Copy the relevant information needed for loading the image */
455 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
456 return -EFAULT;
457
Mona Hossain2892b6b2012-02-17 13:53:11 -0800458 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800459 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
460 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800461 if (IS_ERR_OR_NULL(data->client.ihandle)) {
462 pr_err("Ion client could not retrieve the handle\n");
463 return -ENOMEM;
464 }
465 /* Get the physical address of the ION BUF */
466 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
467 /* Populate the structure for sending scm call to load image */
468 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700469 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800470 data->client.sb_phys = pa;
471 data->client.sb_length = req.sb_len;
472 data->client.user_virt_sb_base = req.virt_sb_base;
473 return 0;
474}
475
Mona Hossain2892b6b2012-02-17 13:53:11 -0800476static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
477{
478 int ret;
479 ret = (qseecom.send_resp_flag != 0);
480 return ret || data->abort;
481}
482
483static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
484 struct qseecom_command_scm_resp *resp)
485{
486 int ret = 0;
Mona Hossain0b3a2792013-02-05 17:38:55 -0800487 int rc = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800488 uint32_t lstnr;
489 unsigned long flags;
490 struct qseecom_client_listener_data_irsp send_data_rsp;
491 struct qseecom_registered_listener_list *ptr_svc = NULL;
492
493
494 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
495 lstnr = resp->data;
496 /*
497 * Wake up blocking lsitener service with the lstnr id
498 */
499 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
500 flags);
501 list_for_each_entry(ptr_svc,
502 &qseecom.registered_listener_list_head, list) {
503 if (ptr_svc->svc.listener_id == lstnr) {
504 ptr_svc->rcv_req_flag = 1;
505 wake_up_interruptible(&ptr_svc->rcv_req_wq);
506 break;
507 }
508 }
509 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
510 flags);
511 if (ptr_svc->svc.listener_id != lstnr) {
512 pr_warning("Service requested for does on exist\n");
513 return -ERESTARTSYS;
514 }
515 pr_debug("waking up rcv_req_wq and "
516 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700517 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800518 __qseecom_listener_has_sent_rsp(data))) {
519 pr_warning("Interrupted: exiting send_cmd loop\n");
520 return -ERESTARTSYS;
521 }
522
523 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700524 pr_err("Aborting listener service %d\n",
525 data->listener.id);
Mona Hossain0b3a2792013-02-05 17:38:55 -0800526 rc = -ENODEV;
527 send_data_rsp.status = QSEOS_RESULT_FAILURE;
528 } else {
529 send_data_rsp.status = QSEOS_RESULT_SUCCESS;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800530 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800531
Mona Hossain2892b6b2012-02-17 13:53:11 -0800532 qseecom.send_resp_flag = 0;
533 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
534 send_data_rsp.listener_id = lstnr ;
535
536 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
537 (const void *)&send_data_rsp,
538 sizeof(send_data_rsp), resp,
539 sizeof(*resp));
540 if (ret) {
541 pr_err("qseecom_scm_call failed with err: %d\n", ret);
542 return ret;
543 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700544 if (resp->result == QSEOS_RESULT_FAILURE) {
545 pr_err("Response result %d not supported\n",
546 resp->result);
547 return -EINVAL;
548 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800549 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800550 if (rc)
551 return rc;
552
Mona Hossain2892b6b2012-02-17 13:53:11 -0800553 return ret;
554}
555
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700556static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
557{
558 int32_t ret;
559 struct qseecom_command_scm_resp resp;
560
561 /* SCM_CALL to check if app_id for the mentioned app exists */
562 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
563 sizeof(struct qseecom_check_app_ireq),
564 &resp, sizeof(resp));
565 if (ret) {
566 pr_err("scm_call to check if app is already loaded failed\n");
567 return -EINVAL;
568 }
569
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700570 if (resp.result == QSEOS_RESULT_FAILURE) {
571 return 0;
572 } else {
573 switch (resp.resp_type) {
574 /*qsee returned listener type response */
575 case QSEOS_LISTENER_ID:
576 pr_err("resp type is of listener type instead of app");
577 return -EINVAL;
578 break;
579 case QSEOS_APP_ID:
580 return resp.data;
581 default:
582 pr_err("invalid resp type (%d) from qsee",
583 resp.resp_type);
584 return -ENODEV;
585 break;
586 }
587 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700588}
589
Mona Hossain2892b6b2012-02-17 13:53:11 -0800590static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
591{
592 struct qseecom_registered_app_list *entry = NULL;
593 unsigned long flags = 0;
594 u32 app_id = 0;
595 struct ion_handle *ihandle; /* Ion handle */
596 struct qseecom_load_img_req load_img_req;
597 int32_t ret;
598 ion_phys_addr_t pa = 0;
599 uint32_t len;
600 struct qseecom_command_scm_resp resp;
Mona Hossain436b75f2012-11-20 17:10:40 -0800601 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700602 struct qseecom_load_app_ireq load_req;
603
Mona Hossain2892b6b2012-02-17 13:53:11 -0800604 /* Copy the relevant information needed for loading the image */
605 if (__copy_from_user(&load_img_req,
606 (void __user *)argp,
607 sizeof(struct qseecom_load_img_req))) {
608 pr_err("copy_from_user failed\n");
609 return -EFAULT;
610 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700611 /* Vote for the SFPB clock */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800612 ret = qsee_vote_for_clock(data, CLK_SFPB);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700613 if (ret)
614 pr_warning("Unable to vote for SFPB clock");
Mona Hossain436b75f2012-11-20 17:10:40 -0800615 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
616 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800617
Mona Hossain436b75f2012-11-20 17:10:40 -0800618 ret = __qseecom_check_app_exists(req);
619 if (ret < 0)
620 return ret;
621 else
622 app_id = ret;
623
624 if (app_id) {
625 pr_warn("App id %d (%s) already exists\n", app_id,
626 (char *)(req.app_name));
627 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
628 list_for_each_entry(entry,
629 &qseecom.registered_app_list_head, list){
630 if (entry->app_id == app_id) {
631 entry->ref_cnt++;
632 break;
633 }
634 }
635 spin_unlock_irqrestore(
636 &qseecom.registered_app_list_lock, flags);
637 } else {
638 pr_warn("App (%s) does'nt exist, loading apps for first time\n",
Mona Hossaind44a3842012-10-15 09:41:35 -0700639 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800640 /* Get the handle of the shared fd */
641 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800642 load_img_req.ifd_data_fd);
Mona Hossain436b75f2012-11-20 17:10:40 -0800643 if (IS_ERR_OR_NULL(ihandle)) {
644 pr_err("Ion client could not retrieve the handle\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800645 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800646 return -ENOMEM;
647 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800648
Mona Hossain436b75f2012-11-20 17:10:40 -0800649 /* Get the physical address of the ION BUF */
650 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800651
Mona Hossain436b75f2012-11-20 17:10:40 -0800652 /* Populate the structure for sending scm call to load image */
653 memcpy(load_req.app_name, load_img_req.img_name,
654 MAX_APP_NAME_SIZE);
655 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
656 load_req.mdt_len = load_img_req.mdt_len;
657 load_req.img_len = load_img_req.img_len;
658 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800659
Mona Hossain436b75f2012-11-20 17:10:40 -0800660 /* SCM_CALL to load the app and get the app_id back */
661 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700662 sizeof(struct qseecom_load_app_ireq),
663 &resp, sizeof(resp));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700664 if (ret) {
Mona Hossain436b75f2012-11-20 17:10:40 -0800665 pr_err("scm_call to load app failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -0800666 if (!IS_ERR_OR_NULL(ihandle))
667 ion_free(qseecom.ion_clnt, ihandle);
668 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800669 return -EINVAL;
670 }
671
672 if (resp.result == QSEOS_RESULT_FAILURE) {
673 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700674 if (!IS_ERR_OR_NULL(ihandle))
675 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800676 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800677 return -EFAULT;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700678 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700679
Mona Hossain436b75f2012-11-20 17:10:40 -0800680 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
681 ret = __qseecom_process_incomplete_cmd(data, &resp);
682 if (ret) {
683 pr_err("process_incomplete_cmd failed err: %d\n",
684 ret);
685 if (!IS_ERR_OR_NULL(ihandle))
686 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800687 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800688 return ret;
689 }
690 }
691
692 if (resp.result != QSEOS_RESULT_SUCCESS) {
693 pr_err("scm_call failed resp.result unknown, %d\n",
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700694 resp.result);
Mona Hossain436b75f2012-11-20 17:10:40 -0800695 if (!IS_ERR_OR_NULL(ihandle))
696 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800697 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800698 return -EFAULT;
699 }
700
701 app_id = resp.data;
702
703 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
704 if (!entry) {
705 pr_err("kmalloc failed\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800706 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800707 return -ENOMEM;
708 }
709 entry->app_id = app_id;
710 entry->ref_cnt = 1;
711
712 /* Deallocate the handle */
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700713 if (!IS_ERR_OR_NULL(ihandle))
714 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700715
Mona Hossain436b75f2012-11-20 17:10:40 -0800716 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
717 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
718 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
719 flags);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700720
Mona Hossain436b75f2012-11-20 17:10:40 -0800721 pr_warn("App with id %d (%s) now loaded\n", app_id,
Mona Hossaind44a3842012-10-15 09:41:35 -0700722 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800723 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800724 data->client.app_id = app_id;
725 load_img_req.app_id = app_id;
726 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
727 pr_err("copy_to_user failed\n");
728 kzfree(entry);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800729 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800730 return -EFAULT;
731 }
Mona Hossain04d3fac2012-12-03 10:10:37 -0800732 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800733 return 0;
734}
735
736static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
737{
738 wake_up_all(&qseecom.send_resp_wq);
739 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700740 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800741 atomic_read(&data->ioctl_count) <= 1)) {
742 pr_err("Interrupted from abort\n");
743 return -ERESTARTSYS;
744 break;
745 }
746 }
747 /* Set unload app */
748 return 1;
749}
750
751static int qseecom_unload_app(struct qseecom_dev_handle *data)
752{
753 unsigned long flags;
754 int ret = 0;
755 struct qseecom_command_scm_resp resp;
756 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700757 bool unload = false;
758 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800759
Mona Hossain1fb538f2012-08-30 16:19:38 -0700760 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
761 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800762 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
763 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
764 list) {
765 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700766 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800767 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700768 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800769 break;
770 } else {
771 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700772 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700773 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800774 break;
775 }
776 }
777 }
778 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
779 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700780 if (found_app == false) {
781 pr_err("Cannot find app with id = %d\n",
782 data->client.app_id);
783 return -EINVAL;
784 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800785 }
786
787 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
788 struct qseecom_unload_app_ireq req;
789
Mona Hossain340dba82012-08-07 19:54:46 -0700790 __qseecom_cleanup_app(data);
791 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
792 list_del(&ptr_app->list);
793 kzfree(ptr_app);
794 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
795 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800796 /* Populate the structure for sending scm call to load image */
797 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
798 req.app_id = data->client.app_id;
799
800 /* SCM_CALL to unload the app */
801 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
802 sizeof(struct qseecom_unload_app_ireq),
803 &resp, sizeof(resp));
804 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700805 pr_err("scm_call to unload app (id = %d) failed\n",
806 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800807 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700808 } else {
809 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800810 }
811 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
812 ret = __qseecom_process_incomplete_cmd(data, &resp);
813 if (ret) {
814 pr_err("process_incomplete_cmd fail err: %d\n",
815 ret);
816 return ret;
817 }
818 }
819 }
820
821 if (qseecom.qseos_version == QSEOS_VERSION_13) {
822 data->abort = 1;
823 wake_up_all(&qseecom.send_resp_wq);
824 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700825 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800826 atomic_read(&data->ioctl_count) <= 0)) {
827 pr_err("Interrupted from abort\n");
828 ret = -ERESTARTSYS;
829 break;
830 }
831 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800832 }
Mona Hossain340dba82012-08-07 19:54:46 -0700833 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
834 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
835 ion_free(qseecom.ion_clnt, data->client.ihandle);
836 data->client.ihandle = NULL;
837 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800838 data->released = true;
839 return ret;
840}
841
842static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
843 uint32_t virt)
844{
845 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
846}
847
848static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
849 struct qseecom_send_cmd_req *req)
850{
851 int ret = 0;
852 unsigned long flags;
853 u32 reqd_len_sb_in = 0;
854 struct qseecom_command cmd;
855 struct qseecom_response resp;
856
857
858 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
859 pr_err("cmd buffer or response buffer is null\n");
860 return -EINVAL;
861 }
862
863 if (req->cmd_req_len <= 0 ||
864 req->resp_len <= 0 ||
865 req->cmd_req_len > data->client.sb_length ||
866 req->resp_len > data->client.sb_length) {
867 pr_err("cmd buffer length or "
868 "response buffer length not valid\n");
869 return -EINVAL;
870 }
871
872 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
873 if (reqd_len_sb_in > data->client.sb_length) {
874 pr_debug("Not enough memory to fit cmd_buf and "
875 "resp_buf. Required: %u, Available: %u\n",
876 reqd_len_sb_in, data->client.sb_length);
877 return -ENOMEM;
878 }
879 cmd.cmd_type = TZ_SCHED_CMD_NEW;
880 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
881 cmd.sb_in_cmd_len = req->cmd_req_len;
882
883 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
884 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
885 resp.sb_in_rsp_len = req->resp_len;
886
887 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
888 sizeof(cmd), &resp, sizeof(resp));
889
890 if (ret) {
891 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
892 return ret;
893 }
894
895 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
896 /*
897 * If cmd is incomplete, get the callback cmd out from SB out
898 * and put it on the list
899 */
900 struct qseecom_registered_listener_list *ptr_svc = NULL;
901 /*
902 * We don't know which service can handle the command. so we
903 * wake up all blocking services and let them figure out if
904 * they can handle the given command.
905 */
906 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
907 flags);
908 list_for_each_entry(ptr_svc,
909 &qseecom.registered_listener_list_head, list) {
910 ptr_svc->rcv_req_flag = 1;
911 wake_up_interruptible(&ptr_svc->rcv_req_wq);
912 }
913 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
914 flags);
915
916 pr_debug("waking up rcv_req_wq and "
917 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700918 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800919 __qseecom_listener_has_sent_rsp(data))) {
920 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
921 return -ERESTARTSYS;
922 }
923
924 if (data->abort) {
925 pr_err("Aborting driver\n");
926 return -ENODEV;
927 }
928 qseecom.send_resp_flag = 0;
929 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
930 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
931 sizeof(cmd), &resp, sizeof(resp));
932 if (ret) {
933 pr_err("qseecom_scm_call failed with err: %d\n", ret);
934 return ret;
935 }
936 }
937 return ret;
938}
939
940static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
941 struct qseecom_send_cmd_req *req)
942{
943 int ret = 0;
944 u32 reqd_len_sb_in = 0;
945 struct qseecom_client_send_data_ireq send_data_req;
946 struct qseecom_command_scm_resp resp;
947
948 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
949 pr_err("cmd buffer or response buffer is null\n");
950 return -EINVAL;
951 }
952
953 if (req->cmd_req_len <= 0 ||
954 req->resp_len <= 0 ||
955 req->cmd_req_len > data->client.sb_length ||
956 req->resp_len > data->client.sb_length) {
957 pr_err("cmd buffer length or "
958 "response buffer length not valid\n");
959 return -EINVAL;
960 }
961
962 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
963 if (reqd_len_sb_in > data->client.sb_length) {
964 pr_debug("Not enough memory to fit cmd_buf and "
965 "resp_buf. Required: %u, Available: %u\n",
966 reqd_len_sb_in, data->client.sb_length);
967 return -ENOMEM;
968 }
969
970 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
971 send_data_req.app_id = data->client.app_id;
972 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
973 (uint32_t)req->cmd_req_buf));
974 send_data_req.req_len = req->cmd_req_len;
975 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
976 (uint32_t)req->resp_buf));
977 send_data_req.rsp_len = req->resp_len;
978
979 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
980 sizeof(send_data_req),
981 &resp, sizeof(resp));
982 if (ret) {
983 pr_err("qseecom_scm_call failed with err: %d\n", ret);
984 return ret;
985 }
986
987 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
988 ret = __qseecom_process_incomplete_cmd(data, &resp);
989 if (ret) {
990 pr_err("process_incomplete_cmd failed err: %d\n", ret);
991 return ret;
992 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700993 } else {
994 if (resp.result != QSEOS_RESULT_SUCCESS) {
995 pr_err("Response result %d not supported\n",
996 resp.result);
997 ret = -EINVAL;
998 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800999 }
1000 return ret;
1001}
1002
1003
1004static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1005{
1006 int ret = 0;
1007 struct qseecom_send_cmd_req req;
1008
1009 ret = copy_from_user(&req, argp, sizeof(req));
1010 if (ret) {
1011 pr_err("copy_from_user failed\n");
1012 return ret;
1013 }
1014 if (qseecom.qseos_version == QSEOS_VERSION_14)
1015 ret = __qseecom_send_cmd(data, &req);
1016 else
1017 ret = __qseecom_send_cmd_legacy(data, &req);
1018 if (ret)
1019 return ret;
1020
1021 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1022 req.resp_len, req.resp_buf);
1023 return ret;
1024}
1025
1026static int __qseecom_send_cmd_req_clean_up(
1027 struct qseecom_send_modfd_cmd_req *req)
1028{
1029 char *field;
1030 uint32_t *update;
1031 int ret = 0;
1032 int i = 0;
1033
1034 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001035 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001036 field = (char *)req->cmd_req_buf +
1037 req->ifd_data[i].cmd_buf_offset;
1038 update = (uint32_t *) field;
1039 *update = 0;
1040 }
1041 }
1042 return ret;
1043}
1044
1045static int __qseecom_update_with_phy_addr(
1046 struct qseecom_send_modfd_cmd_req *req)
1047{
1048 struct ion_handle *ihandle;
1049 char *field;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001050 int ret = 0;
1051 int i = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001052
1053 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001054 struct sg_table *sg_ptr = NULL;
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001055 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001056 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001057 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001058 req->ifd_data[i].fd);
1059 if (IS_ERR_OR_NULL(ihandle)) {
1060 pr_err("Ion client can't retrieve the handle\n");
1061 return -ENOMEM;
1062 }
1063 field = (char *) req->cmd_req_buf +
1064 req->ifd_data[i].cmd_buf_offset;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001065
1066 /* Populate the cmd data structure with the phys_addr */
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001067 sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
1068 if (sg_ptr == NULL) {
1069 pr_err("IOn client could not retrieve sg table\n");
1070 goto err;
1071 }
1072 if (sg_ptr->nents == 0) {
1073 pr_err("Num of scattered entries is 0\n");
1074 goto err;
1075 }
1076 if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
1077 pr_err("Num of scattered entries");
1078 pr_err(" (%d) is greater than max supported %d\n",
1079 sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
1080 goto err;
1081 }
1082 if (sg_ptr->nents == 1) {
1083 uint32_t *update;
1084 update = (uint32_t *) field;
1085 *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
1086 } else {
1087 struct qseecom_sg_entry *update;
1088 struct scatterlist *sg;
1089 int j = 0;
1090 update = (struct qseecom_sg_entry *) field;
1091 sg = sg_ptr->sgl;
1092 for (j = 0; j < sg_ptr->nents; j++) {
1093 update->phys_addr = (uint32_t)
1094 sg_dma_address(sg);
1095 update->len = (uint32_t)sg->length;
1096 update++;
1097 sg = sg_next(sg);
1098 }
1099 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001100 /* Deallocate the handle */
1101 if (!IS_ERR_OR_NULL(ihandle))
1102 ion_free(qseecom.ion_clnt, ihandle);
1103 }
1104 }
1105 return ret;
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001106err:
1107 if (!IS_ERR_OR_NULL(ihandle))
1108 ion_free(qseecom.ion_clnt, ihandle);
1109 return -ENOMEM;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001110}
1111
1112static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1113 void __user *argp)
1114{
1115 int ret = 0;
1116 struct qseecom_send_modfd_cmd_req req;
1117 struct qseecom_send_cmd_req send_cmd_req;
1118
1119 ret = copy_from_user(&req, argp, sizeof(req));
1120 if (ret) {
1121 pr_err("copy_from_user failed\n");
1122 return ret;
1123 }
1124 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1125 send_cmd_req.cmd_req_len = req.cmd_req_len;
1126 send_cmd_req.resp_buf = req.resp_buf;
1127 send_cmd_req.resp_len = req.resp_len;
1128
1129 ret = __qseecom_update_with_phy_addr(&req);
1130 if (ret)
1131 return ret;
1132 if (qseecom.qseos_version == QSEOS_VERSION_14)
1133 ret = __qseecom_send_cmd(data, &send_cmd_req);
1134 else
1135 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1136 __qseecom_send_cmd_req_clean_up(&req);
1137
1138 if (ret)
1139 return ret;
1140
1141 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1142 req.resp_len, req.resp_buf);
1143 return ret;
1144}
1145
1146static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1147 struct qseecom_registered_listener_list *svc)
1148{
1149 int ret;
1150 ret = (svc->rcv_req_flag != 0);
1151 return ret || data->abort;
1152}
1153
1154static int qseecom_receive_req(struct qseecom_dev_handle *data)
1155{
1156 int ret = 0;
1157 struct qseecom_registered_listener_list *this_lstnr;
1158
1159 this_lstnr = __qseecom_find_svc(data->listener.id);
1160 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001161 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001162 __qseecom_listener_has_rcvd_req(data,
1163 this_lstnr))) {
1164 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1165 /* woken up for different reason */
1166 return -ERESTARTSYS;
1167 }
1168
1169 if (data->abort) {
1170 pr_err("Aborting driver!\n");
1171 return -ENODEV;
1172 }
1173 this_lstnr->rcv_req_flag = 0;
1174 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1175 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1176 break;
1177 } else {
1178 break;
1179 }
1180 }
1181 return ret;
1182}
1183
Mona Hossaind44a3842012-10-15 09:41:35 -07001184static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
1185{
1186 struct elf32_hdr *ehdr;
1187
1188 if (fw_entry->size < sizeof(*ehdr)) {
1189 pr_err("%s: Not big enough to be an elf header\n",
1190 qseecom.pdev->init_name);
1191 return false;
1192 }
1193 ehdr = (struct elf32_hdr *)fw_entry->data;
1194 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
1195 pr_err("%s: Not an elf header\n",
1196 qseecom.pdev->init_name);
1197 return false;
1198 }
1199
1200 if (ehdr->e_phnum == 0) {
1201 pr_err("%s: No loadable segments\n",
1202 qseecom.pdev->init_name);
1203 return false;
1204 }
1205 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
1206 sizeof(struct elf32_hdr) > fw_entry->size) {
1207 pr_err("%s: Program headers not within mdt\n",
1208 qseecom.pdev->init_name);
1209 return false;
1210 }
1211 return true;
1212}
1213
1214static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size)
1215{
1216 int ret = -1;
1217 int i = 0, rc = 0;
1218 const struct firmware *fw_entry = NULL;
1219 struct elf32_phdr *phdr;
1220 char fw_name[MAX_APP_NAME_SIZE];
1221 struct elf32_hdr *ehdr;
1222 int num_images = 0;
1223
1224 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1225 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1226 if (rc) {
1227 pr_err("error with request_firmware\n");
1228 ret = -EIO;
1229 goto err;
1230 }
1231 if (!__qseecom_is_fw_image_valid(fw_entry)) {
1232 ret = -EIO;
1233 goto err;
1234 }
1235 *fw_size = fw_entry->size;
1236 phdr = (struct elf32_phdr *)(fw_entry->data + sizeof(struct elf32_hdr));
1237 ehdr = (struct elf32_hdr *)fw_entry->data;
1238 num_images = ehdr->e_phnum;
1239 release_firmware(fw_entry);
1240 for (i = 0; i < num_images; i++, phdr++) {
1241 memset(fw_name, 0, sizeof(fw_name));
1242 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1243 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1244 if (ret)
1245 goto err;
1246 *fw_size += fw_entry->size;
1247 release_firmware(fw_entry);
1248 }
1249 return ret;
1250err:
1251 if (fw_entry)
1252 release_firmware(fw_entry);
1253 *fw_size = 0;
1254 return ret;
1255}
1256
1257static int __qseecom_get_fw_data(char *appname, u8 *img_data,
1258 struct qseecom_load_app_ireq *load_req)
1259{
1260 int ret = -1;
1261 int i = 0, rc = 0;
1262 const struct firmware *fw_entry = NULL;
1263 char fw_name[MAX_APP_NAME_SIZE];
1264 u8 *img_data_ptr = img_data;
1265 struct elf32_hdr *ehdr;
1266 int num_images = 0;
1267
1268 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1269 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1270 if (rc) {
1271 ret = -EIO;
1272 goto err;
1273 }
1274 load_req->img_len = fw_entry->size;
1275 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1276 img_data_ptr = img_data_ptr + fw_entry->size;
1277 load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
1278 ehdr = (struct elf32_hdr *)fw_entry->data;
1279 num_images = ehdr->e_phnum;
1280 release_firmware(fw_entry);
1281 for (i = 0; i < num_images; i++) {
1282 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1283 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1284 if (ret) {
1285 pr_err("Failed to locate blob %s\n", fw_name);
1286 goto err;
1287 }
1288 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1289 img_data_ptr = img_data_ptr + fw_entry->size;
1290 load_req->img_len += fw_entry->size;
1291 release_firmware(fw_entry);
1292 }
1293 load_req->phy_addr = virt_to_phys(img_data);
1294 return ret;
1295err:
1296 release_firmware(fw_entry);
1297 return ret;
1298}
1299
1300static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
1301{
1302 int ret = -1;
1303 uint32_t fw_size = 0;
1304 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1305 struct qseecom_command_scm_resp resp;
1306 u8 *img_data = NULL;
1307
1308 if (__qseecom_get_fw_size(appname, &fw_size))
1309 return -EIO;
1310
1311 img_data = kzalloc(fw_size, GFP_KERNEL);
1312 if (!img_data) {
1313 pr_err("Failied to allocate memory for copying image data\n");
1314 return -ENOMEM;
1315 }
1316 ret = __qseecom_get_fw_data(appname, img_data, &load_req);
1317 if (ret) {
1318 kzfree(img_data);
1319 return -EIO;
1320 }
1321
1322 /* Populate the remaining parameters */
1323 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
1324 memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001325 ret = qsee_vote_for_clock(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001326 if (ret) {
1327 kzfree(img_data);
1328 pr_warning("Unable to vote for SFPB clock");
Mona Hossain60f9fb02012-11-05 13:51:50 -08001329 return -EIO;
1330 }
1331
Mona Hossaind44a3842012-10-15 09:41:35 -07001332 /* SCM_CALL to load the image */
1333 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1334 sizeof(struct qseecom_load_app_ireq),
1335 &resp, sizeof(resp));
1336 kzfree(img_data);
1337 if (ret) {
1338 pr_err("scm_call to load failed : ret %d\n", ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001339 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001340 return -EIO;
1341 }
1342
1343 switch (resp.result) {
1344 case QSEOS_RESULT_SUCCESS:
1345 ret = resp.data;
1346 break;
1347 case QSEOS_RESULT_INCOMPLETE:
1348 ret = __qseecom_process_incomplete_cmd(data, &resp);
1349 if (ret)
1350 pr_err("process_incomplete_cmd FAILED\n");
1351 else
1352 ret = resp.data;
1353 break;
1354 case QSEOS_RESULT_FAILURE:
1355 pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
1356 break;
1357 default:
1358 pr_err("scm call return unknown response %d\n", resp.result);
1359 ret = -EINVAL;
1360 break;
1361 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08001362 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001363
Mona Hossaind44a3842012-10-15 09:41:35 -07001364 return ret;
1365}
1366
Mona Hossain9498f5e2013-01-23 18:08:45 -08001367static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
Mona Hossain05c73562012-10-29 17:49:01 -07001368{
1369 int32_t ret = 0;
1370 uint32_t fw_size = 0;
1371 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1372 struct qseecom_command_scm_resp resp;
1373 u8 *img_data = NULL;
1374
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001375 if (__qseecom_get_fw_size("cmnlib", &fw_size))
Mona Hossain05c73562012-10-29 17:49:01 -07001376 return -EIO;
1377
1378 img_data = kzalloc(fw_size, GFP_KERNEL);
1379 if (!img_data) {
1380 pr_err("Mem allocation for lib image data failed\n");
1381 return -ENOMEM;
1382 }
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001383 ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
Mona Hossain05c73562012-10-29 17:49:01 -07001384 if (ret) {
1385 kzfree(img_data);
1386 return -EIO;
1387 }
1388 /* Populate the remaining parameters */
1389 load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
1390 /* SCM_CALL to load the image */
1391 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1392 sizeof(struct qseecom_load_lib_image_ireq),
1393 &resp, sizeof(resp));
Mona Hossain05c73562012-10-29 17:49:01 -07001394 if (ret) {
1395 pr_err("scm_call to load failed : ret %d\n", ret);
1396 ret = -EIO;
1397 } else {
1398 switch (resp.result) {
1399 case QSEOS_RESULT_SUCCESS:
1400 break;
1401 case QSEOS_RESULT_FAILURE:
1402 pr_err("scm call failed w/response result%d\n",
1403 resp.result);
1404 ret = -EINVAL;
1405 break;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001406 case QSEOS_RESULT_INCOMPLETE:
1407 ret = __qseecom_process_incomplete_cmd(data, &resp);
1408 if (ret)
1409 pr_err("process_incomplete_cmd failed err: %d\n",
1410 ret);
1411 break;
Mona Hossain05c73562012-10-29 17:49:01 -07001412 default:
1413 pr_err("scm call return unknown response %d\n",
1414 resp.result);
1415 ret = -EINVAL;
1416 break;
1417 }
1418 }
Hariprasad Dhalinarasimha1a81ca32013-01-31 18:32:32 -08001419 kzfree(img_data);
Mona Hossain05c73562012-10-29 17:49:01 -07001420 return ret;
1421}
1422
1423static int qseecom_unload_commonlib_image(void)
1424{
1425 int ret = -EINVAL;
1426 struct qseecom_unload_lib_image_ireq unload_req = {0};
1427 struct qseecom_command_scm_resp resp;
1428
1429 /* Populate the remaining parameters */
1430 unload_req.qsee_cmd_id = QSEOS_UNLOAD_SERV_IMAGE_COMMAND;
1431 /* SCM_CALL to load the image */
1432 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &unload_req,
1433 sizeof(struct qseecom_unload_lib_image_ireq),
1434 &resp, sizeof(resp));
1435 if (ret) {
1436 pr_err("scm_call to unload lib failed : ret %d\n", ret);
1437 ret = -EIO;
1438 } else {
1439 switch (resp.result) {
1440 case QSEOS_RESULT_SUCCESS:
1441 break;
1442 case QSEOS_RESULT_FAILURE:
1443 pr_err("scm fail resp.result QSEOS_RESULT FAILURE\n");
1444 break;
1445 default:
1446 pr_err("scm call return unknown response %d\n",
1447 resp.result);
1448 ret = -EINVAL;
1449 break;
1450 }
1451 }
1452 return ret;
1453}
1454
Mona Hossaind44a3842012-10-15 09:41:35 -07001455int qseecom_start_app(struct qseecom_handle **handle,
1456 char *app_name, uint32_t size)
1457{
Mona Hossain05c73562012-10-29 17:49:01 -07001458 int32_t ret = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07001459 unsigned long flags = 0;
1460 struct qseecom_dev_handle *data = NULL;
1461 struct qseecom_check_app_ireq app_ireq;
1462 struct qseecom_registered_app_list *entry = NULL;
1463 struct qseecom_registered_kclient_list *kclient_entry = NULL;
1464 bool found_app = false;
1465 uint32_t len;
1466 ion_phys_addr_t pa;
1467
1468 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1469 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1470 return -EINVAL;
1471 }
1472
Mona Hossain823f9882012-11-23 14:42:20 -08001473 *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
1474 if (!(*handle)) {
1475 pr_err("failed to allocate memory for kernel client handle\n");
1476 return -ENOMEM;
1477 }
1478
Mona Hossaind44a3842012-10-15 09:41:35 -07001479 data = kzalloc(sizeof(*data), GFP_KERNEL);
1480 if (!data) {
1481 pr_err("kmalloc failed\n");
1482 if (ret == 0) {
1483 kfree(*handle);
1484 *handle = NULL;
1485 }
1486 return -ENOMEM;
1487 }
1488 data->abort = 0;
1489 data->service = false;
1490 data->released = false;
1491 data->client.app_id = ret;
1492 data->client.sb_length = size;
1493 data->client.user_virt_sb_base = 0;
1494 data->client.ihandle = NULL;
1495
1496 init_waitqueue_head(&data->abort_wq);
1497 atomic_set(&data->ioctl_count, 0);
1498
1499 data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
1500 ION_HEAP(ION_QSECOM_HEAP_ID), 0);
1501 if (IS_ERR_OR_NULL(data->client.ihandle)) {
1502 pr_err("Ion client could not retrieve the handle\n");
1503 kfree(data);
1504 kfree(*handle);
1505 *handle = NULL;
1506 return -EINVAL;
1507 }
1508
Mona Hossain9498f5e2013-01-23 18:08:45 -08001509 if (qseecom.qsee_version > QSEEE_VERSION_00) {
1510 mutex_lock(&app_access_lock);
1511 if (qseecom.commonlib_loaded == false) {
1512 ret = qseecom_load_commonlib_image(data);
1513 if (ret == 0)
1514 qseecom.commonlib_loaded = true;
1515 }
1516 mutex_unlock(&app_access_lock);
1517 }
1518
1519 if (ret) {
1520 pr_err("Failed to loadd commonlib image\n");
1521 kfree(data);
1522 kfree(*handle);
1523 *handle = NULL;
1524 return -EIO;
1525 }
1526
1527 app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1528 memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
1529 ret = __qseecom_check_app_exists(app_ireq);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001530 if (ret < 0) {
1531 kzfree(data);
1532 kfree(*handle);
1533 *handle = NULL;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001534 return -EINVAL;
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001535 }
Mona Hossain9498f5e2013-01-23 18:08:45 -08001536
Mona Hossaind44a3842012-10-15 09:41:35 -07001537 if (ret > 0) {
1538 pr_warn("App id %d for [%s] app exists\n", ret,
1539 (char *)app_ireq.app_name);
1540 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1541 list_for_each_entry(entry,
1542 &qseecom.registered_app_list_head, list){
1543 if (entry->app_id == ret) {
1544 entry->ref_cnt++;
1545 found_app = true;
1546 break;
1547 }
1548 }
1549 spin_unlock_irqrestore(
1550 &qseecom.registered_app_list_lock, flags);
1551 if (!found_app)
1552 pr_warn("App_id %d [%s] was loaded but not registered\n",
1553 ret, (char *)app_ireq.app_name);
1554 } else {
1555 /* load the app and get the app_id */
1556 pr_debug("%s: Loading app for the first time'\n",
1557 qseecom.pdev->init_name);
1558 mutex_lock(&app_access_lock);
1559 ret = __qseecom_load_fw(data, app_name);
1560 mutex_unlock(&app_access_lock);
1561
1562 if (ret < 0) {
1563 kfree(*handle);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001564 kfree(data);
Mona Hossaind44a3842012-10-15 09:41:35 -07001565 *handle = NULL;
1566 return ret;
1567 }
1568 data->client.app_id = ret;
1569 }
1570 if (!found_app) {
1571 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1572 if (!entry) {
1573 pr_err("kmalloc failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001574 kfree(data);
1575 kfree(*handle);
1576 *handle = NULL;
Mona Hossaind44a3842012-10-15 09:41:35 -07001577 return -ENOMEM;
1578 }
1579 entry->app_id = ret;
1580 entry->ref_cnt = 1;
1581
1582 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1583 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
1584 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
1585 flags);
1586 }
1587
1588 /* Get the physical address of the ION BUF */
1589 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
1590 /* Populate the structure for sending scm call to load image */
1591 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
1592 data->client.ihandle);
Hariprasad Dhalinarasimhaacfb09c2013-01-10 13:16:15 -08001593 data->client.user_virt_sb_base = (uint32_t)data->client.sb_virt;
Mona Hossaind44a3842012-10-15 09:41:35 -07001594 data->client.sb_phys = pa;
1595 (*handle)->dev = (void *)data;
1596 (*handle)->sbuf = (unsigned char *)data->client.sb_virt;
1597 (*handle)->sbuf_len = data->client.sb_length;
1598
1599 kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
1600 if (!kclient_entry) {
1601 pr_err("kmalloc failed\n");
1602 return -ENOMEM;
1603 }
1604 kclient_entry->handle = *handle;
1605
1606 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1607 list_add_tail(&kclient_entry->list,
1608 &qseecom.registered_kclient_list_head);
1609 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1610
1611 return 0;
1612}
1613EXPORT_SYMBOL(qseecom_start_app);
1614
1615int qseecom_shutdown_app(struct qseecom_handle **handle)
1616{
1617 int ret = -EINVAL;
Mona Hossain33824022013-02-25 09:32:33 -08001618 struct qseecom_dev_handle *data;
1619
Mona Hossaind44a3842012-10-15 09:41:35 -07001620 struct qseecom_registered_kclient_list *kclient = NULL;
1621 unsigned long flags = 0;
1622 bool found_handle = false;
1623
1624 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1625 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1626 return -EINVAL;
1627 }
Mona Hossain33824022013-02-25 09:32:33 -08001628 if ((handle == NULL) || (*handle == NULL)) {
Mona Hossaind44a3842012-10-15 09:41:35 -07001629 pr_err("Handle is not initialized\n");
1630 return -EINVAL;
1631 }
Mona Hossain33824022013-02-25 09:32:33 -08001632 data = (struct qseecom_dev_handle *) ((*handle)->dev);
Mona Hossaind44a3842012-10-15 09:41:35 -07001633 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1634 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
1635 list) {
1636 if (kclient->handle == (*handle)) {
1637 list_del(&kclient->list);
1638 found_handle = true;
1639 break;
1640 }
1641 }
1642 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1643 if (!found_handle)
1644 pr_err("Unable to find the handle, exiting\n");
1645 else
1646 ret = qseecom_unload_app(data);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001647 if (data->client.fast_load_enabled == true)
1648 qsee_disable_clock_vote(data, CLK_SFPB);
1649 if (data->client.perf_enabled == true)
1650 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001651 if (ret == 0) {
1652 kzfree(data);
1653 kzfree(*handle);
1654 kzfree(kclient);
1655 *handle = NULL;
1656 }
1657 return ret;
1658}
1659EXPORT_SYMBOL(qseecom_shutdown_app);
1660
1661int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
1662 uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
1663{
1664 int ret = 0;
1665 struct qseecom_send_cmd_req req = {0, 0, 0, 0};
1666 struct qseecom_dev_handle *data;
1667
1668 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1669 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1670 return -EINVAL;
1671 }
1672
1673 if (handle == NULL) {
1674 pr_err("Handle is not initialized\n");
1675 return -EINVAL;
1676 }
1677 data = handle->dev;
1678
1679 req.cmd_req_len = sbuf_len;
1680 req.resp_len = rbuf_len;
1681 req.cmd_req_buf = send_buf;
1682 req.resp_buf = resp_buf;
1683
1684 mutex_lock(&app_access_lock);
1685 atomic_inc(&data->ioctl_count);
1686
1687 ret = __qseecom_send_cmd(data, &req);
1688
1689 atomic_dec(&data->ioctl_count);
1690 mutex_unlock(&app_access_lock);
1691
1692 if (ret)
1693 return ret;
1694
1695 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1696 req.resp_len, req.resp_buf);
1697 return ret;
1698}
1699EXPORT_SYMBOL(qseecom_send_command);
1700
Mona Hossain91a8fc92012-11-07 19:58:30 -08001701int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
1702{
Mona Hossainfca6f422013-01-12 13:00:35 -08001703 int ret = 0;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001704 if ((handle == NULL) || (handle->dev == NULL)) {
1705 pr_err("No valid kernel client\n");
1706 return -EINVAL;
1707 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001708 if (high) {
1709 ret = qsee_vote_for_clock(handle->dev, CLK_DFAB);
1710 if (ret)
1711 pr_err("Failed to vote for DFAB clock%d\n", ret);
1712 ret = qsee_vote_for_clock(handle->dev, CLK_SFPB);
1713 if (ret) {
1714 pr_err("Failed to vote for SFPB clock%d\n", ret);
1715 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
1716 }
1717 } else {
Mona Hossain04d3fac2012-12-03 10:10:37 -08001718 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
Mona Hossainfca6f422013-01-12 13:00:35 -08001719 qsee_disable_clock_vote(handle->dev, CLK_SFPB);
Mona Hossain91a8fc92012-11-07 19:58:30 -08001720 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001721 return ret;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001722}
1723EXPORT_SYMBOL(qseecom_set_bandwidth);
1724
Mona Hossain2892b6b2012-02-17 13:53:11 -08001725static int qseecom_send_resp(void)
1726{
1727 qseecom.send_resp_flag = 1;
1728 wake_up_interruptible(&qseecom.send_resp_wq);
1729 return 0;
1730}
1731
1732static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1733 void __user *argp)
1734{
1735 struct qseecom_qseos_version_req req;
1736
1737 if (copy_from_user(&req, argp, sizeof(req))) {
1738 pr_err("copy_from_user failed");
1739 return -EINVAL;
1740 }
1741 req.qseos_version = qseecom.qseos_version;
1742 if (copy_to_user(argp, &req, sizeof(req))) {
1743 pr_err("copy_to_user failed");
1744 return -EINVAL;
1745 }
1746 return 0;
1747}
1748
Mona Hossain04d3fac2012-12-03 10:10:37 -08001749static int qsee_vote_for_clock(struct qseecom_dev_handle *data,
1750 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001751{
1752 int ret = 0;
1753
1754 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001755 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001756
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001757 switch (clk_type) {
1758 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001759 mutex_lock(&qsee_bw_mutex);
1760 if (!qsee_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001761 if (qsee_sfpb_bw_count > 0)
1762 ret = msm_bus_scale_client_update_request(
1763 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001764 else {
1765 if (ce_core_src_clk != NULL)
1766 clk_set_rate(ce_core_src_clk,
1767 QSEE_CE_CLK_100MHZ);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001768 ret = msm_bus_scale_client_update_request(
1769 qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001770 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001771 if (ret)
1772 pr_err("DFAB Bandwidth req failed (%d)\n",
1773 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001774 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001775 qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001776 data->client.perf_enabled = true;
1777 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001778 } else {
1779 qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001780 data->client.perf_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001781 }
1782 mutex_unlock(&qsee_bw_mutex);
1783 break;
1784 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001785 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001786 if (!qsee_sfpb_bw_count) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001787 if (qsee_bw_count > 0)
1788 ret = msm_bus_scale_client_update_request(
1789 qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001790 else {
1791 if (ce_core_src_clk != NULL)
1792 clk_set_rate(ce_core_src_clk,
1793 QSEE_CE_CLK_100MHZ);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001794 ret = msm_bus_scale_client_update_request(
1795 qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001796 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001797
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001798 if (ret)
1799 pr_err("SFPB Bandwidth req failed (%d)\n",
1800 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001801 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001802 qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001803 data->client.fast_load_enabled = true;
1804 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001805 } else {
1806 qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001807 data->client.fast_load_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001808 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001809 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001810 break;
1811 default:
1812 pr_err("Clock type not defined\n");
1813 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001814 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001815 return ret;
1816}
1817
Mona Hossain04d3fac2012-12-03 10:10:37 -08001818static void qsee_disable_clock_vote(struct qseecom_dev_handle *data,
1819 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001820{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001821 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001822
Mona Hossain2892b6b2012-02-17 13:53:11 -08001823 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001824 return;
1825
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001826 switch (clk_type) {
1827 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001828 mutex_lock(&qsee_bw_mutex);
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001829 if (qsee_bw_count == 0) {
1830 pr_err("Client error.Extra call to disable DFAB clk\n");
1831 mutex_unlock(&qsee_bw_mutex);
1832 return;
1833 }
1834
Mona Hossain04d3fac2012-12-03 10:10:37 -08001835 if (qsee_bw_count == 1) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001836 if (qsee_sfpb_bw_count > 0)
1837 ret = msm_bus_scale_client_update_request(
1838 qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001839 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001840 ret = msm_bus_scale_client_update_request(
1841 qsee_perf_client, 0);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001842 if (ce_core_src_clk != NULL)
1843 clk_set_rate(ce_core_src_clk,
1844 QSEE_CE_CLK_50MHZ);
1845 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001846 if (ret)
1847 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001848 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001849 else {
1850 qsee_bw_count--;
1851 data->client.perf_enabled = false;
1852 }
1853 } else {
1854 qsee_bw_count--;
1855 data->client.perf_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001856 }
1857 mutex_unlock(&qsee_bw_mutex);
1858 break;
1859 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001860 mutex_lock(&qsee_bw_mutex);
1861 if (qsee_sfpb_bw_count == 0) {
1862 pr_err("Client error.Extra call to disable SFPB clk\n");
1863 mutex_unlock(&qsee_bw_mutex);
1864 return;
1865 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08001866 if (qsee_sfpb_bw_count == 1) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001867 if (qsee_bw_count > 0)
1868 ret = msm_bus_scale_client_update_request(
1869 qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001870 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001871 ret = msm_bus_scale_client_update_request(
1872 qsee_perf_client, 0);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001873 if (ce_core_src_clk != NULL)
1874 clk_set_rate(ce_core_src_clk,
1875 QSEE_CE_CLK_50MHZ);
1876 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001877 if (ret)
1878 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001879 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001880 else {
1881 qsee_sfpb_bw_count--;
1882 data->client.fast_load_enabled = false;
1883 }
1884 } else {
1885 qsee_sfpb_bw_count--;
1886 data->client.fast_load_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001887 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001888 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001889 break;
1890 default:
1891 pr_err("Clock type not defined\n");
1892 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001893 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001894
Mona Hossain2892b6b2012-02-17 13:53:11 -08001895}
1896
Mona Hossain5ab9d772012-04-11 21:00:40 -07001897static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1898 void __user *argp)
1899{
1900 struct ion_handle *ihandle; /* Ion handle */
1901 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001902 int ret;
1903 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001904 ion_phys_addr_t pa = 0;
1905 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001906 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001907 struct qseecom_load_app_ireq load_req;
1908 struct qseecom_command_scm_resp resp;
1909
1910 /* Copy the relevant information needed for loading the image */
1911 if (__copy_from_user(&load_img_req,
1912 (void __user *)argp,
1913 sizeof(struct qseecom_load_img_req))) {
1914 pr_err("copy_from_user failed\n");
1915 return -EFAULT;
1916 }
1917
1918 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001919 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001920 load_img_req.ifd_data_fd);
1921 if (IS_ERR_OR_NULL(ihandle)) {
1922 pr_err("Ion client could not retrieve the handle\n");
1923 return -ENOMEM;
1924 }
1925
1926 /* Get the physical address of the ION BUF */
1927 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1928
1929 /* Populate the structure for sending scm call to load image */
1930 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1931 load_req.mdt_len = load_img_req.mdt_len;
1932 load_req.img_len = load_img_req.img_len;
1933 load_req.phy_addr = pa;
1934
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001935 /* SCM_CALL tied to Core0 */
1936 mask = CPU_MASK_CPU0;
1937 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1938 if (set_cpu_ret) {
1939 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1940 set_cpu_ret);
1941 ret = -EFAULT;
1942 goto qseecom_load_external_elf_set_cpu_err;
1943 }
1944
Mona Hossain5ab9d772012-04-11 21:00:40 -07001945 /* SCM_CALL to load the external elf */
1946 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1947 sizeof(struct qseecom_load_app_ireq),
1948 &resp, sizeof(resp));
1949 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001950 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07001951 ret);
1952 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001953 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001954 }
1955
1956 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1957 ret = __qseecom_process_incomplete_cmd(data, &resp);
1958 if (ret)
1959 pr_err("process_incomplete_cmd failed err: %d\n",
1960 ret);
1961 } else {
1962 if (resp.result != QSEOS_RESULT_SUCCESS) {
1963 pr_err("scm_call to load image failed resp.result =%d\n",
1964 resp.result);
1965 ret = -EFAULT;
1966 }
1967 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001968
1969qseecom_load_external_elf_scm_err:
1970 /* Restore the CPU mask */
1971 mask = CPU_MASK_ALL;
1972 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1973 if (set_cpu_ret) {
1974 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1975 set_cpu_ret);
1976 ret = -EFAULT;
1977 }
1978
1979qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07001980 /* Deallocate the handle */
1981 if (!IS_ERR_OR_NULL(ihandle))
1982 ion_free(qseecom.ion_clnt, ihandle);
1983
1984 return ret;
1985}
1986
1987static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1988{
1989 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001990 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001991 struct qseecom_command_scm_resp resp;
1992 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001993 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001994
1995 /* Populate the structure for sending scm call to unload image */
1996 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001997
1998 /* SCM_CALL tied to Core0 */
1999 mask = CPU_MASK_CPU0;
2000 ret = set_cpus_allowed_ptr(current, &mask);
2001 if (ret) {
2002 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
2003 ret);
2004 return -EFAULT;
2005 }
2006
Mona Hossain5ab9d772012-04-11 21:00:40 -07002007 /* SCM_CALL to unload the external elf */
2008 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
2009 sizeof(struct qseecom_unload_app_ireq),
2010 &resp, sizeof(resp));
2011 if (ret) {
2012 pr_err("scm_call to unload failed : ret %d\n",
2013 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002014 ret = -EFAULT;
2015 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002016 }
2017 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2018 ret = __qseecom_process_incomplete_cmd(data, &resp);
2019 if (ret)
2020 pr_err("process_incomplete_cmd fail err: %d\n",
2021 ret);
2022 } else {
2023 if (resp.result != QSEOS_RESULT_SUCCESS) {
2024 pr_err("scm_call to unload image failed resp.result =%d\n",
2025 resp.result);
2026 ret = -EFAULT;
2027 }
2028 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002029
2030qseecom_unload_external_elf_scm_err:
2031 /* Restore the CPU mask */
2032 mask = CPU_MASK_ALL;
2033 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2034 if (set_cpu_ret) {
2035 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2036 set_cpu_ret);
2037 ret = -EFAULT;
2038 }
2039
Mona Hossain5ab9d772012-04-11 21:00:40 -07002040 return ret;
2041}
Mona Hossain2892b6b2012-02-17 13:53:11 -08002042
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002043static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
2044 void __user *argp)
2045{
2046
2047 int32_t ret;
2048 struct qseecom_qseos_app_load_query query_req;
2049 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002050 struct qseecom_registered_app_list *entry = NULL;
2051 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002052
2053 /* Copy the relevant information needed for loading the image */
2054 if (__copy_from_user(&query_req,
2055 (void __user *)argp,
2056 sizeof(struct qseecom_qseos_app_load_query))) {
2057 pr_err("copy_from_user failed\n");
2058 return -EFAULT;
2059 }
2060
2061 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
2062 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
2063
2064 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002065
2066 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002067 pr_err(" scm call to check if app is loaded failed");
2068 return ret; /* scm call failed */
2069 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002070 pr_warn("App id %d (%s) already exists\n", ret,
2071 (char *)(req.app_name));
2072 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
2073 list_for_each_entry(entry,
2074 &qseecom.registered_app_list_head, list){
2075 if (entry->app_id == ret) {
2076 entry->ref_cnt++;
2077 break;
2078 }
2079 }
2080 spin_unlock_irqrestore(
2081 &qseecom.registered_app_list_lock, flags);
2082 data->client.app_id = ret;
2083 query_req.app_id = ret;
2084
2085 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
2086 pr_err("copy_to_user failed\n");
2087 return -EFAULT;
2088 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002089 return -EEXIST; /* app already loaded */
2090 } else {
2091 return 0; /* app not loaded */
2092 }
2093}
2094
Mona Hossain2892b6b2012-02-17 13:53:11 -08002095static long qseecom_ioctl(struct file *file, unsigned cmd,
2096 unsigned long arg)
2097{
2098 int ret = 0;
2099 struct qseecom_dev_handle *data = file->private_data;
2100 void __user *argp = (void __user *) arg;
2101
2102 if (data->abort) {
2103 pr_err("Aborting qseecom driver\n");
2104 return -ENODEV;
2105 }
2106
2107 switch (cmd) {
2108 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
2109 pr_debug("ioctl register_listener_req()\n");
2110 atomic_inc(&data->ioctl_count);
2111 ret = qseecom_register_listener(data, argp);
2112 atomic_dec(&data->ioctl_count);
2113 wake_up_all(&data->abort_wq);
2114 if (ret)
2115 pr_err("failed qseecom_register_listener: %d\n", ret);
2116 break;
2117 }
2118 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
2119 pr_debug("ioctl unregister_listener_req()\n");
2120 atomic_inc(&data->ioctl_count);
2121 ret = qseecom_unregister_listener(data);
2122 atomic_dec(&data->ioctl_count);
2123 wake_up_all(&data->abort_wq);
2124 if (ret)
2125 pr_err("failed qseecom_unregister_listener: %d\n", ret);
2126 break;
2127 }
2128 case QSEECOM_IOCTL_SEND_CMD_REQ: {
2129 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002130 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002131 atomic_inc(&data->ioctl_count);
2132 ret = qseecom_send_cmd(data, argp);
2133 atomic_dec(&data->ioctl_count);
2134 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002135 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002136 if (ret)
2137 pr_err("failed qseecom_send_cmd: %d\n", ret);
2138 break;
2139 }
2140 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
2141 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002142 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002143 atomic_inc(&data->ioctl_count);
2144 ret = qseecom_send_modfd_cmd(data, argp);
2145 atomic_dec(&data->ioctl_count);
2146 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002147 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002148 if (ret)
2149 pr_err("failed qseecom_send_cmd: %d\n", ret);
2150 break;
2151 }
2152 case QSEECOM_IOCTL_RECEIVE_REQ: {
2153 atomic_inc(&data->ioctl_count);
2154 ret = qseecom_receive_req(data);
2155 atomic_dec(&data->ioctl_count);
2156 wake_up_all(&data->abort_wq);
2157 if (ret)
2158 pr_err("failed qseecom_receive_req: %d\n", ret);
2159 break;
2160 }
2161 case QSEECOM_IOCTL_SEND_RESP_REQ: {
2162 atomic_inc(&data->ioctl_count);
2163 ret = qseecom_send_resp();
2164 atomic_dec(&data->ioctl_count);
2165 wake_up_all(&data->abort_wq);
2166 if (ret)
2167 pr_err("failed qseecom_send_resp: %d\n", ret);
2168 break;
2169 }
2170 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
2171 ret = qseecom_set_client_mem_param(data, argp);
2172 if (ret)
2173 pr_err("failed Qqseecom_set_mem_param request: %d\n",
2174 ret);
2175 break;
2176 }
2177 case QSEECOM_IOCTL_LOAD_APP_REQ: {
2178 mutex_lock(&app_access_lock);
2179 atomic_inc(&data->ioctl_count);
Mona Hossain05c73562012-10-29 17:49:01 -07002180 if (qseecom.qsee_version > QSEEE_VERSION_00) {
2181 if (qseecom.commonlib_loaded == false) {
Mona Hossain9498f5e2013-01-23 18:08:45 -08002182 ret = qseecom_load_commonlib_image(data);
Mona Hossain05c73562012-10-29 17:49:01 -07002183 if (ret == 0)
2184 qseecom.commonlib_loaded = true;
2185 }
2186 }
2187 if (ret == 0)
2188 ret = qseecom_load_app(data, argp);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002189 atomic_dec(&data->ioctl_count);
2190 mutex_unlock(&app_access_lock);
2191 if (ret)
2192 pr_err("failed load_app request: %d\n", ret);
2193 break;
2194 }
2195 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
2196 mutex_lock(&app_access_lock);
2197 atomic_inc(&data->ioctl_count);
2198 ret = qseecom_unload_app(data);
2199 atomic_dec(&data->ioctl_count);
2200 mutex_unlock(&app_access_lock);
2201 if (ret)
2202 pr_err("failed unload_app request: %d\n", ret);
2203 break;
2204 }
2205 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
2206 atomic_inc(&data->ioctl_count);
2207 ret = qseecom_get_qseos_version(data, argp);
2208 if (ret)
2209 pr_err("qseecom_get_qseos_version: %d\n", ret);
2210 atomic_dec(&data->ioctl_count);
2211 break;
2212 }
2213 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
2214 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002215 ret = qsee_vote_for_clock(data, CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002216 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002217 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002218 ret = qsee_vote_for_clock(data, CLK_SFPB);
2219 if (ret)
2220 pr_err("Failed to vote for SFPB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002221 atomic_dec(&data->ioctl_count);
2222 break;
2223 }
2224 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
2225 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002226 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002227 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002228 atomic_dec(&data->ioctl_count);
2229 break;
2230 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07002231 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
2232 data->released = true;
2233 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2234 pr_err("Loading External elf image unsupported in rev 0x13\n");
2235 ret = -EINVAL;
2236 break;
2237 }
2238 mutex_lock(&app_access_lock);
2239 atomic_inc(&data->ioctl_count);
2240 ret = qseecom_load_external_elf(data, argp);
2241 atomic_dec(&data->ioctl_count);
2242 mutex_unlock(&app_access_lock);
2243 if (ret)
2244 pr_err("failed load_external_elf request: %d\n", ret);
2245 break;
2246 }
2247 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
2248 data->released = true;
2249 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2250 pr_err("Unloading External elf image unsupported in rev 0x13\n");
2251 ret = -EINVAL;
2252 break;
2253 }
2254 mutex_lock(&app_access_lock);
2255 atomic_inc(&data->ioctl_count);
2256 ret = qseecom_unload_external_elf(data);
2257 atomic_dec(&data->ioctl_count);
2258 mutex_unlock(&app_access_lock);
2259 if (ret)
2260 pr_err("failed unload_app request: %d\n", ret);
2261 break;
2262 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002263 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
2264 mutex_lock(&app_access_lock);
2265 atomic_inc(&data->ioctl_count);
2266 ret = qseecom_query_app_loaded(data, argp);
2267 atomic_dec(&data->ioctl_count);
2268 mutex_unlock(&app_access_lock);
2269 break;
2270 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002271 default:
2272 return -EINVAL;
2273 }
2274 return ret;
2275}
2276
2277static int qseecom_open(struct inode *inode, struct file *file)
2278{
2279 int ret = 0;
2280 struct qseecom_dev_handle *data;
2281
2282 data = kzalloc(sizeof(*data), GFP_KERNEL);
2283 if (!data) {
2284 pr_err("kmalloc failed\n");
2285 return -ENOMEM;
2286 }
2287 file->private_data = data;
2288 data->abort = 0;
2289 data->service = false;
2290 data->released = false;
2291 init_waitqueue_head(&data->abort_wq);
2292 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002293 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2294 int pil_error;
2295 mutex_lock(&pil_access_lock);
2296 if (pil_ref_cnt == 0) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002297 pil = subsystem_get("tzapps");
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002298 if (IS_ERR(pil)) {
2299 pr_err("Playready PIL image load failed\n");
2300 pil_error = PTR_ERR(pil);
2301 pil = NULL;
2302 pr_debug("tzapps image load FAILED\n");
2303 mutex_unlock(&pil_access_lock);
2304 return pil_error;
2305 }
2306 }
2307 pil_ref_cnt++;
2308 mutex_unlock(&pil_access_lock);
2309 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002310 return ret;
2311}
2312
2313static int qseecom_release(struct inode *inode, struct file *file)
2314{
2315 struct qseecom_dev_handle *data = file->private_data;
2316 int ret = 0;
2317
2318 if (data->released == false) {
2319 pr_warn("data->released == false\n");
2320 if (data->service)
2321 ret = qseecom_unregister_listener(data);
2322 else
2323 ret = qseecom_unload_app(data);
2324 if (ret) {
2325 pr_err("Close failed\n");
2326 return ret;
2327 }
2328 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08002329 if (data->client.fast_load_enabled == true)
2330 qsee_disable_clock_vote(data, CLK_SFPB);
2331 if (data->client.perf_enabled == true)
2332 qsee_disable_clock_vote(data, CLK_DFAB);
2333
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002334 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2335 mutex_lock(&pil_access_lock);
2336 if (pil_ref_cnt == 1)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002337 subsystem_put(pil);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002338 pil_ref_cnt--;
2339 mutex_unlock(&pil_access_lock);
2340 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002341 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002342
Mona Hossain2892b6b2012-02-17 13:53:11 -08002343 return ret;
2344}
2345
Mona Hossain2892b6b2012-02-17 13:53:11 -08002346static const struct file_operations qseecom_fops = {
2347 .owner = THIS_MODULE,
2348 .unlocked_ioctl = qseecom_ioctl,
2349 .open = qseecom_open,
2350 .release = qseecom_release
2351};
2352
Mona Hossaind39e33b2012-11-05 13:36:40 -08002353static int __qseecom_enable_clk(void)
2354{
2355 int rc = 0;
2356
2357 /* Enable CE core clk */
2358 rc = clk_prepare_enable(ce_core_clk);
2359 if (rc) {
2360 pr_err("Unable to enable/prepare CE core clk\n");
2361 return -EIO;
2362 } else {
2363 /* Enable CE clk */
2364 rc = clk_prepare_enable(ce_clk);
2365 if (rc) {
2366 pr_err("Unable to enable/prepare CE iface clk\n");
2367 clk_disable_unprepare(ce_core_clk);
2368 return -EIO;
2369 } else {
2370 /* Enable AXI clk */
2371 rc = clk_prepare_enable(ce_bus_clk);
2372 if (rc) {
2373 pr_err("Unable to enable/prepare CE iface clk\n");
2374 clk_disable_unprepare(ce_core_clk);
2375 clk_disable_unprepare(ce_clk);
2376 return -EIO;
2377 }
2378 }
2379 }
2380 return rc;
2381}
2382
2383static void __qseecom_disable_clk(void)
2384{
2385 if (ce_clk != NULL)
2386 clk_disable_unprepare(ce_clk);
2387 if (ce_core_clk != NULL)
2388 clk_disable_unprepare(ce_core_clk);
2389 if (ce_bus_clk != NULL)
2390 clk_disable_unprepare(ce_bus_clk);
2391}
2392
2393static int __qseecom_init_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002394{
2395 int rc = 0;
2396 struct device *pdev;
2397
2398 pdev = qseecom.pdev;
2399 /* Get CE3 src core clk. */
2400 ce_core_src_clk = clk_get(pdev, "core_clk_src");
2401 if (!IS_ERR(ce_core_src_clk)) {
Mona Hossaind39e33b2012-11-05 13:36:40 -08002402 /* Set the core src clk @50Mhz */
2403 rc = clk_set_rate(ce_core_src_clk, QSEE_CE_CLK_50MHZ);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002404 if (rc) {
2405 clk_put(ce_core_src_clk);
2406 pr_err("Unable to set the core src clk @100Mhz.\n");
Mona Hossaind39e33b2012-11-05 13:36:40 -08002407 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002408 }
2409 } else {
2410 pr_warn("Unable to get CE core src clk, set to NULL\n");
2411 ce_core_src_clk = NULL;
2412 }
2413
2414 /* Get CE core clk */
2415 ce_core_clk = clk_get(pdev, "core_clk");
2416 if (IS_ERR(ce_core_clk)) {
2417 rc = PTR_ERR(ce_core_clk);
2418 pr_err("Unable to get CE core clk\n");
2419 if (ce_core_src_clk != NULL)
2420 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002421 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002422 }
2423
2424 /* Get CE Interface clk */
2425 ce_clk = clk_get(pdev, "iface_clk");
2426 if (IS_ERR(ce_clk)) {
2427 rc = PTR_ERR(ce_clk);
2428 pr_err("Unable to get CE interface clk\n");
2429 if (ce_core_src_clk != NULL)
2430 clk_put(ce_core_src_clk);
2431 clk_put(ce_core_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002432 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002433 }
2434
2435 /* Get CE AXI clk */
2436 ce_bus_clk = clk_get(pdev, "bus_clk");
2437 if (IS_ERR(ce_bus_clk)) {
2438 rc = PTR_ERR(ce_bus_clk);
2439 pr_err("Unable to get CE BUS interface clk\n");
2440 if (ce_core_src_clk != NULL)
2441 clk_put(ce_core_src_clk);
2442 clk_put(ce_core_clk);
2443 clk_put(ce_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002444 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002445 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002446 return rc;
2447}
2448
Mona Hossaind39e33b2012-11-05 13:36:40 -08002449static void __qseecom_deinit_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002450{
Mona Hossaind39e33b2012-11-05 13:36:40 -08002451 if (ce_clk != NULL) {
2452 clk_put(ce_clk);
2453 ce_clk = NULL;
2454 }
2455 if (ce_core_clk != NULL) {
2456 clk_put(ce_core_clk);
2457 ce_clk = NULL;
2458 }
2459 if (ce_bus_clk != NULL) {
2460 clk_put(ce_bus_clk);
2461 ce_clk = NULL;
2462 }
2463 if (ce_core_src_clk != NULL) {
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002464 clk_put(ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002465 ce_core_src_clk = NULL;
2466 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002467}
2468
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002469static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002470{
2471 int rc;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002472 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002473 struct device *class_dev;
2474 char qsee_not_legacy = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07002475 struct msm_bus_scale_pdata *qseecom_platform_support = NULL;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002476 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
2477
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002478 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002479 qsee_perf_client = 0;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002480 qsee_sfpb_bw_count = 0;
2481
2482 ce_core_clk = NULL;
2483 ce_clk = NULL;
2484 ce_core_src_clk = NULL;
2485 ce_bus_clk = NULL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002486
Mona Hossain2892b6b2012-02-17 13:53:11 -08002487 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
2488 if (rc < 0) {
2489 pr_err("alloc_chrdev_region failed %d\n", rc);
2490 return rc;
2491 }
2492
2493 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
2494 if (IS_ERR(driver_class)) {
2495 rc = -ENOMEM;
2496 pr_err("class_create failed %d\n", rc);
2497 goto unregister_chrdev_region;
2498 }
2499
2500 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
2501 QSEECOM_DEV);
2502 if (!class_dev) {
2503 pr_err("class_device_create failed %d\n", rc);
2504 rc = -ENOMEM;
2505 goto class_destroy;
2506 }
2507
2508 cdev_init(&qseecom_cdev, &qseecom_fops);
2509 qseecom_cdev.owner = THIS_MODULE;
2510
2511 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
2512 if (rc < 0) {
2513 pr_err("cdev_add failed %d\n", rc);
2514 goto err;
2515 }
2516
2517 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
2518 spin_lock_init(&qseecom.registered_listener_list_lock);
2519 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
2520 spin_lock_init(&qseecom.registered_app_list_lock);
Mona Hossaind44a3842012-10-15 09:41:35 -07002521 INIT_LIST_HEAD(&qseecom.registered_kclient_list_head);
2522 spin_lock_init(&qseecom.registered_kclient_list_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002523 init_waitqueue_head(&qseecom.send_resp_wq);
2524 qseecom.send_resp_flag = 0;
2525
2526 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
2527 &qsee_not_legacy, sizeof(qsee_not_legacy));
2528 if (rc) {
Mona Hossain05c73562012-10-29 17:49:01 -07002529 pr_err("Failed to retrieve QSEOS version information %d\n", rc);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002530 goto err;
2531 }
Mona Hossain05c73562012-10-29 17:49:01 -07002532 if (qsee_not_legacy) {
2533 uint32_t feature = 10;
2534
2535 qseecom.qsee_version = QSEEE_VERSION_00;
2536 rc = scm_call(6, 3, &feature, sizeof(feature),
2537 &qseecom.qsee_version, sizeof(qseecom.qsee_version));
2538 if (rc) {
2539 pr_err("Failed to get QSEE version info %d\n", rc);
2540 goto err;
2541 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002542 qseecom.qseos_version = QSEOS_VERSION_14;
Mona Hossain05c73562012-10-29 17:49:01 -07002543 } else {
Mona Hossain2892b6b2012-02-17 13:53:11 -08002544 qseecom.qseos_version = QSEOS_VERSION_13;
Mona Hossain05c73562012-10-29 17:49:01 -07002545 qseecom.qsee_version = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002546 pil = NULL;
2547 pil_ref_cnt = 0;
2548 }
Mona Hossain05c73562012-10-29 17:49:01 -07002549 qseecom.commonlib_loaded = false;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002550 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002551 /* Create ION msm client */
Mona Hossaind44a3842012-10-15 09:41:35 -07002552 qseecom.ion_clnt = msm_ion_client_create(-1, "qseecom-kernel");
Mona Hossain2892b6b2012-02-17 13:53:11 -08002553 if (qseecom.ion_clnt == NULL) {
2554 pr_err("Ion client cannot be created\n");
2555 rc = -ENOMEM;
2556 goto err;
2557 }
2558
2559 /* register client for bus scaling */
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002560 if (pdev->dev.of_node) {
2561 ret = __qseecom_init_clk();
2562 if (ret)
2563 goto err;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002564 ret = __qseecom_enable_clk();
2565 if (ret) {
2566 __qseecom_deinit_clk();
2567 goto err;
2568 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002569 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2570 msm_bus_cl_get_pdata(pdev);
Mona Hossain5b76a622012-11-15 20:09:08 -08002571 if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
2572 struct resource *resource = NULL;
2573 struct qsee_apps_region_info_ireq req;
2574 struct qseecom_command_scm_resp resp;
2575
2576 resource = platform_get_resource_byname(pdev,
2577 IORESOURCE_MEM, "secapp-region");
2578 if (resource) {
2579 req.qsee_cmd_id = QSEOS_APP_REGION_NOTIFICATION;
2580 req.addr = resource->start;
2581 req.size = resource_size(resource);
2582 pr_warn("secure app region addr=0x%x size=0x%x",
2583 req.addr, req.size);
2584 } else {
2585 pr_err("Fail to get secure app region info\n");
2586 rc = -EINVAL;
2587 goto err;
2588 }
2589 rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
2590 &resp, sizeof(resp));
2591 if (rc) {
2592 pr_err("Failed to send secapp region info %d\n",
2593 rc);
2594 goto err;
2595 }
2596 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002597 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002598 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2599 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002600 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002601
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002602 qsee_perf_client = msm_bus_scale_register_client(
2603 qseecom_platform_support);
2604
2605 if (!qsee_perf_client)
2606 pr_err("Unable to register bus client\n");
2607 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002608err:
2609 device_destroy(driver_class, qseecom_device_no);
2610class_destroy:
2611 class_destroy(driver_class);
2612unregister_chrdev_region:
2613 unregister_chrdev_region(qseecom_device_no, 1);
2614 return rc;
2615}
2616
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002617static int __devinit qseecom_remove(struct platform_device *pdev)
2618{
Mona Hossaind44a3842012-10-15 09:41:35 -07002619 struct qseecom_registered_kclient_list *kclient = NULL;
2620 unsigned long flags = 0;
2621 int ret = 0;
2622
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002623 if (pdev->dev.platform_data != NULL)
2624 msm_bus_scale_unregister_client(qsee_perf_client);
Mona Hossaind44a3842012-10-15 09:41:35 -07002625
2626 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2627 kclient = list_entry((&qseecom.registered_kclient_list_head)->next,
2628 struct qseecom_registered_kclient_list, list);
2629 if (list_empty(&kclient->list)) {
2630 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2631 flags);
2632 return 0;
2633 }
2634 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
2635 list) {
2636 if (kclient)
2637 list_del(&kclient->list);
2638 break;
2639 }
2640 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
2641
2642
2643 while (kclient->handle != NULL) {
2644 ret = qseecom_unload_app(kclient->handle->dev);
2645 if (ret == 0) {
2646 kzfree(kclient->handle->dev);
2647 kzfree(kclient->handle);
2648 kzfree(kclient);
2649 }
2650 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2651 kclient = list_entry(
2652 (&qseecom.registered_kclient_list_head)->next,
2653 struct qseecom_registered_kclient_list, list);
2654 if (list_empty(&kclient->list)) {
2655 spin_unlock_irqrestore(
2656 &qseecom.registered_kclient_list_lock, flags);
2657 return 0;
2658 }
2659 list_for_each_entry(kclient,
2660 &qseecom.registered_kclient_list_head, list) {
2661 if (kclient)
2662 list_del(&kclient->list);
2663 break;
2664 }
2665 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2666 flags);
2667 if (!kclient) {
2668 ret = 0;
2669 break;
2670 }
2671 }
Mona Hossain05c73562012-10-29 17:49:01 -07002672 if (qseecom.qseos_version > QSEEE_VERSION_00)
2673 qseecom_unload_commonlib_image();
Mona Hossaind39e33b2012-11-05 13:36:40 -08002674
2675 if (qsee_perf_client)
2676 msm_bus_scale_client_update_request(qsee_perf_client, 0);
2677 /* register client for bus scaling */
2678 if (pdev->dev.of_node) {
2679 __qseecom_disable_clk();
2680 __qseecom_deinit_clk();
2681 }
Mona Hossaind44a3842012-10-15 09:41:35 -07002682 return ret;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002683};
2684
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002685static struct of_device_id qseecom_match[] = {
2686 {
2687 .compatible = "qcom,qseecom",
2688 },
2689 {}
2690};
2691
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002692static struct platform_driver qseecom_plat_driver = {
2693 .probe = qseecom_probe,
2694 .remove = qseecom_remove,
2695 .driver = {
2696 .name = "qseecom",
2697 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002698 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002699 },
2700};
2701
2702static int __devinit qseecom_init(void)
2703{
2704 return platform_driver_register(&qseecom_plat_driver);
2705}
2706
2707static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002708{
Mona Hossain2892b6b2012-02-17 13:53:11 -08002709 device_destroy(driver_class, qseecom_device_no);
2710 class_destroy(driver_class);
2711 unregister_chrdev_region(qseecom_device_no, 1);
2712 ion_client_destroy(qseecom.ion_clnt);
2713}
2714
2715MODULE_LICENSE("GPL v2");
2716MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2717
2718module_init(qseecom_init);
2719module_exit(qseecom_exit);