blob: 3b943c806918ba19a24133884a2dd6a874565bc8 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Qualcomm TrustZone communicator driver
2 *
Mona Hossain6e2e2692012-01-05 11:28:04 -08003 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#define KMSG_COMPONENT "TZCOM"
16#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
17
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/platform_device.h>
23#include <linux/debugfs.h>
24#include <linux/cdev.h>
25#include <linux/uaccess.h>
26#include <linux/sched.h>
27#include <linux/list.h>
28#include <linux/mutex.h>
29#include <linux/android_pmem.h>
30#include <linux/io.h>
Mona Hossain6e2e2692012-01-05 11:28:04 -080031#include <linux/ion.h>
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -080032#include <linux/tzcom.h>
33#include <linux/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034#include <mach/scm.h>
35#include <mach/peripheral-loader.h>
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -080036#include <mach/msm_bus.h>
37#include <mach/msm_bus_board.h>
38#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039#include "tzcomi.h"
40
41#define TZCOM_DEV "tzcom"
42
43#define TZSCHEDULER_CMD_ID 1 /* CMD id of the trustzone scheduler */
44
45#undef PDEBUG
46#define PDEBUG(fmt, args...) pr_debug("%s(%i, %s): " fmt "\n", \
47 __func__, current->pid, current->comm, ## args)
48
49#undef PERR
50#define PERR(fmt, args...) pr_err("%s(%i, %s): " fmt "\n", \
51 __func__, current->pid, current->comm, ## args)
52
Sachin Shahf0b51cd2011-08-11 11:54:57 -070053#undef PWARN
54#define PWARN(fmt, args...) pr_warning("%s(%i, %s): " fmt "\n", \
55 __func__, current->pid, current->comm, ## args)
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -080058static uint32_t tzcom_perf_client;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059static struct class *driver_class;
60static dev_t tzcom_device_no;
61static struct cdev tzcom_cdev;
Mona Hossain6e2e2692012-01-05 11:28:04 -080062struct ion_client *ion_clnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063static u8 *sb_in_virt;
64static s32 sb_in_phys;
65static size_t sb_in_length = 20 * SZ_1K;
66static u8 *sb_out_virt;
67static s32 sb_out_phys;
68static size_t sb_out_length = 20 * SZ_1K;
69
70static void *pil;
71
72static atomic_t svc_instance_ctr = ATOMIC_INIT(0);
73static DEFINE_MUTEX(sb_in_lock);
74static DEFINE_MUTEX(sb_out_lock);
75static DEFINE_MUTEX(send_cmd_lock);
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -080076static DEFINE_MUTEX(tzcom_bw_mutex);
77static int tzcom_bw_count;
78static struct clk *tzcom_bus_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079struct tzcom_callback_list {
80 struct list_head list;
81 struct tzcom_callback callback;
82};
83
84struct tzcom_registered_svc_list {
85 struct list_head list;
86 struct tzcom_register_svc_op_req svc;
87 wait_queue_head_t next_cmd_wq;
88 int next_cmd_flag;
89};
90
91struct tzcom_data_t {
92 struct list_head callback_list_head;
93 struct mutex callback_list_lock;
94 struct list_head registered_svc_list_head;
95 spinlock_t registered_svc_list_lock;
96 wait_queue_head_t cont_cmd_wq;
97 int cont_cmd_flag;
98 u32 handled_cmd_svc_instance_id;
Sachin Shahd7e02d42011-09-12 20:31:02 -070099 int abort;
100 wait_queue_head_t abort_wq;
101 atomic_t ioctl_count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102};
103
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -0800104static int tzcom_enable_bus_scaling(void)
105{
106 int ret = 0;
107 if (!tzcom_perf_client)
108 return -EINVAL;
109
110 if (IS_ERR_OR_NULL(tzcom_bus_clk))
111 return -EINVAL;
112
113 mutex_lock(&tzcom_bw_mutex);
114 if (!tzcom_bw_count) {
115 ret = msm_bus_scale_client_update_request(
116 tzcom_perf_client, 1);
117 if (ret) {
118 pr_err("Bandwidth request failed (%d)\n", ret);
119 } else {
120 ret = clk_enable(tzcom_bus_clk);
121 if (ret)
122 pr_err("Clock enable failed\n");
123 }
124 }
125 if (ret)
126 msm_bus_scale_client_update_request(tzcom_perf_client, 0);
127 else
128 tzcom_bw_count++;
129 mutex_unlock(&tzcom_bw_mutex);
130 return ret;
131}
132
133static void tzcom_disable_bus_scaling(void)
134{
135 if (!tzcom_perf_client)
136 return ;
137
138 if (IS_ERR_OR_NULL(tzcom_bus_clk))
139 return ;
140
141 mutex_lock(&tzcom_bw_mutex);
142 if (tzcom_bw_count > 0)
143 if (tzcom_bw_count-- == 1) {
144 msm_bus_scale_client_update_request(tzcom_perf_client,
145 0);
146 clk_disable(tzcom_bus_clk);
147 }
148 mutex_unlock(&tzcom_bw_mutex);
149}
150
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len,
152 void *resp_buf, size_t resp_len)
153{
154 return scm_call(SCM_SVC_TZSCHEDULER, TZSCHEDULER_CMD_ID,
155 cmd_buf, cmd_len, resp_buf, resp_len);
156}
157
158static s32 tzcom_virt_to_phys(u8 *virt)
159{
160 if (virt >= sb_in_virt &&
161 virt < (sb_in_virt + sb_in_length)) {
162 return sb_in_phys + (virt - sb_in_virt);
163 } else if (virt >= sb_out_virt &&
164 virt < (sb_out_virt + sb_out_length)) {
165 return sb_out_phys + (virt - sb_out_virt);
166 } else {
167 return virt_to_phys(virt);
168 }
169}
170
171static u8 *tzcom_phys_to_virt(s32 phys)
172{
173 if (phys >= sb_in_phys &&
174 phys < (sb_in_phys + sb_in_length)) {
175 return sb_in_virt + (phys - sb_in_phys);
176 } else if (phys >= sb_out_phys &&
177 phys < (sb_out_phys + sb_out_length)) {
178 return sb_out_virt + (phys - sb_out_phys);
179 } else {
180 return phys_to_virt(phys);
181 }
182}
183
184static int __tzcom_is_svc_unique(struct tzcom_data_t *data,
185 struct tzcom_register_svc_op_req svc)
186{
187 struct tzcom_registered_svc_list *ptr;
188 int unique = 1;
189 unsigned long flags;
190
191 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
192 list_for_each_entry(ptr, &data->registered_svc_list_head, list) {
193 if (ptr->svc.svc_id == svc.svc_id) {
194 PERR("Service id: %u is already registered",
195 ptr->svc.svc_id);
196 unique = 0;
197 break;
198 } else if (svc.cmd_id_low >= ptr->svc.cmd_id_low &&
199 svc.cmd_id_low <= ptr->svc.cmd_id_high) {
200 PERR("Cmd id low falls in the range of another"
201 "registered service");
202 unique = 0;
203 break;
204 } else if (svc.cmd_id_high >= ptr->svc.cmd_id_low &&
205 svc.cmd_id_high <= ptr->svc.cmd_id_high) {
206 PERR("Cmd id high falls in the range of another"
207 "registered service");
208 unique = 0;
209 break;
210 }
211 }
212 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
213 return unique;
214}
215
216static int tzcom_register_service(struct tzcom_data_t *data, void __user *argp)
217{
218 int ret;
219 unsigned long flags;
220 struct tzcom_register_svc_op_req rcvd_svc;
221 struct tzcom_registered_svc_list *new_entry;
222
223 ret = copy_from_user(&rcvd_svc, argp, sizeof(rcvd_svc));
224
225 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700226 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 return ret;
228 }
229
230 PDEBUG("svc_id: %u, cmd_id_low: %u, cmd_id_high: %u",
231 rcvd_svc.svc_id, rcvd_svc.cmd_id_low,
232 rcvd_svc.cmd_id_high);
233 if (!__tzcom_is_svc_unique(data, rcvd_svc)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700234 PERR("Provided service is not unique");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 return -EINVAL;
236 }
237
238 rcvd_svc.instance_id = atomic_inc_return(&svc_instance_ctr);
239
240 ret = copy_to_user(argp, &rcvd_svc, sizeof(rcvd_svc));
241 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700242 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 return ret;
244 }
245
246 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
247 if (!new_entry) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700248 PERR("kmalloc failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 return -ENOMEM;
250 }
251 memcpy(&new_entry->svc, &rcvd_svc, sizeof(rcvd_svc));
252 new_entry->next_cmd_flag = 0;
253 init_waitqueue_head(&new_entry->next_cmd_wq);
254
255 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
256 list_add_tail(&new_entry->list, &data->registered_svc_list_head);
257 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
258
259
260 return ret;
261}
262
263static int tzcom_unregister_service(struct tzcom_data_t *data,
264 void __user *argp)
265{
266 int ret = 0;
267 unsigned long flags;
268 struct tzcom_unregister_svc_op_req req;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700269 struct tzcom_registered_svc_list *ptr, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270 ret = copy_from_user(&req, argp, sizeof(req));
271 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700272 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273 return ret;
274 }
275
276 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700277 list_for_each_entry_safe(ptr, next, &data->registered_svc_list_head,
278 list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 if (req.svc_id == ptr->svc.svc_id &&
280 req.instance_id == ptr->svc.instance_id) {
281 wake_up_all(&ptr->next_cmd_wq);
282 list_del(&ptr->list);
283 kfree(ptr);
284 spin_unlock_irqrestore(&data->registered_svc_list_lock,
285 flags);
286 return 0;
287 }
288 }
289 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
290
291 return -EINVAL;
292}
293
Sachin Shahd7e02d42011-09-12 20:31:02 -0700294static int __tzcom_is_cont_cmd(struct tzcom_data_t *data)
295{
296 int ret;
297 ret = (data->cont_cmd_flag != 0);
298 return ret || data->abort;
299}
300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301/**
302 * +---------+ +-----+ +-----------------+
303 * | TZCOM | | SCM | | TZCOM_SCHEDULER |
304 * +----+----+ +--+--+ +--------+--------+
305 * | | |
306 * | scm_call | |
307 * |------------------------------------->| |
308 * | cmd_buf = struct tzcom_command { | |
309 * | cmd_type, |------------------>|
310 * +------+------------- sb_in_cmd_addr, | |
311 * | | sb_in_cmd_len | |
312 * | | } | |
313 * | | resp_buf = struct tzcom_response { | |
314 * | cmd_status, | |
315 * | +---------- sb_in_rsp_addr, | |
316 * | | sb_in_rsp_len |<------------------|
317 * | | }
318 * | | struct tzcom_callback {---------+
319 * | | uint32_t cmd_id; |
320 * | | uint32_t sb_out_cb_data_len;|
321 * | +---------------+ uint32_t sb_out_cb_data_off;|
322 * | | } |
323 * | _________________________|_______________________________ |
324 * | +-----------------------+| +----------------------+ |
325 * +--->+ copy from req.cmd_buf |+>| copy to req.resp_buf | |
326 * +-----------------------+ +----------------------+ |
327 * _________________________________________________________ |
328 * INPUT SHARED BUFFER |
329 * +------------------------------------------------------------------------+
330 * | _________________________________________________________
331 * | +---------------------------------------------+
332 * +->| cmd_id | data_len | data_off | data... |
333 * +---------------------------------------------+
334 * |<------------>|copy to next_cmd.req_buf
335 * _________________________________________________________
336 * OUTPUT SHARED BUFFER
337 */
Mona Hossain6e2e2692012-01-05 11:28:04 -0800338static int __tzcom_send_cmd(struct tzcom_data_t *data,
339 struct tzcom_send_cmd_op_req *req)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340{
341 int ret = 0;
342 unsigned long flags;
343 u32 reqd_len_sb_in = 0;
344 u32 reqd_len_sb_out = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 struct tzcom_command cmd;
346 struct tzcom_response resp;
347 struct tzcom_callback *next_callback;
348 void *cb_data = NULL;
349 struct tzcom_callback_list *new_entry;
350 struct tzcom_callback *cb;
351 size_t new_entry_len = 0;
352 struct tzcom_registered_svc_list *ptr_svc;
353
Mona Hossain6e2e2692012-01-05 11:28:04 -0800354 if (req->cmd_buf == NULL || req->resp_buf == NULL) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700355 PERR("cmd buffer or response buffer is null");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356 return -EINVAL;
357 }
358
Mona Hossain6e2e2692012-01-05 11:28:04 -0800359 if (req->cmd_len <= 0 || req->resp_len <= 0 ||
360 req->cmd_len > sb_in_length || req->resp_len > sb_in_length) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700361 PERR("cmd buffer length or "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 "response buffer length not valid");
363 return -EINVAL;
364 }
365 PDEBUG("received cmd_req.req: 0x%p",
Mona Hossain6e2e2692012-01-05 11:28:04 -0800366 req->cmd_buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 PDEBUG("received cmd_req.rsp size: %u, ptr: 0x%p",
Mona Hossain6e2e2692012-01-05 11:28:04 -0800368 req->resp_len,
369 req->resp_buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370
Mona Hossain6e2e2692012-01-05 11:28:04 -0800371 reqd_len_sb_in = req->cmd_len + req->resp_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 if (reqd_len_sb_in > sb_in_length) {
373 PDEBUG("Not enough memory to fit cmd_buf and "
374 "resp_buf. Required: %u, Available: %u",
375 reqd_len_sb_in, sb_in_length);
376 return -ENOMEM;
377 }
378
Mona Hossain6e2e2692012-01-05 11:28:04 -0800379 /* Copy req->cmd_buf to SB in and set
380 * req->resp_buf to SB in + cmd_len
381 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 mutex_lock(&sb_in_lock);
383 PDEBUG("Before memcpy on sb_in");
Mona Hossain6e2e2692012-01-05 11:28:04 -0800384 memcpy(sb_in_virt, req->cmd_buf, req->cmd_len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 PDEBUG("After memcpy on sb_in");
386
387 /* cmd_type will always be a new here */
388 cmd.cmd_type = TZ_SCHED_CMD_NEW;
389 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
Mona Hossain6e2e2692012-01-05 11:28:04 -0800390 cmd.sb_in_cmd_len = req->cmd_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391
392 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
393 resp.sb_in_rsp_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt +
Mona Hossain6e2e2692012-01-05 11:28:04 -0800394 req->cmd_len);
395 resp.sb_in_rsp_len = req->resp_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Mona Hossain6e2e2692012-01-05 11:28:04 -0800397 PDEBUG("before call tzcom_scm_call, cmd_id = : %u", req->cmd_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 PDEBUG("before call tzcom_scm_call, sizeof(cmd) = : %u", sizeof(cmd));
399
Sachin Shahbae4ec02011-08-15 19:52:31 -0700400 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd),
401 &resp, sizeof(resp));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 mutex_unlock(&sb_in_lock);
403
Sachin Shahbae4ec02011-08-15 19:52:31 -0700404 if (ret) {
405 PERR("tzcom_scm_call failed with err: %d", ret);
406 return ret;
407 }
408
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
410 /*
411 * If cmd is incomplete, get the callback cmd out from SB out
412 * and put it on the list
413 */
414 PDEBUG("cmd_status is incomplete.");
415 next_callback = (struct tzcom_callback *)sb_out_virt;
416
417 mutex_lock(&sb_out_lock);
418 reqd_len_sb_out = sizeof(*next_callback)
419 + next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700420 if (reqd_len_sb_out > sb_out_length ||
421 reqd_len_sb_out < sizeof(*next_callback) ||
422 next_callback->sb_out_cb_data_len > sb_out_length) {
423 PERR("Incorrect callback data length"
424 " Required: %u, Available: %u, Min: %u",
425 reqd_len_sb_out, sb_out_length,
426 sizeof(*next_callback));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427 mutex_unlock(&sb_out_lock);
428 return -ENOMEM;
429 }
430
431 /* Assumption is cb_data_off is sizeof(tzcom_callback) */
432 new_entry_len = sizeof(*new_entry)
433 + next_callback->sb_out_cb_data_len;
434 new_entry = kmalloc(new_entry_len, GFP_KERNEL);
435 if (!new_entry) {
436 PERR("kmalloc failed");
437 mutex_unlock(&sb_out_lock);
438 return -ENOMEM;
439 }
440
441 cb = &new_entry->callback;
442 cb->cmd_id = next_callback->cmd_id;
443 cb->sb_out_cb_data_len = next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700444 cb->sb_out_cb_data_off = sizeof(*cb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445
446 cb_data = (u8 *)next_callback
447 + next_callback->sb_out_cb_data_off;
448 memcpy((u8 *)cb + cb->sb_out_cb_data_off, cb_data,
449 next_callback->sb_out_cb_data_len);
450 mutex_unlock(&sb_out_lock);
451
452 mutex_lock(&data->callback_list_lock);
453 list_add_tail(&new_entry->list, &data->callback_list_head);
454 mutex_unlock(&data->callback_list_lock);
455
456 /*
457 * We don't know which service can handle the command. so we
458 * wake up all blocking services and let them figure out if
459 * they can handle the given command.
460 */
461 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
462 list_for_each_entry(ptr_svc,
463 &data->registered_svc_list_head, list) {
464 ptr_svc->next_cmd_flag = 1;
465 wake_up_interruptible(&ptr_svc->next_cmd_wq);
466 }
467 spin_unlock_irqrestore(&data->registered_svc_list_lock,
468 flags);
469
470 PDEBUG("waking up next_cmd_wq and "
471 "waiting for cont_cmd_wq");
472 if (wait_event_interruptible(data->cont_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700473 __tzcom_is_cont_cmd(data))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700474 PWARN("Interrupted: exiting send_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475 return -ERESTARTSYS;
476 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700477
478 if (data->abort) {
479 PERR("Aborting driver");
480 return -ENODEV;
481 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482 data->cont_cmd_flag = 0;
483 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
484 mutex_lock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700485 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 sizeof(resp));
487 mutex_unlock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700488 if (ret) {
489 PERR("tzcom_scm_call failed with err: %d", ret);
490 return ret;
491 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492 }
493
494 mutex_lock(&sb_in_lock);
495 resp.sb_in_rsp_addr = sb_in_virt + cmd.sb_in_cmd_len;
Mona Hossain6e2e2692012-01-05 11:28:04 -0800496 resp.sb_in_rsp_len = req->resp_len;
497 memcpy(req->resp_buf, resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
Sachin Shahc3f8dd32011-06-17 11:39:10 -0700498 /* Zero out memory for security purpose */
499 memset(sb_in_virt, 0, reqd_len_sb_in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500 mutex_unlock(&sb_in_lock);
501
Mona Hossain6e2e2692012-01-05 11:28:04 -0800502 return ret;
503}
504
505
506static int tzcom_send_cmd(struct tzcom_data_t *data, void __user *argp)
507{
508 int ret = 0;
509 struct tzcom_send_cmd_op_req req;
510
511 ret = copy_from_user(&req, argp, sizeof(req));
512 if (ret) {
513 PERR("copy_from_user failed");
514 return ret;
515 }
516 ret = __tzcom_send_cmd(data, &req);
517 if (ret)
518 return ret;
519
520 PDEBUG("sending cmd_req->rsp "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 "size: %u, ptr: 0x%p", req.resp_len,
522 req.resp_buf);
523 ret = copy_to_user(argp, &req, sizeof(req));
524 if (ret) {
525 PDEBUG("copy_to_user failed");
526 return ret;
527 }
Mona Hossain6e2e2692012-01-05 11:28:04 -0800528 return ret;
529}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530
Mona Hossain6e2e2692012-01-05 11:28:04 -0800531static int __tzcom_send_cmd_req_clean_up(
532 struct tzcom_send_cmd_fd_op_req *req)
533{
534 char *field;
535 uint32_t *update;
536 int ret = 0;
537 int i = 0;
538
539 for (i = 0; i < MAX_ION_FD; i++) {
540 if (req->ifd_data[i].fd != 0) {
541 field = (char *)req->cmd_buf +
542 req->ifd_data[i].cmd_buf_offset;
543 update = (uint32_t *) field;
544 *update = 0;
545 }
546 }
547 return ret;
548}
549
550static int __tzcom_update_with_phy_addr(
551 struct tzcom_send_cmd_fd_op_req *req)
552{
553 struct ion_handle *ihandle;
554 char *field;
555 uint32_t *update;
556 ion_phys_addr_t pa;
557 int ret = 0;
558 int i = 0;
559 uint32_t length;
560
561 for (i = 0; i < MAX_ION_FD; i++) {
562 if (req->ifd_data[i].fd != 0) {
563 /* Get the handle of the shared fd */
564 ihandle = ion_import_fd(ion_clnt, req->ifd_data[i].fd);
565 if (ihandle == NULL) {
566 PERR("Ion client can't retrieve the handle\n");
567 return -ENOMEM;
568 }
569 field = (char *) req->cmd_buf +
570 req->ifd_data[i].cmd_buf_offset;
571 update = (uint32_t *) field;
572
573 /* Populate the cmd data structure with the phys_addr */
574 ret = ion_phys(ion_clnt, ihandle, &pa, &length);
575 if (ret)
576 return -ENOMEM;
577
578 *update = (uint32_t)pa;
579 ion_free(ion_clnt, ihandle);
580 }
581 }
582 return ret;
583}
584
585static int tzcom_send_cmd_with_fd(struct tzcom_data_t *data,
586 void __user *argp)
587{
588 int ret = 0;
589 struct tzcom_send_cmd_fd_op_req req;
590 struct tzcom_send_cmd_op_req send_cmd_req;
591
592 ret = copy_from_user(&req, argp, sizeof(req));
593 if (ret) {
594 PERR("copy_from_user failed");
595 return ret;
596 }
597
598 send_cmd_req.cmd_id = req.cmd_id;
599 send_cmd_req.cmd_buf = req.cmd_buf;
600 send_cmd_req.cmd_len = req.cmd_len;
601 send_cmd_req.resp_buf = req.resp_buf;
602 send_cmd_req.resp_len = req.resp_len;
603
604 ret = __tzcom_update_with_phy_addr(&req);
605 if (ret)
606 return ret;
607 ret = __tzcom_send_cmd(data, &send_cmd_req);
608 __tzcom_send_cmd_req_clean_up(&req);
609
610 if (ret)
611 return ret;
612
613 PDEBUG("sending cmd_req->rsp "
614 "size: %u, ptr: 0x%p", req.resp_len,
615 req.resp_buf);
616 ret = copy_to_user(argp, &req, sizeof(req));
617 if (ret) {
618 PDEBUG("copy_to_user failed");
619 return ret;
620 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621 return ret;
622}
623
624static struct tzcom_registered_svc_list *__tzcom_find_svc(
625 struct tzcom_data_t *data,
626 uint32_t instance_id)
627{
628 struct tzcom_registered_svc_list *entry;
629 unsigned long flags;
630
631 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
632 list_for_each_entry(entry,
633 &data->registered_svc_list_head, list) {
634 if (entry->svc.instance_id == instance_id)
635 break;
636 }
637 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
638
639 return entry;
640}
641
642static int __tzcom_copy_cmd(struct tzcom_data_t *data,
643 struct tzcom_next_cmd_op_req *req,
644 struct tzcom_registered_svc_list *ptr_svc)
645{
646 int found = 0;
647 int ret = -EAGAIN;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700648 struct tzcom_callback_list *entry, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 struct tzcom_callback *cb;
650
651 PDEBUG("In here");
652 mutex_lock(&data->callback_list_lock);
653 PDEBUG("Before looping through cmd and svc lists.");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700654 list_for_each_entry_safe(entry, next, &data->callback_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655 cb = &entry->callback;
656 if (req->svc_id == ptr_svc->svc.svc_id &&
657 req->instance_id == ptr_svc->svc.instance_id &&
658 cb->cmd_id >= ptr_svc->svc.cmd_id_low &&
659 cb->cmd_id <= ptr_svc->svc.cmd_id_high) {
660 PDEBUG("Found matching entry");
661 found = 1;
662 if (cb->sb_out_cb_data_len <= req->req_len) {
663 PDEBUG("copying cmd buffer %p to req "
664 "buffer %p, length: %u",
665 (u8 *)cb + cb->sb_out_cb_data_off,
666 req->req_buf, cb->sb_out_cb_data_len);
667 req->cmd_id = cb->cmd_id;
668 ret = copy_to_user(req->req_buf,
669 (u8 *)cb + cb->sb_out_cb_data_off,
670 cb->sb_out_cb_data_len);
671 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700672 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 break;
674 }
675 list_del(&entry->list);
676 kfree(entry);
677 ret = 0;
678 } else {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700679 PERR("callback data buffer is "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 "larger than provided buffer."
681 "Required: %u, Provided: %u",
682 cb->sb_out_cb_data_len,
683 req->req_len);
684 ret = -ENOMEM;
685 }
686 break;
687 }
688 }
689 PDEBUG("After looping through cmd and svc lists.");
690 mutex_unlock(&data->callback_list_lock);
691 return ret;
692}
693
Sachin Shahd7e02d42011-09-12 20:31:02 -0700694static int __tzcom_is_next_cmd(struct tzcom_data_t *data,
695 struct tzcom_registered_svc_list *svc)
696{
697 int ret;
698 ret = (svc->next_cmd_flag != 0);
699 return ret || data->abort;
700}
701
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
703{
704 int ret = 0;
705 struct tzcom_next_cmd_op_req req;
706 struct tzcom_registered_svc_list *this_svc;
707
708 ret = copy_from_user(&req, argp, sizeof(req));
709 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700710 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711 return ret;
712 }
713
714 if (req.instance_id > atomic_read(&svc_instance_ctr)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700715 PERR("Invalid instance_id for the request");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716 return -EINVAL;
717 }
718
719 if (!req.req_buf || req.req_len == 0) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700720 PERR("Invalid request buffer or buffer length");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721 return -EINVAL;
722 }
723
724 PDEBUG("Before next_cmd loop");
725 this_svc = __tzcom_find_svc(data, req.instance_id);
726
727 while (1) {
728 PDEBUG("Before wait_event next_cmd.");
729 if (wait_event_interruptible(this_svc->next_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700730 __tzcom_is_next_cmd(data, this_svc))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700731 PWARN("Interrupted: exiting wait_next_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732 /* woken up for different reason */
733 return -ERESTARTSYS;
734 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700735
736 if (data->abort) {
737 PERR("Aborting driver");
738 return -ENODEV;
739 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740 PDEBUG("After wait_event next_cmd.");
741 this_svc->next_cmd_flag = 0;
742
743 ret = __tzcom_copy_cmd(data, &req, this_svc);
744 if (ret == 0) {
745 PDEBUG("Successfully found svc for cmd");
746 data->handled_cmd_svc_instance_id = req.instance_id;
747 break;
748 } else if (ret == -ENOMEM) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700749 PERR("Not enough memory");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750 return ret;
751 }
752 }
753 ret = copy_to_user(argp, &req, sizeof(req));
754 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700755 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 return ret;
757 }
758 PDEBUG("copy_to_user is done.");
759 return ret;
760}
761
762static int tzcom_cont_cmd(struct tzcom_data_t *data, void __user *argp)
763{
764 int ret = 0;
765 struct tzcom_cont_cmd_op_req req;
766 ret = copy_from_user(&req, argp, sizeof(req));
767 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700768 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 return ret;
770 }
771
772 /*
773 * Only the svc instance that handled the cmd (in read_next_cmd method)
774 * can call continue cmd
775 */
776 if (data->handled_cmd_svc_instance_id != req.instance_id) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700777 PWARN("Only the service instance that handled the last "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778 "callback can continue cmd. "
779 "Expected: %u, Received: %u",
780 data->handled_cmd_svc_instance_id,
781 req.instance_id);
782 return -EINVAL;
783 }
784
785 if (req.resp_buf) {
786 mutex_lock(&sb_out_lock);
787 memcpy(sb_out_virt, req.resp_buf, req.resp_len);
788 mutex_unlock(&sb_out_lock);
789 }
790
791 data->cont_cmd_flag = 1;
792 wake_up_interruptible(&data->cont_cmd_wq);
793 return ret;
794}
795
Sachin Shahd7e02d42011-09-12 20:31:02 -0700796static int tzcom_abort(struct tzcom_data_t *data)
797{
798 int ret = 0;
799 unsigned long flags;
800 struct tzcom_registered_svc_list *lsvc, *nsvc;
801 if (data->abort) {
802 PERR("Already aborting");
803 return -EINVAL;
804 }
805
806 data->abort = 1;
807
808 PDEBUG("Waking up cont_cmd_wq");
809 wake_up_all(&data->cont_cmd_wq);
810
811 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
812 PDEBUG("Before waking up service wait queues");
813 list_for_each_entry_safe(lsvc, nsvc,
814 &data->registered_svc_list_head, list) {
815 wake_up_all(&lsvc->next_cmd_wq);
816 }
817 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
818
819 PDEBUG("ioctl_count before loop: %d", atomic_read(&data->ioctl_count));
820 while (atomic_read(&data->ioctl_count) > 0) {
821 if (wait_event_interruptible(data->abort_wq,
822 atomic_read(&data->ioctl_count) <= 0)) {
823 PERR("Interrupted from abort");
824 ret = -ERESTARTSYS;
825 break;
826 }
827 }
828 return ret;
829}
830
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700831static long tzcom_ioctl(struct file *file, unsigned cmd,
832 unsigned long arg)
833{
834 int ret = 0;
835 struct tzcom_data_t *tzcom_data = file->private_data;
836 void __user *argp = (void __user *) arg;
837 PDEBUG("enter tzcom_ioctl()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700838 if (tzcom_data->abort) {
839 PERR("Aborting tzcom driver");
840 return -ENODEV;
841 }
842
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700843 switch (cmd) {
844 case TZCOM_IOCTL_REGISTER_SERVICE_REQ: {
845 PDEBUG("ioctl register_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700846 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700847 ret = tzcom_register_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700848 atomic_dec(&tzcom_data->ioctl_count);
849 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700851 PERR("failed tzcom_register_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 break;
853 }
854 case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: {
855 PDEBUG("ioctl unregister_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700856 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857 ret = tzcom_unregister_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700858 atomic_dec(&tzcom_data->ioctl_count);
859 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700861 PERR("failed tzcom_unregister_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700862 break;
863 }
864 case TZCOM_IOCTL_SEND_CMD_REQ: {
865 PDEBUG("ioctl send_cmd_req()");
866 /* Only one client allowed here at a time */
867 mutex_lock(&send_cmd_lock);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700868 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 ret = tzcom_send_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700870 atomic_dec(&tzcom_data->ioctl_count);
871 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 mutex_unlock(&send_cmd_lock);
873 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700874 PERR("failed tzcom_send_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875 break;
876 }
Mona Hossain6e2e2692012-01-05 11:28:04 -0800877 case TZCOM_IOCTL_SEND_CMD_FD_REQ: {
878 PDEBUG("ioctl send_cmd_req()");
879 /* Only one client allowed here at a time */
880 mutex_lock(&send_cmd_lock);
881 atomic_inc(&tzcom_data->ioctl_count);
882 ret = tzcom_send_cmd_with_fd(tzcom_data, argp);
883 atomic_dec(&tzcom_data->ioctl_count);
884 wake_up_interruptible(&tzcom_data->abort_wq);
885 mutex_unlock(&send_cmd_lock);
886 if (ret)
887 PERR("failed tzcom_send_cmd: %d", ret);
888 break;
889 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700890 case TZCOM_IOCTL_READ_NEXT_CMD_REQ: {
891 PDEBUG("ioctl read_next_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700892 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893 ret = tzcom_read_next_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700894 atomic_dec(&tzcom_data->ioctl_count);
895 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700897 PERR("failed tzcom_read_next: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700898 break;
899 }
900 case TZCOM_IOCTL_CONTINUE_CMD_REQ: {
901 PDEBUG("ioctl continue_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700902 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700903 ret = tzcom_cont_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700904 atomic_dec(&tzcom_data->ioctl_count);
905 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700907 PERR("failed tzcom_cont_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 break;
909 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700910 case TZCOM_IOCTL_ABORT_REQ: {
911 PDEBUG("ioctl abort_req()");
912 ret = tzcom_abort(tzcom_data);
913 if (ret)
914 PERR("failed tzcom_abort: %d", ret);
915 break;
916 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700917 default:
918 return -EINVAL;
919 }
920 return ret;
921}
922
923static int tzcom_open(struct inode *inode, struct file *file)
924{
Sachin Shahbae4ec02011-08-15 19:52:31 -0700925 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700926 long pil_error;
927 struct tz_pr_init_sb_req_s sb_out_init_req;
928 struct tz_pr_init_sb_rsp_s sb_out_init_rsp;
929 void *rsp_addr_virt;
930 struct tzcom_command cmd;
931 struct tzcom_response resp;
932 struct tzcom_data_t *tzcom_data;
933
934 PDEBUG("In here");
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -0800935
936 ret = tzcom_enable_bus_scaling();
937
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938 if (pil == NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -0700939 pil = pil_get("tzapps");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 if (IS_ERR(pil)) {
941 PERR("Playready PIL image load failed");
942 pil_error = PTR_ERR(pil);
943 pil = NULL;
944 return pil_error;
945 }
Stephen Boyd700819d2011-09-30 17:14:13 -0700946 PDEBUG("tzapps image loaded successfully");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947 }
948
949 sb_out_init_req.pr_cmd = TZ_SCHED_CMD_ID_INIT_SB_OUT;
950 sb_out_init_req.sb_len = sb_out_length;
951 sb_out_init_req.sb_ptr = tzcom_virt_to_phys(sb_out_virt);
952 PDEBUG("sb_out_init_req { pr_cmd: %d, sb_len: %u, "
953 "sb_ptr (phys): 0x%x }",
954 sb_out_init_req.pr_cmd,
955 sb_out_init_req.sb_len,
956 sb_out_init_req.sb_ptr);
957
958 mutex_lock(&sb_in_lock);
959 PDEBUG("Before memcpy on sb_in");
960 memcpy(sb_in_virt, &sb_out_init_req, sizeof(sb_out_init_req));
961 PDEBUG("After memcpy on sb_in");
962
963 /* It will always be a new cmd from this method */
964 cmd.cmd_type = TZ_SCHED_CMD_NEW;
965 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
966 cmd.sb_in_cmd_len = sizeof(sb_out_init_req);
967 PDEBUG("tzcom_command { cmd_type: %u, sb_in_cmd_addr: %p, "
968 "sb_in_cmd_len: %u }",
969 cmd.cmd_type, cmd.sb_in_cmd_addr, cmd.sb_in_cmd_len);
970
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700971 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972
973 PDEBUG("Before scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700974 ret = tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
975 if (ret) {
976 PERR("tzcom_scm_call failed with err: %d", ret);
977 return ret;
978 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 PDEBUG("After scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700980
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700981 PDEBUG("tzcom_response after scm cmd_status: %u", resp.cmd_status);
982 if (resp.cmd_status == TZ_SCHED_STATUS_COMPLETE) {
983 resp.sb_in_rsp_addr = (u8 *)cmd.sb_in_cmd_addr +
984 cmd.sb_in_cmd_len;
985 resp.sb_in_rsp_len = sizeof(sb_out_init_rsp);
986 PDEBUG("tzcom_response sb_in_rsp_addr: %p, sb_in_rsp_len: %u",
987 resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700988 rsp_addr_virt = tzcom_phys_to_virt((unsigned long)
989 resp.sb_in_rsp_addr);
990 PDEBUG("Received response phys: %p, virt: %p",
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700991 resp.sb_in_rsp_addr, rsp_addr_virt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700992 memcpy(&sb_out_init_rsp, rsp_addr_virt, resp.sb_in_rsp_len);
993 } else {
994 PERR("Error with SB initialization");
995 mutex_unlock(&sb_in_lock);
996 return -EPERM;
997 }
998 mutex_unlock(&sb_in_lock);
999
1000 PDEBUG("sb_out_init_rsp { pr_cmd: %d, ret: %d }",
1001 sb_out_init_rsp.pr_cmd, sb_out_init_rsp.ret);
1002
1003 if (sb_out_init_rsp.ret) {
1004 PERR("sb_out_init_req failed: %d", sb_out_init_rsp.ret);
1005 return -EPERM;
1006 }
1007
1008 tzcom_data = kmalloc(sizeof(*tzcom_data), GFP_KERNEL);
1009 if (!tzcom_data) {
1010 PERR("kmalloc failed");
1011 return -ENOMEM;
1012 }
1013 file->private_data = tzcom_data;
1014
1015 INIT_LIST_HEAD(&tzcom_data->callback_list_head);
1016 mutex_init(&tzcom_data->callback_list_lock);
1017
1018 INIT_LIST_HEAD(&tzcom_data->registered_svc_list_head);
1019 spin_lock_init(&tzcom_data->registered_svc_list_lock);
1020
1021 init_waitqueue_head(&tzcom_data->cont_cmd_wq);
1022 tzcom_data->cont_cmd_flag = 0;
1023 tzcom_data->handled_cmd_svc_instance_id = 0;
Sachin Shahd7e02d42011-09-12 20:31:02 -07001024 tzcom_data->abort = 0;
1025 init_waitqueue_head(&tzcom_data->abort_wq);
1026 atomic_set(&tzcom_data->ioctl_count, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001027 return 0;
1028}
1029
1030static int tzcom_release(struct inode *inode, struct file *file)
1031{
1032 struct tzcom_data_t *tzcom_data = file->private_data;
1033 struct tzcom_callback_list *lcb, *ncb;
1034 struct tzcom_registered_svc_list *lsvc, *nsvc;
Sachin Shahd7e02d42011-09-12 20:31:02 -07001035 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 PDEBUG("In here");
1037
Sachin Shahd7e02d42011-09-12 20:31:02 -07001038 if (!tzcom_data->abort) {
1039 PDEBUG("Calling abort");
1040 tzcom_abort(tzcom_data);
1041 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001042
Sachin Shahd7e02d42011-09-12 20:31:02 -07001043 PDEBUG("Before removing callback list");
1044 mutex_lock(&tzcom_data->callback_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001045 list_for_each_entry_safe(lcb, ncb,
1046 &tzcom_data->callback_list_head, list) {
1047 list_del(&lcb->list);
1048 kfree(lcb);
1049 }
Sachin Shahd7e02d42011-09-12 20:31:02 -07001050 mutex_unlock(&tzcom_data->callback_list_lock);
1051 PDEBUG("After removing callback list");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001052
Sachin Shahd7e02d42011-09-12 20:31:02 -07001053 PDEBUG("Before removing svc list");
1054 spin_lock_irqsave(&tzcom_data->registered_svc_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001055 list_for_each_entry_safe(lsvc, nsvc,
1056 &tzcom_data->registered_svc_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001057 list_del(&lsvc->list);
1058 kfree(lsvc);
1059 }
Sachin Shahd7e02d42011-09-12 20:31:02 -07001060 spin_unlock_irqrestore(&tzcom_data->registered_svc_list_lock, flags);
1061 PDEBUG("After removing svc list");
Mona Hossain67e1cbe2012-02-13 15:55:28 -08001062 if (pil != NULL) {
1063 pil_put(pil);
1064 pil = NULL;
1065 }
Sachin Shahd7e02d42011-09-12 20:31:02 -07001066 PDEBUG("Freeing tzcom data");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001067 kfree(tzcom_data);
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -08001068 tzcom_disable_bus_scaling();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001069 return 0;
1070}
1071
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -08001072static struct msm_bus_paths tzcom_bw_table[] = {
1073 {
1074 .vectors = (struct msm_bus_vectors[]){
1075 {
1076 .src = MSM_BUS_MASTER_SPS,
1077 .dst = MSM_BUS_SLAVE_EBI_CH0,
1078 },
1079 },
1080 .num_paths = 1,
1081 },
1082 {
1083 .vectors = (struct msm_bus_vectors[]){
1084 {
1085 .src = MSM_BUS_MASTER_SPS,
1086 .dst = MSM_BUS_SLAVE_EBI_CH0,
1087 .ib = (492 * 8) * 1000000UL,
1088 .ab = (492 * 8) * 100000UL,
1089 },
1090 },
1091 .num_paths = 1,
1092 },
1093
1094};
1095
1096static struct msm_bus_scale_pdata tzcom_bus_pdata = {
1097 .usecase = tzcom_bw_table,
1098 .num_usecases = ARRAY_SIZE(tzcom_bw_table),
1099 .name = "tzcom",
1100};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001101static const struct file_operations tzcom_fops = {
1102 .owner = THIS_MODULE,
1103 .unlocked_ioctl = tzcom_ioctl,
1104 .open = tzcom_open,
1105 .release = tzcom_release
1106};
1107
1108static int __init tzcom_init(void)
1109{
1110 int rc;
1111 struct device *class_dev;
1112
1113 PDEBUG("Hello tzcom");
1114
1115 rc = alloc_chrdev_region(&tzcom_device_no, 0, 1, TZCOM_DEV);
1116 if (rc < 0) {
1117 PERR("alloc_chrdev_region failed %d", rc);
1118 return rc;
1119 }
1120
1121 driver_class = class_create(THIS_MODULE, TZCOM_DEV);
1122 if (IS_ERR(driver_class)) {
1123 rc = -ENOMEM;
1124 PERR("class_create failed %d", rc);
1125 goto unregister_chrdev_region;
1126 }
1127
1128 class_dev = device_create(driver_class, NULL, tzcom_device_no, NULL,
1129 TZCOM_DEV);
1130 if (!class_dev) {
1131 PERR("class_device_create failed %d", rc);
1132 rc = -ENOMEM;
1133 goto class_destroy;
1134 }
1135
1136 cdev_init(&tzcom_cdev, &tzcom_fops);
1137 tzcom_cdev.owner = THIS_MODULE;
1138
1139 rc = cdev_add(&tzcom_cdev, MKDEV(MAJOR(tzcom_device_no), 0), 1);
1140 if (rc < 0) {
1141 PERR("cdev_add failed %d", rc);
1142 goto class_device_destroy;
1143 }
1144
1145 sb_in_phys = pmem_kalloc(sb_in_length, PMEM_MEMTYPE_EBI1 |
1146 PMEM_ALIGNMENT_4K);
1147 if (IS_ERR((void *)sb_in_phys)) {
1148 PERR("could not allocte in kernel pmem buffers for sb_in");
Sachin Shah545fbfa2011-09-27 14:37:16 -07001149 sb_in_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150 rc = -ENOMEM;
1151 goto class_device_destroy;
1152 }
1153 PDEBUG("physical_addr for sb_in: 0x%x", sb_in_phys);
1154
1155 sb_in_virt = (u8 *) ioremap((unsigned long)sb_in_phys,
1156 sb_in_length);
1157 if (!sb_in_virt) {
1158 PERR("Shared buffer IN allocation failed.");
1159 rc = -ENOMEM;
1160 goto class_device_destroy;
1161 }
1162 PDEBUG("sb_in virt address: %p, phys address: 0x%x",
1163 sb_in_virt, tzcom_virt_to_phys(sb_in_virt));
1164
1165 sb_out_phys = pmem_kalloc(sb_out_length, PMEM_MEMTYPE_EBI1 |
1166 PMEM_ALIGNMENT_4K);
1167 if (IS_ERR((void *)sb_out_phys)) {
1168 PERR("could not allocte in kernel pmem buffers for sb_out");
Sachin Shah545fbfa2011-09-27 14:37:16 -07001169 sb_out_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001170 rc = -ENOMEM;
1171 goto class_device_destroy;
1172 }
1173 PDEBUG("physical_addr for sb_out: 0x%x", sb_out_phys);
1174
1175 sb_out_virt = (u8 *) ioremap((unsigned long)sb_out_phys,
1176 sb_out_length);
1177 if (!sb_out_virt) {
1178 PERR("Shared buffer OUT allocation failed.");
1179 rc = -ENOMEM;
1180 goto class_device_destroy;
1181 }
1182 PDEBUG("sb_out virt address: %p, phys address: 0x%x",
1183 sb_out_virt, tzcom_virt_to_phys(sb_out_virt));
Mona Hossain6e2e2692012-01-05 11:28:04 -08001184 ion_clnt = msm_ion_client_create(0x03, "tzcom");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185 /* Initialized in tzcom_open */
1186 pil = NULL;
1187
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -08001188 tzcom_perf_client = msm_bus_scale_register_client(
1189 &tzcom_bus_pdata);
1190 if (!tzcom_perf_client)
1191 pr_err("Unable to register bus client");
1192
1193 tzcom_bus_clk = clk_get(class_dev, "bus_clk");
1194 if (IS_ERR(tzcom_bus_clk)) {
1195 tzcom_bus_clk = NULL;
1196 } else if (tzcom_bus_clk != NULL) {
1197 pr_debug("Enabled DFAB clock\n");
1198 clk_set_rate(tzcom_bus_clk, 64000000);
1199 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001200 return 0;
1201
1202class_device_destroy:
1203 if (sb_in_virt)
1204 iounmap(sb_in_virt);
1205 if (sb_in_phys)
1206 pmem_kfree(sb_in_phys);
1207 if (sb_out_virt)
1208 iounmap(sb_out_virt);
1209 if (sb_out_phys)
1210 pmem_kfree(sb_out_phys);
1211 device_destroy(driver_class, tzcom_device_no);
1212class_destroy:
1213 class_destroy(driver_class);
1214unregister_chrdev_region:
1215 unregister_chrdev_region(tzcom_device_no, 1);
1216 return rc;
1217}
1218
1219static void __exit tzcom_exit(void)
1220{
1221 PDEBUG("Goodbye tzcom");
1222 if (sb_in_virt)
1223 iounmap(sb_in_virt);
1224 if (sb_in_phys)
1225 pmem_kfree(sb_in_phys);
1226 if (sb_out_virt)
1227 iounmap(sb_out_virt);
1228 if (sb_out_phys)
1229 pmem_kfree(sb_out_phys);
1230 if (pil != NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -07001231 pil_put(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001232 pil = NULL;
1233 }
Ramesh Masavarapueb6e9342012-02-13 19:59:50 -08001234 clk_put(tzcom_bus_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001235 device_destroy(driver_class, tzcom_device_no);
1236 class_destroy(driver_class);
1237 unregister_chrdev_region(tzcom_device_no, 1);
Mona Hossain6e2e2692012-01-05 11:28:04 -08001238 ion_client_destroy(ion_clnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001239}
1240
1241
1242MODULE_LICENSE("GPL v2");
1243MODULE_AUTHOR("Sachin Shah <sachins@codeaurora.org>");
1244MODULE_DESCRIPTION("Qualcomm TrustZone Communicator");
1245MODULE_VERSION("1.00");
1246
1247module_init(tzcom_init);
1248module_exit(tzcom_exit);