import PULS_20180308
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / cmdq / cmdq_driver.c
CommitLineData
6fa3eb70
S
1#include "cmdq_driver.h"
2#include "cmdq_struct.h"
3#include "cmdq_core.h"
4#include "cmdq_reg.h"
5#include "cmdq_mdp.h"
6#include "cmdq_device.h"
7#include "cmdq_platform.h"
8#include "cmdq_sec.h"
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/pm.h>
14#include <linux/proc_fs.h>
15#include <linux/seq_file.h>
16#include <linux/fs.h>
17#include <linux/cdev.h>
18#include <linux/interrupt.h>
19#include <linux/uaccess.h>
20#include <linux/errno.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/sched.h>
24#include <linux/pm.h>
25#include <linux/suspend.h>
26
27#ifndef CMDQ_OF_SUPPORT
28#include <mach/mt_irq.h> // mt_irq.h is not available on device tree enabled platforms
29#endif
30
31#ifdef CMDQ_OF_SUPPORT
32/**
33 * @device tree porting note
34 * alps/kernel-3.10/arch/arm64/boot/dts/{platform}.dts
35 * - use of_device_id to match driver and device
36 * - use io_map to map and get VA of HW's rgister
37**/
38static const struct of_device_id cmdq_of_ids[] = {
39 {.compatible = "mediatek,GCE",},
40 {}
41};
42#endif
43
4b9e9796
S
44#define CMDQ_MAX_DUMP_REG_COUNT (2048)
45#define CMDQ_MAX_COMMAND_SIZE (0x10000)
46#define CMDQ_MAX_WRITE_ADDR_COUNT (PAGE_SIZE / sizeof(u32))
47
6fa3eb70
S
48static dev_t gCmdqDevNo;
49static struct cdev *gCmdqCDev;
50static struct class *gCMDQClass;
51
52static ssize_t cmdq_driver_dummy_write(struct device *dev,
53 struct device_attribute *attr, const char *buf, size_t size)
54{
55 return -EACCES;
56}
57
58static DEVICE_ATTR(status, S_IRUSR | S_IWUSR, cmdqCorePrintStatus, cmdq_driver_dummy_write);
59static DEVICE_ATTR(error, S_IRUSR | S_IWUSR, cmdqCorePrintError, cmdq_driver_dummy_write);
60static DEVICE_ATTR(record, S_IRUSR | S_IWUSR, cmdqCorePrintRecord, cmdq_driver_dummy_write);
61static DEVICE_ATTR(log_level, S_IRUSR | S_IWUSR, cmdqCorePrintLogLevel, cmdqCoreWriteLogLevel);
62static DEVICE_ATTR(profile_enable, S_IRUSR | S_IWUSR, cmdqCorePrintProfileEnable,
63 cmdqCoreWriteProfileEnable);
64
65
66static int cmdq_proc_status_open(struct inode *inode, struct file *file)
67{
68 return single_open(file, cmdqCorePrintStatusSeq, inode->i_private);
69}
70
71static int cmdq_proc_error_open(struct inode *inode, struct file *file)
72{
73 return single_open(file, cmdqCorePrintErrorSeq, inode->i_private);
74}
75
76static int cmdq_proc_record_open(struct inode *inode, struct file *file)
77{
78 return single_open(file, cmdqCorePrintRecordSeq, inode->i_private);
79}
80
81static const struct file_operations cmdqDebugStatusOp = {
82 .owner = THIS_MODULE,
83 .open = cmdq_proc_status_open,
84 .read = seq_read,
85 .llseek = seq_lseek,
86 .release = single_release,
87};
88
89static const struct file_operations cmdqDebugErrorOp = {
90 .owner = THIS_MODULE,
91 .open = cmdq_proc_error_open,
92 .read = seq_read,
93 .llseek = seq_lseek,
94 .release = single_release,
95};
96
97static const struct file_operations cmdqDebugRecordOp = {
98 .owner = THIS_MODULE,
99 .open = cmdq_proc_record_open,
100 .read = seq_read,
101 .llseek = seq_lseek,
102 .release = single_release,
103};
104
105static int cmdq_open(struct inode *pInode, struct file *pFile)
106{
107 cmdqFileNodeStruct *pNode;
108
109 CMDQ_VERBOSE("CMDQ driver open fd=%p begin\n", pFile);
110
111 pFile->private_data = kzalloc(sizeof(cmdqFileNodeStruct), GFP_KERNEL);
112 if (NULL == pFile->private_data) {
113 CMDQ_ERR("Can't allocate memory for CMDQ file node\n");
114 return -ENOMEM;
115 }
116
117 pNode = (cmdqFileNodeStruct *) pFile->private_data;
118 pNode->userPID = current->pid;
119 pNode->userTGID = current->tgid;
120
121 INIT_LIST_HEAD(&(pNode->taskList));
122 spin_lock_init(&pNode->nodeLock);
123
124 CMDQ_VERBOSE("CMDQ driver open end\n");
125
126 return 0;
127}
128
129
130static int cmdq_release(struct inode *pInode, struct file *pFile)
131{
132 cmdqFileNodeStruct *pNode;
133 unsigned long flags;
134
135 CMDQ_VERBOSE("CMDQ driver release fd=%p begin\n", pFile);
136
137 pNode = (cmdqFileNodeStruct *) pFile->private_data;
138
139 if (NULL == pNode) {
140 CMDQ_ERR("CMDQ file node NULL\n");
141 return -EFAULT;
142 }
143
144 spin_lock_irqsave(&pNode->nodeLock, flags);
145
146 /* note that we did not release CMDQ tasks */
147 /* issued by this file node, */
148 /* since their HW operation may be pending. */
149
150 spin_unlock_irqrestore(&pNode->nodeLock, flags);
151
152 /* scan through tasks that created by this file node and release them */
153 cmdq_core_release_task_by_file_node((void *)pNode);
154
155 if (NULL != pFile->private_data) {
156 kfree(pFile->private_data);
157 pFile->private_data = NULL;
158 }
159
160 CMDQ_VERBOSE("CMDQ driver release end\n");
161
162 return 0;
163}
164
165static int cmdq_driver_create_reg_address_buffer(cmdqCommandStruct *pCommand)
166{
167 int status = 0;
168 uint32_t totalRegCount = 0;
169 uint32_t *regAddrBuf = NULL;
170
171 uint32_t *kernelRegAddr = NULL;
172 uint32_t kernelRegCount = 0;
173
174 const uint32_t userRegCount = pCommand->regRequest.count;
175
176
177 if (0 != pCommand->debugRegDump) {
178 /* get kernel dump request count */
179 status =
180 cmdqCoreDebugRegDumpBegin(pCommand->debugRegDump, &kernelRegCount,
181 &kernelRegAddr);
182 if (0 != status) {
183 CMDQ_ERR
184 ("cmdqCoreDebugRegDumpBegin returns %d, ignore kernel reg dump request\n",
185 status);
186 kernelRegCount = 0;
187 kernelRegAddr = NULL;
188 }
189 }
190
191 /* how many register to dump? */
4b9e9796
S
192 if (kernelRegCount > CMDQ_MAX_DUMP_REG_COUNT || userRegCount > CMDQ_MAX_DUMP_REG_COUNT)
193 return -EINVAL;
6fa3eb70
S
194 totalRegCount = kernelRegCount + userRegCount;
195
196 if (0 == totalRegCount) {
197 /* no need to dump register */
198 pCommand->regRequest.count = 0;
199 pCommand->regValue.count = 0;
200 pCommand->regRequest.regAddresses = NULL;
201 pCommand->regValue.regValues = NULL;
202 } else {
203 regAddrBuf = kzalloc(totalRegCount * sizeof(uint32_t), GFP_KERNEL);
204 if (NULL == regAddrBuf) {
205 return -ENOMEM;
206 }
207
208 /* collect user space dump request */
209 if (userRegCount) {
210 if (copy_from_user
211 (regAddrBuf, CMDQ_U32_PTR(pCommand->regRequest.regAddresses),
212 userRegCount * sizeof(uint32_t))) {
213 kfree(regAddrBuf);
214 return -EFAULT;
215 }
216 }
217
218 /* collect kernel space dump request, concatnate after user space request */
219 if (kernelRegCount) {
220 memcpy(regAddrBuf + userRegCount, kernelRegAddr,
221 kernelRegCount * sizeof(uint32_t));
222 }
223
224
225 /* replace address buffer and value address buffer with kzalloc memory */
226 pCommand->regRequest.regAddresses = regAddrBuf;
227 pCommand->regRequest.count = totalRegCount;
228 }
229
230 return 0;
231}
232
233static void cmdq_driver_process_read_address_request(cmdqReadAddressStruct *req_user)
234{
235 /* create kernel-space buffer for working */
236 uint32_t *addrs = NULL;
237 uint32_t *values = NULL;
238 dma_addr_t pa = 0;
239 int i = 0;
240
241 CMDQ_LOG("[READ_PA] cmdq_driver_process_read_address_request()\n");
242
243 do {
244 if (NULL == req_user ||
245 0 == req_user->count ||
4b9e9796 246 req_user->count > CMDQ_MAX_DUMP_REG_COUNT ||
6fa3eb70
S
247 NULL == req_user->values || NULL == req_user->dmaAddresses) {
248 CMDQ_ERR("[READ_PA] invalid req_user\n");
249 break;
250 }
251
252 addrs = kzalloc(req_user->count * sizeof(uint32_t), GFP_KERNEL);
253 if (NULL == addrs) {
254 CMDQ_ERR("[READ_PA] fail to alloc addr buf\n");
255 break;
256 }
257
258 values = kzalloc(req_user->count * sizeof(uint32_t), GFP_KERNEL);
259 if (NULL == values) {
260 CMDQ_ERR("[READ_PA] fail to alloc value buf\n");
261 break;
262 }
263
264 /* copy from user */
265 if (copy_from_user
266 (addrs, req_user->dmaAddresses, req_user->count * sizeof(uint32_t))) {
267 CMDQ_ERR("[READ_PA] fail to copy user dmaAddresses\n");
268 break;
269 }
270
271 /* actually read these PA write buffers */
272 for (i = 0; i < req_user->count; ++i) {
273 pa = (0xFFFFFFFF & addrs[i]);
274 CMDQ_LOG("[READ_PA] req read dma addresss 0x%pa\n", &pa);
275 values[i] = cmdqCoreReadWriteAddress(pa);
276 }
277
278 /* copy value to user */
279 if (copy_to_user(req_user->values, values, req_user->count * sizeof(uint32_t))) {
280 CMDQ_ERR("[READ_PA] fail to copy to user value buf\n");
281 break;
282 }
283
284 } while (0);
285
286
287 if (addrs) {
288 kfree(addrs);
289 }
290
291 if (values) {
292 kfree(values);
293 }
294
295}
296
297static long cmdq_driver_destroy_secure_medadata(cmdqCommandStruct *pCommand)
298{
299 if (pCommand->secData.addrMetadatas) {
300 kfree(CMDQ_U32_PTR(pCommand->secData.addrMetadatas));
301 pCommand->secData.addrMetadatas = NULL;
302 }
303
304 return 0;
305}
306
307static long cmdq_driver_create_secure_medadata(cmdqCommandStruct *pCommand)
308{
309 void *pAddrMetadatas = NULL;
310 const uint32_t length = (pCommand->secData.addrMetadataCount) * sizeof(cmdqSecAddrMetadataStruct);
311
312 /* verify parameter */
313 if ((false == pCommand->secData.isSecure) &&
314 (0 != pCommand->secData.addrMetadataCount)) {
315
316 /* normal path with non-zero secure metadata */
317 CMDQ_ERR("[secData]mismatch secData.isSecure(%d) and secData.addrMetadataCount(%d)\n",
318 pCommand->secData.isSecure,
319 pCommand->secData.addrMetadataCount);
320 return -EFAULT;
321 }
322
323 /* revise max count field */
324 pCommand->secData.addrMetadataMaxCount = pCommand->secData.addrMetadataCount;
325
326 /* bypass 0 metadata case*/
327 if (0 == pCommand->secData.addrMetadataCount) {
328 pCommand->secData.addrMetadatas = NULL;
329 return 0;
330 }
331
332 /* create kernel-space buffer for working */
333 pAddrMetadatas = kzalloc(length, GFP_KERNEL);
334 if (NULL == pAddrMetadatas) {
335 CMDQ_ERR("[secData]kzalloc for addrMetadatas failed, count:%d, alloacted_size:%d\n",
336 pCommand->secData.addrMetadataCount, length);
337 return -ENOMEM;
338 }
339
340 /* copy from user */
341 if (copy_from_user(pAddrMetadatas, pCommand->secData.addrMetadatas, length)) {
342
343 CMDQ_ERR("[secData]fail to copy user addrMetadatas\n");
344
345 /* replace buffer first to ensure that */
346 /* addrMetadatas is valid kernel space buffer address when free it */
347 pCommand->secData.addrMetadatas = pAddrMetadatas;
348 /* free secure path metadata */
349 cmdq_driver_destroy_secure_medadata(pCommand);
350 return -EFAULT;
351 }
352
353 /* replace buffer */
354 pCommand->secData.addrMetadatas = pAddrMetadatas;
355
356#if 0
357 cmdq_core_dump_secure_metadata(&(pCommand->secData));
358#endif
359
360 return 0;
361}
362
363static long cmdq_driver_process_command_request(cmdqCommandStruct *pCommand)
364{
365 int32_t status = 0;
366 uint32_t *userRegValue = NULL;
367 uint32_t userRegCount = 0;
368
369 if (pCommand->regRequest.count != pCommand->regValue.count) {
370 CMDQ_ERR("mismatch regRequest and regValue\n");
371 return -EFAULT;
372 }
373
4b9e9796
S
374 if (pCommand->regRequest.count > CMDQ_MAX_DUMP_REG_COUNT)
375 return -EINVAL;
376
6fa3eb70
S
377 /* allocate secure medatata */
378 status = cmdq_driver_create_secure_medadata(pCommand);
379 if (0 != status) {
380 return status;
381 }
382
383 /* backup since we are going to replace these */
384 userRegValue = CMDQ_U32_PTR(pCommand->regValue.regValues);
385 userRegCount = pCommand->regValue.count;
386
387 /* create kernel-space address buffer */
388 status = cmdq_driver_create_reg_address_buffer(pCommand);
389 if (0 != status) {
390 /* free secure path metadata */
391 cmdq_driver_destroy_secure_medadata(pCommand);
392 return status;
393 }
394
395 /* create kernel-space value buffer */
396 pCommand->regValue.regValues = (cmdqU32Ptr_t)(unsigned long)
397 kzalloc(pCommand->regRequest.count * sizeof(uint32_t), GFP_KERNEL);
398 pCommand->regValue.count = pCommand->regRequest.count;
399 if (NULL == pCommand->regValue.regValues) {
400 kfree(CMDQ_U32_PTR(pCommand->regRequest.regAddresses));
401 return -ENOMEM;
402 }
403
404 /* scenario id fixup */
405 cmdq_core_fix_command_desc_scenario_for_user_space_request(pCommand);
406
407 status = cmdqCoreSubmitTask(pCommand);
408 if (0 > status) {
409 CMDQ_ERR("Submit user commands for execution failed = %d\n", status);
410 cmdq_driver_destroy_secure_medadata(pCommand);
411
412 kfree(CMDQ_U32_PTR(pCommand->regRequest.regAddresses));
413 kfree(CMDQ_U32_PTR(pCommand->regValue.regValues));
414 return -EFAULT;
415 }
416
417 /* notify kernel space dump callback */
418 if (0 != pCommand->debugRegDump) {
419 status = cmdqCoreDebugRegDumpEnd(pCommand->debugRegDump,
420 pCommand->regRequest.count - userRegCount,
421 CMDQ_U32_PTR(pCommand->regValue.regValues) + userRegCount);
422 if (0 != status) {
423 CMDQ_ERR("cmdqCoreDebugRegDumpEnd returns %d\n", status);
424 }
425 }
426
427 /* copy back to user space buffer */
428 if (userRegValue && userRegCount) {
429 /* copy results back to user space */
430 CMDQ_VERBOSE("regValue[0] is %d\n", CMDQ_U32_PTR(pCommand->regValue.regValues)[0]);
431 if (copy_to_user
432 (userRegValue, CMDQ_U32_PTR(pCommand->regValue.regValues),
433 userRegCount * sizeof(uint32_t))) {
434 CMDQ_ERR("Copy REGVALUE to user space failed\n");
435 }
436 }
437
438 /* free allocated kernel buffers */
439 kfree(CMDQ_U32_PTR(pCommand->regRequest.regAddresses));
440 kfree(CMDQ_U32_PTR(pCommand->regValue.regValues));
441
442 if (pCommand->readAddress.count > 0) {
443 cmdq_driver_process_read_address_request(&pCommand->readAddress);
444 }
445
446 /* free allocated secure metadata */
447 cmdq_driver_destroy_secure_medadata(pCommand);
448
449 return 0;
450}
451
452static long cmdq_ioctl(struct file *pFile, unsigned int code, unsigned long param)
453{
454 struct cmdqCommandStruct command;
455 struct cmdqJobStruct job;
456 int count[CMDQ_MAX_ENGINE_COUNT];
457 struct TaskStruct *pTask;
458 int32_t status;
459 struct cmdqJobResultStruct jobResult;
460 uint32_t *userRegValue = NULL;
461 uint32_t userRegCount = 0;
462 /* backup value after task release */
463 uint32_t regCount = 0, regCountUserSpace = 0, regUserToken = 0;
464
465 switch (code) {
466 case CMDQ_IOCTL_EXEC_COMMAND:
467 if (copy_from_user(&command, (void *)param, sizeof(cmdqCommandStruct))) {
468 return -EFAULT;
469 }
470
4b9e9796
S
471 if (command.regRequest.count > CMDQ_MAX_DUMP_REG_COUNT ||
472 !command.blockSize ||
473 command.blockSize > CMDQ_MAX_COMMAND_SIZE)
474 return -EINVAL;
475
6fa3eb70
S
476 /* insert private_data for resource reclaim */
477 command.privateData = (void *)pFile->private_data;
478
479 if (cmdq_driver_process_command_request(&command)) {
480 return -EFAULT;
481 }
482 break;
483 case CMDQ_IOCTL_QUERY_USAGE:
484 if (cmdqCoreQueryUsage(count)) {
485 return -EFAULT;
486 }
487
488 if (copy_to_user((void *)param, count, sizeof(int32_t) * CMDQ_MAX_ENGINE_COUNT)) {
489 CMDQ_ERR("CMDQ_IOCTL_QUERY_USAGE copy_to_user failed\n");
490 return -EFAULT;
491 }
492 break;
493 case CMDQ_IOCTL_ASYNC_JOB_EXEC:
494 if (copy_from_user(&job, (void *)param, sizeof(cmdqJobStruct))) {
495 return -EFAULT;
496 }
497
4b9e9796
S
498 if (job.command.blockSize > CMDQ_MAX_COMMAND_SIZE)
499 return -EINVAL;
500
6fa3eb70
S
501 /* not support secure path for async ioctl yet */
502 if (true == job.command.secData.isSecure) {
503 CMDQ_ERR("not support secure path for CMDQ_IOCTL_ASYNC_JOB_EXEC\n");
504 return -EFAULT;
505 }
506
507 /* backup */
508 userRegCount = job.command.regRequest.count;
509
510 /* insert private_data for resource reclaim */
511 job.command.privateData = (void *)pFile->private_data;
512
513 /* create kernel-space address buffer */
514 status = cmdq_driver_create_reg_address_buffer(&job.command);
515 if (0 != status) {
516 return status;
517 }
518
519 /* scenario id fixup */
520 cmdq_core_fix_command_desc_scenario_for_user_space_request(&job.command);
521
522 status = cmdqCoreSubmitTaskAsync(&job.command, NULL, 0, &pTask);
523
524 /* store user space request count in TaskStruct */
525 /* for later retrieval */
526 if (pTask) {
527 pTask->regCountUserSpace = userRegCount;
528 pTask->regUserToken = job.command.debugRegDump;
529 }
530
531 /* we don't need regAddress anymore, free it now */
532 kfree(CMDQ_U32_PTR(job.command.regRequest.regAddresses));
533 job.command.regRequest.regAddresses = NULL;
534
535 if (status >= 0) {
536 job.hJob = (unsigned long)pTask;
537 if (copy_to_user((void *)param, (void *)&job, sizeof(cmdqJobStruct))) {
538 CMDQ_ERR("CMDQ_IOCTL_ASYNC_JOB_EXEC copy_to_user failed\n");
539 return -EFAULT;
540 }
541 } else {
542 job.hJob = (unsigned long)NULL;
543 return -EFAULT;
544 }
545 break;
546 case CMDQ_IOCTL_ASYNC_JOB_WAIT_AND_CLOSE:
547 if (copy_from_user(&jobResult, (void *)param, sizeof(jobResult))) {
548 CMDQ_ERR("copy_from_user jobResult fail\n");
549 return -EFAULT;
550 }
551
552 /* verify job handle */
553 if (!cmdqIsValidTaskPtr((TaskStruct *)(unsigned long)jobResult.hJob)) {
554 CMDQ_ERR("invalid task ptr = 0x%llx\n", jobResult.hJob);
555 return -EFAULT;
556 }
557 pTask = (TaskStruct *)(unsigned long)jobResult.hJob;
4b9e9796
S
558 if (pTask->regCount > CMDQ_MAX_DUMP_REG_COUNT)
559 return -EINVAL;
6fa3eb70
S
560
561 /* utility service, fill the engine flag. */
562 /* this is required by MDP. */
563 jobResult.engineFlag = pTask->engineFlag;
564
565 /* check if reg buffer suffices */
566 if (jobResult.regValue.count < pTask->regCountUserSpace) {
567 jobResult.regValue.count = pTask->regCountUserSpace;
568 if (copy_to_user((void *)param, (void *)&jobResult, sizeof(jobResult))) {
569 CMDQ_ERR("copy_to_user fail, line=%d\n", __LINE__);
570 return -EINVAL;
571 }
572 CMDQ_ERR("insufficient register buffer\n");
573 return -ENOMEM;
574 }
575
576 /* inform client the actual read register count */
577 jobResult.regValue.count = pTask->regCountUserSpace;
578 /* update user space before we replace the regValues pointer. */
579 if (copy_to_user((void *)param, (void *)&jobResult, sizeof(jobResult))) {
580 CMDQ_ERR("copy_to_user fail line=%d\n", __LINE__);
581 return -EINVAL;
582 }
583
584 /* allocate kernel space result buffer */
585 /* which contains kernel + user space requests */
586 userRegValue = jobResult.regValue.regValues;
587 jobResult.regValue.regValues =
588 kzalloc(pTask->regCount * sizeof(uint32_t), GFP_KERNEL);
589 jobResult.regValue.count = pTask->regCount;
590 if (NULL == jobResult.regValue.regValues) {
591 CMDQ_ERR("no reg value buffer\n");
592 return -ENOMEM;
593 }
594
595 /* backup value after task release */
596 regCount = pTask->regCount;
597 regCountUserSpace = pTask->regCountUserSpace;
598 regUserToken = pTask->regUserToken;
599
600 /* make sure the task is running and wait for it */
601 status = cmdqCoreWaitResultAndReleaseTask(pTask,
602 &jobResult.regValue,
603 msecs_to_jiffies
604 (CMDQ_DEFAULT_TIMEOUT_MS));
605 if (status < 0) {
606 CMDQ_ERR("waitResultAndReleaseTask fail=%d\n", status);
607 /* free kernel space result buffer */
608 kfree(jobResult.regValue.regValues);
609 return status;
610 }
611
612 /* pTask is released, do not access it any more */
613 pTask = NULL;
614
615 /* notify kernel space dump callback */
616 if (regCount > regCountUserSpace) {
617 CMDQ_VERBOSE("kernel space reg dump = %d, %d, %d\n", regCount,
618 regCountUserSpace, regUserToken);
619 status =
620 cmdqCoreDebugRegDumpEnd(regUserToken, regCount - regCountUserSpace,
621 jobResult.regValue.regValues +
622 regCountUserSpace);
623 if (0 != status) {
624 CMDQ_ERR("cmdqCoreDebugRegDumpEnd returns %d\n", status);
625 }
626 }
627
628 /* copy result to user space */
629 if (copy_to_user
630 ((void *)userRegValue, (void *)(unsigned long)jobResult.regValue.regValues,
631 regCountUserSpace * sizeof(uint32_t))) {
632 CMDQ_ERR("Copy REGVALUE to user space failed\n");
633 return -EFAULT;
634 }
635
636 if (jobResult.readAddress.count > 0) {
637 cmdq_driver_process_read_address_request(&jobResult.readAddress);
638 }
639
640 /* free kernel space result buffer */
641 kfree(jobResult.regValue.regValues);
642 break;
643 case CMDQ_IOCTL_ALLOC_WRITE_ADDRESS:
644 do {
645 cmdqWriteAddressStruct addrReq;
646 dma_addr_t paStart = 0;
647
648 CMDQ_LOG("CMDQ_IOCTL_ALLOC_WRITE_ADDRESS\n");
649
650 if (copy_from_user(&addrReq, (void *)param, sizeof(addrReq))) {
651 CMDQ_ERR("CMDQ_IOCTL_ALLOC_WRITE_ADDRESS copy_from_user failed\n");
652 return -EFAULT;
653 }
654
4b9e9796
S
655 if (!addrReq.count || addrReq.count > CMDQ_MAX_WRITE_ADDR_COUNT) {
656 CMDQ_ERR(
657 "CMDQ_IOCTL_ALLOC_WRITE_ADDRESS invalid alloc write addr count:%u\n",
658 addrReq.count);
659 return -EINVAL;
660 }
661
6fa3eb70
S
662 status = cmdqCoreAllocWriteAddress(addrReq.count, &paStart);
663 if (0 != status) {
664 CMDQ_ERR
665 ("CMDQ_IOCTL_ALLOC_WRITE_ADDRESS cmdqCoreAllocWriteAddress() failed\n");
666 return status;
667 }
668
669
670 addrReq.startPA = (uint32_t) paStart;
671 CMDQ_LOG("CMDQ_IOCTL_ALLOC_WRITE_ADDRESS get 0x%08x\n", addrReq.startPA);
672
673 if (copy_to_user((void *)param, &addrReq, sizeof(addrReq))) {
674 CMDQ_ERR("CMDQ_IOCTL_ALLOC_WRITE_ADDRESS copy_to_user failed\n");
675 return -EFAULT;
676 }
677 status = 0;
678 } while (0);
679 break;
680 case CMDQ_IOCTL_FREE_WRITE_ADDRESS:
681 do {
682 cmdqWriteAddressStruct freeReq;
683
684 CMDQ_LOG("CMDQ_IOCTL_FREE_WRITE_ADDRESS\n");
685
686 if (copy_from_user(&freeReq, (void *)param, sizeof(freeReq))) {
687 CMDQ_ERR("CMDQ_IOCTL_FREE_WRITE_ADDRESS copy_from_user failed\n");
688 return -EFAULT;
689 }
690
691 status = cmdqCoreFreeWriteAddress(freeReq.startPA);
692 if (0 != status) {
693 return status;
694 }
695 status = 0;
696 } while (0);
697 break;
698 case CMDQ_IOCTL_READ_ADDRESS_VALUE:
699 do {
700 cmdqReadAddressStruct readReq;
701
702 CMDQ_LOG("CMDQ_IOCTL_READ_ADDRESS_VALUE\n");
703
704 if (copy_from_user(&readReq, (void *)param, sizeof(readReq))) {
705 CMDQ_ERR("CMDQ_IOCTL_READ_ADDRESS_VALUE copy_from_user failed\n");
706 return -EFAULT;
707 }
708
709 /* this will copy result to readReq->values buffer */
710 cmdq_driver_process_read_address_request(&readReq);
711
712 status = 0;
713
714 } while (0);
715 break;
716 case CMDQ_IOCTL_QUERY_CAP_BITS:
717 do {
718 int capBits = 0;
719 if (cmdq_core_support_wait_and_receive_event_in_same_tick()) {
720 capBits |= (1L << CMDQ_CAP_WFE);
721 } else {
722 capBits &= ~(1L << CMDQ_CAP_WFE);
723 }
724
725 if (copy_to_user((void *)param, &capBits, sizeof(int))) {
726 CMDQ_ERR("Copy capacity bits to user space failed\n");
727 return -EFAULT;
728 }
729 } while (0);
730 break;
731 default:
732 CMDQ_ERR("unrecognized ioctl 0x%08x\n", code);
733 break;
734 }
735
736 return 0;
737}
738
739static long cmdq_ioctl_compat(struct file *pFile, unsigned int code, unsigned long param)
740{
741#ifdef CONFIG_COMPAT
742 switch(code)
743 {
744 case CMDQ_IOCTL_QUERY_USAGE:
745 case CMDQ_IOCTL_EXEC_COMMAND:
746 case CMDQ_IOCTL_ASYNC_JOB_EXEC:
747 case CMDQ_IOCTL_ASYNC_JOB_WAIT_AND_CLOSE:
748 case CMDQ_IOCTL_ALLOC_WRITE_ADDRESS:
749 case CMDQ_IOCTL_FREE_WRITE_ADDRESS:
750 case CMDQ_IOCTL_READ_ADDRESS_VALUE:
751 case CMDQ_IOCTL_QUERY_CAP_BITS:
752 // All ioctl structures should be the same size in 32-bit and 64-bit linux.
753 return cmdq_ioctl(pFile, code, param);
754 case CMDQ_IOCTL_LOCK_MUTEX:
755 case CMDQ_IOCTL_UNLOCK_MUTEX:
756 CMDQ_ERR("[COMPAT]deprecated ioctl 0x%08x\n", code);
757 return -ENOIOCTLCMD;
758 default:
759 CMDQ_ERR("[COMPAT]unrecognized ioctl 0x%08x\n", code);
760 return -ENOIOCTLCMD;
761 }
762
763 CMDQ_ERR("[COMPAT]unrecognized ioctl 0x%08x\n", code);
764 return -ENOIOCTLCMD;
765#endif
766
767 return -ENOIOCTLCMD;
768}
769
770
771static const struct file_operations cmdqOP = {
772 .owner = THIS_MODULE,
773 .open = cmdq_open,
774 .release = cmdq_release,
775 .unlocked_ioctl = cmdq_ioctl,
776#ifdef CONFIG_COMPAT
777 .compat_ioctl = cmdq_ioctl_compat,
778#endif
779};
780
781static int cmdq_pm_notifier_cb(struct notifier_block *nb, unsigned long event, void *ptr)
782{
783 switch (event) {
784 case PM_SUSPEND_PREPARE:/* Going to suspend the system */
785 /* The next stage is freeze process. */
786 /* We will queue all request in suspend callback, */
787 /* so don't care this stage*/
788 return NOTIFY_DONE; /* don't care this event */
789 case PM_POST_SUSPEND:
790 /* processes had resumed in previous stage (system resume callback) */
791 /* resume CMDQ driver to execute. */
792 cmdqCoreResumedNotifier();
793 return NOTIFY_OK; /* process done */
794 default:
795 return NOTIFY_DONE;
796 }
797 return NOTIFY_DONE;
798}
799
800/* Hibernation and suspend events */
801static struct notifier_block cmdq_pm_notifier_block = {
802 .notifier_call = cmdq_pm_notifier_cb,
803 .priority = 5,
804};
805
806static irqreturn_t cmdq_irq_handler(int IRQ, void *pDevice)
807{
808 int index;
809 uint32_t irqStatus;
810 bool handled = false; /* we share IRQ bit with CQ-DMA, */
811 /* so it is possible that this handler */
812 /* is called but GCE does not have IRQ flag. */
813 do {
814 if (cmdq_dev_get_irq_id() == IRQ) {
815 irqStatus = CMDQ_REG_GET32(CMDQ_CURR_IRQ_STATUS) & 0x0FFFF;
816 for (index = 0; (irqStatus != 0xFFFF) && index < CMDQ_MAX_THREAD_COUNT;
817 index++) {
818 /* STATUS bit set to 0 means IRQ asserted */
819 if (irqStatus & (1 << index)) {
820 continue;
821 }
822 /* so we mark irqStatus to 1 to denote finished processing */
823 /* and we can early-exit if no more threads being asserted */
824 irqStatus |= (1 << index);
825
826 cmdqCoreHandleIRQ(index);
827 handled = true;
828 }
829 } else if (cmdq_dev_get_irq_secure_id() == IRQ){
830 CMDQ_ERR("receive secure IRQ %d in NWD\n", IRQ);
831 }
832 } while (0);
833
834 if (handled) {
835 cmdq_core_add_consume_task();
836 return IRQ_HANDLED;
837 } else {
838 /* allow CQ-DMA to process this IRQ bit */
839 return IRQ_NONE;
840 }
841}
842
843static int cmdq_create_debug_entries(void)
844{
845 struct proc_dir_entry *debugDirEntry = NULL;
846 debugDirEntry = proc_mkdir(CMDQ_DRIVER_DEVICE_NAME "_debug", NULL);
847 if (debugDirEntry) {
848 struct proc_dir_entry *entry = NULL;
849 entry = proc_create("status", 0440, debugDirEntry, &cmdqDebugStatusOp);
850 entry = proc_create("error", 0440, debugDirEntry, &cmdqDebugErrorOp);
851 entry = proc_create("record", 0440, debugDirEntry, &cmdqDebugRecordOp);
852 }
853
854 return 0;
855}
856
857static int cmdq_probe(struct platform_device *pDevice)
858{
859 int status;
860 struct device *object;
861
862 CMDQ_MSG("CMDQ driver probe begin\n");
863
864 /* init cmdq device related data */
865 cmdq_dev_init(pDevice);
866
867 /* init cmdq context */
868 cmdqCoreInitialize();
869
870 status = alloc_chrdev_region(&gCmdqDevNo, 0, 1, CMDQ_DRIVER_DEVICE_NAME);
871 if (status != 0) {
872 CMDQ_ERR("Get CMDQ device major number(%d) failed(%d)\n", gCmdqDevNo, status);
873 } else {
874 CMDQ_MSG("Get CMDQ device major number(%d) success(%d)\n", gCmdqDevNo, status);
875 }
876
877 /* ioctl access point (/dev/mtk_cmdq) */
878 gCmdqCDev = cdev_alloc();
879 gCmdqCDev->owner = THIS_MODULE;
880 gCmdqCDev->ops = &cmdqOP;
881
882 status = cdev_add(gCmdqCDev, gCmdqDevNo, 1);
883
884 gCMDQClass = class_create(THIS_MODULE, CMDQ_DRIVER_DEVICE_NAME);
885 object = device_create(gCMDQClass, NULL, gCmdqDevNo, NULL, CMDQ_DRIVER_DEVICE_NAME);
886
887 status =
888 request_irq(cmdq_dev_get_irq_id(), cmdq_irq_handler, IRQF_TRIGGER_LOW | IRQF_SHARED,
889 CMDQ_DRIVER_DEVICE_NAME, gCmdqCDev);
890 if (status != 0) {
891 CMDQ_ERR("Register cmdq driver irq handler(%d) failed(%d)\n", gCmdqDevNo, status);
892 return -EFAULT;
893 }
894
895 /* although secusre CMDQ driver is responsible for handle secure IRQ, */
896 /* MUST registet secure IRQ to GIC in normal world to ensure it will be initialize correctly */
897 /* (that's because t-base does not support GIC init IRQ in secure world...) */
898#ifdef CMDQ_SECURE_PATH_SUPPORT
899 status =
900 request_irq(cmdq_dev_get_irq_secure_id(), cmdq_irq_handler, IRQF_TRIGGER_LOW,
901 CMDQ_DRIVER_DEVICE_NAME, gCmdqCDev);
902 CMDQ_MSG("register sec IRQ:%d\n", cmdq_dev_get_irq_secure_id());
903 if (status != 0) {
904 CMDQ_ERR("Register cmdq driver secure irq handler(%d) failed(%d)\n", gCmdqDevNo, status);
905 return -EFAULT;
906 }
907#endif
908
909 /* global ioctl access point (/proc/mtk_cmdq) */
910 if (NULL == proc_create(CMDQ_DRIVER_DEVICE_NAME, 0644, NULL, &cmdqOP)) {
911 CMDQ_ERR("CMDQ procfs node create failed\n");
912 return -EFAULT;
913 }
914
915 /* proc debug access point */
916 cmdq_create_debug_entries();
917
918 /* device attributes for debugging */
919 device_create_file(&pDevice->dev, &dev_attr_status);
920 device_create_file(&pDevice->dev, &dev_attr_error);
921 device_create_file(&pDevice->dev, &dev_attr_record);
922 device_create_file(&pDevice->dev, &dev_attr_log_level);
923 device_create_file(&pDevice->dev, &dev_attr_profile_enable);
924
925 CMDQ_MSG("CMDQ driver probe end\n");
926
927 return 0;
928}
929
930
931static int cmdq_remove(struct platform_device *pDevice)
932{
933 disable_irq(cmdq_dev_get_irq_id());
934
935 device_remove_file(&pDevice->dev, &dev_attr_status);
936 device_remove_file(&pDevice->dev, &dev_attr_error);
937 device_remove_file(&pDevice->dev, &dev_attr_record);
938 device_remove_file(&pDevice->dev, &dev_attr_log_level);
939 device_remove_file(&pDevice->dev, &dev_attr_profile_enable);
940 return 0;
941}
942
943
944static int cmdq_suspend(struct device *pDevice)
945{
946 return cmdqCoreSuspend();
947}
948
949
950static int cmdq_resume(struct device *pDevice)
951{
952 return cmdqCoreResume();
953}
954
955static int cmdq_pm_restore_noirq(struct device *pDevice)
956{
957 return 0;
958}
959
960static struct dev_pm_ops cmdq_pm_ops = {
961 .suspend = cmdq_suspend,
962 .resume = cmdq_resume,
963 .freeze = NULL,
964 .thaw = NULL,
965 .poweroff = NULL,
966 .restore = NULL,
967 .restore_noirq = cmdq_pm_restore_noirq,
968};
969
970
971static struct platform_driver gCmdqDriver = {
972 .probe = cmdq_probe,
973 .remove = cmdq_remove,
974 .driver = {
975 .name = CMDQ_DRIVER_DEVICE_NAME,
976 .owner = THIS_MODULE,
977 .pm = &cmdq_pm_ops,
978#ifdef CMDQ_OF_SUPPORT
979 .of_match_table = cmdq_of_ids,
980#endif
981 }
982};
983
984static int __init cmdq_init(void)
985{
986 int status;
987
988 CMDQ_MSG("CMDQ driver init begin\n");
989
990 /* Initialize group callback */
991 cmdqCoreInitGroupCB();
992
993 /* Register MDP callback */
994 cmdqCoreRegisterCB(CMDQ_GROUP_MDP,
995 cmdqMdpClockOn, cmdqMdpDumpInfo, cmdqMdpResetEng, cmdqMdpClockOff);
996
997 /* Register VENC callback */
998 cmdqCoreRegisterCB(CMDQ_GROUP_VENC, NULL, cmdqVEncDumpInfo, NULL, NULL);
999
1000 status = platform_driver_register(&gCmdqDriver);
1001 if (0 != status) {
1002 CMDQ_ERR("Failed to register the CMDQ driver(%d)\n", status);
1003 return -ENODEV;
1004 }
1005
1006 /* register pm notifier */
1007 status = register_pm_notifier(&cmdq_pm_notifier_block);
1008 if (0 != status) {
1009 CMDQ_ERR("Failed to register_pm_notifier(%d)\n", status);
1010 return -ENODEV;
1011 }
1012
1013 CMDQ_MSG("CMDQ driver init end\n");
1014
1015 return 0;
1016}
1017
1018
1019static void __exit cmdq_exit(void)
1020{
1021 int32_t status;
1022
1023 CMDQ_MSG("CMDQ driver exit begin\n");
1024
1025 device_destroy(gCMDQClass, gCmdqDevNo);
1026
1027 class_destroy(gCMDQClass);
1028
1029 cdev_del(gCmdqCDev);
1030
1031 gCmdqCDev = NULL;
1032
1033 unregister_chrdev_region(gCmdqDevNo, 1);
1034
1035 platform_driver_unregister(&gCmdqDriver);
1036
1037 /* register pm notifier */
1038 status = unregister_pm_notifier(&cmdq_pm_notifier_block);
1039 if (0 != status) {
1040 CMDQ_ERR("Failed to unregister_pm_notifier(%d)\n", status);
1041 }
1042
1043 /* Unregister MDP callback */
1044 cmdqCoreRegisterCB(CMDQ_GROUP_MDP, NULL, NULL, NULL, NULL);
1045
1046 /* Unregister VENC callback */
1047 cmdqCoreRegisterCB(CMDQ_GROUP_VENC, NULL, NULL, NULL, NULL);
1048
1049 /* De-Initialize group callback */
1050 cmdqCoreDeinitGroupCB();
1051
1052 /* De-Initialize cmdq core */
1053 cmdqCoreDeInitialize();
1054
1055 /* De-Initialize cmdq dev related data */
1056 cmdq_dev_deinit();
1057
1058 CMDQ_MSG("CMDQ driver exit end\n");
1059}
1060
1061
1062subsys_initcall(cmdq_init);
1063module_exit(cmdq_exit);
1064
1065MODULE_DESCRIPTION("MTK CMDQ driver");
1066MODULE_AUTHOR("Pablo<pablo.sun@mediatek.com>");
1067MODULE_LICENSE("GPL");