blob: 7d76b435f2eaf26cc6390c024263f2e8bad8ce60 [file] [log] [blame]
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001
Mona Hossainacea1022012-04-09 13:37:27 -07002
Mona Hossaind44a3842012-10-15 09:41:35 -07003/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
Mona Hossain2892b6b2012-02-17 13:53:11 -08004 *
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08005 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Mona Hossain2892b6b2012-02-17 13:53:11 -08006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 and
9 * only version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__
18
19#include <linux/kernel.h>
20#include <linux/slab.h>
21#include <linux/module.h>
22#include <linux/fs.h>
23#include <linux/platform_device.h>
24#include <linux/debugfs.h>
25#include <linux/cdev.h>
26#include <linux/uaccess.h>
27#include <linux/sched.h>
28#include <linux/list.h>
29#include <linux/mutex.h>
30#include <linux/io.h>
Mitchel Humpherys4f8be2e2012-09-06 10:41:41 -070031#include <linux/msm_ion.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080032#include <linux/types.h>
33#include <linux/clk.h>
34#include <linux/qseecom.h>
Mona Hossaind44a3842012-10-15 09:41:35 -070035#include <linux/elf.h>
36#include <linux/firmware.h>
Mona Hossainacea1022012-04-09 13:37:27 -070037#include <linux/freezer.h>
Mona Hossainf1f2ed62012-11-15 19:51:33 -080038#include <linux/scatterlist.h>
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070039#include <mach/board.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080040#include <mach/msm_bus.h>
41#include <mach/msm_bus_board.h>
42#include <mach/scm.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070043#include <mach/subsystem_restart.h>
Ramesh Masavarapuff377032012-09-14 12:11:32 -070044#include <mach/socinfo.h>
Mona Hossain803c3d92012-11-21 13:33:42 -080045#include <mach/qseecomi.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080046#include "qseecom_legacy.h"
Mona Hossaind44a3842012-10-15 09:41:35 -070047#include "qseecom_kernel.h"
Mona Hossain2892b6b2012-02-17 13:53:11 -080048
49#define QSEECOM_DEV "qseecom"
50#define QSEOS_VERSION_13 0x13
51#define QSEOS_VERSION_14 0x14
Mona Hossain05c73562012-10-29 17:49:01 -070052#define QSEEE_VERSION_00 0x400000
Mona Hossain5b76a622012-11-15 20:09:08 -080053#define QSEE_VERSION_01 0x401000
54#define QSEE_VERSION_02 0x402000
55
Mona Hossain05c73562012-10-29 17:49:01 -070056
57#define QSEOS_CHECK_VERSION_CMD 0x00001803
Mona Hossain2892b6b2012-02-17 13:53:11 -080058
Mona Hossaind39e33b2012-11-05 13:36:40 -080059#define QSEE_CE_CLK_100MHZ 100000000
Mona Hossaind39e33b2012-11-05 13:36:40 -080060
Mona Hossain13dd8922013-01-03 06:11:09 -080061#define QSEECOM_MAX_SG_ENTRY 512
Mona Hossainf1f2ed62012-11-15 19:51:33 -080062
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070063enum qseecom_clk_definitions {
64 CLK_DFAB = 0,
65 CLK_SFPB,
66};
67
Mona Hossain2892b6b2012-02-17 13:53:11 -080068static struct class *driver_class;
69static dev_t qseecom_device_no;
70static struct cdev qseecom_cdev;
71
72/* Data structures used in legacy support */
73static void *pil;
74static uint32_t pil_ref_cnt;
75static DEFINE_MUTEX(pil_access_lock);
76
Mona Hossain2892b6b2012-02-17 13:53:11 -080077static DEFINE_MUTEX(qsee_bw_mutex);
78static DEFINE_MUTEX(app_access_lock);
79
Mona Hossain2892b6b2012-02-17 13:53:11 -080080struct qseecom_registered_listener_list {
81 struct list_head list;
82 struct qseecom_register_listener_req svc;
83 u8 *sb_reg_req;
84 u8 *sb_virt;
85 s32 sb_phys;
86 size_t sb_length;
87 struct ion_handle *ihandle; /* Retrieve phy addr */
88
89 wait_queue_head_t rcv_req_wq;
90 int rcv_req_flag;
91};
92
93struct qseecom_registered_app_list {
94 struct list_head list;
95 u32 app_id;
96 u32 ref_cnt;
97};
98
Mona Hossaind44a3842012-10-15 09:41:35 -070099struct qseecom_registered_kclient_list {
100 struct list_head list;
101 struct qseecom_handle *handle;
102};
103
Mona Hossain17a4faf2013-03-22 16:40:56 -0700104struct qseecom_clk {
105 struct clk *ce_core_clk;
106 struct clk *ce_clk;
107 struct clk *ce_core_src_clk;
108 struct clk *ce_bus_clk;
109};
110
Mona Hossain2892b6b2012-02-17 13:53:11 -0800111struct qseecom_control {
112 struct ion_client *ion_clnt; /* Ion client */
113 struct list_head registered_listener_list_head;
114 spinlock_t registered_listener_list_lock;
115
116 struct list_head registered_app_list_head;
117 spinlock_t registered_app_list_lock;
118
Mona Hossaind44a3842012-10-15 09:41:35 -0700119 struct list_head registered_kclient_list_head;
120 spinlock_t registered_kclient_list_lock;
121
Mona Hossain2892b6b2012-02-17 13:53:11 -0800122 wait_queue_head_t send_resp_wq;
123 int send_resp_flag;
124
125 uint32_t qseos_version;
Mona Hossain05c73562012-10-29 17:49:01 -0700126 uint32_t qsee_version;
Ramesh Masavarapuff377032012-09-14 12:11:32 -0700127 struct device *pdev;
Mona Hossain05c73562012-10-29 17:49:01 -0700128 bool commonlib_loaded;
Mona Hossain17a4faf2013-03-22 16:40:56 -0700129
130 int qsee_bw_count;
131 int qsee_sfpb_bw_count;
132
133 uint32_t qsee_perf_client;
134 struct qseecom_clk qsee;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800135};
136
137struct qseecom_client_handle {
138 u32 app_id;
139 u8 *sb_virt;
140 s32 sb_phys;
141 uint32_t user_virt_sb_base;
142 size_t sb_length;
143 struct ion_handle *ihandle; /* Retrieve phy addr */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800144 bool perf_enabled;
145 bool fast_load_enabled;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800146};
147
148struct qseecom_listener_handle {
149 u32 id;
150};
151
152static struct qseecom_control qseecom;
153
154struct qseecom_dev_handle {
155 bool service;
156 union {
157 struct qseecom_client_handle client;
158 struct qseecom_listener_handle listener;
159 };
160 bool released;
161 int abort;
162 wait_queue_head_t abort_wq;
163 atomic_t ioctl_count;
164};
165
Mona Hossainf1f2ed62012-11-15 19:51:33 -0800166struct qseecom_sg_entry {
167 uint32_t phys_addr;
168 uint32_t len;
169};
170
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700171/* Function proto types */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800172static int qsee_vote_for_clock(struct qseecom_dev_handle *, int32_t);
173static void qsee_disable_clock_vote(struct qseecom_dev_handle *, int32_t);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700174
Mona Hossain2892b6b2012-02-17 13:53:11 -0800175static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800176 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800177{
178 struct qseecom_registered_listener_list *ptr;
179 int unique = 1;
180 unsigned long flags;
181
182 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
183 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800184 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800185 pr_err("Service id: %u is already registered\n",
186 ptr->svc.listener_id);
187 unique = 0;
188 break;
189 }
190 }
191 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
192 return unique;
193}
194
195static struct qseecom_registered_listener_list *__qseecom_find_svc(
196 int32_t listener_id)
197{
198 struct qseecom_registered_listener_list *entry = NULL;
199 unsigned long flags;
200
201 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
202 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
203 {
204 if (entry->svc.listener_id == listener_id)
205 break;
206 }
207 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
208 return entry;
209}
210
211static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
212 struct qseecom_dev_handle *handle,
213 struct qseecom_register_listener_req *listener)
214{
215 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800216 struct qseecom_register_listener_ireq req;
217 struct qseecom_command_scm_resp resp;
218 ion_phys_addr_t pa;
219
220 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800221 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
222 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800223 if (svc->ihandle == NULL) {
224 pr_err("Ion client could not retrieve the handle\n");
225 return -ENOMEM;
226 }
227
228 /* Get the physical address of the ION BUF */
229 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
230
231 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700232 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800233 svc->sb_phys = pa;
234
235 if (qseecom.qseos_version == QSEOS_VERSION_14) {
236 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
237 req.listener_id = svc->svc.listener_id;
238 req.sb_len = svc->sb_length;
239 req.sb_ptr = (void *)svc->sb_phys;
240
241 resp.result = QSEOS_RESULT_INCOMPLETE;
242
243 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
244 sizeof(req), &resp, sizeof(resp));
245 if (ret) {
246 pr_err("qseecom_scm_call failed with err: %d\n", ret);
247 return -EINVAL;
248 }
249
250 if (resp.result != QSEOS_RESULT_SUCCESS) {
251 pr_err("Error SB registration req: resp.result = %d\n",
252 resp.result);
253 return -EPERM;
254 }
255 } else {
256 struct qseecom_command cmd;
257 struct qseecom_response resp;
258 struct qse_pr_init_sb_req_s sb_init_req;
259 struct qse_pr_init_sb_rsp_s sb_init_rsp;
260
261 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
262 sizeof(sb_init_rsp)), GFP_KERNEL);
263
264 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
265 sb_init_req.listener_id = svc->svc.listener_id;
266 sb_init_req.sb_len = svc->sb_length;
267 sb_init_req.sb_ptr = svc->sb_phys;
268
269 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
270
271 /* It will always be a new cmd from this method */
272 cmd.cmd_type = TZ_SCHED_CMD_NEW;
273 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
274 cmd.sb_in_cmd_len = sizeof(sb_init_req);
275
276 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
277
278 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
279 , &resp, sizeof(resp));
280
281 if (ret) {
282 pr_err("qseecom_scm_call failed with err: %d\n", ret);
283 return -EINVAL;
284 }
285
286 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
287 pr_err("SB registration fail resp.cmd_status %d\n",
288 resp.cmd_status);
289 return -EINVAL;
290 }
291 memset(svc->sb_virt, 0, svc->sb_length);
292 }
293 return 0;
294}
295
296static int qseecom_register_listener(struct qseecom_dev_handle *data,
297 void __user *argp)
298{
299 int ret = 0;
300 unsigned long flags;
301 struct qseecom_register_listener_req rcvd_lstnr;
302 struct qseecom_registered_listener_list *new_entry;
303
304 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
305 if (ret) {
306 pr_err("copy_from_user failed\n");
307 return ret;
308 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800309 data->listener.id = 0;
310 data->service = true;
311 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800312 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800313 data->released = true;
314 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800315 }
316
317 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
318 if (!new_entry) {
319 pr_err("kmalloc failed\n");
320 return -ENOMEM;
321 }
322 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
323 new_entry->rcv_req_flag = 0;
324
325 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
326 new_entry->sb_length = rcvd_lstnr.sb_size;
327 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
328 pr_err("qseecom_set_sb_memoryfailed\n");
329 kzfree(new_entry);
330 return -ENOMEM;
331 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800332
Mona Hossain2892b6b2012-02-17 13:53:11 -0800333 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800334 init_waitqueue_head(&new_entry->rcv_req_wq);
335
336 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
337 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
338 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800339
Mona Hossain2892b6b2012-02-17 13:53:11 -0800340 return ret;
341}
342
343static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
344{
345 int ret = 0;
346 unsigned long flags;
347 uint32_t unmap_mem = 0;
348 struct qseecom_register_listener_ireq req;
349 struct qseecom_registered_listener_list *ptr_svc = NULL;
350 struct qseecom_command_scm_resp resp;
351 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
352
353 if (qseecom.qseos_version == QSEOS_VERSION_14) {
354 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
355 req.listener_id = data->listener.id;
356 resp.result = QSEOS_RESULT_INCOMPLETE;
357
358 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
359 sizeof(req), &resp, sizeof(resp));
360 if (ret) {
Mona Hossain17a4faf2013-03-22 16:40:56 -0700361 pr_err("scm_call() failed with err: %d (lstnr id=%d)\n",
362 ret, data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800363 return ret;
364 }
365
366 if (resp.result != QSEOS_RESULT_SUCCESS) {
Mona Hossain17a4faf2013-03-22 16:40:56 -0700367 pr_err("Failed resp.result=%d,(lstnr id=%d)\n",
368 resp.result, data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800369 return -EPERM;
370 }
371 } else {
372 struct qse_pr_init_sb_req_s sb_init_req;
373 struct qseecom_command cmd;
374 struct qseecom_response resp;
375 struct qseecom_registered_listener_list *svc;
376
377 svc = __qseecom_find_svc(data->listener.id);
378 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
379 sb_init_req.listener_id = data->listener.id;
380 sb_init_req.sb_len = 0;
381 sb_init_req.sb_ptr = 0;
382
383 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
384
385 /* It will always be a new cmd from this method */
386 cmd.cmd_type = TZ_SCHED_CMD_NEW;
387 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
388 cmd.sb_in_cmd_len = sizeof(sb_init_req);
389 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
390
391 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
392 &resp, sizeof(resp));
393 if (ret) {
394 pr_err("qseecom_scm_call failed with err: %d\n", ret);
395 return ret;
396 }
397 kzfree(svc->sb_reg_req);
398 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
399 pr_err("Error with SB initialization\n");
400 return -EPERM;
401 }
402 }
403 data->abort = 1;
404 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
405 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
406 list) {
407 if (ptr_svc->svc.listener_id == data->listener.id) {
408 wake_up_all(&ptr_svc->rcv_req_wq);
409 break;
410 }
411 }
412 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
413
414 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700415 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800416 atomic_read(&data->ioctl_count) <= 1)) {
417 pr_err("Interrupted from abort\n");
418 ret = -ERESTARTSYS;
419 break;
420 }
421 }
422
423 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
424 list_for_each_entry(ptr_svc,
425 &qseecom.registered_listener_list_head,
426 list)
427 {
428 if (ptr_svc->svc.listener_id == data->listener.id) {
429 if (ptr_svc->sb_virt) {
430 unmap_mem = 1;
431 ihandle = ptr_svc->ihandle;
432 }
433 list_del(&ptr_svc->list);
434 kzfree(ptr_svc);
435 break;
436 }
437 }
438 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
439
440 /* Unmap the memory */
441 if (unmap_mem) {
442 if (!IS_ERR_OR_NULL(ihandle)) {
443 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
444 ion_free(qseecom.ion_clnt, ihandle);
445 }
446 }
447 data->released = true;
448 return ret;
449}
450
451static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
452 void __user *argp)
453{
454 ion_phys_addr_t pa;
455 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800456 struct qseecom_set_sb_mem_param_req req;
457 uint32_t len;
458
459 /* Copy the relevant information needed for loading the image */
460 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
461 return -EFAULT;
462
Mona Hossain2892b6b2012-02-17 13:53:11 -0800463 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800464 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
465 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800466 if (IS_ERR_OR_NULL(data->client.ihandle)) {
467 pr_err("Ion client could not retrieve the handle\n");
468 return -ENOMEM;
469 }
470 /* Get the physical address of the ION BUF */
471 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
472 /* Populate the structure for sending scm call to load image */
473 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700474 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800475 data->client.sb_phys = pa;
476 data->client.sb_length = req.sb_len;
477 data->client.user_virt_sb_base = req.virt_sb_base;
478 return 0;
479}
480
Mona Hossain2892b6b2012-02-17 13:53:11 -0800481static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
482{
483 int ret;
484 ret = (qseecom.send_resp_flag != 0);
485 return ret || data->abort;
486}
487
488static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
489 struct qseecom_command_scm_resp *resp)
490{
491 int ret = 0;
Mona Hossain0b3a2792013-02-05 17:38:55 -0800492 int rc = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800493 uint32_t lstnr;
494 unsigned long flags;
495 struct qseecom_client_listener_data_irsp send_data_rsp;
496 struct qseecom_registered_listener_list *ptr_svc = NULL;
497
498
499 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
500 lstnr = resp->data;
501 /*
502 * Wake up blocking lsitener service with the lstnr id
503 */
504 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
505 flags);
506 list_for_each_entry(ptr_svc,
507 &qseecom.registered_listener_list_head, list) {
508 if (ptr_svc->svc.listener_id == lstnr) {
509 ptr_svc->rcv_req_flag = 1;
510 wake_up_interruptible(&ptr_svc->rcv_req_wq);
511 break;
512 }
513 }
514 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
515 flags);
516 if (ptr_svc->svc.listener_id != lstnr) {
517 pr_warning("Service requested for does on exist\n");
518 return -ERESTARTSYS;
519 }
520 pr_debug("waking up rcv_req_wq and "
521 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700522 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800523 __qseecom_listener_has_sent_rsp(data))) {
524 pr_warning("Interrupted: exiting send_cmd loop\n");
525 return -ERESTARTSYS;
526 }
527
528 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700529 pr_err("Aborting listener service %d\n",
530 data->listener.id);
Mona Hossain0b3a2792013-02-05 17:38:55 -0800531 rc = -ENODEV;
532 send_data_rsp.status = QSEOS_RESULT_FAILURE;
533 } else {
534 send_data_rsp.status = QSEOS_RESULT_SUCCESS;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800535 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800536
Mona Hossain2892b6b2012-02-17 13:53:11 -0800537 qseecom.send_resp_flag = 0;
538 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
539 send_data_rsp.listener_id = lstnr ;
540
541 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
542 (const void *)&send_data_rsp,
543 sizeof(send_data_rsp), resp,
544 sizeof(*resp));
545 if (ret) {
Mona Hossain17a4faf2013-03-22 16:40:56 -0700546 pr_err("scm_call() failed with err: %d (app_id = %d)\n",
547 ret, data->client.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800548 return ret;
549 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700550 if (resp->result == QSEOS_RESULT_FAILURE) {
Mona Hossain17a4faf2013-03-22 16:40:56 -0700551 pr_err("Response result %d FAIL (app_id = %d)\n",
552 resp->result, data->client.app_id);
Mona Hossainbb0bca12012-04-12 11:47:45 -0700553 return -EINVAL;
554 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800555 }
Mona Hossain0b3a2792013-02-05 17:38:55 -0800556 if (rc)
557 return rc;
558
Mona Hossain2892b6b2012-02-17 13:53:11 -0800559 return ret;
560}
561
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700562static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
563{
564 int32_t ret;
565 struct qseecom_command_scm_resp resp;
566
567 /* SCM_CALL to check if app_id for the mentioned app exists */
568 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
569 sizeof(struct qseecom_check_app_ireq),
570 &resp, sizeof(resp));
571 if (ret) {
572 pr_err("scm_call to check if app is already loaded failed\n");
573 return -EINVAL;
574 }
575
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700576 if (resp.result == QSEOS_RESULT_FAILURE) {
577 return 0;
578 } else {
579 switch (resp.resp_type) {
580 /*qsee returned listener type response */
581 case QSEOS_LISTENER_ID:
582 pr_err("resp type is of listener type instead of app");
583 return -EINVAL;
584 break;
585 case QSEOS_APP_ID:
586 return resp.data;
587 default:
588 pr_err("invalid resp type (%d) from qsee",
589 resp.resp_type);
590 return -ENODEV;
591 break;
592 }
593 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700594}
595
Mona Hossain2892b6b2012-02-17 13:53:11 -0800596static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
597{
598 struct qseecom_registered_app_list *entry = NULL;
599 unsigned long flags = 0;
600 u32 app_id = 0;
601 struct ion_handle *ihandle; /* Ion handle */
602 struct qseecom_load_img_req load_img_req;
603 int32_t ret;
604 ion_phys_addr_t pa = 0;
605 uint32_t len;
606 struct qseecom_command_scm_resp resp;
Mona Hossain436b75f2012-11-20 17:10:40 -0800607 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700608 struct qseecom_load_app_ireq load_req;
609
Mona Hossain2892b6b2012-02-17 13:53:11 -0800610 /* Copy the relevant information needed for loading the image */
611 if (__copy_from_user(&load_img_req,
612 (void __user *)argp,
613 sizeof(struct qseecom_load_img_req))) {
614 pr_err("copy_from_user failed\n");
615 return -EFAULT;
616 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700617 /* Vote for the SFPB clock */
Mona Hossain04d3fac2012-12-03 10:10:37 -0800618 ret = qsee_vote_for_clock(data, CLK_SFPB);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700619 if (ret)
620 pr_warning("Unable to vote for SFPB clock");
Mona Hossain436b75f2012-11-20 17:10:40 -0800621 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
622 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800623
Mona Hossain436b75f2012-11-20 17:10:40 -0800624 ret = __qseecom_check_app_exists(req);
625 if (ret < 0)
626 return ret;
627 else
628 app_id = ret;
629
630 if (app_id) {
631 pr_warn("App id %d (%s) already exists\n", app_id,
632 (char *)(req.app_name));
633 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
634 list_for_each_entry(entry,
635 &qseecom.registered_app_list_head, list){
636 if (entry->app_id == app_id) {
637 entry->ref_cnt++;
638 break;
639 }
640 }
641 spin_unlock_irqrestore(
642 &qseecom.registered_app_list_lock, flags);
643 } else {
644 pr_warn("App (%s) does'nt exist, loading apps for first time\n",
Mona Hossaind44a3842012-10-15 09:41:35 -0700645 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800646 /* Get the handle of the shared fd */
647 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800648 load_img_req.ifd_data_fd);
Mona Hossain436b75f2012-11-20 17:10:40 -0800649 if (IS_ERR_OR_NULL(ihandle)) {
650 pr_err("Ion client could not retrieve the handle\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800651 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800652 return -ENOMEM;
653 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800654
Mona Hossain436b75f2012-11-20 17:10:40 -0800655 /* Get the physical address of the ION BUF */
656 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800657
Mona Hossain436b75f2012-11-20 17:10:40 -0800658 /* Populate the structure for sending scm call to load image */
659 memcpy(load_req.app_name, load_img_req.img_name,
660 MAX_APP_NAME_SIZE);
661 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
662 load_req.mdt_len = load_img_req.mdt_len;
663 load_req.img_len = load_img_req.img_len;
664 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800665
Mona Hossain436b75f2012-11-20 17:10:40 -0800666 /* SCM_CALL to load the app and get the app_id back */
667 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700668 sizeof(struct qseecom_load_app_ireq),
669 &resp, sizeof(resp));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700670 if (ret) {
Mona Hossain436b75f2012-11-20 17:10:40 -0800671 pr_err("scm_call to load app failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -0800672 if (!IS_ERR_OR_NULL(ihandle))
673 ion_free(qseecom.ion_clnt, ihandle);
674 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800675 return -EINVAL;
676 }
677
678 if (resp.result == QSEOS_RESULT_FAILURE) {
679 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700680 if (!IS_ERR_OR_NULL(ihandle))
681 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800682 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800683 return -EFAULT;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700684 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700685
Mona Hossain436b75f2012-11-20 17:10:40 -0800686 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
687 ret = __qseecom_process_incomplete_cmd(data, &resp);
688 if (ret) {
689 pr_err("process_incomplete_cmd failed err: %d\n",
690 ret);
691 if (!IS_ERR_OR_NULL(ihandle))
692 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800693 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800694 return ret;
695 }
696 }
697
698 if (resp.result != QSEOS_RESULT_SUCCESS) {
699 pr_err("scm_call failed resp.result unknown, %d\n",
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700700 resp.result);
Mona Hossain436b75f2012-11-20 17:10:40 -0800701 if (!IS_ERR_OR_NULL(ihandle))
702 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800703 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800704 return -EFAULT;
705 }
706
707 app_id = resp.data;
708
709 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
710 if (!entry) {
711 pr_err("kmalloc failed\n");
Mona Hossain04d3fac2012-12-03 10:10:37 -0800712 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain436b75f2012-11-20 17:10:40 -0800713 return -ENOMEM;
714 }
715 entry->app_id = app_id;
716 entry->ref_cnt = 1;
717
718 /* Deallocate the handle */
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700719 if (!IS_ERR_OR_NULL(ihandle))
720 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700721
Mona Hossain436b75f2012-11-20 17:10:40 -0800722 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
723 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
724 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
725 flags);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700726
Mona Hossain436b75f2012-11-20 17:10:40 -0800727 pr_warn("App with id %d (%s) now loaded\n", app_id,
Mona Hossaind44a3842012-10-15 09:41:35 -0700728 (char *)(load_img_req.img_name));
Mona Hossain436b75f2012-11-20 17:10:40 -0800729 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800730 data->client.app_id = app_id;
731 load_img_req.app_id = app_id;
732 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
733 pr_err("copy_to_user failed\n");
734 kzfree(entry);
Mona Hossain04d3fac2012-12-03 10:10:37 -0800735 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800736 return -EFAULT;
737 }
Mona Hossain04d3fac2012-12-03 10:10:37 -0800738 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800739 return 0;
740}
741
742static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
743{
744 wake_up_all(&qseecom.send_resp_wq);
745 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700746 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800747 atomic_read(&data->ioctl_count) <= 1)) {
748 pr_err("Interrupted from abort\n");
749 return -ERESTARTSYS;
750 break;
751 }
752 }
753 /* Set unload app */
754 return 1;
755}
756
757static int qseecom_unload_app(struct qseecom_dev_handle *data)
758{
759 unsigned long flags;
760 int ret = 0;
761 struct qseecom_command_scm_resp resp;
762 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700763 bool unload = false;
764 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800765
Mona Hossain1fb538f2012-08-30 16:19:38 -0700766 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
767 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800768 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
769 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
770 list) {
771 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700772 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800773 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700774 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800775 break;
776 } else {
777 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700778 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700779 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800780 break;
781 }
782 }
783 }
784 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
785 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700786 if (found_app == false) {
787 pr_err("Cannot find app with id = %d\n",
788 data->client.app_id);
789 return -EINVAL;
790 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800791 }
792
793 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
794 struct qseecom_unload_app_ireq req;
795
Mona Hossain340dba82012-08-07 19:54:46 -0700796 __qseecom_cleanup_app(data);
797 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
798 list_del(&ptr_app->list);
799 kzfree(ptr_app);
800 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
801 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800802 /* Populate the structure for sending scm call to load image */
803 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
804 req.app_id = data->client.app_id;
805
806 /* SCM_CALL to unload the app */
807 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
808 sizeof(struct qseecom_unload_app_ireq),
809 &resp, sizeof(resp));
810 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700811 pr_err("scm_call to unload app (id = %d) failed\n",
812 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800813 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700814 } else {
815 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800816 }
817 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
818 ret = __qseecom_process_incomplete_cmd(data, &resp);
819 if (ret) {
820 pr_err("process_incomplete_cmd fail err: %d\n",
821 ret);
822 return ret;
823 }
824 }
825 }
826
827 if (qseecom.qseos_version == QSEOS_VERSION_13) {
828 data->abort = 1;
829 wake_up_all(&qseecom.send_resp_wq);
830 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700831 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800832 atomic_read(&data->ioctl_count) <= 0)) {
833 pr_err("Interrupted from abort\n");
834 ret = -ERESTARTSYS;
835 break;
836 }
837 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800838 }
Mona Hossain340dba82012-08-07 19:54:46 -0700839 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
840 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
841 ion_free(qseecom.ion_clnt, data->client.ihandle);
842 data->client.ihandle = NULL;
843 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800844 data->released = true;
845 return ret;
846}
847
848static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
849 uint32_t virt)
850{
851 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
852}
853
854static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
855 struct qseecom_send_cmd_req *req)
856{
857 int ret = 0;
858 unsigned long flags;
859 u32 reqd_len_sb_in = 0;
860 struct qseecom_command cmd;
861 struct qseecom_response resp;
862
863
864 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
865 pr_err("cmd buffer or response buffer is null\n");
866 return -EINVAL;
867 }
868
869 if (req->cmd_req_len <= 0 ||
870 req->resp_len <= 0 ||
871 req->cmd_req_len > data->client.sb_length ||
872 req->resp_len > data->client.sb_length) {
873 pr_err("cmd buffer length or "
874 "response buffer length not valid\n");
875 return -EINVAL;
876 }
877
878 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
879 if (reqd_len_sb_in > data->client.sb_length) {
880 pr_debug("Not enough memory to fit cmd_buf and "
881 "resp_buf. Required: %u, Available: %u\n",
882 reqd_len_sb_in, data->client.sb_length);
883 return -ENOMEM;
884 }
885 cmd.cmd_type = TZ_SCHED_CMD_NEW;
886 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
887 cmd.sb_in_cmd_len = req->cmd_req_len;
888
889 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
890 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
891 resp.sb_in_rsp_len = req->resp_len;
892
893 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
894 sizeof(cmd), &resp, sizeof(resp));
895
896 if (ret) {
897 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
898 return ret;
899 }
900
901 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
902 /*
903 * If cmd is incomplete, get the callback cmd out from SB out
904 * and put it on the list
905 */
906 struct qseecom_registered_listener_list *ptr_svc = NULL;
907 /*
908 * We don't know which service can handle the command. so we
909 * wake up all blocking services and let them figure out if
910 * they can handle the given command.
911 */
912 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
913 flags);
914 list_for_each_entry(ptr_svc,
915 &qseecom.registered_listener_list_head, list) {
916 ptr_svc->rcv_req_flag = 1;
917 wake_up_interruptible(&ptr_svc->rcv_req_wq);
918 }
919 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
920 flags);
921
922 pr_debug("waking up rcv_req_wq and "
923 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700924 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800925 __qseecom_listener_has_sent_rsp(data))) {
926 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
927 return -ERESTARTSYS;
928 }
929
930 if (data->abort) {
931 pr_err("Aborting driver\n");
932 return -ENODEV;
933 }
934 qseecom.send_resp_flag = 0;
935 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
936 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
937 sizeof(cmd), &resp, sizeof(resp));
938 if (ret) {
939 pr_err("qseecom_scm_call failed with err: %d\n", ret);
940 return ret;
941 }
942 }
943 return ret;
944}
945
946static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
947 struct qseecom_send_cmd_req *req)
948{
949 int ret = 0;
950 u32 reqd_len_sb_in = 0;
951 struct qseecom_client_send_data_ireq send_data_req;
952 struct qseecom_command_scm_resp resp;
953
954 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
955 pr_err("cmd buffer or response buffer is null\n");
956 return -EINVAL;
957 }
958
959 if (req->cmd_req_len <= 0 ||
960 req->resp_len <= 0 ||
961 req->cmd_req_len > data->client.sb_length ||
962 req->resp_len > data->client.sb_length) {
963 pr_err("cmd buffer length or "
964 "response buffer length not valid\n");
965 return -EINVAL;
966 }
967
968 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
969 if (reqd_len_sb_in > data->client.sb_length) {
970 pr_debug("Not enough memory to fit cmd_buf and "
971 "resp_buf. Required: %u, Available: %u\n",
972 reqd_len_sb_in, data->client.sb_length);
973 return -ENOMEM;
974 }
975
976 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
977 send_data_req.app_id = data->client.app_id;
978 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
979 (uint32_t)req->cmd_req_buf));
980 send_data_req.req_len = req->cmd_req_len;
981 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
982 (uint32_t)req->resp_buf));
983 send_data_req.rsp_len = req->resp_len;
984
985 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
986 sizeof(send_data_req),
987 &resp, sizeof(resp));
988 if (ret) {
Mona Hossain17a4faf2013-03-22 16:40:56 -0700989 pr_err("scm_call() failed with err: %d (app_id = %d)\n",
990 ret, data->client.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800991 return ret;
992 }
993
994 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
995 ret = __qseecom_process_incomplete_cmd(data, &resp);
996 if (ret) {
997 pr_err("process_incomplete_cmd failed err: %d\n", ret);
998 return ret;
999 }
Mona Hossainbb0bca12012-04-12 11:47:45 -07001000 } else {
1001 if (resp.result != QSEOS_RESULT_SUCCESS) {
1002 pr_err("Response result %d not supported\n",
1003 resp.result);
1004 ret = -EINVAL;
1005 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001006 }
1007 return ret;
1008}
1009
1010
1011static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1012{
1013 int ret = 0;
1014 struct qseecom_send_cmd_req req;
1015
1016 ret = copy_from_user(&req, argp, sizeof(req));
1017 if (ret) {
1018 pr_err("copy_from_user failed\n");
1019 return ret;
1020 }
1021 if (qseecom.qseos_version == QSEOS_VERSION_14)
1022 ret = __qseecom_send_cmd(data, &req);
1023 else
1024 ret = __qseecom_send_cmd_legacy(data, &req);
1025 if (ret)
1026 return ret;
1027
1028 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1029 req.resp_len, req.resp_buf);
1030 return ret;
1031}
1032
1033static int __qseecom_send_cmd_req_clean_up(
1034 struct qseecom_send_modfd_cmd_req *req)
1035{
1036 char *field;
1037 uint32_t *update;
1038 int ret = 0;
1039 int i = 0;
1040
1041 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001042 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001043 field = (char *)req->cmd_req_buf +
1044 req->ifd_data[i].cmd_buf_offset;
1045 update = (uint32_t *) field;
1046 *update = 0;
1047 }
1048 }
1049 return ret;
1050}
1051
1052static int __qseecom_update_with_phy_addr(
1053 struct qseecom_send_modfd_cmd_req *req)
1054{
1055 struct ion_handle *ihandle;
1056 char *field;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001057 int ret = 0;
1058 int i = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001059
1060 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001061 struct sg_table *sg_ptr = NULL;
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001062 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001063 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001064 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001065 req->ifd_data[i].fd);
1066 if (IS_ERR_OR_NULL(ihandle)) {
1067 pr_err("Ion client can't retrieve the handle\n");
1068 return -ENOMEM;
1069 }
1070 field = (char *) req->cmd_req_buf +
1071 req->ifd_data[i].cmd_buf_offset;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001072
1073 /* Populate the cmd data structure with the phys_addr */
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001074 sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
1075 if (sg_ptr == NULL) {
1076 pr_err("IOn client could not retrieve sg table\n");
1077 goto err;
1078 }
1079 if (sg_ptr->nents == 0) {
1080 pr_err("Num of scattered entries is 0\n");
1081 goto err;
1082 }
1083 if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
1084 pr_err("Num of scattered entries");
1085 pr_err(" (%d) is greater than max supported %d\n",
1086 sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
1087 goto err;
1088 }
1089 if (sg_ptr->nents == 1) {
1090 uint32_t *update;
1091 update = (uint32_t *) field;
1092 *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
1093 } else {
1094 struct qseecom_sg_entry *update;
1095 struct scatterlist *sg;
1096 int j = 0;
1097 update = (struct qseecom_sg_entry *) field;
1098 sg = sg_ptr->sgl;
1099 for (j = 0; j < sg_ptr->nents; j++) {
1100 update->phys_addr = (uint32_t)
1101 sg_dma_address(sg);
1102 update->len = (uint32_t)sg->length;
1103 update++;
1104 sg = sg_next(sg);
1105 }
1106 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001107 /* Deallocate the handle */
1108 if (!IS_ERR_OR_NULL(ihandle))
1109 ion_free(qseecom.ion_clnt, ihandle);
1110 }
1111 }
1112 return ret;
Mona Hossainf1f2ed62012-11-15 19:51:33 -08001113err:
1114 if (!IS_ERR_OR_NULL(ihandle))
1115 ion_free(qseecom.ion_clnt, ihandle);
1116 return -ENOMEM;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001117}
1118
1119static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1120 void __user *argp)
1121{
1122 int ret = 0;
1123 struct qseecom_send_modfd_cmd_req req;
1124 struct qseecom_send_cmd_req send_cmd_req;
1125
1126 ret = copy_from_user(&req, argp, sizeof(req));
1127 if (ret) {
1128 pr_err("copy_from_user failed\n");
1129 return ret;
1130 }
1131 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1132 send_cmd_req.cmd_req_len = req.cmd_req_len;
1133 send_cmd_req.resp_buf = req.resp_buf;
1134 send_cmd_req.resp_len = req.resp_len;
1135
1136 ret = __qseecom_update_with_phy_addr(&req);
1137 if (ret)
1138 return ret;
1139 if (qseecom.qseos_version == QSEOS_VERSION_14)
1140 ret = __qseecom_send_cmd(data, &send_cmd_req);
1141 else
1142 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1143 __qseecom_send_cmd_req_clean_up(&req);
1144
1145 if (ret)
1146 return ret;
1147
1148 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1149 req.resp_len, req.resp_buf);
1150 return ret;
1151}
1152
1153static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1154 struct qseecom_registered_listener_list *svc)
1155{
1156 int ret;
1157 ret = (svc->rcv_req_flag != 0);
1158 return ret || data->abort;
1159}
1160
1161static int qseecom_receive_req(struct qseecom_dev_handle *data)
1162{
1163 int ret = 0;
1164 struct qseecom_registered_listener_list *this_lstnr;
1165
1166 this_lstnr = __qseecom_find_svc(data->listener.id);
1167 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001168 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001169 __qseecom_listener_has_rcvd_req(data,
1170 this_lstnr))) {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001171 pr_warning("Interrupted: exiting Listener Service = %d\n",
1172 (uint32_t)data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001173 /* woken up for different reason */
1174 return -ERESTARTSYS;
1175 }
1176
1177 if (data->abort) {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001178 pr_err("Aborting Listener Service = %d\n",
1179 (uint32_t)data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001180 return -ENODEV;
1181 }
1182 this_lstnr->rcv_req_flag = 0;
1183 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1184 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1185 break;
1186 } else {
1187 break;
1188 }
1189 }
1190 return ret;
1191}
1192
Mona Hossaind44a3842012-10-15 09:41:35 -07001193static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
1194{
1195 struct elf32_hdr *ehdr;
1196
1197 if (fw_entry->size < sizeof(*ehdr)) {
1198 pr_err("%s: Not big enough to be an elf header\n",
1199 qseecom.pdev->init_name);
1200 return false;
1201 }
1202 ehdr = (struct elf32_hdr *)fw_entry->data;
1203 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
1204 pr_err("%s: Not an elf header\n",
1205 qseecom.pdev->init_name);
1206 return false;
1207 }
1208
1209 if (ehdr->e_phnum == 0) {
1210 pr_err("%s: No loadable segments\n",
1211 qseecom.pdev->init_name);
1212 return false;
1213 }
1214 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
1215 sizeof(struct elf32_hdr) > fw_entry->size) {
1216 pr_err("%s: Program headers not within mdt\n",
1217 qseecom.pdev->init_name);
1218 return false;
1219 }
1220 return true;
1221}
1222
1223static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size)
1224{
1225 int ret = -1;
1226 int i = 0, rc = 0;
1227 const struct firmware *fw_entry = NULL;
1228 struct elf32_phdr *phdr;
1229 char fw_name[MAX_APP_NAME_SIZE];
1230 struct elf32_hdr *ehdr;
1231 int num_images = 0;
1232
1233 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1234 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1235 if (rc) {
1236 pr_err("error with request_firmware\n");
1237 ret = -EIO;
1238 goto err;
1239 }
1240 if (!__qseecom_is_fw_image_valid(fw_entry)) {
1241 ret = -EIO;
1242 goto err;
1243 }
1244 *fw_size = fw_entry->size;
1245 phdr = (struct elf32_phdr *)(fw_entry->data + sizeof(struct elf32_hdr));
1246 ehdr = (struct elf32_hdr *)fw_entry->data;
1247 num_images = ehdr->e_phnum;
1248 release_firmware(fw_entry);
1249 for (i = 0; i < num_images; i++, phdr++) {
1250 memset(fw_name, 0, sizeof(fw_name));
1251 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1252 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1253 if (ret)
1254 goto err;
1255 *fw_size += fw_entry->size;
1256 release_firmware(fw_entry);
1257 }
1258 return ret;
1259err:
1260 if (fw_entry)
1261 release_firmware(fw_entry);
1262 *fw_size = 0;
1263 return ret;
1264}
1265
1266static int __qseecom_get_fw_data(char *appname, u8 *img_data,
1267 struct qseecom_load_app_ireq *load_req)
1268{
1269 int ret = -1;
1270 int i = 0, rc = 0;
1271 const struct firmware *fw_entry = NULL;
1272 char fw_name[MAX_APP_NAME_SIZE];
1273 u8 *img_data_ptr = img_data;
1274 struct elf32_hdr *ehdr;
1275 int num_images = 0;
1276
1277 snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
1278 rc = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1279 if (rc) {
1280 ret = -EIO;
1281 goto err;
1282 }
1283 load_req->img_len = fw_entry->size;
1284 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1285 img_data_ptr = img_data_ptr + fw_entry->size;
1286 load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
1287 ehdr = (struct elf32_hdr *)fw_entry->data;
1288 num_images = ehdr->e_phnum;
1289 release_firmware(fw_entry);
1290 for (i = 0; i < num_images; i++) {
1291 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
1292 ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
1293 if (ret) {
1294 pr_err("Failed to locate blob %s\n", fw_name);
1295 goto err;
1296 }
1297 memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
1298 img_data_ptr = img_data_ptr + fw_entry->size;
1299 load_req->img_len += fw_entry->size;
1300 release_firmware(fw_entry);
1301 }
1302 load_req->phy_addr = virt_to_phys(img_data);
1303 return ret;
1304err:
1305 release_firmware(fw_entry);
1306 return ret;
1307}
1308
1309static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
1310{
1311 int ret = -1;
1312 uint32_t fw_size = 0;
1313 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1314 struct qseecom_command_scm_resp resp;
1315 u8 *img_data = NULL;
1316
1317 if (__qseecom_get_fw_size(appname, &fw_size))
1318 return -EIO;
1319
1320 img_data = kzalloc(fw_size, GFP_KERNEL);
1321 if (!img_data) {
1322 pr_err("Failied to allocate memory for copying image data\n");
1323 return -ENOMEM;
1324 }
1325 ret = __qseecom_get_fw_data(appname, img_data, &load_req);
1326 if (ret) {
1327 kzfree(img_data);
1328 return -EIO;
1329 }
1330
1331 /* Populate the remaining parameters */
1332 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
1333 memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001334 ret = qsee_vote_for_clock(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001335 if (ret) {
1336 kzfree(img_data);
1337 pr_warning("Unable to vote for SFPB clock");
Mona Hossain60f9fb02012-11-05 13:51:50 -08001338 return -EIO;
1339 }
1340
Mona Hossaind44a3842012-10-15 09:41:35 -07001341 /* SCM_CALL to load the image */
1342 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1343 sizeof(struct qseecom_load_app_ireq),
1344 &resp, sizeof(resp));
1345 kzfree(img_data);
1346 if (ret) {
1347 pr_err("scm_call to load failed : ret %d\n", ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001348 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001349 return -EIO;
1350 }
1351
1352 switch (resp.result) {
1353 case QSEOS_RESULT_SUCCESS:
1354 ret = resp.data;
1355 break;
1356 case QSEOS_RESULT_INCOMPLETE:
1357 ret = __qseecom_process_incomplete_cmd(data, &resp);
1358 if (ret)
1359 pr_err("process_incomplete_cmd FAILED\n");
1360 else
1361 ret = resp.data;
1362 break;
1363 case QSEOS_RESULT_FAILURE:
1364 pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
1365 break;
1366 default:
1367 pr_err("scm call return unknown response %d\n", resp.result);
1368 ret = -EINVAL;
1369 break;
1370 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08001371 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain60f9fb02012-11-05 13:51:50 -08001372
Mona Hossaind44a3842012-10-15 09:41:35 -07001373 return ret;
1374}
1375
Mona Hossain9498f5e2013-01-23 18:08:45 -08001376static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
Mona Hossain05c73562012-10-29 17:49:01 -07001377{
1378 int32_t ret = 0;
1379 uint32_t fw_size = 0;
1380 struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
1381 struct qseecom_command_scm_resp resp;
1382 u8 *img_data = NULL;
1383
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001384 if (__qseecom_get_fw_size("cmnlib", &fw_size))
Mona Hossain05c73562012-10-29 17:49:01 -07001385 return -EIO;
1386
1387 img_data = kzalloc(fw_size, GFP_KERNEL);
1388 if (!img_data) {
1389 pr_err("Mem allocation for lib image data failed\n");
1390 return -ENOMEM;
1391 }
Hariprasad Dhalinarasimhaa3b96442013-01-02 10:40:40 -08001392 ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
Mona Hossain05c73562012-10-29 17:49:01 -07001393 if (ret) {
1394 kzfree(img_data);
1395 return -EIO;
1396 }
1397 /* Populate the remaining parameters */
1398 load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
Mona Hossain6311d572013-03-01 15:54:02 -08001399 /* Vote for the SFPB clock */
1400 ret = qsee_vote_for_clock(data, CLK_SFPB);
1401 if (ret) {
1402 pr_err("Unable to vote for SFPB clock: ret = %d", ret);
1403 kzfree(img_data);
1404 return -EIO;
1405 }
1406
Mona Hossain05c73562012-10-29 17:49:01 -07001407 /* SCM_CALL to load the image */
1408 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1409 sizeof(struct qseecom_load_lib_image_ireq),
1410 &resp, sizeof(resp));
Mona Hossain05c73562012-10-29 17:49:01 -07001411 if (ret) {
1412 pr_err("scm_call to load failed : ret %d\n", ret);
1413 ret = -EIO;
1414 } else {
1415 switch (resp.result) {
1416 case QSEOS_RESULT_SUCCESS:
1417 break;
1418 case QSEOS_RESULT_FAILURE:
1419 pr_err("scm call failed w/response result%d\n",
1420 resp.result);
1421 ret = -EINVAL;
1422 break;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001423 case QSEOS_RESULT_INCOMPLETE:
1424 ret = __qseecom_process_incomplete_cmd(data, &resp);
1425 if (ret)
1426 pr_err("process_incomplete_cmd failed err: %d\n",
1427 ret);
1428 break;
Mona Hossain05c73562012-10-29 17:49:01 -07001429 default:
1430 pr_err("scm call return unknown response %d\n",
1431 resp.result);
1432 ret = -EINVAL;
1433 break;
1434 }
1435 }
Hariprasad Dhalinarasimha1a81ca32013-01-31 18:32:32 -08001436 kzfree(img_data);
Mona Hossain6311d572013-03-01 15:54:02 -08001437 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain05c73562012-10-29 17:49:01 -07001438 return ret;
1439}
1440
1441static int qseecom_unload_commonlib_image(void)
1442{
1443 int ret = -EINVAL;
1444 struct qseecom_unload_lib_image_ireq unload_req = {0};
1445 struct qseecom_command_scm_resp resp;
1446
1447 /* Populate the remaining parameters */
1448 unload_req.qsee_cmd_id = QSEOS_UNLOAD_SERV_IMAGE_COMMAND;
1449 /* SCM_CALL to load the image */
1450 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &unload_req,
1451 sizeof(struct qseecom_unload_lib_image_ireq),
1452 &resp, sizeof(resp));
1453 if (ret) {
1454 pr_err("scm_call to unload lib failed : ret %d\n", ret);
1455 ret = -EIO;
1456 } else {
1457 switch (resp.result) {
1458 case QSEOS_RESULT_SUCCESS:
1459 break;
1460 case QSEOS_RESULT_FAILURE:
1461 pr_err("scm fail resp.result QSEOS_RESULT FAILURE\n");
1462 break;
1463 default:
1464 pr_err("scm call return unknown response %d\n",
1465 resp.result);
1466 ret = -EINVAL;
1467 break;
1468 }
1469 }
1470 return ret;
1471}
1472
Mona Hossaind44a3842012-10-15 09:41:35 -07001473int qseecom_start_app(struct qseecom_handle **handle,
1474 char *app_name, uint32_t size)
1475{
Mona Hossain05c73562012-10-29 17:49:01 -07001476 int32_t ret = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07001477 unsigned long flags = 0;
1478 struct qseecom_dev_handle *data = NULL;
1479 struct qseecom_check_app_ireq app_ireq;
1480 struct qseecom_registered_app_list *entry = NULL;
1481 struct qseecom_registered_kclient_list *kclient_entry = NULL;
1482 bool found_app = false;
1483 uint32_t len;
1484 ion_phys_addr_t pa;
1485
1486 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1487 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1488 return -EINVAL;
1489 }
1490
Mona Hossain823f9882012-11-23 14:42:20 -08001491 *handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
1492 if (!(*handle)) {
1493 pr_err("failed to allocate memory for kernel client handle\n");
1494 return -ENOMEM;
1495 }
1496
Mona Hossaind44a3842012-10-15 09:41:35 -07001497 data = kzalloc(sizeof(*data), GFP_KERNEL);
1498 if (!data) {
1499 pr_err("kmalloc failed\n");
1500 if (ret == 0) {
1501 kfree(*handle);
1502 *handle = NULL;
1503 }
1504 return -ENOMEM;
1505 }
1506 data->abort = 0;
1507 data->service = false;
1508 data->released = false;
1509 data->client.app_id = ret;
1510 data->client.sb_length = size;
1511 data->client.user_virt_sb_base = 0;
1512 data->client.ihandle = NULL;
1513
1514 init_waitqueue_head(&data->abort_wq);
1515 atomic_set(&data->ioctl_count, 0);
1516
1517 data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
1518 ION_HEAP(ION_QSECOM_HEAP_ID), 0);
1519 if (IS_ERR_OR_NULL(data->client.ihandle)) {
1520 pr_err("Ion client could not retrieve the handle\n");
1521 kfree(data);
1522 kfree(*handle);
1523 *handle = NULL;
1524 return -EINVAL;
1525 }
1526
Mona Hossain9498f5e2013-01-23 18:08:45 -08001527 if (qseecom.qsee_version > QSEEE_VERSION_00) {
1528 mutex_lock(&app_access_lock);
1529 if (qseecom.commonlib_loaded == false) {
1530 ret = qseecom_load_commonlib_image(data);
1531 if (ret == 0)
1532 qseecom.commonlib_loaded = true;
1533 }
1534 mutex_unlock(&app_access_lock);
1535 }
1536
1537 if (ret) {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001538 pr_err("Failed to load commonlib image\n");
Mona Hossain9498f5e2013-01-23 18:08:45 -08001539 kfree(data);
1540 kfree(*handle);
1541 *handle = NULL;
1542 return -EIO;
1543 }
1544
1545 app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1546 memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
1547 ret = __qseecom_check_app_exists(app_ireq);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001548 if (ret < 0) {
1549 kzfree(data);
1550 kfree(*handle);
1551 *handle = NULL;
Mona Hossain9498f5e2013-01-23 18:08:45 -08001552 return -EINVAL;
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001553 }
Mona Hossain9498f5e2013-01-23 18:08:45 -08001554
Mona Hossaind44a3842012-10-15 09:41:35 -07001555 if (ret > 0) {
1556 pr_warn("App id %d for [%s] app exists\n", ret,
1557 (char *)app_ireq.app_name);
1558 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1559 list_for_each_entry(entry,
1560 &qseecom.registered_app_list_head, list){
1561 if (entry->app_id == ret) {
1562 entry->ref_cnt++;
1563 found_app = true;
1564 break;
1565 }
1566 }
1567 spin_unlock_irqrestore(
1568 &qseecom.registered_app_list_lock, flags);
1569 if (!found_app)
1570 pr_warn("App_id %d [%s] was loaded but not registered\n",
1571 ret, (char *)app_ireq.app_name);
1572 } else {
1573 /* load the app and get the app_id */
1574 pr_debug("%s: Loading app for the first time'\n",
1575 qseecom.pdev->init_name);
1576 mutex_lock(&app_access_lock);
1577 ret = __qseecom_load_fw(data, app_name);
1578 mutex_unlock(&app_access_lock);
1579
1580 if (ret < 0) {
1581 kfree(*handle);
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001582 kfree(data);
Mona Hossaind44a3842012-10-15 09:41:35 -07001583 *handle = NULL;
1584 return ret;
1585 }
1586 data->client.app_id = ret;
1587 }
1588 if (!found_app) {
1589 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1590 if (!entry) {
1591 pr_err("kmalloc failed\n");
Hariprasad Dhalinarasimha56d35122013-02-28 17:55:51 -08001592 kfree(data);
1593 kfree(*handle);
1594 *handle = NULL;
Mona Hossaind44a3842012-10-15 09:41:35 -07001595 return -ENOMEM;
1596 }
1597 entry->app_id = ret;
1598 entry->ref_cnt = 1;
1599
1600 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1601 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
1602 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
1603 flags);
1604 }
1605
1606 /* Get the physical address of the ION BUF */
1607 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
1608 /* Populate the structure for sending scm call to load image */
1609 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
1610 data->client.ihandle);
Hariprasad Dhalinarasimhaacfb09c2013-01-10 13:16:15 -08001611 data->client.user_virt_sb_base = (uint32_t)data->client.sb_virt;
Mona Hossaind44a3842012-10-15 09:41:35 -07001612 data->client.sb_phys = pa;
1613 (*handle)->dev = (void *)data;
1614 (*handle)->sbuf = (unsigned char *)data->client.sb_virt;
1615 (*handle)->sbuf_len = data->client.sb_length;
1616
1617 kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
1618 if (!kclient_entry) {
1619 pr_err("kmalloc failed\n");
1620 return -ENOMEM;
1621 }
1622 kclient_entry->handle = *handle;
1623
1624 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1625 list_add_tail(&kclient_entry->list,
1626 &qseecom.registered_kclient_list_head);
1627 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1628
1629 return 0;
1630}
1631EXPORT_SYMBOL(qseecom_start_app);
1632
1633int qseecom_shutdown_app(struct qseecom_handle **handle)
1634{
1635 int ret = -EINVAL;
Mona Hossain33824022013-02-25 09:32:33 -08001636 struct qseecom_dev_handle *data;
1637
Mona Hossaind44a3842012-10-15 09:41:35 -07001638 struct qseecom_registered_kclient_list *kclient = NULL;
1639 unsigned long flags = 0;
1640 bool found_handle = false;
1641
1642 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1643 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1644 return -EINVAL;
1645 }
Mona Hossain33824022013-02-25 09:32:33 -08001646 if ((handle == NULL) || (*handle == NULL)) {
Mona Hossaind44a3842012-10-15 09:41:35 -07001647 pr_err("Handle is not initialized\n");
1648 return -EINVAL;
1649 }
Mona Hossain33824022013-02-25 09:32:33 -08001650 data = (struct qseecom_dev_handle *) ((*handle)->dev);
Mona Hossaind44a3842012-10-15 09:41:35 -07001651 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
1652 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
1653 list) {
1654 if (kclient->handle == (*handle)) {
1655 list_del(&kclient->list);
1656 found_handle = true;
1657 break;
1658 }
1659 }
1660 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
1661 if (!found_handle)
1662 pr_err("Unable to find the handle, exiting\n");
1663 else
1664 ret = qseecom_unload_app(data);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001665 if (data->client.fast_load_enabled == true)
1666 qsee_disable_clock_vote(data, CLK_SFPB);
1667 if (data->client.perf_enabled == true)
1668 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossaind44a3842012-10-15 09:41:35 -07001669 if (ret == 0) {
1670 kzfree(data);
1671 kzfree(*handle);
1672 kzfree(kclient);
1673 *handle = NULL;
1674 }
1675 return ret;
1676}
1677EXPORT_SYMBOL(qseecom_shutdown_app);
1678
1679int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
1680 uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
1681{
1682 int ret = 0;
1683 struct qseecom_send_cmd_req req = {0, 0, 0, 0};
1684 struct qseecom_dev_handle *data;
1685
1686 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1687 pr_err("This functionality is UNSUPPORTED in version 1.3\n");
1688 return -EINVAL;
1689 }
1690
1691 if (handle == NULL) {
1692 pr_err("Handle is not initialized\n");
1693 return -EINVAL;
1694 }
1695 data = handle->dev;
1696
1697 req.cmd_req_len = sbuf_len;
1698 req.resp_len = rbuf_len;
1699 req.cmd_req_buf = send_buf;
1700 req.resp_buf = resp_buf;
1701
1702 mutex_lock(&app_access_lock);
1703 atomic_inc(&data->ioctl_count);
1704
1705 ret = __qseecom_send_cmd(data, &req);
1706
1707 atomic_dec(&data->ioctl_count);
1708 mutex_unlock(&app_access_lock);
1709
1710 if (ret)
1711 return ret;
1712
1713 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1714 req.resp_len, req.resp_buf);
1715 return ret;
1716}
1717EXPORT_SYMBOL(qseecom_send_command);
1718
Mona Hossain91a8fc92012-11-07 19:58:30 -08001719int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
1720{
Mona Hossainfca6f422013-01-12 13:00:35 -08001721 int ret = 0;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001722 if ((handle == NULL) || (handle->dev == NULL)) {
1723 pr_err("No valid kernel client\n");
1724 return -EINVAL;
1725 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001726 if (high) {
1727 ret = qsee_vote_for_clock(handle->dev, CLK_DFAB);
1728 if (ret)
1729 pr_err("Failed to vote for DFAB clock%d\n", ret);
1730 ret = qsee_vote_for_clock(handle->dev, CLK_SFPB);
1731 if (ret) {
1732 pr_err("Failed to vote for SFPB clock%d\n", ret);
1733 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
1734 }
1735 } else {
Mona Hossain04d3fac2012-12-03 10:10:37 -08001736 qsee_disable_clock_vote(handle->dev, CLK_DFAB);
Mona Hossainfca6f422013-01-12 13:00:35 -08001737 qsee_disable_clock_vote(handle->dev, CLK_SFPB);
Mona Hossain91a8fc92012-11-07 19:58:30 -08001738 }
Mona Hossainfca6f422013-01-12 13:00:35 -08001739 return ret;
Mona Hossain91a8fc92012-11-07 19:58:30 -08001740}
1741EXPORT_SYMBOL(qseecom_set_bandwidth);
1742
Mona Hossain2892b6b2012-02-17 13:53:11 -08001743static int qseecom_send_resp(void)
1744{
1745 qseecom.send_resp_flag = 1;
1746 wake_up_interruptible(&qseecom.send_resp_wq);
1747 return 0;
1748}
1749
1750static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1751 void __user *argp)
1752{
1753 struct qseecom_qseos_version_req req;
1754
1755 if (copy_from_user(&req, argp, sizeof(req))) {
1756 pr_err("copy_from_user failed");
1757 return -EINVAL;
1758 }
1759 req.qseos_version = qseecom.qseos_version;
1760 if (copy_to_user(argp, &req, sizeof(req))) {
1761 pr_err("copy_to_user failed");
1762 return -EINVAL;
1763 }
1764 return 0;
1765}
1766
Mona Hossain6311d572013-03-01 15:54:02 -08001767static int __qseecom_enable_clk(void)
1768{
1769 int rc = 0;
Mona Hossain17a4faf2013-03-22 16:40:56 -07001770 struct qseecom_clk *qclk;
Mona Hossain6311d572013-03-01 15:54:02 -08001771
Mona Hossain17a4faf2013-03-22 16:40:56 -07001772 qclk = &qseecom.qsee;
Mona Hossain6311d572013-03-01 15:54:02 -08001773 /* Enable CE core clk */
Mona Hossain17a4faf2013-03-22 16:40:56 -07001774 rc = clk_prepare_enable(qclk->ce_core_clk);
Mona Hossain6311d572013-03-01 15:54:02 -08001775 if (rc) {
1776 pr_err("Unable to enable/prepare CE core clk\n");
1777 goto err;
Mona Hossain17a4faf2013-03-22 16:40:56 -07001778 }
1779 /* Enable CE clk */
1780 rc = clk_prepare_enable(qclk->ce_clk);
1781 if (rc) {
1782 pr_err("Unable to enable/prepare CE iface clk\n");
1783 goto ce_clk_err;
1784 }
1785 /* Enable AXI clk */
1786 rc = clk_prepare_enable(qclk->ce_bus_clk);
1787 if (rc) {
1788 pr_err("Unable to enable/prepare CE bus clk\n");
1789 goto ce_bus_clk_err;
Mona Hossain6311d572013-03-01 15:54:02 -08001790 }
1791 return 0;
1792
1793ce_bus_clk_err:
Mona Hossain17a4faf2013-03-22 16:40:56 -07001794 clk_disable_unprepare(qclk->ce_clk);
Mona Hossain6311d572013-03-01 15:54:02 -08001795ce_clk_err:
Mona Hossain17a4faf2013-03-22 16:40:56 -07001796 clk_disable_unprepare(qclk->ce_core_clk);
Mona Hossain6311d572013-03-01 15:54:02 -08001797err:
1798 return -EIO;
1799}
1800
1801static void __qseecom_disable_clk(void)
1802{
Mona Hossain17a4faf2013-03-22 16:40:56 -07001803 struct qseecom_clk *qclk;
1804
1805 qclk = &qseecom.qsee;
1806 if (qclk->ce_clk != NULL)
1807 clk_disable_unprepare(qclk->ce_clk);
1808 if (qclk->ce_core_clk != NULL)
1809 clk_disable_unprepare(qclk->ce_core_clk);
1810 if (qclk->ce_bus_clk != NULL)
1811 clk_disable_unprepare(qclk->ce_bus_clk);
Mona Hossain6311d572013-03-01 15:54:02 -08001812}
1813
Mona Hossain04d3fac2012-12-03 10:10:37 -08001814static int qsee_vote_for_clock(struct qseecom_dev_handle *data,
1815 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001816{
1817 int ret = 0;
Mona Hossain17a4faf2013-03-22 16:40:56 -07001818 struct qseecom_clk *qclk;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001819
Mona Hossain17a4faf2013-03-22 16:40:56 -07001820 qclk = &qseecom.qsee;
1821 if (!qseecom.qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001822 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001823
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001824 switch (clk_type) {
1825 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001826 mutex_lock(&qsee_bw_mutex);
Mona Hossain17a4faf2013-03-22 16:40:56 -07001827 if (!qseecom.qsee_bw_count) {
1828 if (qseecom.qsee_sfpb_bw_count > 0)
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001829 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001830 qseecom.qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001831 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001832 if (qclk->ce_core_src_clk != NULL)
Mona Hossain6311d572013-03-01 15:54:02 -08001833 ret = __qseecom_enable_clk();
1834 if (!ret) {
1835 ret =
1836 msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001837 qseecom.qsee_perf_client, 1);
1838 if ((ret) &&
1839 (qclk->ce_core_src_clk != NULL))
Mona Hossain6311d572013-03-01 15:54:02 -08001840 __qseecom_disable_clk();
1841 }
Mona Hossaind39e33b2012-11-05 13:36:40 -08001842 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001843 if (ret)
1844 pr_err("DFAB Bandwidth req failed (%d)\n",
1845 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001846 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001847 qseecom.qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001848 data->client.perf_enabled = true;
1849 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001850 } else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001851 qseecom.qsee_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001852 data->client.perf_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001853 }
1854 mutex_unlock(&qsee_bw_mutex);
1855 break;
1856 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001857 mutex_lock(&qsee_bw_mutex);
Mona Hossain17a4faf2013-03-22 16:40:56 -07001858 if (!qseecom.qsee_sfpb_bw_count) {
1859 if (qseecom.qsee_bw_count > 0)
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001860 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001861 qseecom.qsee_perf_client, 3);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001862 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001863 if (qclk->ce_core_src_clk != NULL)
Mona Hossain6311d572013-03-01 15:54:02 -08001864 ret = __qseecom_enable_clk();
1865 if (!ret) {
1866 ret =
1867 msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001868 qseecom.qsee_perf_client, 2);
1869 if ((ret) &&
1870 (qclk->ce_core_src_clk != NULL))
Mona Hossain6311d572013-03-01 15:54:02 -08001871 __qseecom_disable_clk();
1872 }
Mona Hossaind39e33b2012-11-05 13:36:40 -08001873 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001874
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001875 if (ret)
1876 pr_err("SFPB Bandwidth req failed (%d)\n",
1877 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001878 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001879 qseecom.qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001880 data->client.fast_load_enabled = true;
1881 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001882 } else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001883 qseecom.qsee_sfpb_bw_count++;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001884 data->client.fast_load_enabled = true;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001885 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001886 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001887 break;
1888 default:
1889 pr_err("Clock type not defined\n");
1890 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001891 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001892 return ret;
1893}
1894
Mona Hossain04d3fac2012-12-03 10:10:37 -08001895static void qsee_disable_clock_vote(struct qseecom_dev_handle *data,
1896 int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001897{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001898 int32_t ret = 0;
Mona Hossain17a4faf2013-03-22 16:40:56 -07001899 struct qseecom_clk *qclk;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001900
Mona Hossain17a4faf2013-03-22 16:40:56 -07001901 qclk = &qseecom.qsee;
1902 if (!qseecom.qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001903 return;
1904
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001905 switch (clk_type) {
1906 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001907 mutex_lock(&qsee_bw_mutex);
Mona Hossain17a4faf2013-03-22 16:40:56 -07001908 if (qseecom.qsee_bw_count == 0) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001909 pr_err("Client error.Extra call to disable DFAB clk\n");
1910 mutex_unlock(&qsee_bw_mutex);
1911 return;
1912 }
1913
Mona Hossain17a4faf2013-03-22 16:40:56 -07001914 if (qseecom.qsee_bw_count == 1) {
1915 if (qseecom.qsee_sfpb_bw_count > 0)
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001916 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001917 qseecom.qsee_perf_client, 2);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001918 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001919 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001920 qseecom.qsee_perf_client, 0);
1921 if ((!ret) && (qclk->ce_core_src_clk != NULL))
Mona Hossain6311d572013-03-01 15:54:02 -08001922 __qseecom_disable_clk();
Mona Hossaind39e33b2012-11-05 13:36:40 -08001923 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001924 if (ret)
1925 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001926 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001927 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001928 qseecom.qsee_bw_count--;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001929 data->client.perf_enabled = false;
1930 }
1931 } else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001932 qseecom.qsee_bw_count--;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001933 data->client.perf_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001934 }
1935 mutex_unlock(&qsee_bw_mutex);
1936 break;
1937 case CLK_SFPB:
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001938 mutex_lock(&qsee_bw_mutex);
Mona Hossain17a4faf2013-03-22 16:40:56 -07001939 if (qseecom.qsee_sfpb_bw_count == 0) {
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001940 pr_err("Client error.Extra call to disable SFPB clk\n");
1941 mutex_unlock(&qsee_bw_mutex);
1942 return;
1943 }
Mona Hossain17a4faf2013-03-22 16:40:56 -07001944 if (qseecom.qsee_sfpb_bw_count == 1) {
1945 if (qseecom.qsee_bw_count > 0)
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001946 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001947 qseecom.qsee_perf_client, 1);
Mona Hossaind39e33b2012-11-05 13:36:40 -08001948 else {
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001949 ret = msm_bus_scale_client_update_request(
Mona Hossain17a4faf2013-03-22 16:40:56 -07001950 qseecom.qsee_perf_client, 0);
1951 if ((!ret) && (qclk->ce_core_src_clk != NULL))
Mona Hossain6311d572013-03-01 15:54:02 -08001952 __qseecom_disable_clk();
Mona Hossaind39e33b2012-11-05 13:36:40 -08001953 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001954 if (ret)
1955 pr_err("SFPB Bandwidth req fail (%d)\n",
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001956 ret);
Mona Hossain04d3fac2012-12-03 10:10:37 -08001957 else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001958 qseecom.qsee_sfpb_bw_count--;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001959 data->client.fast_load_enabled = false;
1960 }
1961 } else {
Mona Hossain17a4faf2013-03-22 16:40:56 -07001962 qseecom.qsee_sfpb_bw_count--;
Mona Hossain04d3fac2012-12-03 10:10:37 -08001963 data->client.fast_load_enabled = false;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001964 }
Ramesh Masavarapu8d756582012-10-03 10:18:06 -07001965 mutex_unlock(&qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001966 break;
1967 default:
1968 pr_err("Clock type not defined\n");
1969 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001970 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001971
Mona Hossain2892b6b2012-02-17 13:53:11 -08001972}
1973
Mona Hossain5ab9d772012-04-11 21:00:40 -07001974static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1975 void __user *argp)
1976{
1977 struct ion_handle *ihandle; /* Ion handle */
1978 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001979 int ret;
1980 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001981 ion_phys_addr_t pa = 0;
1982 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001983 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001984 struct qseecom_load_app_ireq load_req;
1985 struct qseecom_command_scm_resp resp;
1986
1987 /* Copy the relevant information needed for loading the image */
1988 if (__copy_from_user(&load_img_req,
1989 (void __user *)argp,
1990 sizeof(struct qseecom_load_img_req))) {
1991 pr_err("copy_from_user failed\n");
1992 return -EFAULT;
1993 }
1994
1995 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001996 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001997 load_img_req.ifd_data_fd);
1998 if (IS_ERR_OR_NULL(ihandle)) {
1999 pr_err("Ion client could not retrieve the handle\n");
2000 return -ENOMEM;
2001 }
2002
2003 /* Get the physical address of the ION BUF */
2004 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
2005
2006 /* Populate the structure for sending scm call to load image */
2007 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
2008 load_req.mdt_len = load_img_req.mdt_len;
2009 load_req.img_len = load_img_req.img_len;
2010 load_req.phy_addr = pa;
2011
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002012 /* SCM_CALL tied to Core0 */
2013 mask = CPU_MASK_CPU0;
2014 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2015 if (set_cpu_ret) {
2016 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
2017 set_cpu_ret);
2018 ret = -EFAULT;
2019 goto qseecom_load_external_elf_set_cpu_err;
2020 }
Mona Hossain6311d572013-03-01 15:54:02 -08002021 /* Vote for the SFPB clock */
2022 ret = qsee_vote_for_clock(data, CLK_SFPB);
2023 if (ret) {
2024 pr_err("Unable to vote for SFPB clock: ret = %d", ret);
2025 ret = -EIO;
2026 goto qseecom_load_external_elf_set_cpu_err;
2027 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002028
Mona Hossain5ab9d772012-04-11 21:00:40 -07002029 /* SCM_CALL to load the external elf */
2030 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
2031 sizeof(struct qseecom_load_app_ireq),
2032 &resp, sizeof(resp));
2033 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002034 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07002035 ret);
2036 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002037 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002038 }
2039
2040 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2041 ret = __qseecom_process_incomplete_cmd(data, &resp);
2042 if (ret)
2043 pr_err("process_incomplete_cmd failed err: %d\n",
2044 ret);
2045 } else {
2046 if (resp.result != QSEOS_RESULT_SUCCESS) {
2047 pr_err("scm_call to load image failed resp.result =%d\n",
2048 resp.result);
2049 ret = -EFAULT;
2050 }
2051 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002052
2053qseecom_load_external_elf_scm_err:
2054 /* Restore the CPU mask */
2055 mask = CPU_MASK_ALL;
2056 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2057 if (set_cpu_ret) {
2058 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2059 set_cpu_ret);
2060 ret = -EFAULT;
2061 }
2062
2063qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07002064 /* Deallocate the handle */
2065 if (!IS_ERR_OR_NULL(ihandle))
2066 ion_free(qseecom.ion_clnt, ihandle);
Mona Hossain6311d572013-03-01 15:54:02 -08002067 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain5ab9d772012-04-11 21:00:40 -07002068 return ret;
2069}
2070
2071static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
2072{
2073 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002074 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002075 struct qseecom_command_scm_resp resp;
2076 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002077 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002078
2079 /* Populate the structure for sending scm call to unload image */
2080 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002081
2082 /* SCM_CALL tied to Core0 */
2083 mask = CPU_MASK_CPU0;
2084 ret = set_cpus_allowed_ptr(current, &mask);
2085 if (ret) {
2086 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
2087 ret);
2088 return -EFAULT;
2089 }
2090
Mona Hossain5ab9d772012-04-11 21:00:40 -07002091 /* SCM_CALL to unload the external elf */
2092 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
2093 sizeof(struct qseecom_unload_app_ireq),
2094 &resp, sizeof(resp));
2095 if (ret) {
2096 pr_err("scm_call to unload failed : ret %d\n",
2097 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002098 ret = -EFAULT;
2099 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07002100 }
2101 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
2102 ret = __qseecom_process_incomplete_cmd(data, &resp);
2103 if (ret)
2104 pr_err("process_incomplete_cmd fail err: %d\n",
2105 ret);
2106 } else {
2107 if (resp.result != QSEOS_RESULT_SUCCESS) {
2108 pr_err("scm_call to unload image failed resp.result =%d\n",
2109 resp.result);
2110 ret = -EFAULT;
2111 }
2112 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07002113
2114qseecom_unload_external_elf_scm_err:
2115 /* Restore the CPU mask */
2116 mask = CPU_MASK_ALL;
2117 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
2118 if (set_cpu_ret) {
2119 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
2120 set_cpu_ret);
2121 ret = -EFAULT;
2122 }
2123
Mona Hossain5ab9d772012-04-11 21:00:40 -07002124 return ret;
2125}
Mona Hossain2892b6b2012-02-17 13:53:11 -08002126
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002127static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
2128 void __user *argp)
2129{
2130
2131 int32_t ret;
2132 struct qseecom_qseos_app_load_query query_req;
2133 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002134 struct qseecom_registered_app_list *entry = NULL;
2135 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002136
2137 /* Copy the relevant information needed for loading the image */
2138 if (__copy_from_user(&query_req,
2139 (void __user *)argp,
2140 sizeof(struct qseecom_qseos_app_load_query))) {
2141 pr_err("copy_from_user failed\n");
2142 return -EFAULT;
2143 }
2144
2145 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
2146 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
2147
2148 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002149
2150 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002151 pr_err(" scm call to check if app is loaded failed");
2152 return ret; /* scm call failed */
2153 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07002154 pr_warn("App id %d (%s) already exists\n", ret,
2155 (char *)(req.app_name));
2156 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
2157 list_for_each_entry(entry,
2158 &qseecom.registered_app_list_head, list){
2159 if (entry->app_id == ret) {
2160 entry->ref_cnt++;
2161 break;
2162 }
2163 }
2164 spin_unlock_irqrestore(
2165 &qseecom.registered_app_list_lock, flags);
2166 data->client.app_id = ret;
2167 query_req.app_id = ret;
2168
2169 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
2170 pr_err("copy_to_user failed\n");
2171 return -EFAULT;
2172 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002173 return -EEXIST; /* app already loaded */
2174 } else {
2175 return 0; /* app not loaded */
2176 }
2177}
2178
Mona Hossain2892b6b2012-02-17 13:53:11 -08002179static long qseecom_ioctl(struct file *file, unsigned cmd,
2180 unsigned long arg)
2181{
2182 int ret = 0;
2183 struct qseecom_dev_handle *data = file->private_data;
2184 void __user *argp = (void __user *) arg;
2185
2186 if (data->abort) {
2187 pr_err("Aborting qseecom driver\n");
2188 return -ENODEV;
2189 }
2190
2191 switch (cmd) {
2192 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
2193 pr_debug("ioctl register_listener_req()\n");
2194 atomic_inc(&data->ioctl_count);
2195 ret = qseecom_register_listener(data, argp);
2196 atomic_dec(&data->ioctl_count);
2197 wake_up_all(&data->abort_wq);
2198 if (ret)
2199 pr_err("failed qseecom_register_listener: %d\n", ret);
2200 break;
2201 }
2202 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
2203 pr_debug("ioctl unregister_listener_req()\n");
2204 atomic_inc(&data->ioctl_count);
2205 ret = qseecom_unregister_listener(data);
2206 atomic_dec(&data->ioctl_count);
2207 wake_up_all(&data->abort_wq);
2208 if (ret)
2209 pr_err("failed qseecom_unregister_listener: %d\n", ret);
2210 break;
2211 }
2212 case QSEECOM_IOCTL_SEND_CMD_REQ: {
2213 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002214 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002215 atomic_inc(&data->ioctl_count);
2216 ret = qseecom_send_cmd(data, argp);
2217 atomic_dec(&data->ioctl_count);
2218 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002219 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002220 if (ret)
2221 pr_err("failed qseecom_send_cmd: %d\n", ret);
2222 break;
2223 }
2224 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
2225 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002226 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002227 atomic_inc(&data->ioctl_count);
2228 ret = qseecom_send_modfd_cmd(data, argp);
2229 atomic_dec(&data->ioctl_count);
2230 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07002231 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002232 if (ret)
2233 pr_err("failed qseecom_send_cmd: %d\n", ret);
2234 break;
2235 }
2236 case QSEECOM_IOCTL_RECEIVE_REQ: {
2237 atomic_inc(&data->ioctl_count);
2238 ret = qseecom_receive_req(data);
2239 atomic_dec(&data->ioctl_count);
2240 wake_up_all(&data->abort_wq);
2241 if (ret)
2242 pr_err("failed qseecom_receive_req: %d\n", ret);
2243 break;
2244 }
2245 case QSEECOM_IOCTL_SEND_RESP_REQ: {
2246 atomic_inc(&data->ioctl_count);
2247 ret = qseecom_send_resp();
2248 atomic_dec(&data->ioctl_count);
2249 wake_up_all(&data->abort_wq);
2250 if (ret)
2251 pr_err("failed qseecom_send_resp: %d\n", ret);
2252 break;
2253 }
2254 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
2255 ret = qseecom_set_client_mem_param(data, argp);
2256 if (ret)
2257 pr_err("failed Qqseecom_set_mem_param request: %d\n",
2258 ret);
2259 break;
2260 }
2261 case QSEECOM_IOCTL_LOAD_APP_REQ: {
2262 mutex_lock(&app_access_lock);
2263 atomic_inc(&data->ioctl_count);
Mona Hossain05c73562012-10-29 17:49:01 -07002264 if (qseecom.qsee_version > QSEEE_VERSION_00) {
2265 if (qseecom.commonlib_loaded == false) {
Mona Hossain9498f5e2013-01-23 18:08:45 -08002266 ret = qseecom_load_commonlib_image(data);
Mona Hossain05c73562012-10-29 17:49:01 -07002267 if (ret == 0)
2268 qseecom.commonlib_loaded = true;
2269 }
2270 }
2271 if (ret == 0)
2272 ret = qseecom_load_app(data, argp);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002273 atomic_dec(&data->ioctl_count);
2274 mutex_unlock(&app_access_lock);
2275 if (ret)
2276 pr_err("failed load_app request: %d\n", ret);
2277 break;
2278 }
2279 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
2280 mutex_lock(&app_access_lock);
2281 atomic_inc(&data->ioctl_count);
2282 ret = qseecom_unload_app(data);
2283 atomic_dec(&data->ioctl_count);
2284 mutex_unlock(&app_access_lock);
2285 if (ret)
2286 pr_err("failed unload_app request: %d\n", ret);
2287 break;
2288 }
2289 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
2290 atomic_inc(&data->ioctl_count);
2291 ret = qseecom_get_qseos_version(data, argp);
2292 if (ret)
2293 pr_err("qseecom_get_qseos_version: %d\n", ret);
2294 atomic_dec(&data->ioctl_count);
2295 break;
2296 }
2297 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
2298 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002299 ret = qsee_vote_for_clock(data, CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002300 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002301 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002302 ret = qsee_vote_for_clock(data, CLK_SFPB);
2303 if (ret)
2304 pr_err("Failed to vote for SFPB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002305 atomic_dec(&data->ioctl_count);
2306 break;
2307 }
2308 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
2309 atomic_inc(&data->ioctl_count);
Mona Hossain04d3fac2012-12-03 10:10:37 -08002310 qsee_disable_clock_vote(data, CLK_DFAB);
Mona Hossain8e2d73a2013-01-10 04:30:04 -08002311 qsee_disable_clock_vote(data, CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002312 atomic_dec(&data->ioctl_count);
2313 break;
2314 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07002315 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
2316 data->released = true;
2317 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2318 pr_err("Loading External elf image unsupported in rev 0x13\n");
2319 ret = -EINVAL;
2320 break;
2321 }
2322 mutex_lock(&app_access_lock);
2323 atomic_inc(&data->ioctl_count);
2324 ret = qseecom_load_external_elf(data, argp);
2325 atomic_dec(&data->ioctl_count);
2326 mutex_unlock(&app_access_lock);
2327 if (ret)
2328 pr_err("failed load_external_elf request: %d\n", ret);
2329 break;
2330 }
2331 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
2332 data->released = true;
2333 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2334 pr_err("Unloading External elf image unsupported in rev 0x13\n");
2335 ret = -EINVAL;
2336 break;
2337 }
2338 mutex_lock(&app_access_lock);
2339 atomic_inc(&data->ioctl_count);
2340 ret = qseecom_unload_external_elf(data);
2341 atomic_dec(&data->ioctl_count);
2342 mutex_unlock(&app_access_lock);
2343 if (ret)
2344 pr_err("failed unload_app request: %d\n", ret);
2345 break;
2346 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07002347 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
2348 mutex_lock(&app_access_lock);
2349 atomic_inc(&data->ioctl_count);
2350 ret = qseecom_query_app_loaded(data, argp);
2351 atomic_dec(&data->ioctl_count);
2352 mutex_unlock(&app_access_lock);
2353 break;
2354 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002355 default:
2356 return -EINVAL;
2357 }
2358 return ret;
2359}
2360
2361static int qseecom_open(struct inode *inode, struct file *file)
2362{
2363 int ret = 0;
2364 struct qseecom_dev_handle *data;
2365
2366 data = kzalloc(sizeof(*data), GFP_KERNEL);
2367 if (!data) {
2368 pr_err("kmalloc failed\n");
2369 return -ENOMEM;
2370 }
2371 file->private_data = data;
2372 data->abort = 0;
2373 data->service = false;
2374 data->released = false;
2375 init_waitqueue_head(&data->abort_wq);
2376 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002377 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2378 int pil_error;
2379 mutex_lock(&pil_access_lock);
2380 if (pil_ref_cnt == 0) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002381 pil = subsystem_get("tzapps");
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002382 if (IS_ERR(pil)) {
2383 pr_err("Playready PIL image load failed\n");
2384 pil_error = PTR_ERR(pil);
2385 pil = NULL;
2386 pr_debug("tzapps image load FAILED\n");
2387 mutex_unlock(&pil_access_lock);
2388 return pil_error;
2389 }
2390 }
2391 pil_ref_cnt++;
2392 mutex_unlock(&pil_access_lock);
2393 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002394 return ret;
2395}
2396
2397static int qseecom_release(struct inode *inode, struct file *file)
2398{
2399 struct qseecom_dev_handle *data = file->private_data;
2400 int ret = 0;
2401
2402 if (data->released == false) {
2403 pr_warn("data->released == false\n");
2404 if (data->service)
2405 ret = qseecom_unregister_listener(data);
2406 else
2407 ret = qseecom_unload_app(data);
2408 if (ret) {
2409 pr_err("Close failed\n");
2410 return ret;
2411 }
2412 }
Mona Hossain04d3fac2012-12-03 10:10:37 -08002413 if (data->client.fast_load_enabled == true)
2414 qsee_disable_clock_vote(data, CLK_SFPB);
2415 if (data->client.perf_enabled == true)
2416 qsee_disable_clock_vote(data, CLK_DFAB);
2417
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002418 if (qseecom.qseos_version == QSEOS_VERSION_13) {
2419 mutex_lock(&pil_access_lock);
2420 if (pil_ref_cnt == 1)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002421 subsystem_put(pil);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07002422 pil_ref_cnt--;
2423 mutex_unlock(&pil_access_lock);
2424 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002425 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002426
Mona Hossain2892b6b2012-02-17 13:53:11 -08002427 return ret;
2428}
2429
Mona Hossain2892b6b2012-02-17 13:53:11 -08002430static const struct file_operations qseecom_fops = {
2431 .owner = THIS_MODULE,
2432 .unlocked_ioctl = qseecom_ioctl,
2433 .open = qseecom_open,
2434 .release = qseecom_release
2435};
2436
Mona Hossaind39e33b2012-11-05 13:36:40 -08002437static int __qseecom_init_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002438{
2439 int rc = 0;
2440 struct device *pdev;
Mona Hossain17a4faf2013-03-22 16:40:56 -07002441 struct qseecom_clk *qclk;
2442
2443 qclk = &qseecom.qsee;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002444
2445 pdev = qseecom.pdev;
2446 /* Get CE3 src core clk. */
Mona Hossain17a4faf2013-03-22 16:40:56 -07002447
2448 qclk->ce_core_src_clk = clk_get(pdev, "core_clk_src");
2449 if (!IS_ERR(qclk->ce_core_src_clk)) {
Mona Hossain6311d572013-03-01 15:54:02 -08002450 /* Set the core src clk @100Mhz */
Mona Hossain17a4faf2013-03-22 16:40:56 -07002451 rc = clk_set_rate(qclk->ce_core_src_clk, QSEE_CE_CLK_100MHZ);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002452 if (rc) {
Mona Hossain17a4faf2013-03-22 16:40:56 -07002453 clk_put(qclk->ce_core_src_clk);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002454 pr_err("Unable to set the core src clk @100Mhz.\n");
Mona Hossaind39e33b2012-11-05 13:36:40 -08002455 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002456 }
2457 } else {
2458 pr_warn("Unable to get CE core src clk, set to NULL\n");
Mona Hossain17a4faf2013-03-22 16:40:56 -07002459 qclk->ce_core_src_clk = NULL;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002460 }
2461
2462 /* Get CE core clk */
Mona Hossain17a4faf2013-03-22 16:40:56 -07002463 qclk->ce_core_clk = clk_get(pdev, "core_clk");
2464 if (IS_ERR(qclk->ce_core_clk)) {
2465 rc = PTR_ERR(qclk->ce_core_clk);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002466 pr_err("Unable to get CE core clk\n");
Mona Hossain17a4faf2013-03-22 16:40:56 -07002467 if (qclk->ce_core_src_clk != NULL)
2468 clk_put(qclk->ce_core_src_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002469 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002470 }
2471
2472 /* Get CE Interface clk */
Mona Hossain17a4faf2013-03-22 16:40:56 -07002473 qclk->ce_clk = clk_get(pdev, "iface_clk");
2474 if (IS_ERR(qclk->ce_clk)) {
2475 rc = PTR_ERR(qclk->ce_clk);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002476 pr_err("Unable to get CE interface clk\n");
Mona Hossain17a4faf2013-03-22 16:40:56 -07002477 if (qclk->ce_core_src_clk != NULL)
2478 clk_put(qclk->ce_core_src_clk);
2479 clk_put(qclk->ce_core_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002480 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002481 }
2482
2483 /* Get CE AXI clk */
Mona Hossain17a4faf2013-03-22 16:40:56 -07002484 qclk->ce_bus_clk = clk_get(pdev, "bus_clk");
2485 if (IS_ERR(qclk->ce_bus_clk)) {
2486 rc = PTR_ERR(qclk->ce_bus_clk);
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002487 pr_err("Unable to get CE BUS interface clk\n");
Mona Hossain17a4faf2013-03-22 16:40:56 -07002488 if (qclk->ce_core_src_clk != NULL)
2489 clk_put(qclk->ce_core_src_clk);
2490 clk_put(qclk->ce_core_clk);
2491 clk_put(qclk->ce_clk);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002492 return -EIO;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002493 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002494 return rc;
2495}
2496
Mona Hossaind39e33b2012-11-05 13:36:40 -08002497static void __qseecom_deinit_clk(void)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002498{
Mona Hossain17a4faf2013-03-22 16:40:56 -07002499 struct qseecom_clk *qclk;
2500
2501 qclk = &qseecom.qsee;
2502
2503 if (qclk->ce_clk != NULL) {
2504 clk_put(qclk->ce_clk);
2505 qclk->ce_clk = NULL;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002506 }
Mona Hossain17a4faf2013-03-22 16:40:56 -07002507 if (qclk->ce_core_clk != NULL) {
2508 clk_put(qclk->ce_core_clk);
2509 qclk->ce_clk = NULL;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002510 }
Mona Hossain17a4faf2013-03-22 16:40:56 -07002511 if (qclk->ce_bus_clk != NULL) {
2512 clk_put(qclk->ce_bus_clk);
2513 qclk->ce_clk = NULL;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002514 }
Mona Hossain17a4faf2013-03-22 16:40:56 -07002515 if (qclk->ce_core_src_clk != NULL) {
2516 clk_put(qclk->ce_core_src_clk);
2517 qclk->ce_core_src_clk = NULL;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002518 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002519}
2520
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002521static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002522{
2523 int rc;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002524 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002525 struct device *class_dev;
2526 char qsee_not_legacy = 0;
Mona Hossaind44a3842012-10-15 09:41:35 -07002527 struct msm_bus_scale_pdata *qseecom_platform_support = NULL;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002528 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
2529
Mona Hossain17a4faf2013-03-22 16:40:56 -07002530 qseecom.qsee_bw_count = 0;
2531 qseecom.qsee_perf_client = 0;
2532 qseecom.qsee_sfpb_bw_count = 0;
Mona Hossaind39e33b2012-11-05 13:36:40 -08002533
Mona Hossain17a4faf2013-03-22 16:40:56 -07002534 qseecom.qsee.ce_core_clk = NULL;
2535 qseecom.qsee.ce_clk = NULL;
2536 qseecom.qsee.ce_core_src_clk = NULL;
2537 qseecom.qsee.ce_bus_clk = NULL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07002538
Mona Hossain2892b6b2012-02-17 13:53:11 -08002539 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
2540 if (rc < 0) {
2541 pr_err("alloc_chrdev_region failed %d\n", rc);
2542 return rc;
2543 }
2544
2545 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
2546 if (IS_ERR(driver_class)) {
2547 rc = -ENOMEM;
2548 pr_err("class_create failed %d\n", rc);
2549 goto unregister_chrdev_region;
2550 }
2551
2552 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
2553 QSEECOM_DEV);
2554 if (!class_dev) {
2555 pr_err("class_device_create failed %d\n", rc);
2556 rc = -ENOMEM;
2557 goto class_destroy;
2558 }
2559
2560 cdev_init(&qseecom_cdev, &qseecom_fops);
2561 qseecom_cdev.owner = THIS_MODULE;
2562
2563 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
2564 if (rc < 0) {
2565 pr_err("cdev_add failed %d\n", rc);
2566 goto err;
2567 }
2568
2569 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
2570 spin_lock_init(&qseecom.registered_listener_list_lock);
2571 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
2572 spin_lock_init(&qseecom.registered_app_list_lock);
Mona Hossaind44a3842012-10-15 09:41:35 -07002573 INIT_LIST_HEAD(&qseecom.registered_kclient_list_head);
2574 spin_lock_init(&qseecom.registered_kclient_list_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002575 init_waitqueue_head(&qseecom.send_resp_wq);
2576 qseecom.send_resp_flag = 0;
2577
2578 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
2579 &qsee_not_legacy, sizeof(qsee_not_legacy));
2580 if (rc) {
Mona Hossain05c73562012-10-29 17:49:01 -07002581 pr_err("Failed to retrieve QSEOS version information %d\n", rc);
Mona Hossain2892b6b2012-02-17 13:53:11 -08002582 goto err;
2583 }
Mona Hossain05c73562012-10-29 17:49:01 -07002584 if (qsee_not_legacy) {
2585 uint32_t feature = 10;
2586
2587 qseecom.qsee_version = QSEEE_VERSION_00;
2588 rc = scm_call(6, 3, &feature, sizeof(feature),
2589 &qseecom.qsee_version, sizeof(qseecom.qsee_version));
2590 if (rc) {
2591 pr_err("Failed to get QSEE version info %d\n", rc);
2592 goto err;
2593 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08002594 qseecom.qseos_version = QSEOS_VERSION_14;
Mona Hossain05c73562012-10-29 17:49:01 -07002595 } else {
Mona Hossain2892b6b2012-02-17 13:53:11 -08002596 qseecom.qseos_version = QSEOS_VERSION_13;
Mona Hossain05c73562012-10-29 17:49:01 -07002597 qseecom.qsee_version = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002598 pil = NULL;
2599 pil_ref_cnt = 0;
2600 }
Mona Hossain05c73562012-10-29 17:49:01 -07002601 qseecom.commonlib_loaded = false;
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002602 qseecom.pdev = class_dev;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002603 /* Create ION msm client */
Mona Hossaind44a3842012-10-15 09:41:35 -07002604 qseecom.ion_clnt = msm_ion_client_create(-1, "qseecom-kernel");
Mona Hossain2892b6b2012-02-17 13:53:11 -08002605 if (qseecom.ion_clnt == NULL) {
2606 pr_err("Ion client cannot be created\n");
2607 rc = -ENOMEM;
2608 goto err;
2609 }
2610
2611 /* register client for bus scaling */
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002612 if (pdev->dev.of_node) {
2613 ret = __qseecom_init_clk();
2614 if (ret)
2615 goto err;
Mona Hossain6311d572013-03-01 15:54:02 -08002616
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002617 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2618 msm_bus_cl_get_pdata(pdev);
Mona Hossain5b76a622012-11-15 20:09:08 -08002619 if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
2620 struct resource *resource = NULL;
2621 struct qsee_apps_region_info_ireq req;
2622 struct qseecom_command_scm_resp resp;
2623
2624 resource = platform_get_resource_byname(pdev,
2625 IORESOURCE_MEM, "secapp-region");
2626 if (resource) {
2627 req.qsee_cmd_id = QSEOS_APP_REGION_NOTIFICATION;
2628 req.addr = resource->start;
2629 req.size = resource_size(resource);
2630 pr_warn("secure app region addr=0x%x size=0x%x",
2631 req.addr, req.size);
2632 } else {
2633 pr_err("Fail to get secure app region info\n");
2634 rc = -EINVAL;
2635 goto err;
2636 }
2637 rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
2638 &resp, sizeof(resp));
2639 if (rc) {
2640 pr_err("Failed to send secapp region info %d\n",
2641 rc);
2642 goto err;
2643 }
2644 }
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002645 } else {
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002646 qseecom_platform_support = (struct msm_bus_scale_pdata *)
2647 pdev->dev.platform_data;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002648 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08002649
Mona Hossain17a4faf2013-03-22 16:40:56 -07002650 qseecom.qsee_perf_client = msm_bus_scale_register_client(
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002651 qseecom_platform_support);
2652
Mona Hossain17a4faf2013-03-22 16:40:56 -07002653 if (!qseecom.qsee_perf_client)
Ramesh Masavarapuff377032012-09-14 12:11:32 -07002654 pr_err("Unable to register bus client\n");
2655 return 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -08002656err:
2657 device_destroy(driver_class, qseecom_device_no);
2658class_destroy:
2659 class_destroy(driver_class);
2660unregister_chrdev_region:
2661 unregister_chrdev_region(qseecom_device_no, 1);
2662 return rc;
2663}
2664
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002665static int __devinit qseecom_remove(struct platform_device *pdev)
2666{
Mona Hossaind44a3842012-10-15 09:41:35 -07002667 struct qseecom_registered_kclient_list *kclient = NULL;
2668 unsigned long flags = 0;
2669 int ret = 0;
2670
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002671 if (pdev->dev.platform_data != NULL)
Mona Hossain17a4faf2013-03-22 16:40:56 -07002672 msm_bus_scale_unregister_client(qseecom.qsee_perf_client);
Mona Hossaind44a3842012-10-15 09:41:35 -07002673
2674 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2675 kclient = list_entry((&qseecom.registered_kclient_list_head)->next,
2676 struct qseecom_registered_kclient_list, list);
2677 if (list_empty(&kclient->list)) {
2678 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2679 flags);
2680 return 0;
2681 }
2682 list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
2683 list) {
2684 if (kclient)
2685 list_del(&kclient->list);
2686 break;
2687 }
2688 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
2689
2690
2691 while (kclient->handle != NULL) {
2692 ret = qseecom_unload_app(kclient->handle->dev);
2693 if (ret == 0) {
2694 kzfree(kclient->handle->dev);
2695 kzfree(kclient->handle);
2696 kzfree(kclient);
2697 }
2698 spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
2699 kclient = list_entry(
2700 (&qseecom.registered_kclient_list_head)->next,
2701 struct qseecom_registered_kclient_list, list);
2702 if (list_empty(&kclient->list)) {
2703 spin_unlock_irqrestore(
2704 &qseecom.registered_kclient_list_lock, flags);
2705 return 0;
2706 }
2707 list_for_each_entry(kclient,
2708 &qseecom.registered_kclient_list_head, list) {
2709 if (kclient)
2710 list_del(&kclient->list);
2711 break;
2712 }
2713 spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
2714 flags);
2715 if (!kclient) {
2716 ret = 0;
2717 break;
2718 }
2719 }
Mona Hossain05c73562012-10-29 17:49:01 -07002720 if (qseecom.qseos_version > QSEEE_VERSION_00)
2721 qseecom_unload_commonlib_image();
Mona Hossaind39e33b2012-11-05 13:36:40 -08002722
Mona Hossain17a4faf2013-03-22 16:40:56 -07002723 if (qseecom.qsee_perf_client)
2724 msm_bus_scale_client_update_request(qseecom.qsee_perf_client,
2725 0);
Mona Hossaind39e33b2012-11-05 13:36:40 -08002726 /* register client for bus scaling */
Mona Hossain6311d572013-03-01 15:54:02 -08002727 if (pdev->dev.of_node)
Mona Hossaind39e33b2012-11-05 13:36:40 -08002728 __qseecom_deinit_clk();
Mona Hossain6311d572013-03-01 15:54:02 -08002729
Mona Hossaind44a3842012-10-15 09:41:35 -07002730 return ret;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002731};
2732
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002733static struct of_device_id qseecom_match[] = {
2734 {
2735 .compatible = "qcom,qseecom",
2736 },
2737 {}
2738};
2739
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002740static struct platform_driver qseecom_plat_driver = {
2741 .probe = qseecom_probe,
2742 .remove = qseecom_remove,
2743 .driver = {
2744 .name = "qseecom",
2745 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07002746 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07002747 },
2748};
2749
2750static int __devinit qseecom_init(void)
2751{
2752 return platform_driver_register(&qseecom_plat_driver);
2753}
2754
2755static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08002756{
Mona Hossain2892b6b2012-02-17 13:53:11 -08002757 device_destroy(driver_class, qseecom_device_no);
2758 class_destroy(driver_class);
2759 unregister_chrdev_region(qseecom_device_no, 1);
2760 ion_client_destroy(qseecom.ion_clnt);
2761}
2762
2763MODULE_LICENSE("GPL v2");
2764MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
2765
2766module_init(qseecom_init);
2767module_exit(qseecom_exit);