cmdq_driver.c 32 KB

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