// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmdq_record_private.h" #include "cmdq_reg.h" #include "cmdq_virtual.h" #include "cmdq_mdp_common.h" #include "cmdq_device.h" #ifdef CMDQ_CONFIG_SMI #include "smi_public.h" #endif #define CMDQ_TEST #ifdef CMDQ_TEST #define CMDQ_TESTCASE_PARAMETER_MAX 4 #define CMDQ_MONITOR_EVENT_MAX 10 #define CMDQ_TEST_MMSYS_DUMMY_PA CMDQ_THR_SPR3_PA(3) #define CMDQ_TEST_MMSYS_DUMMY_VA CMDQ_THR_SPR3(3) #define CMDQ_TEST_GCE_DUMMY_PA CMDQ_GPR_R32_PA(CMDQ_DATA_REG_2D_SHARPNESS_1) #define CMDQ_TEST_GCE_DUMMY_VA CMDQ_GPR_R32(CMDQ_DATA_REG_2D_SHARPNESS_1) /* test configuration */ static DEFINE_MUTEX(gCmdqTestProcLock); enum CMDQ_TEST_TYPE_ENUM { CMDQ_TEST_TYPE_NORMAL = 0, CMDQ_TEST_TYPE_SECURE = 1, CMDQ_TEST_TYPE_MONITOR_EVENT = 2, CMDQ_TEST_TYPE_MONITOR_POLL = 3, CMDQ_TEST_TYPE_OPEN_COMMAND_DUMP = 4, CMDQ_TEST_TYPE_DUMP_DTS = 5, CMDQ_TEST_TYPE_FEATURE_CONFIG = 6, CMDQ_TEST_TYPE_MMSYS_PERFORMANCE = 7, CMDQ_TEST_TYPE_SECURE_MTEE = 8, CMDQ_TEST_TYPE_MAX /* ALWAYS keep at the end */ }; enum CMDQ_MOITOR_TYPE_ENUM { CMDQ_MOITOR_TYPE_FLUSH = 0, CMDQ_MOITOR_TYPE_WFE = 1, /* wait for event and clear */ CMDQ_MOITOR_TYPE_WAIT_NO_CLEAR = 2, CMDQ_MOITOR_TYPE_QUERYREGISTER = 3, CMDQ_MOITOR_TYPE_MAX /* ALWAYS keep at the end */ }; struct cmdqMonitorEventStruct { bool status; struct cmdqRecStruct *cmdqHandle; cmdqBackupSlotHandle slotHandle; u32 monitorNUM; u32 waitType[CMDQ_MONITOR_EVENT_MAX]; u64 monitorEvent[CMDQ_MONITOR_EVENT_MAX]; u32 previousValue[CMDQ_MONITOR_EVENT_MAX]; }; struct cmdqMonitorPollStruct { bool status; struct cmdqRecStruct *cmdqHandle; cmdqBackupSlotHandle slotHandle; u64 pollReg; u64 pollValue; u64 pollMask; u32 delayTime; struct delayed_work delayContinueWork; }; static s64 gCmdqTestConfig[CMDQ_MONITOR_EVENT_MAX]; static s8 gCmdqTestSecure; static struct cmdqMonitorEventStruct gEventMonitor; static struct cmdqMonitorPollStruct gPollMonitor; #ifdef CMDQ_TEST_PROC static struct proc_dir_entry *gCmdqTestProcEntry; #endif #define CMDQ_TEST_FAIL(string, args...) \ do { \ if (1) { \ pr_notice("[CMDQ][ERR]TEST FAIL: "string, ##args); \ } \ } while (0) /* wrapper of cmdq_pkt_flush_async_ex */ static s32 _test_flush_async(struct cmdqRecStruct *handle) { return cmdq_pkt_flush_async_ex(handle, NULL, 0, false); } /* call flush or mdp flush by check scenario */ static s32 _test_flush_task(struct cmdqRecStruct *handle) { s32 status; if (cmdq_get_func()->isDynamic(handle->scenario)) { if (!handle->finalized) cmdq_op_finalize_command(handle, false); /* go mdp path dispatch and wait */ status = cmdq_mdp_flush_async_impl(handle); if (status < 0) CMDQ_ERR("flush failed handle:0x%p\n", handle); else status = cmdq_mdp_wait(handle, NULL); } else { /* use static thread flush */ status = cmdq_task_flush(handle); } return status; } /* call async flush or async mdp flush by check scenario */ static s32 _test_flush_task_async(struct cmdqRecStruct *handle) { s32 status; if (cmdq_get_func()->isDynamic(handle->scenario)) { /* go mdp path dispatch and wait */ status = cmdq_mdp_flush_async_impl(handle); if (status < 0) CMDQ_ERR("flush failed handle:0x%p\n", handle); } else { /* use static thread flush */ status = cmdq_pkt_flush_async_ex(handle, NULL, 0, false); } return status; } static s32 _test_wait_task(struct cmdqRecStruct *handle) { s32 status; if (cmdq_get_func()->isDynamic(handle->scenario)) status = cmdq_mdp_wait(handle, NULL); else status = cmdq_pkt_wait_flush_ex_result(handle); return status; } static void testcase_scenario(void) { struct cmdqRecStruct *handle; s32 ret; int i = 0; CMDQ_LOG("%s\n", __func__); /* make sure each scenario runs properly with empty commands */ for (i = 0; i < CMDQ_MAX_SCENARIO_COUNT; i++) { if (cmdq_mdp_is_request_from_user_space(i) || i == CMDQ_SCENARIO_TIMER_LOOP) continue; cmdq_task_create((enum CMDQ_SCENARIO_ENUM)i, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); ret = _test_flush_task(handle); if (ret < 0) { CMDQ_TEST_FAIL("scenario fail:%d ret:%d\n", i, ret); } cmdq_task_destroy(handle); } CMDQ_LOG("%s END\n", __func__); } struct cmdq_test_timer { struct timer_list test_timer; u32 event; }; static struct cmdq_test_timer cmdq_ttm; static bool test_timer_stop; static void _testcase_sync_token_timer_func(struct timer_list *t) { struct cmdq_test_timer *tm = from_timer(tm, t, test_timer); /* trigger sync event */ CMDQ_MSG("trigger event:0x%08lx\n", (1L << 16) | tm->event); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | tm->event); } static void _testcase_sync_token_timer_loop_func(struct timer_list *t) { struct cmdq_test_timer *tm = from_timer(tm, t, test_timer); /* trigger sync event */ CMDQ_MSG("trigger event:0x%08lx\n", (1L << 16) | tm->event); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | tm->event); if (test_timer_stop) { del_timer(&tm->test_timer); return; } /* repeate timeout until user delete it */ mod_timer(&tm->test_timer, jiffies + msecs_to_jiffies(10)); } static void testcase_sync_token(void) { struct cmdqRecStruct *hRec; s32 ret = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_SUB_DISP, &hRec); do { cmdq_task_reset(hRec); cmdq_task_set_secure(hRec, gCmdqTestSecure); /* setup timer to trigger sync token */ cmdq_ttm.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_ttm.test_timer, _testcase_sync_token_timer_func, 0); mod_timer(&cmdq_ttm.test_timer, jiffies + msecs_to_jiffies(1000)); /* wait for sync token */ cmdq_op_wait(hRec, CMDQ_SYNC_TOKEN_USER_0); CMDQ_MSG("start waiting\n"); ret = cmdq_task_flush(hRec); CMDQ_MSG("waiting done\n"); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); del_timer(&cmdq_ttm.test_timer); } while (0); CMDQ_MSG("%s, timeout case\n", __func__); /* */ /* test for timeout */ /* */ do { cmdq_task_reset(hRec); cmdq_task_set_secure(hRec, gCmdqTestSecure); /* wait for sync token */ cmdq_op_wait(hRec, CMDQ_SYNC_TOKEN_USER_0); CMDQ_MSG("start waiting\n"); ret = cmdq_task_flush(hRec); CMDQ_MSG("waiting done\n"); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); } while (0); cmdq_task_destroy(hRec); CMDQ_LOG("%s END\n", __func__); } static void testcase_async_suspend_resume(void) { struct cmdqRecStruct *hReqA; s32 ret = 0; CMDQ_LOG("%s\n", __func__); /* setup timer to trigger sync token * timer_setup(&timer_reqA, &_testcase_sync_token_timer_func, * CMDQ_SYNC_TOKEN_USER_0); */ /* mod_timer(&timer_reqA, jiffies + msecs_to_jiffies(300)); */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); do { /* let this thread wait for user token, then finish */ cmdq_task_create(CMDQ_SCENARIO_PRIMARY_ALL, &hReqA); cmdq_task_reset(hReqA); cmdq_task_set_secure(hReqA, gCmdqTestSecure); cmdq_op_wait(hReqA, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(hReqA, false); ret = _test_flush_async(hReqA); if (ret < 0) { CMDQ_TEST_FAIL("%s submit fail:%d handle:0x%p\n", __func__, ret, hReqA); break; } CMDQ_MSG("%s handle:%p engine:0x%llx scenario:%d\n", __func__, hReqA, hReqA->engineFlag, hReqA->scenario); CMDQ_MSG("%s start suspend+resume thread 0========\n", __func__); cmdq_core_suspend_hw_thread(0); CMDQ_REG_SET32(CMDQ_THR_SUSPEND_TASK(0), 0x00); /* resume */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | CMDQ_SYNC_TOKEN_USER_0); msleep_interruptible(500); CMDQ_MSG("%s start wait A========\n", __func__); ret = cmdq_pkt_wait_flush_ex_result(hReqA); } while (0); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_destroy(hReqA); /* del_timer(&timer_reqA); */ CMDQ_LOG("%s END\n", __func__); } static void testcase_errors(void) { struct cmdqRecStruct *handle; s32 ret; const u8 UNKNOWN_OP = 0x50; ret = 0; do { /* SW timeout */ CMDQ_LOG("=============== INIFINITE Wait ===============\n"); cmdqCoreClearEvent(CMDQ_EVENT_MDP_RSZ0_EOF); cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); /* turn on ALL engine flag to test dump */ for (ret = 0; ret < CMDQ_MAX_ENGINE_COUNT; ++ret) handle->engineFlag |= 1LL << ret; cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_wait(handle, CMDQ_EVENT_MDP_RSZ0_EOF); cmdq_task_flush(handle); cmdq_core_reset_first_dump(); CMDQ_LOG("=============== POLL INIFINITE ===============\n"); CMDQ_MSG("testReg: %lx\n", CMDQ_TEST_GCE_DUMMY_VA); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0x0); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_poll(handle, CMDQ_TEST_GCE_DUMMY_PA, 1, 0xFFFFFFFF); cmdq_task_flush(handle); cmdq_core_reset_first_dump(); CMDQ_LOG("=============== INVALID INSTR ===============\n"); /* invalid instruction */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_append_command(handle, CMDQ_CODE_JUMP, -1, 0, 0, 0); cmdq_task_flush(handle); cmdq_core_reset_first_dump(); CMDQ_LOG("======= INVALID INSTR: UNKNOWN OP(0x%x) =======\n", UNKNOWN_OP); /* invalid instruction is asserted when unknown OP */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_pkt_append_command(handle->pkt, 0, 0, 0, 0, 0, 0, 0, UNKNOWN_OP); cmdq_task_flush(handle); cmdq_core_reset_first_dump(); } while (0); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static s32 finishCallback(unsigned long data) { CMDQ_LOG("callback() with data=0x%08lx\n", data); return 0; } static void testcase_fire_and_forget(void) { struct cmdqRecStruct *hReqA, *hReqB; CMDQ_LOG("%s\n", __func__); do { cmdq_task_create(CMDQ_SCENARIO_DEBUG, &hReqA); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &hReqB); cmdq_task_reset(hReqA); cmdq_task_reset(hReqB); cmdq_task_set_secure(hReqA, gCmdqTestSecure); cmdq_task_set_secure(hReqB, gCmdqTestSecure); CMDQ_MSG("%s %d\n", __func__, __LINE__); cmdq_task_flush_async(hReqA); CMDQ_MSG("%s %d\n", __func__, __LINE__); cmdq_task_flush_async_callback(hReqB, finishCallback, 443); CMDQ_MSG("%s %d\n", __func__, __LINE__); } while (0); cmdq_task_destroy(hReqA); cmdq_task_destroy(hReqB); CMDQ_LOG("%s END\n", __func__); } static struct cmdq_test_timer cmdq_treqa; static struct cmdq_test_timer cmdq_treqb; static void testcase_async_request(void) { struct cmdqRecStruct *hReqA, *hReqB; s32 ret = 0; CMDQ_LOG("%s\n", __func__); /* setup timer to trigger sync token */ cmdq_treqa.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_treqa.test_timer, _testcase_sync_token_timer_func, 0); mod_timer(&cmdq_treqa.test_timer, jiffies + msecs_to_jiffies(1000)); cmdq_treqa.event = CMDQ_SYNC_TOKEN_USER_1; timer_setup(&cmdq_treqb.test_timer, _testcase_sync_token_timer_func, 0); /* mod_timer(&timer_reqB, jiffies + msecs_to_jiffies(1300)); */ /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_1); do { cmdq_task_create(CMDQ_SCENARIO_SUB_DISP, &hReqA); cmdq_task_reset(hReqA); cmdq_task_set_secure(hReqA, gCmdqTestSecure); cmdq_op_wait(hReqA, CMDQ_SYNC_TOKEN_USER_0); cmdq_append_command(hReqA, CMDQ_CODE_EOC, 0, 1, 0, 0); cmdq_append_command(hReqA, CMDQ_CODE_JUMP, 0, 8, 0, 0); cmdq_task_create(CMDQ_SCENARIO_SUB_DISP, &hReqB); cmdq_task_reset(hReqB); cmdq_task_set_secure(hReqB, gCmdqTestSecure); cmdq_op_wait(hReqB, CMDQ_SYNC_TOKEN_USER_1); cmdq_append_command(hReqB, CMDQ_CODE_EOC, 0, 1, 0, 0); cmdq_append_command(hReqB, CMDQ_CODE_JUMP, 0, 8, 0, 0); ret = _test_flush_async(hReqA); ret = _test_flush_async(hReqB); CMDQ_MSG("%s start wait sleep========\n", __func__); msleep_interruptible(500); CMDQ_MSG("%s start wait A========\n", __func__); ret = cmdq_pkt_wait_flush_ex_result(hReqA); CMDQ_MSG("%s start wait B, this should timeout========\n", __func__); ret = cmdq_pkt_wait_flush_ex_result(hReqB); CMDQ_MSG("%s wait B get %d ========\n", __func__, ret); } while (0); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_1); cmdq_task_destroy(hReqA); cmdq_task_destroy(hReqB); del_timer(&cmdq_treqa.test_timer); del_timer(&cmdq_treqb.test_timer); CMDQ_LOG("%s END\n", __func__); } static void testcase_multiple_async_request(void) { #define TEST_REQ_COUNT_ASYNC 9 struct cmdqRecStruct *hReq[TEST_REQ_COUNT_ASYNC] = { 0 }; s32 ret = 0; int i; CMDQ_LOG("%s\n", __func__); test_timer_stop = false; cmdq_ttm.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_ttm.test_timer, _testcase_sync_token_timer_loop_func, 0); mod_timer(&cmdq_ttm.test_timer, jiffies + msecs_to_jiffies(10)); /* Queue multiple async request */ /* to test dynamic task allocation */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); for (i = 0; i < TEST_REQ_COUNT_ASYNC; i++) { ret = cmdq_task_create(CMDQ_SCENARIO_DEBUG, &hReq[i]); if (ret < 0) { CMDQ_ERR("%s cmdq_task_create failed:%d i:%d\n ", __func__, ret, i); continue; } cmdq_task_reset(hReq[i]); cmdq_task_set_secure(hReq[i], gCmdqTestSecure); /* specify engine flag in order to dispatch all tasks to the * same HW thread */ hReq[i]->engineFlag = (1LL << CMDQ_ENG_MDP_CAMIN); cmdq_op_wait(hReq[i], CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(hReq[i], false); /* higher priority for later tasks */ hReq[i]->pkt->priority = i; _test_flush_async(hReq[i]); CMDQ_LOG("create pkt:%2d 0x%p done thread:%d\n", i, hReq[i]->pkt, hReq[i]->thread); } /* release token and wait them */ for (i = 0; i < TEST_REQ_COUNT_ASYNC; i++) { if (hReq[i] == NULL) { CMDQ_ERR("%s handle[%d] is NULL\n", __func__, i); continue; } msleep_interruptible(100); CMDQ_LOG("======== wait task[%2d] 0x%p ========\n", i, hReq[i]); ret = cmdq_pkt_wait_flush_ex_result(hReq[i]); cmdq_task_destroy(hReq[i]); } /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); test_timer_stop = true; del_timer(&cmdq_ttm.test_timer); CMDQ_LOG("%s END\n", __func__); } static void testcase_async_request_partial_engine(void) { s32 ret = 0; int i; enum CMDQ_SCENARIO_ENUM scn[] = { CMDQ_SCENARIO_PRIMARY_DISP, CMDQ_SCENARIO_JPEG_DEC, CMDQ_SCENARIO_PRIMARY_MEMOUT, CMDQ_SCENARIO_SUB_DISP, CMDQ_SCENARIO_DEBUG, }; struct cmdqRecStruct *handles[ARRAY_SIZE(scn)] = { 0 }; struct cmdq_test_timer *timers; timers = kmalloc_array(ARRAY_SIZE(scn), sizeof(*timers), GFP_ATOMIC); if (!timers) return; CMDQ_LOG("%s\n", __func__); /* setup timer to trigger sync token */ for (i = 0; i < ARRAY_SIZE(scn); i++) { timers[i].event = CMDQ_SYNC_TOKEN_USER_0 + i; timer_setup(&timers[i].test_timer, _testcase_sync_token_timer_func, 0); mod_timer(&timers[i].test_timer, jiffies + msecs_to_jiffies(50 * (1 + i))); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0 + i); cmdq_task_create(scn[i], &handles[i]); cmdq_task_reset(handles[i]); cmdq_task_set_secure(handles[i], false); cmdq_op_wait(handles[i], CMDQ_SYNC_TOKEN_USER_0 + i); cmdq_op_finalize_command(handles[i], false); CMDQ_LOG("TEST: SUBMIT scneario:%d thread:%d\n", scn[i], handles[i]->thread); ret = _test_flush_task_async(handles[i]); if (ret) { CMDQ_LOG("[warn] handle:%d flush failed, ret:%d\n", i, ret); cmdq_task_destroy(handles[i]); handles[i] = NULL; continue; } } /* wait for task completion */ for (i = 0; i < ARRAY_SIZE(scn); i++) { if (!handles[i]) continue; ret = _test_wait_task(handles[i]); cmdq_task_destroy(handles[i]); } /* clear token */ for (i = 0; i < ARRAY_SIZE(scn); i++) { CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0 + i); del_timer(&timers[i].test_timer); } if (timers != NULL) { kfree(timers); timers = NULL; } CMDQ_LOG("%s END\n", __func__); } static void _testcase_unlock_all_event_timer_func(struct timer_list *t) { u32 token = 0; CMDQ_LOG("%s\n", __func__); /* trigger sync event */ CMDQ_MSG("trigger events\n"); for (token = 0; token < CMDQ_SYNC_TOKEN_MAX; ++token) { /* 3 threads waiting, so update 3 times */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | token); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | token); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | token); } } static void testcase_sync_token_threaded(void) { enum CMDQ_SCENARIO_ENUM scn[] = { CMDQ_SCENARIO_PRIMARY_DISP, /* high prio */ CMDQ_SCENARIO_JPEG_DEC, /* normal prio */ CMDQ_SCENARIO_TRIGGER_LOOP /* normal prio */ }; s32 ret = 0; int i = 0; u32 token = 0; struct cmdq_test_timer timers[ARRAY_SIZE(scn)]; struct cmdqRecStruct *handles[ARRAY_SIZE(scn)] = { 0 }; CMDQ_LOG("%s\n", __func__); /* setup timer to trigger sync token */ for (i = 0; i < ARRAY_SIZE(scn); i++) { timer_setup(&timers[i].test_timer, _testcase_unlock_all_event_timer_func, 0); mod_timer(&timers[i].test_timer, jiffies + msecs_to_jiffies(500)); /* */ /* 3 threads, all wait & clear 511 events */ /* */ cmdq_task_create(scn[i], &handles[i]); cmdq_task_reset(handles[i]); cmdq_task_set_secure(handles[i], false); for (token = 0; token < CMDQ_SYNC_TOKEN_MAX; ++token) cmdq_op_wait(handles[i], (enum cmdq_event) token); cmdq_op_finalize_command(handles[i], false); CMDQ_MSG("TEST: SUBMIT scneario %d\n", scn[i]); ret = _test_flush_async(handles[i]); } /* wait for task completion */ msleep_interruptible(1000); for (i = 0; i < ARRAY_SIZE(scn); i++) ret = cmdq_pkt_wait_flush_ex_result(handles[i]); /* clear token */ for (i = 0; i < ARRAY_SIZE(scn); ++i) { cmdq_task_destroy(handles[i]); del_timer(&timers[i].test_timer); } CMDQ_LOG("%s END\n", __func__); } static struct cmdq_test_timer cmdq_tltm; static int g_loopIter; static struct cmdqRecStruct *hLoopReq; static void _testcase_loop_timer_func(struct timer_list *t) { struct cmdq_test_timer *tm = from_timer(tm, t, test_timer); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | tm->event); mod_timer(&tm->test_timer, jiffies + msecs_to_jiffies(300)); g_loopIter++; } static void testcase_loop(void) { int status = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_TRIGGER_LOOP, &hLoopReq); cmdq_task_reset(hLoopReq); cmdq_task_set_secure(hLoopReq, false); cmdq_op_wait(hLoopReq, CMDQ_SYNC_TOKEN_USER_0); cmdq_tltm.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_tltm.test_timer, _testcase_loop_timer_func, 0); mod_timer(&cmdq_tltm.test_timer, jiffies + msecs_to_jiffies(300)); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); g_loopIter = 0; /* should success */ status = cmdq_task_start_loop(hLoopReq); /* should fail because already started */ CMDQ_LOG("%s start loop thread:%d handle:0x%p pkt:0x%p\n", __func__, hLoopReq->thread, hLoopReq, hLoopReq->pkt); status = cmdq_task_start_loop(hLoopReq); cmdq_pkt_dump_command(hLoopReq); /* WAIT */ while (g_loopIter < 20) msleep_interruptible(2000); msleep_interruptible(2000); CMDQ_LOG("%s stop timer\n", __func__); cmdq_task_destroy(hLoopReq); del_timer(&cmdq_tltm.test_timer); CMDQ_LOG("%s end\n", __func__); } static unsigned long gLoopCount; static void _testcase_trigger_func(struct timer_list *t) { /* trigger sync event */ CMDQ_MSG("_testcase_trigger_func"); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | CMDQ_SYNC_TOKEN_USER_0); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, (1L << 16) | CMDQ_SYNC_TOKEN_USER_1); if (test_timer_stop) { del_timer(&cmdq_ttm.test_timer); return; } /* start again */ mod_timer(&cmdq_ttm.test_timer, jiffies + msecs_to_jiffies(1000)); gLoopCount++; } static void testcase_trigger_thread(void) { struct cmdqRecStruct *trigger; struct cmdqRecStruct *config; s32 ret = 0; int index = 0; CMDQ_LOG("%s\n", __func__); /* setup timer to trigger sync token for every 1 sec */ test_timer_stop = false; timer_setup(&cmdq_ttm.test_timer, _testcase_trigger_func, 0); mod_timer(&cmdq_ttm.test_timer, jiffies + msecs_to_jiffies(1000)); /* THREAD 1, trigger loop */ cmdq_task_create(CMDQ_SCENARIO_TRIGGER_LOOP, &trigger); cmdq_task_reset(trigger); /* WAIT and CLEAR config dirty */ /* cmdq_op_wait(trigger, CMDQ_SYNC_TOKEN_CONFIG_DIRTY); */ /* WAIT and CLEAR TE */ /* cmdq_op_wait(trigger, CMDQ_EVENT_MDP_DSI0_TE_SOF); */ /* WAIT and CLEAR stream done */ /* cmdq_op_wait(trigger, CMDQ_EVENT_MUTEX0_STREAM_EOF); */ /* WRITE mutex enable */ /* cmdq_op_wait(trigger, MM_MUTEX_BASE + 0x20); */ cmdq_op_wait(trigger, CMDQ_SYNC_TOKEN_USER_0); /* RUN forever but each IRQ trigger is bypass * to my_irq_callback */ ret = cmdq_task_start_loop(trigger); /* THREAD 2, config thread */ cmdq_task_create(CMDQ_SCENARIO_JPEG_DEC, &config); cmdq_task_reset(config); config->pkt->priority = 1; /* insert tons of instructions */ for (index = 0; index < 10; index++) cmdq_append_command(config, CMDQ_CODE_MOVE, 0, 0x1, 0, 0); ret = cmdq_task_flush(config); CMDQ_MSG("flush 0\n"); cmdq_task_reset(config); config->pkt->priority = 3; /* insert tons of instructions */ for (index = 0; index < 10; index++) cmdq_append_command(config, CMDQ_CODE_MOVE, 0, 0x1, 0, 0); ret = cmdq_task_flush(config); CMDQ_MSG("flush 1\n"); cmdq_task_reset(config); config->pkt->priority = 3; /* insert tons of instructions */ for (index = 0; index < 500; index++) cmdq_append_command(config, CMDQ_CODE_MOVE, 0, 0x1, 0, 0); ret = cmdq_task_flush(config); CMDQ_MSG("flush 2\n"); /* WAIT */ while (gLoopCount < 20) msleep_interruptible(2000); test_timer_stop = true; del_timer(&cmdq_ttm.test_timer); cmdq_task_destroy(trigger); cmdq_task_destroy(config); CMDQ_LOG("%s END\n", __func__); } static void testcase_prefetch_scenarios(void) { /* make sure both prefetch and non-prefetch cases */ /* handle 248+ instructions properly */ struct cmdqRecStruct *hConfig; s32 ret = 0; int index = 0, scn = 0; const int INSTRUCTION_COUNT = 500; CMDQ_LOG("%s\n", __func__); /* make sure each scenario runs properly with 248+ commands */ for (scn = 0; scn < CMDQ_MAX_SCENARIO_COUNT; ++scn) { if (cmdq_mdp_is_request_from_user_space(scn) || scn == CMDQ_SCENARIO_TIMER_LOOP) continue; CMDQ_MSG("%s scenario:%d\n", __func__, scn); cmdq_task_create((enum CMDQ_SCENARIO_ENUM) scn, &hConfig); cmdq_task_reset(hConfig); /* insert tons of instructions */ for (index = 0; index < INSTRUCTION_COUNT; ++index) cmdq_append_command(hConfig, CMDQ_CODE_MOVE, 0, 0x1, 0, 0); ret = _test_flush_task(hConfig); cmdq_task_destroy(hConfig); } CMDQ_LOG("%s END\n", __func__); } void testcase_clkmgr_impl(enum CMDQ_ENG_ENUM engine, char *name, const unsigned long testWriteReg, const u32 testWriteValue, const unsigned long testReadReg, const bool verifyWriteResult) { /* clkmgr is not available on FPGA */ #ifndef CONFIG_FPGA_EARLY_PORTING u32 value = 0; CMDQ_MSG("====== %s:%s ======\n", __func__, name); CMDQ_VERBOSE("clk engine:%d name:%s\n", engine, name); CMDQ_VERBOSE( "write reg(0x%lx) to 0x%08x read reg(0x%lx) verify write result:%d\n", testWriteReg, testWriteValue, testReadReg, verifyWriteResult); if ((testWriteReg & 0xFFFFF000) == 0 || (testReadReg & 0xFFFFF000) == 0) { CMDQ_TEST_FAIL("%s: invalid write reg:%08lx read reg:%08lx\n", name, testWriteReg, testReadReg); return; } /* turn on CLK, function should work */ CMDQ_MSG("enable_clock\n"); if (engine == CMDQ_ENG_CMDQ) { /* Turn on CMDQ engine */ cmdq_dev_enable_gce_clock(true); } else { /* Turn on MDP engines */ #ifdef CMDQ_CONFIG_SMI smi_bus_prepare_enable(SMI_LARB0, "CMDQ"); #endif cmdq_mdp_get_func()->enableMdpClock(true, engine); } CMDQ_REG_SET32(testWriteReg, testWriteValue); value = CMDQ_REG_GET32(testReadReg); if (verifyWriteResult && testWriteValue != value) { CMDQ_TEST_FAIL("%s: when enable clock reg(0x%lx) = 0x%08x\n", name, testReadReg, value); /* BUG(); */ } /* turn off CLK, function should not work and access register should * not cause hang */ CMDQ_MSG("disable_clock\n"); if (engine == CMDQ_ENG_CMDQ) { /* Turn on CMDQ engine */ cmdq_dev_enable_gce_clock(false); } else { /* Turn on MDP engines */ #ifdef CMDQ_CONFIG_SMI smi_bus_disable_unprepare(SMI_LARB0, "CMDQ"); #endif cmdq_mdp_get_func()->enableMdpClock(false, engine); } CMDQ_REG_SET32(testWriteReg, testWriteValue); value = CMDQ_REG_GET32(testReadReg); if (value != 0) { CMDQ_TEST_FAIL("%s: when disable clock reg(0x%lx) = 0x%08x\n", name, testReadReg, value); /* BUG(); */ } #endif } static void testcase_clkmgr(void) { CMDQ_LOG("%s\n", __func__); #ifdef CMDQ_PWR_AWARE testcase_clkmgr_impl(CMDQ_ENG_CMDQ, "CMDQ_TEST", CMDQ_GPR_R32(CMDQ_DATA_REG_DEBUG), 0xFFFFDEAD, CMDQ_GPR_R32(CMDQ_DATA_REG_DEBUG), true); cmdq_mdp_get_func()->testcaseClkmgrMdp(); #endif /* defined(CMDQ_PWR_AWARE) */ CMDQ_LOG("%s END\n", __func__); } static void testcase_dram_access(void) { struct cmdqRecStruct *handle = NULL; dma_addr_t result_pa, dst_pa; u8 code, sop; u16 arg_a, arg_b, arg_c; u32 *result_va, pa; unsigned long long data64; CMDQ_LOG("%s\n", __func__); result_va = cmdq_core_alloc_hw_buffer(cmdq_dev_get(), sizeof(u32) * 2, &result_pa, GFP_KERNEL); if (!result_va) return; /* set up intput */ result_va[0] = 0xdeaddead; /* this is read-from */ result_va[1] = 0xffffffff; /* this is write-to */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* READ from DRAME: register to read from * note that we force convert to physical reg address. * if it is already physical address, it won't be affected * (at least on this platform) */ CMDQ_MSG("reg pa:%pa size:%zu\n", &result_pa, handle->pkt->cmd_buf_size); /* Move &(regResults[0]) to CMDQ_DATA_REG_DEBUG_DST */ pa = (u32)CMDQ_PHYS_TO_AREG(result_pa); arg_c = pa & 0xffff; arg_b = pa >> 16; #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT arg_a = ((result_pa >> 32) & 0xffff); #else arg_a = 0; #endif sop = CMDQ_DATA_REG_DEBUG_DST & 0x1f; code = CMDQ_CODE_MOVE; cmdq_pkt_append_command(handle->pkt, arg_c, arg_b, arg_a, sop, 0, 0, 1, code); /* WRITE to DRAME: * from src_addr(CMDQ_DATA_REG_DEBUG_DST) to external RAM * (regResults[1]) */ /* Read data from *CMDQ_DATA_REG_DEBUG_DST to CMDQ_DATA_REG_DEBUG */ arg_c = CMDQ_DATA_REG_DEBUG; arg_b = 0; arg_a = 0; sop = CMDQ_DATA_REG_DEBUG_DST; code = CMDQ_CODE_READ; cmdq_pkt_append_command(handle->pkt, arg_c, arg_b, arg_a, sop, 0, 1, 1, code); /* Load dst_addr to GPR: Move &(regResults[1]) to * CMDQ_DATA_REG_DEBUG_DST */ dst_pa = result_pa + 4; /* note regResults is a u32 array */ arg_c = (u16)dst_pa; arg_b = (u16)(dst_pa >> 16); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT arg_a = ((dst_pa >> 32) & 0xffff); #else arg_a = 0; #endif sop = CMDQ_DATA_REG_DEBUG_DST & 0x1f; code = CMDQ_CODE_MOVE; cmdq_pkt_append_command(handle->pkt, arg_c, arg_b, arg_a, sop, 0, 0, 1, code); /* Write from CMDQ_DATA_REG_DEBUG to *CMDQ_DATA_REG_DEBUG_DST */ arg_c = CMDQ_DATA_REG_DEBUG; arg_b = 0; arg_a = 0; sop = CMDQ_DATA_REG_DEBUG_DST & 0x1f; code = CMDQ_CODE_WRITE; cmdq_pkt_append_command(handle->pkt, arg_c, arg_b, arg_a, sop, 0, 1, 1, code); cmdq_task_flush(handle); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); data64 = 0LL; data64 = CMDQ_REG_GET64_GPR_PX(CMDQ_DATA_REG_DEBUG_DST); if (result_va[1] != result_va[0]) { /* Test DRAM access fail */ CMDQ_TEST_FAIL( "results:0x%08x 0x%08x reg debug:0x%08x reg debug dst:0x%llx\n", result_va[0], result_va[1], CMDQ_REG_GET32(CMDQ_GPR_R32(CMDQ_DATA_REG_DEBUG)), data64); } else { /* Test DRAM access success */ CMDQ_MSG( "success results:0x%08x 0x%08x reg debug:0x%08x reg debug dst:0x%llx\n", result_va[0], result_va[1], CMDQ_REG_GET32(CMDQ_GPR_R32(CMDQ_DATA_REG_DEBUG)), data64); } cmdq_core_free_hw_buffer(cmdq_dev_get(), 2 * sizeof(u32), result_va, result_pa); CMDQ_LOG("%s END\n", __func__); } static void testcase_long_command(void) { int i; struct cmdqRecStruct *handle = NULL; u32 data; u32 pattern = 0x0; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* build a 64K instruction buffer */ for (i = 0; i < 64 * 1024 / 8; i++) { pattern = i; cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, pattern, ~0); } CMDQ_LOG("handle:0x%p buf size:%zu size:%zu avail:%zu\n", handle, handle->pkt->cmd_buf_size, handle->pkt->buf_size, handle->pkt->avail_buf_size); _test_flush_task(handle); CMDQ_LOG("handle:0x%p buf size:%zu size:%zu avail:%zu\n", handle, handle->pkt->cmd_buf_size, handle->pkt->buf_size, handle->pkt->avail_buf_size); /* verify data */ do { if (gCmdqTestSecure) { CMDQ_LOG("%s, timeout case in secure path\n", __func__); break; } data = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (pattern != data) { CMDQ_TEST_FAIL( "reg value is 0x%08x not pattern 0x%08x\n", data, pattern); cmdq_core_dump_handle_buffer(handle->pkt, "INFO"); } } while (0); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_perisys_apb(void) { /* write value to PERISYS register */ /* we use MSDC debug to test: */ /* write SEL, read OUT. */ const u32 MSDC_SW_DBG_OUT_OFFSET = 0xa4; const u32 AUDIO_AFE_I2S_CON3_OFFSET = 0x4c; const u32 UAR0_OFFSET = 0xbc; const phys_addr_t MSDC_PA_START = cmdq_dev_get_reference_PA("msdc0", 0); const phys_addr_t AUDIO_TOP_CONF0_PA = cmdq_dev_get_reference_PA( "audio", 0); const phys_addr_t UART0_PA_BASE = cmdq_dev_get_reference_PA("uart0", 0); const unsigned long MSDC_SW_DBG_SEL_PA = MSDC_PA_START + 0xa0; const unsigned long MSDC_SW_DBG_OUT_PA = MSDC_PA_START + MSDC_SW_DBG_OUT_OFFSET; const phys_addr_t AUDIO_PA = AUDIO_TOP_CONF0_PA + AUDIO_AFE_I2S_CON3_OFFSET; const phys_addr_t UART0_PA = UART0_PA_BASE + UAR0_OFFSET; const unsigned long MSDC_VA_BASE = cmdq_dev_alloc_reference_VA_by_name("msdc0"); const unsigned long AUDIO_VA_BASE = cmdq_dev_alloc_reference_VA_by_name("audio"); const unsigned long UART0_VA_BASE = cmdq_dev_alloc_reference_VA_by_name("uart0"); const unsigned long MSDC_SW_DBG_OUT = MSDC_VA_BASE + MSDC_SW_DBG_OUT_OFFSET; const unsigned long AUDIO_VA = AUDIO_VA_BASE + AUDIO_AFE_I2S_CON3_OFFSET; const unsigned long UAR0_BUS_VA = UART0_VA_BASE + UAR0_OFFSET; const u32 write_pattern = 0xaabbccdd; struct cmdqRecStruct *handle = NULL; u32 data = 0; u32 dataRead = 0; CMDQ_LOG("%s\n", __func__); CMDQ_LOG("MSDC_VA_BASE: VA:0x%lx, PA:%pa\n", MSDC_VA_BASE, &MSDC_PA_START); CMDQ_LOG("AUDIO_VA_BASE: VA:0x%lx, PA:%pa\n", AUDIO_VA_BASE, &AUDIO_TOP_CONF0_PA); CMDQ_LOG("UART0: VA:0x%lx, PA:%pa\n", UART0_VA_BASE, &UART0_PA_BASE); if (!MSDC_PA_START || !AUDIO_TOP_CONF0_PA || !UART0_PA_BASE) { CMDQ_TEST_FAIL("msdc or audio node does not porting.\n"); return; } if (cmdq_core_subsys_from_phys_addr(MSDC_PA_START) < 0) cmdq_core_set_addon_subsys(MSDC_PA_START & 0xffff0000, 99, 0xffff0000); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_write_reg(handle, MSDC_SW_DBG_SEL_PA, 1, ~0); cmdq_pkt_dump_command(handle); cmdq_task_flush(handle); /* verify data */ data = CMDQ_REG_GET32(MSDC_SW_DBG_OUT); if (data != ~0) { /* MSDC_SW_DBG_OUT would not same as sel setting */ CMDQ_MSG("write 0xFFFFFFFF to MSDC_SW_DBG_OUT = 0x%08x=====\n", data); CMDQ_MSG("MSDC_SW_DBG_OUT: PA(%pa) VA(0x%lx) =====\n", &MSDC_SW_DBG_OUT_PA, MSDC_SW_DBG_OUT); } /* test read from AP_DMA_GLOBAL_SLOW_DOWN to CMDQ GPR */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_read_to_data_register(handle, MSDC_SW_DBG_OUT_PA, CMDQ_DATA_REG_PQ_COLOR); cmdq_task_flush(handle); /* verify data */ dataRead = CMDQ_REG_GET32(CMDQ_GPR_R32(CMDQ_DATA_REG_PQ_COLOR)); if (data != dataRead || data == 0) { /* test fail */ CMDQ_TEST_FAIL( "CMDQ_DATA_REG_PQ_COLOR:0x%08x should be:0x%08x\n", dataRead, data); CMDQ_ERR("MSDC_SW_DBG_OUT: PA(%pa) VA(0x%lx) =====\n", &MSDC_SW_DBG_OUT_PA, MSDC_SW_DBG_OUT); } else { /* also log success */ CMDQ_LOG("TEST SUCCESS: MSDC_SW_DBG_OUT 0x%08x == 0x%08x\n", dataRead, data); } if (cmdq_core_subsys_from_phys_addr(AUDIO_PA) < 0) cmdq_core_set_addon_subsys(AUDIO_PA & 0xffff0000, 99, 0xffff0000); CMDQ_REG_SET32(AUDIO_VA, write_pattern); data = CMDQ_REG_GET32(AUDIO_VA); if (data != write_pattern) { CMDQ_TEST_FAIL("write 0x%08x to AUDIO_VA result:0x%08x\n", write_pattern, data); CMDQ_ERR("AUDIO_PA: PA(%pa) VA(0x%lx) =====\n", &AUDIO_PA, AUDIO_VA); } else { CMDQ_LOG( "TEST SUCCESS: write 0x%08x to AUDIO_VA = 0x%08x=====\n", write_pattern, data); } CMDQ_REG_SET32(AUDIO_VA, 0); data = CMDQ_REG_GET32(AUDIO_VA); CMDQ_LOG("Before AUDIO_VA = 0x%08x=====\n", data); cmdq_task_reset(handle); cmdq_op_write_reg(handle, AUDIO_PA, write_pattern, ~0); cmdq_pkt_dump_command(handle); cmdq_task_flush(handle); /* verify data */ data = CMDQ_REG_GET32(AUDIO_VA); CMDQ_LOG("after AUDIO_VA = 0x%08x=====\n", data); if (data != write_pattern) { /* test fail */ CMDQ_TEST_FAIL("AUDIO_VA:0x%08x should be:0x%08x\n", data, write_pattern); CMDQ_ERR("AUDIO_VA: PA(%pa) VA(0x%lx) =====\n", &AUDIO_PA, AUDIO_VA); } else { /* also log success */ CMDQ_LOG("TEST SUCCESS: AUDIO_VA 0x%08x == 0x%08x\n", data, write_pattern); } if (cmdq_core_subsys_from_phys_addr(UART0_PA_BASE) < 0) cmdq_core_set_addon_subsys(UART0_PA_BASE & 0xffff0000, 99, 0xffff0000); CMDQ_REG_SET32(UAR0_BUS_VA, 1); data = CMDQ_REG_GET32(UAR0_BUS_VA); if ((data & 0x1) != 1) { CMDQ_TEST_FAIL("CPU: write 0x1 to UAR0_BUS_VA = 0x%08x=====\n", data); CMDQ_ERR("CPU: UAR0_BUS_VA: PA_BASE(%pa) VA(0x%lx) =====\n", &UART0_PA_BASE, UAR0_BUS_VA); } else { /* also log success */ CMDQ_LOG("TEST SUCCESS: UAR0_BUS_VA = 0x%08x\n", data); } CMDQ_REG_SET32(UAR0_BUS_VA, 0); data = CMDQ_REG_GET32(UAR0_BUS_VA); CMDQ_LOG("Before UAR0_BUS_VA = 0x%08x=====\n", data); cmdq_task_reset(handle); cmdq_op_write_reg(handle, UART0_PA, 0x1, ~0); cmdq_pkt_dump_command(handle); cmdq_task_flush(handle); /* verify data */ data = CMDQ_REG_GET32(UAR0_BUS_VA); CMDQ_LOG("after UAR0_BUS_VA = 0x%08x=====\n", data); if ((data & 0x1) != 1) { /* test fail */ CMDQ_TEST_FAIL("UAR0_BUS_VA:0x%08x should be:0x1\n", data); CMDQ_ERR("UAR0_BUS_VA: PA_BASE(%pa) VA(0x%lx) =====\n", &UART0_PA_BASE, UAR0_BUS_VA); } else { /* also log success */ CMDQ_LOG("TEST SUCCESS: UAR0_BUS_VA = 0x%08x\n", data); } cmdq_task_destroy(handle); /* release registers map */ cmdq_dev_free_module_base_VA(MSDC_VA_BASE); cmdq_dev_free_module_base_VA(AUDIO_VA_BASE); cmdq_dev_free_module_base_VA(UART0_VA_BASE); CMDQ_LOG("%s END\n", __func__); } static void testcase_write_address(void) { dma_addr_t pa = 0; u32 value = 0; CMDQ_LOG("%s\n", __func__); cmdqCoreAllocWriteAddress(3, &pa, CMDQ_CLT_UNKN); CMDQ_LOG("ALLOC:%pa\n", &pa); value = cmdqCoreReadWriteAddress(pa); CMDQ_LOG("value 0:0x%08x\n", value); value = cmdqCoreReadWriteAddress(pa + 1); CMDQ_LOG("value 1:0x%08x\n", value); value = cmdqCoreReadWriteAddress(pa + 2); CMDQ_LOG("value 2:0x%08x\n", value); value = cmdqCoreReadWriteAddress(pa + 3); CMDQ_LOG("value 3:0x%08x\n", value); value = cmdqCoreReadWriteAddress(pa + 4); CMDQ_LOG("value 4:0x%08x\n", value); value = cmdqCoreReadWriteAddress(pa + (4 * 20)); CMDQ_LOG("value 80:0x%08x\n", value); /* free invalid start address fist to verify error handle */ CMDQ_LOG("cmdqCoreFreeWriteAddress, pa:0, it's a error case\n"); cmdqCoreFreeWriteAddress(0, CMDQ_CLT_UNKN); /* ok case */ CMDQ_LOG("cmdqCoreFreeWriteAddress, pa:%pa it's a ok case\n", &pa); cmdqCoreFreeWriteAddress(pa, CMDQ_CLT_UNKN); CMDQ_LOG("%s END\n", __func__); } static void testcase_write_from_data_reg(void) { struct cmdqRecStruct *handle = NULL; u32 value; const u32 PATTERN = 0xFFFFDEAD; const u32 srcGprId = CMDQ_DATA_REG_DEBUG; u32 dstRegPA; unsigned long dummy_va; CMDQ_LOG("%s\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dstRegPA = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dstRegPA = CMDQ_TEST_GCE_DUMMY_PA; } /* clean dst register value */ CMDQ_REG_SET32((void *)dummy_va, 0x0); /* init GPR as value 0xFFFFDEAD */ CMDQ_REG_SET32(CMDQ_GPR_R32(srcGprId), PATTERN); value = CMDQ_REG_GET32(CMDQ_GPR_R32(srcGprId)); if (value != PATTERN) { CMDQ_ERR( "init CMDQ_DATA_REG_DEBUG to 0x%08x failed, value:0x%08x\n", PATTERN, value); } /* write GPR data reg to hw register */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_write_from_data_register(handle, srcGprId, dstRegPA); cmdq_task_flush(handle); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); /* verify */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != PATTERN) { CMDQ_ERR("%s failed, dstReg value is not 0x%08x value:0x%08x\n", __func__, PATTERN, value); } CMDQ_LOG("%s END\n", __func__); } static void testcase_read_to_data_reg(void) { #ifdef CMDQ_GPR_SUPPORT struct cmdqRecStruct *handle = NULL; u32 data; unsigned long long data64; CMDQ_LOG("%s\n", __func__); /* init GPR 64 */ CMDQ_REG_SET64_GPR_PX(CMDQ_DATA_REG_PQ_COLOR_DST, 0x1234567890ABCDEFULL); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); /* R4 */ CMDQ_REG_SET32(CMDQ_GPR_R32(CMDQ_DATA_REG_PQ_COLOR), 0xbeefbeef); /* R5 */ CMDQ_REG_SET32(CMDQ_GPR_R32(CMDQ_DATA_REG_2D_SHARPNESS_0), 0x0); cmdq_get_func()->dumpGPR(); /* [read 64 bit test] move data from GPR to GPR_Px: COLOR to * COLOR_DST (64 bit) */ cmdq_op_read_to_data_register(handle, CMDQ_GPR_R32_PA(CMDQ_DATA_REG_PQ_COLOR), CMDQ_DATA_REG_PQ_COLOR_DST); /* [read 32 bit test] move data from register value to * GPR_Rx: MM_DUMMY_REG to COLOR(32 bit) */ cmdq_op_read_to_data_register(handle, CMDQ_TEST_GCE_DUMMY_PA, CMDQ_DATA_REG_PQ_COLOR); cmdq_task_flush(handle); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); cmdq_get_func()->dumpGPR(); /* verify data */ data = CMDQ_REG_GET32(CMDQ_GPR_R32(CMDQ_DATA_REG_PQ_COLOR)); if (data != 0xdeaddead) { /* Print error status */ CMDQ_ERR( "[Read 32 bit from GPR_Rx]TEST FAIL: PQ reg value is 0x%08x\n", data); } data64 = 0LL; data64 = CMDQ_REG_GET64_GPR_PX(CMDQ_DATA_REG_PQ_COLOR_DST); if (data64 != 0xbeefbeef) { CMDQ_ERR( "[Read 64 bit from GPR_Px]TEST FAIL: PQ_DST reg value is 0x%llx\n", data64); } CMDQ_LOG("%s END\n", __func__); return; #else CMDQ_ERR("func:%s failed since CMDQ doesn't support GPR\n", __func__); return; #endif } static void testcase_write_reg_from_slot(void) { const u32 PATTEN = 0xBCBCBCBC; struct cmdqRecStruct *handle = NULL; cmdqBackupSlotHandle hSlot = 0; u32 value = 0; long long value64 = 0LL; const enum cmdq_gpr_reg dstRegId = CMDQ_DATA_REG_DEBUG; const enum cmdq_gpr_reg srcRegId = CMDQ_DATA_REG_DEBUG_DST; CMDQ_LOG("%s\n", __func__); /* init */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); CMDQ_REG_SET32(CMDQ_GPR_R32(dstRegId), 0xdeaddead); CMDQ_REG_SET64_GPR_PX(srcRegId, 0xdeaddeaddeaddead); cmdq_alloc_mem(&hSlot, 1); cmdq_cpu_write_mem(hSlot, 0, PATTEN); cmdq_cpu_read_mem(hSlot, 0, &value); if (value != PATTEN) { /* Print error status */ CMDQ_ERR("%s, slot init failed\n", __func__); } /* Create cmdqRec */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); /* Reset command buffer */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* Insert commands to write register with slot's value */ cmdq_op_read_mem_to_reg(handle, hSlot, 0, CMDQ_TEST_GCE_DUMMY_PA); /* Execute commands */ cmdq_task_flush(handle); /* debug dump command instructions */ cmdq_pkt_dump_command(handle); /* we can destroy cmdqRec handle after flush. */ cmdq_task_destroy(handle); /* verify */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTEN) { /* Print error status */ CMDQ_ERR("%s failed, value:0x%x\n", __func__, value); } value = CMDQ_REG_GET32(CMDQ_GPR_R32(dstRegId)); value64 = CMDQ_REG_GET64_GPR_PX(srcRegId); CMDQ_LOG("srcGPR(%x):0x%llx\n", srcRegId, value64); CMDQ_LOG("dstGPR(%x):0x%08x\n", dstRegId, value); /* release result free slot */ cmdq_free_mem(hSlot); CMDQ_LOG("%s END\n", __func__); return; } static void testcase_backup_reg_to_slot(void) { #ifdef CMDQ_GPR_SUPPORT struct cmdqRecStruct *handle = NULL; cmdqBackupSlotHandle hSlot = 0; int i; u32 value = 0; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); /* Create cmdqRec */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); /* Create Slot */ cmdq_alloc_mem(&hSlot, 5); for (i = 0; i < 5; ++i) cmdq_cpu_write_mem(hSlot, i, i); for (i = 0; i < 5; i++) { cmdq_cpu_read_mem(hSlot, i, &value); if (value != i) { /* Print error status */ CMDQ_ERR( "testcase_cmdqBackupWriteSlot FAILED!!!!!\n"); } CMDQ_LOG("testcase_cmdqBackupWriteSlot OK!!!!!\n"); } /* Reset command buffer */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* Insert commands to backup registers */ for (i = 0; i < 5; ++i) cmdq_op_read_reg_to_mem(handle, hSlot, i, CMDQ_TEST_GCE_DUMMY_PA); /* Execute commands */ cmdq_task_flush(handle); /* debug dump command instructions */ cmdq_pkt_dump_command(handle); /* we can destroy cmdqRec handle after flush. */ cmdq_task_destroy(handle); /* verify data by reading it back from slot */ for (i = 0; i < 5; i++) { cmdq_cpu_read_mem(hSlot, i, &value); CMDQ_LOG("backup slot %d = 0x%08x\n", i, value); if (value != 0xdeaddead) { /* content error */ CMDQ_ERR("content error!!!!!!!!!!!!!!!!!!!!\n"); } } /* release result free slot */ cmdq_free_mem(hSlot); CMDQ_LOG("%s END\n", __func__); return; #else CMDQ_ERR("func:%s failed since CMDQ doesn't support GPR\n", __func__); return; #endif } static void testcase_update_value_to_slot(void) { s32 i; u32 value = 0; struct cmdqRecStruct *handle = NULL; cmdqBackupSlotHandle hSlot = 0; const u32 PATTERNS[] = { 0xDEAD0000, 0xDEAD0001, 0xDEAD0002, 0xDEAD0003, 0xDEAD0004 }; CMDQ_LOG("%s\n", __func__); /* Create Slot */ cmdq_alloc_mem(&hSlot, 5); /*use CMDQ to update slot value */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); for (i = 0; i < 5; ++i) cmdq_op_write_mem(handle, hSlot, i, PATTERNS[i]); cmdq_task_flush(handle); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); /* CPU verify value by reading it back from slot */ for (i = 0; i < 5; i++) { cmdq_cpu_read_mem(hSlot, i, &value); if (PATTERNS[i] != value) { CMDQ_ERR( "slot[%d] = 0x%08x...content error! It should be 0x%08x\n", i, value, PATTERNS[i]); } else { CMDQ_LOG("slot[%d] = 0x%08x\n", i, value); } } /* release result free slot */ cmdq_free_mem(hSlot); CMDQ_LOG("%s END\n", __func__); } static void testcase_poll_run(u32 poll_value, u32 poll_mask, bool use_mmsys_dummy) { struct cmdqRecStruct *handle = NULL; u32 value = 0; u32 dstRegPA; unsigned long dummy_va; if (gCmdqTestSecure || use_mmsys_dummy) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dstRegPA = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dstRegPA = CMDQ_TEST_GCE_DUMMY_PA; } CMDQ_LOG("%s\n", __func__); CMDQ_LOG("poll value is 0x%08x\n", poll_value); CMDQ_LOG("poll mask is 0x%08x\n", poll_mask); CMDQ_LOG("use_mmsys_dummy is %u\n", use_mmsys_dummy); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_poll(handle, dstRegPA, poll_value, poll_mask); cmdq_op_finalize_command(handle, false); _test_flush_async(handle); cmdq_pkt_dump_command(handle); /* Set MMSYS dummy register value after clock is on */ CMDQ_REG_SET32(dummy_va, poll_value); value = CMDQ_REG_GET32(dummy_va); CMDQ_LOG("target value is 0x%08x\n", value); cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_poll(void) { CMDQ_LOG("%s\n", __func__); testcase_poll_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, false); testcase_poll_run(0xdada1818, 0xFFFFFFFF, false); testcase_poll_run(0xdada1818 & 0x0000FF00, 0x0000FF00, false); testcase_poll_run(0x00001818, 0xFFFFFFFF, false); #ifndef CONFIG_FPGA_EARLY_PORTING testcase_poll_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, true); testcase_poll_run(0xdada1818, 0xFFFFFFFF, true); testcase_poll_run(0xdada1818 & 0x0000FF00, 0x0000FF00, true); testcase_poll_run(0x00001818, 0xFFFFFFFF, true); #endif CMDQ_LOG("%s END\n", __func__); } static void testcase_write_with_mask(void) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); const u32 MASK = (1 << 16); const u32 EXPECT_RESULT = PATTERN & MASK; u32 value = 0; unsigned long dummy_va, dummy_pa; CMDQ_LOG("%s\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } /* set to 0x0 */ CMDQ_REG_SET32((void *)dummy_va, 0x0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); #ifdef CMDQ_SECURE_PATH_SUPPORT cmdq_task_set_secure(handle, gCmdqTestSecure); if (!~gCmdqTestSecure) cmdq_task_set_mtee(handle, true); #endif cmdq_op_write_reg(handle, dummy_pa, PATTERN, MASK); cmdq_task_flush(handle); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != EXPECT_RESULT) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, EXPECT_RESULT); } CMDQ_LOG("%s END\n", __func__); } static void testcase_cross_4k_buffer(void) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; unsigned long dummy_va, dummy_pa; u32 i; CMDQ_LOG("%s\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } /* set to 0xFFFFFFFF */ CMDQ_REG_SET32((void *)dummy_va, ~0); CMDQ_LOG("set reg=%lx to val=%x\n", dummy_va, CMDQ_REG_GET32((void *)dummy_va)); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); for (i = 0; i < 500; i++) cmdq_op_write_reg(handle, dummy_pa, PATTERN, ~0); cmdq_task_flush(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_write(void) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; unsigned long dummy_va, dummy_pa; CMDQ_LOG("%s\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } /* set to 0xFFFFFFFF */ CMDQ_REG_SET32((void *)dummy_va, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); #ifdef CMDQ_SECURE_PATH_SUPPORT cmdq_task_set_secure(handle, gCmdqTestSecure); if (!~gCmdqTestSecure) cmdq_task_set_mtee(handle, true); #endif cmdq_op_write_reg(handle, dummy_pa, PATTERN, ~0); cmdq_task_flush(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_prefetch(void) { struct cmdqRecStruct *handle = NULL; int i; u32 value = 0; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); /* 0xDEADDEAD; */ const u32 testRegPA = CMDQ_TEST_GCE_DUMMY_PA; const u32 REP_COUNT = 500; CMDQ_LOG("%s\n", __func__); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* No prefetch. */ /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); for (i = 0; i < REP_COUNT; ++i) cmdq_op_write_reg(handle, testRegPA, PATTERN, ~0); cmdq_task_flush_async(handle); cmdq_task_flush_async(handle); cmdq_task_flush_async(handle); msleep_interruptible(1000); /* use prefetch */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); for (i = 0; i < REP_COUNT; ++i) cmdq_op_write_reg(handle, testRegPA, PATTERN, ~0); cmdq_task_flush_async(handle); cmdq_task_flush_async(handle); cmdq_task_flush_async(handle); msleep_interruptible(1000); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_backup_register(void) { #ifdef CMDQ_GPR_SUPPORT struct cmdqRecStruct *handle = NULL; int ret = 0; u32 regAddr[3] = { CMDQ_TEST_GCE_DUMMY_PA, CMDQ_THR_CURR_ADDR_PA(5), CMDQ_THR_END_ADDR_PA(5) }; u32 regValue[3] = { 0 }; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xAAAAAAAA); CMDQ_REG_SET32(CMDQ_THR_CURR_ADDR(5), 0xBBBBBBBB); CMDQ_REG_SET32(CMDQ_THR_END_ADDR(5), 0xCCCCCCCC); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); ret = cmdq_task_flush_and_read_register(handle, 3, regAddr, regValue); cmdq_task_destroy(handle); if (regValue[0] != 0xAAAAAAAA) { /* Print error status */ CMDQ_ERR("regValue[0] is 0x%08x wrong!\n", regValue[0]); } if (regValue[1] != 0xBBBBBBBB) { /* Print error status */ CMDQ_ERR("regValue[1] is 0x%08x wrong!\n", regValue[1]); } if (regValue[2] != 0xCCCCCCCC) { /* Print error status */ CMDQ_ERR("regValue[2] is 0x%08x wrong!\n", regValue[2]); } CMDQ_LOG("%s END\n", __func__); #else CMDQ_ERR("func:%s failed since CMDQ doesn't support GPR\n", __func__); #endif } static void testcase_get_result(void) { struct cmdqRecStruct *handle = NULL; int ret = 0; struct cmdqCommandStruct desc = { 0 }; struct cmdq_pkt_buffer *buf; void *desc_buf; int registers[1] = { CMDQ_TEST_GCE_DUMMY_PA }; int result[1] = { 0 }; CMDQ_LOG("%s\n", __func__); /* make sure each scenario runs properly with empty commands */ /* use CMDQ_SCENARIO_PRIMARY_ALL to test */ /* because it has COLOR0 HW flag */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* insert dummy commands */ cmdq_op_finalize_command(handle, false); desc_buf = kzalloc(handle->pkt->cmd_buf_size, GFP_KERNEL); if (!desc_buf) { CMDQ_TEST_FAIL("fail to allocat desc buf size:%zu\n", handle->pkt->cmd_buf_size); cmdq_task_destroy(handle); return; } /* init desc attributes after finalize command to ensure correct * size and buffer addr */ desc.scenario = handle->scenario; desc.priority = handle->pkt->priority; desc.engineFlag = handle->engineFlag; desc.pVABase = (cmdqU32Ptr_t)(unsigned long)desc_buf; desc.blockSize = handle->pkt->cmd_buf_size; buf = list_first_entry(&handle->pkt->buf, typeof(*buf), list_entry); memcpy(desc_buf, buf->va_base, handle->pkt->cmd_buf_size); desc.regRequest.count = 1; desc.regRequest.regAddresses = (cmdqU32Ptr_t)(unsigned long)registers; desc.regValue.count = 1; desc.regValue.regValues = (cmdqU32Ptr_t)(unsigned long)result; desc.secData.is_secure = handle->secData.is_secure; desc.secData.addrMetadataCount = 0; desc.secData.addrMetadataMaxCount = 0; desc.secData.waitCookie = 0; desc.secData.resetExecCnt = false; cmdq_task_destroy(handle); handle = NULL; CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); CMDQ_LOG("flush mdp desc va:0x%p size:%u\n", (void *)(unsigned long)desc.pVABase, desc.blockSize); ret = cmdq_mdp_flush_async(&desc, false, &handle); if (ret) CMDQ_ERR("handle=%p flush failed, ret=%d\n", handle, ret); if (handle) { cmdq_pkt_dump_command(handle); ret = cmdq_mdp_wait(handle, &desc.regValue); if (ret) CMDQ_ERR( "handle=%p wait failed, ret=%d\n", handle, ret); } if (CMDQ_U32_PTR(desc.regValue.regValues)[0] != 0xdeaddead) { CMDQ_ERR("TEST FAIL: reg value is 0x%08x wait ret:%d\n", CMDQ_U32_PTR(desc.regValue.regValues)[0], ret); } cmdq_task_destroy(handle); kfree(desc_buf); CMDQ_LOG("%s END\n", __func__); } static int _testcase_simplest_command_loop_submit( const u32 loop, enum CMDQ_SCENARIO_ENUM scenario, const long long engineFlag, const bool isSecureTask) { struct cmdqRecStruct *handle = NULL; s32 i; CMDQ_LOG("%s\n", __func__); cmdq_task_create(scenario, &handle); for (i = 0; i < loop; i++) { CMDQ_MSG( "pid:%d flush:%4d, engineFlag:0x%llx isSecureTask:%d\n", current->pid, i, engineFlag, isSecureTask); cmdq_task_reset(handle); cmdq_task_set_secure(handle, isSecureTask); handle->engineFlag = engineFlag; _test_flush_task(handle); } cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); return 0; } /* threadfn: int (*threadfn)(void *data) */ static int _testcase_thread_dispatch(void *data) { long long engineFlag; engineFlag = *((long long *)data); _testcase_simplest_command_loop_submit(1000, CMDQ_SCENARIO_DEBUG_MDP, engineFlag, false); return 0; } static void testcase_thread_dispatch(void) { char threadName[20]; struct task_struct *pKThread1; struct task_struct *pKThread2; const long long engineFlag1 = (0x1 << CMDQ_ENG_MDP_RSZ0) | (0x1 << CMDQ_ENG_MDP_CAMIN); const long long engineFlag2 = (0x1 << CMDQ_ENG_MDP_RDMA0) | (0x1 << CMDQ_ENG_MDP_WROT0); int len; CMDQ_LOG("%s\n", __func__); CMDQ_MSG( "=============== 2 THREAD with different engines ===============\n"); len = sprintf(threadName, "cmdqKTHR_%llx", engineFlag1); if (len >= 20) pr_debug("%s:%d len:%d threadName:%s\n", __func__, __LINE__, len, threadName); pKThread1 = kthread_run(_testcase_thread_dispatch, (void *)(&engineFlag1), threadName); if (IS_ERR(pKThread1)) { CMDQ_ERR("create thread failed, thread:%s\n", threadName); return; } len = sprintf(threadName, "cmdqKTHR_%llx", engineFlag2); if (len >= 20) pr_debug("%s:%d len:%d threadName:%s\n", __func__, __LINE__, len, threadName); pKThread2 = kthread_run(_testcase_thread_dispatch, (void *)(&engineFlag2), threadName); if (IS_ERR(pKThread2)) { CMDQ_ERR("create thread failed, thread:%s\n", threadName); return; } msleep_interruptible(5 * 1000); /* ensure both thread execute all command */ _testcase_simplest_command_loop_submit(1, CMDQ_SCENARIO_DEBUG_MDP, engineFlag1, false); _testcase_simplest_command_loop_submit(1, CMDQ_SCENARIO_DEBUG_MDP, engineFlag2, false); CMDQ_LOG("%s END\n", __func__); } static int _testcase_full_thread_array(void *data) { /* this testcase will be passed only when cmdqSecDr support async * config mode because * never execute event setting till IWC back to NWd */ #define MAX_FULL_THREAD_TASK_COUNT 50 struct cmdqRecStruct *handle[MAX_FULL_THREAD_TASK_COUNT] = {0}; s32 i; /* clearn event first */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); for (i = 0; i < MAX_FULL_THREAD_TASK_COUNT; i++) { cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle[i]); /* specify engine flag in order to dispatch all tasks * to the same HW thread */ handle[i]->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_task_reset(handle[i]); cmdq_task_set_secure(handle[i], gCmdqTestSecure); cmdq_op_wait_no_clear(handle[i], CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle[i], false); CMDQ_LOG("pid:%d flush:%6d\n", current->pid, i); if (i == 40) { CMDQ_LOG("set token:%d to 1\n", CMDQ_SYNC_TOKEN_USER_0); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); } _test_flush_async(handle[i]); } for (i = 0; i < MAX_FULL_THREAD_TASK_COUNT; i++) { cmdq_pkt_wait_flush_ex_result(handle[i]); cmdq_task_destroy(handle[i]); } return 0; } static void testcase_full_thread_array(void) { char threadName[20]; struct task_struct *pKThread; int len; CMDQ_LOG("%s\n", __func__); len = sprintf(threadName, "cmdqKTHR"); if (len >= 20) pr_debug("%s:%d len:%d threadName:%s\n", __func__, __LINE__, len, threadName); pKThread = kthread_run(_testcase_full_thread_array, NULL, threadName); if (IS_ERR(pKThread)) { /* create thread failed */ CMDQ_ERR("create thread failed, thread:%s\n", threadName); } msleep_interruptible(5 * 1000); CMDQ_LOG("%s END\n", __func__); } static void testcase_module_full_dump(void) { struct cmdqRecStruct *handle = NULL; const bool alreadyEnableLog = cmdq_core_should_print_msg(); s32 status; CMDQ_LOG("%s\n", __func__); /* enable full dump */ if (!alreadyEnableLog) cmdq_core_set_log_level(1); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); /* clean SW token to invoke SW timeout latter */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); /* turn on ALL except DISP engine flag to test dump */ handle->engineFlag = ~(CMDQ_ENG_DISP_GROUP_BITS); CMDQ_LOG("%s, engine:0x%llx it's a timeout case\n", __func__, handle->engineFlag); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_wait_no_clear(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle, false); status = cmdq_mdp_flush_async_impl(handle); if (status < 0) CMDQ_ERR("flush failed handle:0x%p\n", handle); else status = cmdq_mdp_wait(handle, NULL); /* disable full dump */ if (!alreadyEnableLog) cmdq_core_set_log_level(0); CMDQ_LOG("%s END\n", __func__); } #ifdef CMDQ_SECURE_PATH_SUPPORT #include "cmdq_sec.h" #include "cmdq_sec_iwc_common.h" #include "cmdqsectl_api.h" s32 cmdq_sec_submit_to_secure_world_async_unlocked(u32 iwcCommand, struct cmdqRecStruct *handle, s32 thread, CmdqSecFillIwcCB iwcFillCB, void *data, const bool mtee); #endif void testcase_secure_basic(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT s32 status = 0; CMDQ_LOG("%s\n", __func__); do { CMDQ_MSG("=========== Hello cmdqSecTl ===========\n "); status = cmdq_sec_submit_to_secure_world_async_unlocked( CMD_CMDQ_TL_TEST_HELLO_TL, NULL, CMDQ_INVALID_THREAD, NULL, NULL, gCmdqTestSecure < 0 ? true : false); if (status < 0) { /* entry cmdqSecTL failed */ CMDQ_ERR("entry cmdqSecTL failed, status:%d\n", status); } CMDQ_MSG("=========== Hello cmdqSecDr ===========\n "); status = cmdq_sec_submit_to_secure_world_async_unlocked( CMD_CMDQ_TL_TEST_DUMMY, NULL, CMDQ_INVALID_THREAD, NULL, NULL, gCmdqTestSecure < 0 ? true : false); if (status < 0) { /* entry cmdqSecDr failed */ CMDQ_ERR("entry cmdqSecDr failed, status:%d\n", status); } } while (0); CMDQ_LOG("%s END\n", __func__); #endif } void testcase_secure_disp_scenario(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT /* note: this case used to verify command compose in secure world. * It must test when DISP driver has switched primary DISP to * secure path, otherwise we should disable "enable GCE" in SWd in * order to prevent phone hang */ struct cmdqRecStruct *hDISP; struct cmdqRecStruct *hSubDisp; struct cmdqRecStruct *hDisableDISP; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); CMDQ_LOG("%s\n", __func__); CMDQ_LOG("=========== secure primary path ===========\n"); cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &hDISP); cmdq_task_reset(hDISP); cmdq_task_set_secure(hDISP, true); if (!~gCmdqTestSecure) cmdq_task_set_mtee(hDISP, true); cmdq_op_write_reg(hDISP, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN, ~0); cmdq_task_flush(hDISP); cmdq_task_destroy(hDISP); CMDQ_LOG("=========== secure sub path ===========\n"); cmdq_task_create(CMDQ_SCENARIO_SUB_DISP, &hSubDisp); cmdq_task_reset(hSubDisp); cmdq_task_set_secure(hSubDisp, true); if (!~gCmdqTestSecure) cmdq_task_set_mtee(hSubDisp, true); cmdq_op_write_reg(hSubDisp, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN, ~0); cmdq_task_flush(hSubDisp); cmdq_task_destroy(hSubDisp); CMDQ_LOG("=========== disp secure primary path ===========\n"); cmdq_task_create(CMDQ_SCENARIO_DISP_PRIMARY_DISABLE_SECURE_PATH, &hDisableDISP); cmdq_task_reset(hDisableDISP); cmdq_task_set_secure(hDisableDISP, true); if (!~gCmdqTestSecure) cmdq_task_set_mtee(hDisableDISP, true); cmdq_op_write_reg(hDisableDISP, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN, ~0); cmdq_task_flush(hDisableDISP); cmdq_task_destroy(hDisableDISP); CMDQ_LOG("%s END\n", __func__); #endif } void testcase_secure_meta_data(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT struct cmdqRecStruct *hReqMDP; struct cmdqRecStruct *hReqDISP; const u32 PATTERN_MDP = (1 << 0) | (1 << 2) | (1 << 16); const u32 PATTERN_DISP = 0xBCBCBCBC; u32 value = 0; CMDQ_LOG("%s\n", __func__); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_MMSYS_DUMMY_VA, ~0); CMDQ_MSG("=========== MDP case ===========\n"); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &hReqMDP); cmdq_task_reset(hReqMDP); cmdq_task_set_secure(hReqMDP, true); if (!~gCmdqTestSecure) cmdq_task_set_mtee(hReqMDP, true); /* specify use MDP engine */ hReqMDP->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0); /* enable secure test */ cmdq_task_secure_enable_dapc(hReqMDP, (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0)); cmdq_task_secure_enable_port_security(hReqMDP, (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0)); /* record command */ cmdq_op_write_reg(hReqMDP, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN_MDP, ~0); cmdq_task_flush(hReqMDP); cmdq_task_destroy(hReqMDP); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_MMSYS_DUMMY_VA); if (value != PATTERN_MDP) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN_MDP); } CMDQ_MSG("=========== DISP case ===========\n"); cmdq_task_create(CMDQ_SCENARIO_SUB_DISP, &hReqDISP); cmdq_task_reset(hReqDISP); cmdq_task_set_secure(hReqDISP, true); if (!~gCmdqTestSecure) cmdq_task_set_mtee(hReqDISP, true); /* enable secure test */ cmdq_task_secure_enable_dapc(hReqDISP, (1LL << CMDQ_ENG_DISP_WDMA1)); cmdq_task_secure_enable_port_security(hReqDISP, (1LL << CMDQ_ENG_DISP_WDMA1)); /* record command */ cmdq_op_write_reg(hReqDISP, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN_DISP, ~0); cmdq_task_flush(hReqDISP); cmdq_task_destroy(hReqDISP); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_MMSYS_DUMMY_VA); if (value != PATTERN_DISP) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN_DISP); } CMDQ_LOG("%s END\n", __func__); #else CMDQ_ERR("%s failed since not support secure path\n", __func__); #endif } void testcase_submit_after_error_happened(void) { struct cmdqRecStruct *handle = NULL; const u32 pollingVal = 0x00003001; CMDQ_LOG("%s\n", __func__); CMDQ_MSG("=========== timeout case ===========\n"); /* let poll INIFINITE */ /* CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, pollingVal); */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_poll(handle, CMDQ_TEST_GCE_DUMMY_PA, pollingVal, ~0); cmdq_task_flush(handle); CMDQ_MSG("=========== okay case ===========\n"); _testcase_simplest_command_loop_submit(1, CMDQ_SCENARIO_DEBUG, 0, gCmdqTestSecure); /* clear up */ cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } void testcase_write_stress_test(void) { s32 loop; CMDQ_LOG("%s\n", __func__); loop = 1; CMDQ_MSG("=============== loop x %d ===============\n", loop); _testcase_simplest_command_loop_submit(loop, CMDQ_SCENARIO_DEBUG, 0, gCmdqTestSecure); loop = 100; CMDQ_MSG("=============== loop x %d ===============\n", loop); _testcase_simplest_command_loop_submit(loop, CMDQ_SCENARIO_DEBUG, 0, gCmdqTestSecure); CMDQ_LOG("%s END\n", __func__); } void testcase_prefetch_multiple_command(void) { #define TEST_PREFETCH_MARKER_LOOP 2 s32 i; s32 ret; struct cmdqRecStruct *handle[TEST_PREFETCH_MARKER_LOOP] = { 0 }; /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); CMDQ_LOG("%s\n", __func__); for (i = 0; i < TEST_PREFETCH_MARKER_LOOP; i++) { CMDQ_MSG("=============== flush:%d/%d ===============\n", i, TEST_PREFETCH_MARKER_LOOP); cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &handle[i]); cmdq_task_reset(handle[i]); cmdq_task_set_secure(handle[i], false); /* record instructions which needs prefetch */ cmdqRecEnablePrefetch(handle[i]); cmdq_op_wait(handle[i], CMDQ_SYNC_TOKEN_USER_0); cmdqRecDisablePrefetch(handle[i]); /* record instructions which does not need prefetch */ cmdq_op_write_reg(handle[i], CMDQ_TEST_GCE_DUMMY_PA, 0x3000, ~0); cmdq_op_finalize_command(handle[i], false); cmdq_pkt_dump_command(handle[i]); ret = _test_flush_async(handle[i]); } for (i = 0; i < TEST_PREFETCH_MARKER_LOOP; i++) { if (handle[i] == NULL) { CMDQ_ERR("%s handle[%d] is NULL\n ", __func__, i); continue; } cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); msleep_interruptible(100); CMDQ_MSG("wait 0x%p i:%2d========\n", handle[i], i); ret = cmdq_pkt_wait_flush_ex_result(handle[i]); cmdq_task_destroy(handle[i]); } CMDQ_LOG("%s END\n", __func__); } #ifdef CMDQ_SECURE_PATH_SUPPORT static int _testcase_concurrency(void *data) { u32 securePath; securePath = *((u32 *) data); CMDQ_MSG("start secure(%d) path\n", securePath); _testcase_simplest_command_loop_submit(1000, CMDQ_SCENARIO_DEBUG, (0x1 << CMDQ_ENG_MDP_RSZ0), securePath); return 0; } #endif static void testcase_concurrency_for_normal_path_and_secure_path(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT struct task_struct *pKThread1; struct task_struct *pKThread2; const u32 securePath[2] = { 0, 1 }; CMDQ_LOG("%s\n", __func__); pKThread1 = kthread_run(_testcase_concurrency, (void *)(&securePath[0]), "cmdqNormal"); if (IS_ERR(pKThread1)) { CMDQ_ERR("create cmdqNormal failed\n"); return; } pKThread2 = kthread_run(_testcase_concurrency, (void *)(&securePath[1]), "cmdqSecure"); if (IS_ERR(pKThread2)) { CMDQ_ERR("create cmdqSecure failed\n"); return; } msleep_interruptible(5 * 1000); /* ensure both thread execute all command */ _testcase_simplest_command_loop_submit(1, CMDQ_SCENARIO_DEBUG, 0x0, false); _testcase_simplest_command_loop_submit(1, CMDQ_SCENARIO_DEBUG, 0x0, true); CMDQ_LOG("%s END\n", __func__); return; #endif } static void testcase_nonsuspend_irq(void) { struct cmdqRecStruct *handle, *handle2; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle, false); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle2); cmdq_task_reset(handle2); cmdq_task_set_secure(handle2, gCmdqTestSecure); handle2->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); /* force GCE to wait in second command before EOC */ cmdq_op_wait(handle2, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle2, false); _test_flush_async(handle); _test_flush_async(handle2); msleep_interruptible(500); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* test code: use to trigger GCE continue test command, * put in cmdq_core::handleIRQ to test */ cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); CMDQ_MSG("IRQ: After set user sw token\n"); cmdq_pkt_wait_flush_ex_result(handle); cmdq_pkt_wait_flush_ex_result(handle2); cmdq_task_destroy(handle); cmdq_task_destroy(handle2); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_module_full_mdp_engine(void) { struct cmdqRecStruct *handle = NULL; const bool alreadyEnableLog = cmdq_core_should_print_msg(); s32 status; CMDQ_LOG("%s\n", __func__); /* enable full dump */ if (!alreadyEnableLog) cmdq_core_set_log_level(1); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); /* turn on ALL except DISP engine flag to test clock operation */ handle->engineFlag = ~(CMDQ_ENG_DISP_GROUP_BITS); CMDQ_LOG("%s, engine:0x%llx it's a engine clock test case\n", __func__, handle->engineFlag); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_finalize_command(handle, false); status = cmdq_mdp_flush_async_impl(handle); if (status < 0) CMDQ_ERR("flush failed handle:0x%p\n", handle); else status = cmdq_mdp_wait(handle, NULL); /* disable full dump */ if (!alreadyEnableLog) cmdq_core_set_log_level(0); CMDQ_LOG("%s END\n", __func__); } static void testcase_trigger_engine_dispatch_check(void) { struct cmdqRecStruct *handle, *handle2, *hTrigger; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; u32 loopIndex = 0; CMDQ_LOG("%s\n", __func__); /* Create first task and run without wait */ /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_finalize_command(handle, false); _test_flush_async(handle); /* Create trigger loop */ cmdq_task_create(CMDQ_SCENARIO_TRIGGER_LOOP, &hTrigger); cmdq_task_reset(hTrigger); cmdq_op_wait(hTrigger, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_start_loop(hTrigger); /* Sleep to let trigger loop run fow a while */ CMDQ_MSG("%s before start sleep and trigger token\n", __func__); for (loopIndex = 0; loopIndex < 10; loopIndex++) { msleep_interruptible(500); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); CMDQ_MSG("%s after sleep 5000 and send (%d)\n", __func__, loopIndex); } /* Create second task and should run well */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle2); cmdq_task_reset(handle2); cmdq_task_set_secure(handle2, gCmdqTestSecure); handle2->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_write_reg(handle2, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_task_flush(handle2); cmdq_task_destroy(handle2); /* Call wait to release first task */ cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); cmdq_task_destroy(hTrigger); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_complicated_engine_thread(void) { #define TASK_COUNT 6 struct cmdqRecStruct *handle[TASK_COUNT] = { 0 }; u64 engineFlag[TASK_COUNT] = { 0 }; u32 taskIndex = 0; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); /* config engine flag for test */ engineFlag[0] = (1LL << CMDQ_ENG_MDP_RDMA0); engineFlag[1] = (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_RSZ0); engineFlag[2] = (1LL << CMDQ_ENG_MDP_RSZ0); engineFlag[3] = (1LL << CMDQ_ENG_MDP_TDSHP0); engineFlag[4] = (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_TDSHP0); engineFlag[5] = (1LL << CMDQ_ENG_MDP_TDSHP0) | (1LL << CMDQ_ENG_MDP_RSZ0); for (taskIndex = 0; taskIndex < TASK_COUNT; taskIndex++) { /* Create task and run with wait */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle[taskIndex]); cmdq_task_reset(handle[taskIndex]); cmdq_task_set_secure(handle[taskIndex], gCmdqTestSecure); handle[taskIndex]->engineFlag = engineFlag[taskIndex]; cmdq_op_wait(handle[taskIndex], CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle[taskIndex], false); _test_flush_task_async(handle[taskIndex]); } for (taskIndex = 0; taskIndex < TASK_COUNT; taskIndex++) { cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* Call wait to release task */ _test_wait_task(handle[taskIndex]); cmdq_task_destroy(handle[taskIndex]); msleep_interruptible(1000); } CMDQ_LOG("%s END\n", __func__); } static void testcase_append_task_verify(void) { struct cmdqRecStruct *handle, *handle2; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; u32 loopIndex = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &handle); cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &handle2); for (loopIndex = 0; loopIndex < 2; loopIndex++) { /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); /* clear dummy register */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* Create first task and run with wait */ /* use CMDQ to set to PATTERN */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); if (loopIndex == 1) cmdqRecEnablePrefetch(handle); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); if (loopIndex == 1) cmdqRecDisablePrefetch(handle); cmdq_op_finalize_command(handle, false); /* Create second task and should run well */ cmdq_task_reset(handle2); cmdq_task_set_secure(handle2, gCmdqTestSecure); if (loopIndex == 1) cmdqRecEnablePrefetch(handle2); cmdq_op_write_reg(handle2, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); if (loopIndex == 1) cmdqRecDisablePrefetch(handle2); cmdq_op_finalize_command(handle2, false); _test_flush_async(handle); _test_flush_async(handle2); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* Call wait to release first task */ cmdq_pkt_wait_flush_ex_result(handle); cmdq_pkt_wait_flush_ex_result(handle2); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR( "TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } } cmdq_task_destroy(handle); cmdq_task_destroy(handle2); CMDQ_LOG("%s END\n", __func__); } static void testcase_manual_suspend_resume_test(void) { struct cmdqRecStruct *handle = NULL; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_flush_async(handle); /* Manual suspend and resume */ cmdq_core_suspend(); cmdq_core_resume_notifier(); _test_flush_async(handle); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* Call wait to release second task */ cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_timeout_wait_early_test(void) { struct cmdqRecStruct *handle = NULL; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_wait_no_clear(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle, false); _test_flush_async(handle); cmdq_task_flush(handle); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* Call wait to release first task */ cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_timeout_reorder_test(void) { struct cmdqRecStruct *handle = NULL; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle, false); handle->pkt->priority = 0; cmdq_task_flush_async(handle); handle->pkt->priority = 2; cmdq_task_flush_async(handle); handle->pkt->priority = 4; cmdq_task_flush_async(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_error_irq_for_secure(void) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; unsigned long dummy_va, dummy_pa; CMDQ_LOG("%s\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } /* set to 0xFFFFFFFF */ CMDQ_REG_SET32((void *)dummy_va, ~0); CMDQ_LOG("set reg=%lx to val=%x\n", dummy_va, CMDQ_REG_GET32((void *)dummy_va)); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_write_reg(handle, dummy_pa, PATTERN, ~0); cmdq_append_command(handle, CMDQ_CODE_JUMP, 0, 8, 0, 0); cmdq_append_command(handle, CMDQ_CODE_JUMP, 0, 8, 0, 0); cmdq_pkt_append_command(handle->pkt, 0, 0xdead, 0xddaa, 0, 0, 0, 0, 0x87); cmdq_task_flush(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_error_irq(void) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; const u8 UNKNOWN_OP = 0x50; CMDQ_LOG("%s\n", __func__); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); /* wait and block instruction */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_flush_async(handle); /* invalid instruction */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_append_command(handle, CMDQ_CODE_JUMP, -1, 0, 0, 0); cmdq_pkt_dump_command(handle); cmdq_task_flush_async(handle); /* Normal command */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_task_flush_async(handle); /* invalid instruction is asserted when unknown OP */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_pkt_append_command(handle->pkt, 0, 0, 0, 0, 0, 0, 0, UNKNOWN_OP); cmdq_task_flush_async(handle); /* use CMDQ to set to PATTERN */ cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_op_finalize_command(handle, false); _test_flush_async(handle); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static void testcase_check_dts_correctness(void) { CMDQ_LOG("%s\n", __func__); cmdq_dev_test_dts_correctness(); CMDQ_LOG("%s END\n", __func__); } static s32 testcase_monitor_callback(unsigned long data) { u32 i; u32 monitorValue[CMDQ_MONITOR_EVENT_MAX] = {0}; u32 durationTime[CMDQ_MONITOR_EVENT_MAX] = {0}; if (!gEventMonitor.status) return 0; for (i = 0; i < gEventMonitor.monitorNUM; i++) { /* Read monitor time */ cmdq_cpu_read_mem(gEventMonitor.slotHandle, i, &monitorValue[i]); switch (gEventMonitor.waitType[i]) { case CMDQ_MOITOR_TYPE_WFE: durationTime[i] = (monitorValue[i] - gEventMonitor.previousValue[i]) * 76; CMDQ_LOG( "[MONITOR][WFE] event: %s, duration: (%u ns)\n", cmdq_core_get_event_name_enum( gEventMonitor.monitorEvent[i]), durationTime[i]); CMDQ_MSG("[MONITOR][WFE] time:(%u ns)\n", monitorValue[i]); break; case CMDQ_MOITOR_TYPE_WAIT_NO_CLEAR: durationTime[i] = (monitorValue[i] - gEventMonitor.previousValue[i]) * 76; CMDQ_LOG( "[MONITOR][Wait] event: %s, duration: (%u ns)\n", cmdq_core_get_event_name_enum( gEventMonitor.monitorEvent[i]), durationTime[i]); CMDQ_MSG("[MONITOR] time:(%u ns)\n", monitorValue[i]); break; case CMDQ_MOITOR_TYPE_QUERYREGISTER: CMDQ_LOG( "[MONITOR] Register:0x08%llx, value:(0x04%x)\n", gEventMonitor.monitorEvent[i], monitorValue[i]); break; } /* Update previous monitor time */ gEventMonitor.previousValue[i] = monitorValue[i]; } return 0; } static void testcase_monitor_trigger_initialization(void) { /* Create Slot*/ cmdq_alloc_mem(&gEventMonitor.slotHandle, CMDQ_MONITOR_EVENT_MAX); /* Create CMDQ handle */ cmdq_task_create(CMDQ_SCENARIO_HIGHP_TRIGGER_LOOP, &gEventMonitor.cmdqHandle); cmdq_task_reset(gEventMonitor.cmdqHandle); /* Insert enable pre-fetch instruction */ cmdqRecEnablePrefetch(gEventMonitor.cmdqHandle); } static void testcase_monitor_trigger(u32 waitType, u64 monitorEvent) { s32 eventID; bool successAddInstruction = false; CMDQ_LOG("%s\n", __func__); if (gEventMonitor.status) { /* Reset monitor status */ gEventMonitor.status = false; CMDQ_LOG("stop monitor thread\n"); /* Stop trigger loop */ cmdq_task_stop_loop(gEventMonitor.cmdqHandle); /* Destroy slot & CMDQ handle */ cmdq_free_mem(gEventMonitor.slotHandle); /* Dump CMDQ command */ cmdq_task_destroy(gEventMonitor.cmdqHandle); /* Reset global variable */ memset(&(gEventMonitor), 0x0, sizeof(gEventMonitor)); } if (gEventMonitor.monitorNUM == 0) { /* Monitor trigger thread initialization */ testcase_monitor_trigger_initialization(); } else if (gEventMonitor.monitorNUM >= CMDQ_MONITOR_EVENT_MAX) { waitType = CMDQ_MOITOR_TYPE_FLUSH; CMDQ_LOG("[MONITOR] reach MAX monitor number:%d force flush\n", gEventMonitor.monitorNUM); } switch (waitType) { case CMDQ_MOITOR_TYPE_FLUSH: if (gEventMonitor.monitorNUM > 0) { CMDQ_LOG("start monitor thread\n"); /* Insert disable pre-fetch instruction */ cmdqRecDisablePrefetch(gEventMonitor.cmdqHandle); /* Set monitor status */ gEventMonitor.status = true; /* Start trigger loop */ cmdq_task_start_loop_callback(gEventMonitor.cmdqHandle, &testcase_monitor_callback, 0); cmdq_pkt_dump_command(gEventMonitor.cmdqHandle); } break; case CMDQ_MOITOR_TYPE_WFE: eventID = (s32)monitorEvent; if (eventID >= 0 && eventID < CMDQ_SYNC_TOKEN_MAX) { cmdq_op_wait(gEventMonitor.cmdqHandle, eventID); cmdq_op_backup_TPR(gEventMonitor.cmdqHandle, gEventMonitor.slotHandle, gEventMonitor.monitorNUM); successAddInstruction = true; } break; case CMDQ_MOITOR_TYPE_WAIT_NO_CLEAR: eventID = (s32)monitorEvent; if (eventID >= 0 && eventID < CMDQ_SYNC_TOKEN_MAX) { cmdq_op_wait_no_clear(gEventMonitor.cmdqHandle, eventID); cmdq_op_backup_TPR(gEventMonitor.cmdqHandle, gEventMonitor.slotHandle, gEventMonitor.monitorNUM); successAddInstruction = true; } break; case CMDQ_MOITOR_TYPE_QUERYREGISTER: cmdq_op_read_reg_to_mem(gEventMonitor.cmdqHandle, gEventMonitor.slotHandle, gEventMonitor.monitorNUM, monitorEvent); successAddInstruction = true; break; } if (successAddInstruction) { gEventMonitor.waitType[gEventMonitor.monitorNUM] = waitType; gEventMonitor.monitorEvent[gEventMonitor.monitorNUM] = monitorEvent; gEventMonitor.monitorNUM++; } CMDQ_LOG("%s\n", __func__); } static void testcase_poll_monitor_delay_continue(struct work_struct *workItem) { /* set event to start next polling */ cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_POLL_MONITOR); CMDQ_LOG("monitor after delay: (%d)ms, start polling again\n", gPollMonitor.delayTime); } static s32 testcase_poll_monitor_callback(unsigned long data) { u32 pollTime = 0; if (!gPollMonitor.status) return 0; cmdq_cpu_read_mem(gPollMonitor.slotHandle, 0, &pollTime); CMDQ_LOG( "monitor time:(%u ns) regAddr:0x%08llx regValue:0x%llx regMask=0x%08llx\n", pollTime, gPollMonitor.pollReg, gPollMonitor.pollValue, gPollMonitor.pollMask); schedule_delayed_work(&gPollMonitor.delayContinueWork, gPollMonitor.delayTime); return 0; } static void testcase_poll_monitor_trigger(u64 pollReg, u64 pollValue, u64 pollMask) { CMDQ_LOG("%s\n", __func__); if (gPollMonitor.status) { /* Reset monitor status */ gPollMonitor.status = false; CMDQ_LOG("stop polling monitor thread: regAddr:0x%08llx\n", gPollMonitor.pollReg); /* Stop trigger loop */ cmdq_task_stop_loop(gPollMonitor.cmdqHandle); /* Destroy slot & CMDQ handle */ cmdq_free_mem(gPollMonitor.slotHandle); cmdq_task_destroy(gPollMonitor.cmdqHandle); /* Reset global variable */ memset(&(gPollMonitor), 0x0, sizeof(gPollMonitor)); } if (-1 == pollReg) return; CMDQ_LOG( "start polling monitor thread, regAddr=0x%llx regValue=0x%llx regMask=0x%llx\n", pollReg, pollValue, pollMask); /* Set event to start first polling */ cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_POLL_MONITOR); /* Create slot */ cmdq_alloc_mem(&gPollMonitor.slotHandle, 1); /* Create CMDQ handle */ cmdq_task_create(CMDQ_SCENARIO_LOWP_TRIGGER_LOOP, &gPollMonitor.cmdqHandle); cmdq_task_reset(gPollMonitor.cmdqHandle); /* Insert monitor thread command */ cmdq_op_wait(gPollMonitor.cmdqHandle, CMDQ_SYNC_TOKEN_POLL_MONITOR); if (cmdq_op_poll(gPollMonitor.cmdqHandle, pollReg, pollValue, pollMask) == 0) { cmdq_op_backup_TPR(gPollMonitor.cmdqHandle, gPollMonitor.slotHandle, 0); /* Set value to global variable */ gPollMonitor.pollReg = pollReg; gPollMonitor.pollValue = pollValue; gPollMonitor.pollMask = pollMask; gPollMonitor.delayTime = 1; gPollMonitor.status = true; INIT_DELAYED_WORK(&gPollMonitor.delayContinueWork, testcase_poll_monitor_delay_continue); /* Start trigger loop */ cmdq_task_start_loop_callback(gPollMonitor.cmdqHandle, &testcase_poll_monitor_callback, 0); /* Dump CMDQ command */ cmdq_pkt_dump_command(gPollMonitor.cmdqHandle); } else { /* Destroy slot & CMDQ handle */ cmdq_free_mem(gPollMonitor.slotHandle); cmdq_task_destroy(gPollMonitor.cmdqHandle); } CMDQ_LOG("%s END\n", __func__); } static void testcase_acquire_resource( enum cmdq_event resourceEvent, bool acquireExpected) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; s32 acquireResult; CMDQ_LOG("%s\n", __func__); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); acquireResult = cmdq_resource_acquire_and_write(handle, resourceEvent, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); if (acquireResult < 0) { /* Do error handle for acquire resource fail */ if (acquireExpected) { /* print error message */ CMDQ_ERR( "Acquire resource fail: it's not expected!\n"); } else { /* print message */ CMDQ_LOG("Acquire resource fail: it's expected!\n"); } } else { if (!acquireExpected) { /* print error message */ CMDQ_ERR( "Acquire resource success: it's not expected!\n"); } else { /* print message */ CMDQ_LOG("Acquire resource success: it's expected!\n"); } } cmdq_task_flush(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN && acquireExpected) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } CMDQ_LOG("%s END\n", __func__); } static s32 testcase_res_release_cb(enum cmdq_event resourceEvent) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); CMDQ_LOG("%s\n", __func__); /* Flush release command immedately with wait MUTEX event */ /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* simulate display need to wait single */ cmdq_op_wait_no_clear(handle, CMDQ_SYNC_TOKEN_USER_0); /* simulate release resource via write register */ cmdq_resource_release_and_write(handle, resourceEvent, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_task_flush_async(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); return 0; } static s32 testcase_res_available_cb(enum cmdq_event resourceEvent) { CMDQ_LOG("%s\n", __func__); testcase_acquire_resource(resourceEvent, true); CMDQ_LOG("%s END\n", __func__); return 0; } static void testcase_notify_and_delay_submit(u32 delayTimeMS) { struct cmdqRecStruct *handle = NULL; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); u32 value = 0; const u64 engineFlag = (1LL << CMDQ_ENG_MDP_WROT0); const enum cmdq_event resourceEvent = CMDQ_SYNC_RESOURCE_WROT0; u32 contDelay; CMDQ_LOG("%s\n", __func__); /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); cmdq_mdp_set_resource_callback(resourceEvent, testcase_res_available_cb, testcase_res_release_cb); testcase_acquire_resource(resourceEvent, true); /* notify and delay time*/ if (delayTimeMS > 0) { CMDQ_MSG("Before delay for acquire\n"); msleep_interruptible(delayTimeMS); CMDQ_MSG("Before lock and delay\n"); cmdq_mdp_lock_resource(engineFlag, true); msleep_interruptible(delayTimeMS); CMDQ_MSG("After lock and delay\n"); } /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, gCmdqTestSecure); handle->engineFlag = engineFlag; cmdq_op_wait_no_clear(handle, resourceEvent); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); cmdq_task_flush_async(handle); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); msleep_interruptible(2000); /* Delay and continue sent */ for (contDelay = 300; contDelay < CMDQ_DELAY_RELEASE_RESOURCE_MS*1.2; contDelay += 300) { CMDQ_MSG("Before delay and flush\n"); msleep_interruptible(contDelay); CMDQ_MSG("After delay\n"); cmdq_task_flush(handle); CMDQ_MSG("After flush\n"); } /* Simulate DISP acquire fail case, acquire immediate after flush MDP */ cmdq_task_flush_async(handle); testcase_acquire_resource(resourceEvent, false); cmdq_task_flush_async(handle); cmdq_task_destroy(handle); /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_ERR("TEST FAIL: wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } cmdq_mdp_set_resource_callback(resourceEvent, NULL, NULL); CMDQ_LOG("%s END\n", __func__); } void testcase_prefetch_round_impl(struct cmdqRecStruct *handle, u32 cmdCount, bool withMask, bool withWait, s32 i) { s32 cmd_idx; s32 ret; cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &handle); cmdq_task_reset(handle); cmdq_task_set_secure(handle, false); /* record instructions which needs prefetch */ if (i == 1) /* use pre-fetch with marker */ cmdqRecEnablePrefetch(handle); if (withWait) cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_profile_marker(handle, "ANA_BEGIN"); for (cmd_idx = 0; cmd_idx < cmdCount; cmd_idx++) { /* record instructions which does not need prefetch */ if (withMask) cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, 0x3210, ~0xfff0); else cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, 0x3210, ~0); } if (i == 1) /* disable pre-fetch with marker */ cmdqRecDisablePrefetch(handle); cmdq_op_profile_marker(handle, "ANA_END"); cmdq_op_finalize_command(handle, false); ret = _test_flush_async(handle); if (withWait) { msleep_interruptible(500); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); } CMDQ_MSG("wait 0x%p i:%2d========\n", handle, i); ret = cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); } void testcase_prefetch_round(u32 loopCount, u32 cmdCount, bool withMask, bool withWait) { #define TEST_PREFETCH_LOOP 3 s32 i, j; struct cmdqRecStruct *handle[TEST_PREFETCH_LOOP] = {0}; /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); CMDQ_MSG("%s: count:%d withMask:%d withWait:%d\n", __func__, cmdCount, withMask, withWait); for (i = 0; i < TEST_PREFETCH_LOOP; i++) { CMDQ_MSG("=============== flush:%d/%d ===============\n", i, TEST_PREFETCH_LOOP); for (j = 0; j < loopCount; j++) { CMDQ_MSG( "=============== loop:%d/%d ===============\n", j, loopCount); testcase_prefetch_round_impl(handle[i], cmdCount, withMask, withWait, i); } } CMDQ_LOG("%s END\n", __func__); } void testcase_prefetch_from_DTS(void) { s32 i, j; u32 thread_prefetch_size; CMDQ_LOG("%s\n", __func__); for (i = 0; i < cmdq_dev_get_thread_count(); i++) { thread_prefetch_size = cmdq_core_get_thread_prefetch_size(i); for (j = 100; j <= (thread_prefetch_size + 60); j += 40) { testcase_prefetch_round(1, j, false, true); testcase_prefetch_round(1, j, false, false); } } CMDQ_LOG("%s END\n", __func__); } static void testcase_specific_bus_MMSYS(void) { u32 i; const u32 loop = 1000; const u32 pattern = (1 << 0) | (1 << 2) | (1 << 16); u32 mmsys_register; struct cmdqRecStruct *handle = NULL; cmdqBackupSlotHandle slot_handle = 0; u32 start_time = 0, end_time = 0, duration_time = 0; CMDQ_LOG("%s\n", __func__); cmdq_alloc_mem(&slot_handle, 2); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_backup_TPR(handle, slot_handle, 0); for (i = 0; i < loop; i++) { mmsys_register = CMDQ_TEST_MMSYS_DUMMY_PA + (i%2)*0x4; if (i%11 == 10) cmdq_op_read_to_data_register(handle, mmsys_register, CMDQ_DATA_REG_2D_SHARPNESS_0); else cmdq_op_write_reg(handle, mmsys_register, pattern, ~0); } cmdq_op_backup_TPR(handle, slot_handle, 1); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &start_time); cmdq_cpu_read_mem(slot_handle, 1, &end_time); duration_time = (end_time - start_time) * 76; CMDQ_LOG("duration time, %u, ns\n", duration_time); cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } void cmdq_track_task(const struct cmdqRecStruct *handle) { CMDQ_LOG("track_task: engine:0x%08llx\n", handle->engineFlag); } static void testcase_track_task_cb(void) { struct cmdqRecStruct *handle = NULL; CMDQ_LOG("%s\n", __func__); cmdqCoreRegisterTrackTaskCB(CMDQ_GROUP_MDP, cmdq_track_task); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); handle->engineFlag = (1LL << CMDQ_ENG_MDP_CAMIN); cmdq_task_flush(handle); cmdqCoreRegisterTrackTaskCB(CMDQ_GROUP_MDP, NULL); CMDQ_LOG("%s END\n", __func__); } static void testcase_while_test_mmsys_bus(void) { s32 i; const u32 loop = 5000; CMDQ_LOG("%s\n", __func__); for (i = 0; i < loop; i++) { testcase_specific_bus_MMSYS(); msleep_interruptible(100); } CMDQ_LOG("%s END\n", __func__); } struct thread_set_event_config { enum cmdq_event event; u32 sleep_ms; bool loop; }; static int testcase_set_gce_event(void *data) { struct thread_set_event_config config; CMDQ_LOG("%s\n", __func__); config = *((struct thread_set_event_config *) data); do { if (kthread_should_stop()) break; if (config.sleep_ms > 10) msleep_interruptible(config.sleep_ms); else mdelay(config.sleep_ms); cmdqCoreSetEvent(config.event); } while (config.loop); CMDQ_LOG("%s END\n", __func__); return 0; } static int testcase_cpu_config_non_mmsys(void *data) { CMDQ_LOG("%s\n", __func__); while (1) { if (kthread_should_stop()) break; /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_GPR_R32(CMDQ_DATA_REG_JPEG), ~0); /* udelay(1); */ } CMDQ_LOG("%s END\n", __func__); return 0; } static int testcase_cpu_config_mmsys(void *data) { unsigned long mmsys_register; CMDQ_LOG("%s\n", __func__); cmdq_mdp_get_func()->mdpEnableCommonClock(true); while (1) { if (kthread_should_stop()) break; mmsys_register = CMDQ_TEST_MMSYS_DUMMY_VA + 0x4; /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(mmsys_register, ~0); /* udelay(1); */ } CMDQ_LOG("%s END\n", __func__); cmdq_mdp_get_func()->mdpEnableCommonClock(false); return 0; } #define CMDQ_TEST_MAX_THREAD (32) struct task_struct *set_event_config_th; struct task_struct *busy_mmsys_config_th[CMDQ_TEST_MAX_THREAD] = {NULL}; struct task_struct *busy_non_mmsys_config_th[CMDQ_TEST_MAX_THREAD] = {NULL}; static void testcase_run_set_gce_event(void *data) { set_event_config_th = kthread_run(testcase_set_gce_event, data, "set_cmdq_event_loop"); if (IS_ERR(set_event_config_th)) { /* print error log */ CMDQ_LOG("%s, init kthread_run failed!\n", __func__); set_event_config_th = NULL; } } static void testcase_stop_set_gce_event(void) { if (set_event_config_th == NULL) return; kthread_stop(set_event_config_th); set_event_config_th = NULL; } static void testcase_run_busy_non_mmsys_config_loop(void) { u32 i; for (i = 0; i < CMDQ_TEST_MAX_THREAD; i++) { busy_non_mmsys_config_th[i] = kthread_run( testcase_cpu_config_non_mmsys, NULL, "busy_config_non-mm"); if (IS_ERR(busy_non_mmsys_config_th[i])) { /* print error log */ CMDQ_LOG("%s, thread id:%d init kthread_run failed!\n", __func__, i); busy_non_mmsys_config_th[i] = NULL; } } } static void testcase_stop_busy_non_mmsys_config_loop(void) { u32 i; for (i = 0; i < CMDQ_TEST_MAX_THREAD; i++) { if (busy_non_mmsys_config_th[i] == NULL) continue; kthread_stop(busy_non_mmsys_config_th[i]); busy_non_mmsys_config_th[i] = NULL; } } static void testcase_run_busy_mmsys_config_loop(void) { u32 i; for (i = 0; i < CMDQ_TEST_MAX_THREAD; i++) { busy_mmsys_config_th[i] = kthread_run( testcase_cpu_config_mmsys, NULL, "busy_config_mm"); if (IS_ERR(busy_mmsys_config_th[i])) { /* print error log */ CMDQ_LOG("%s, thread id:%d init kthread_run failed!\n", __func__, i); busy_mmsys_config_th[i] = NULL; } } } static void testcase_stop_busy_mmsys_config_loop(void) { u32 i; for (i = 0; i < CMDQ_TEST_MAX_THREAD; i++) { if (busy_mmsys_config_th[i] == NULL) continue; kthread_stop(busy_mmsys_config_th[i]); busy_mmsys_config_th[i] = NULL; } } static void testcase_basic_logic(void) { struct cmdqRecStruct *handle; const unsigned long test_reg = CMDQ_GPR_R32(CMDQ_DATA_REG_PQ_COLOR); const unsigned long result_reg = CMDQ_GPR_R32(CMDQ_DATA_REG_2D_SHARPNESS_0); const u32 test_reg_pa = CMDQ_GPR_R32_PA(CMDQ_DATA_REG_PQ_COLOR); const u32 result_reg_pa = CMDQ_GPR_R32_PA(CMDQ_DATA_REG_2D_SHARPNESS_0); const u32 test_var_in_reg = 4, test_var = 2; u32 expected_result; u32 test_result; CMDQ_VARIABLE get_value, result; s32 status; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(test_reg, test_var_in_reg); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); /* test logic assign */ cmdq_op_init_variable(&result); cmdq_op_assign(handle, &result, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic assign failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL( "TEST assign FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic add */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_add(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic add failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg + test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL("TEST add FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic subtract */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_subtract(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic subtract failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg - test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL( "TEST subtract FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic multiply */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_multiply(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic multiple failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg * test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL( "TEST multiply FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic exclusive or */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_xor(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL( "test logic exclusive or failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg ^ test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL("TEST xor FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic not */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_not(handle, &result, get_value); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic not failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = ~test_var_in_reg; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL("TEST not FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic or */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_or(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic or failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg | test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL("TEST or FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic and */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_and(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL("test logic and failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg & test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL("TEST and FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic left shift */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_left_shift(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL( "test logic left shift failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg << test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL( "TEST left shift FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); /* test logic right shift */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&result); cmdq_op_init_variable(&get_value); cmdq_op_read_reg(handle, test_reg_pa, &get_value, ~0x0); cmdq_op_right_shift(handle, &result, get_value, test_var); cmdq_op_write_reg(handle, result_reg_pa, result, ~0x0); status = cmdq_op_finalize_command(handle, false); if (status) CMDQ_TEST_FAIL( "test logic right shift failed, handle:0x%p\n", handle); _test_flush_task(handle); /* value check */ test_result = CMDQ_REG_GET32(result_reg); expected_result = test_var_in_reg >> test_var; if (test_result != expected_result) { /* test fail */ CMDQ_TEST_FAIL( "TEST right shift FAIL: value is 0x%08x not 0x%08x\n", test_result, expected_result); cmdq_pkt_dump_buf(handle->pkt, 0); } cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } void testcase_basic_jumpc(void) { struct cmdqRecStruct *handle = NULL; CMDQ_VARIABLE var_compare, var_result; CMDQ_LOG("%s\n", __func__); cmdq_op_init_variable(&var_compare); cmdq_op_init_variable(&var_result); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_assign(handle, &var_compare, 1); cmdq_op_assign(handle, &var_result, 0xff); cmdq_op_if(handle, var_compare, CMDQ_GREATER_THAN, 0); cmdq_op_assign(handle, &var_result, 0xab0); cmdq_op_assign(handle, &var_result, 0xab1); cmdq_op_assign(handle, &var_result, 0xab2); cmdq_op_end_if(handle); cmdq_op_finalize_command(handle, false); _test_flush_task_async(handle); cmdq_pkt_dump_buf(handle->pkt, 0); cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } void testcase_do_while_continue(void) { struct cmdqRecStruct *handle = NULL; CMDQ_VARIABLE cmdq_result, cmdq_op_counter; cmdqBackupSlotHandle slot_handle = 0; const u32 max_loop_count = 20; u32 test_result = 0, expect_result = 0, op_counter = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_alloc_mem(&slot_handle, 1); /* test logic assign */ cmdq_op_init_variable(&cmdq_result); cmdq_op_init_variable(&cmdq_op_counter); cmdq_op_assign(handle, &cmdq_result, 1); cmdq_op_assign(handle, &cmdq_op_counter, 0); cmdq_op_do_while(handle); cmdq_op_add(handle, &cmdq_op_counter, cmdq_op_counter, 1); cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_if(handle, cmdq_result, CMDQ_GREATER_THAN, 20); cmdq_op_continue(handle); cmdq_op_end_if(handle); cmdq_op_add(handle, &cmdq_result, cmdq_result, 18); cmdq_op_end_do_while(handle, cmdq_op_counter, CMDQ_LESS_THAN, max_loop_count); cmdq_op_backup_CPR(handle, cmdq_result, slot_handle, 0); cmdq_op_finalize_command(handle, false); _test_flush_task_async(handle); cmdq_pkt_dump_buf(handle->pkt, 0); cmdq_pkt_wait_flush_ex_result(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_result); expect_result = 1; op_counter = 0; do { op_counter += 1; expect_result += 1; if (expect_result > 20) continue; expect_result += 18; } while (op_counter < max_loop_count); if (test_result != expect_result) { CMDQ_TEST_FAIL("%s test value is 0x%08x not expect 0x%08x\n", __func__, test_result, expect_result); } cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_jump_c(void) { struct cmdqRecStruct *handle = NULL; const u32 max_rows = 10; const u32 max_cols = 12; u32 row_in_value = 0, col_in_value = 0; u32 test_result = 0, expect_result = 0xffffffff, expect_temp_sum; CMDQ_VARIABLE cmdq_row, cmdq_col, cmdq_temp_sum, cmdq_result; cmdqBackupSlotHandle slot_handle = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_alloc_mem(&slot_handle, 1); /* test logic assign */ cmdq_op_init_variable(&cmdq_row); cmdq_op_init_variable(&cmdq_col); cmdq_op_init_variable(&cmdq_temp_sum); cmdq_op_init_variable(&cmdq_result); cmdq_op_assign(handle, &cmdq_row, row_in_value); cmdq_op_assign(handle, &cmdq_result, 1); cmdq_op_while(handle, cmdq_row, CMDQ_LESS_THAN, max_rows); cmdq_op_assign(handle, &cmdq_col, 0); cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_while(handle, cmdq_col, CMDQ_LESS_THAN, max_cols); cmdq_op_add(handle, &cmdq_col, cmdq_col, 1); cmdq_op_if(handle, cmdq_col, CMDQ_GREATER_THAN_AND_EQUAL, 10); cmdq_op_break(handle); cmdq_op_else_if(handle, 6, CMDQ_EQUAL, cmdq_col); cmdq_op_continue(handle); cmdq_op_else_if(handle, 5, CMDQ_EQUAL, cmdq_col); cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_else(handle); cmdq_op_add(handle, &cmdq_temp_sum, cmdq_row, cmdq_col); cmdq_op_add(handle, &cmdq_result, cmdq_result, cmdq_temp_sum); cmdq_op_end_if(handle); cmdq_op_end_while(handle); cmdq_op_add(handle, &cmdq_row, cmdq_row, 1); cmdq_op_end_while(handle); cmdq_op_backup_CPR(handle, cmdq_result, slot_handle, 0); cmdq_op_finalize_command(handle, false); _test_flush_task_async(handle); cmdq_pkt_dump_buf(handle->pkt, 0); cmdq_pkt_wait_flush_ex_result(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_result); /* value check */ expect_result = 1; while (row_in_value < max_rows) { col_in_value = 0; expect_result = expect_result + 1; while (col_in_value < max_cols) { col_in_value++; if (col_in_value >= 10) { break; } else if (col_in_value == 6) { continue; } else if (col_in_value == 5) { expect_result = expect_result + 1; } else { expect_temp_sum = row_in_value + col_in_value; expect_result = expect_result + expect_temp_sum; } } row_in_value++; } if (test_result != expect_result) { /* test fail */ CMDQ_TEST_FAIL("%s value is 0x%08x not 0x%08x\n", __func__, test_result, expect_result); } cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_jump_c_do_while(void) { struct cmdqRecStruct *handle = NULL; const u32 max_rows = 10; const u32 max_cols = 12; u32 row_in_value = 0, col_in_value = 0; u32 test_result = 0, expect_result = 0xffffffff, expect_temp_sum; CMDQ_VARIABLE cmdq_row, cmdq_col, cmdq_temp_sum, cmdq_result; cmdqBackupSlotHandle slot_handle = 0; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_alloc_mem(&slot_handle, 1); /* test logic assign */ cmdq_op_init_variable(&cmdq_row); cmdq_op_init_variable(&cmdq_col); cmdq_op_init_variable(&cmdq_temp_sum); cmdq_op_init_variable(&cmdq_result); cmdq_op_assign(handle, &cmdq_row, row_in_value); cmdq_op_assign(handle, &cmdq_result, 1); cmdq_op_do_while(handle); cmdq_op_assign(handle, &cmdq_col, 0); cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_do_while(handle); cmdq_op_add(handle, &cmdq_col, cmdq_col, 1); cmdq_op_if(handle, cmdq_col, CMDQ_GREATER_THAN_AND_EQUAL, 10); cmdq_op_break(handle); cmdq_op_else_if(handle, 6, CMDQ_EQUAL, cmdq_col); cmdq_op_continue(handle); cmdq_op_else_if(handle, 5, CMDQ_EQUAL, cmdq_col); cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_else(handle); cmdq_op_add(handle, &cmdq_temp_sum, cmdq_row, cmdq_col); cmdq_op_add(handle, &cmdq_result, cmdq_result, cmdq_temp_sum); cmdq_op_end_if(handle); cmdq_op_end_do_while(handle, cmdq_col, CMDQ_LESS_THAN, max_cols); cmdq_op_add(handle, &cmdq_row, cmdq_row, 1); cmdq_op_end_do_while(handle, cmdq_row, CMDQ_LESS_THAN, max_rows); cmdq_op_backup_CPR(handle, cmdq_result, slot_handle, 0); cmdq_op_finalize_command(handle, false); _test_flush_task_async(handle); cmdq_pkt_dump_buf(handle->pkt, 0); cmdq_pkt_wait_flush_ex_result(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_result); /* value check */ expect_result = 1; do { col_in_value = 0; expect_result = expect_result + 1; do { col_in_value++; if (col_in_value >= 10) { break; } else if (col_in_value == 6) { continue; } else if (col_in_value == 5) { expect_result = expect_result + 1; } else { expect_temp_sum = row_in_value + col_in_value; expect_result = expect_result + expect_temp_sum; } } while (col_in_value < max_cols); row_in_value++; } while (row_in_value < max_rows); if (test_result != expect_result) { /* test fail */ CMDQ_TEST_FAIL("%s value is 0x%08x not 0x%08x\n", __func__, test_result, expect_result); } cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_long_jump_c(void) { struct cmdqRecStruct *handle = NULL; const u32 init_val = 1, post_val = 6; u32 test_result = 0, i; CMDQ_VARIABLE cmdq_result = 0; cmdqBackupSlotHandle slot_handle = 0; s32 status = 0; CMDQ_LOG("%s\n", __func__); /* Test jump_c if cross page */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_alloc_mem(&slot_handle, 1); cmdq_op_init_variable(&cmdq_result); cmdq_op_assign(handle, &cmdq_result, init_val); cmdq_op_if(handle, cmdq_result, CMDQ_NOT_EQUAL, init_val); /* this part of instruction should not execute */ for (i = 0; i < CMDQ_CMD_BUFFER_SIZE / CMDQ_INST_SIZE; i++) cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_end_if(handle); /* add more post instruction */ for (i = 0; i < post_val; i++) cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_backup_CPR(handle, cmdq_result, slot_handle, 0); cmdq_op_finalize_command(handle, false); status = _test_flush_task_async(handle); cmdq_pkt_wait_flush_ex_result(handle); /* cmdq_pkt_dump_buf(handle->pkt, 0); */ cmdq_cpu_read_mem(slot_handle, 0, &test_result); if (status < 0 || test_result != init_val + post_val) { /* test fail */ CMDQ_TEST_FAIL( "%s jump_c if, value is 0x%08x not 0x%08x status:%d\n", __func__, test_result, init_val + 1, status); } cmdq_task_destroy(handle); /* Test jump_c while and continue cross page */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); cmdq_task_set_secure(handle, gCmdqTestSecure); cmdq_op_init_variable(&cmdq_result); cmdq_op_assign(handle, &cmdq_result, init_val); cmdq_op_while(handle, cmdq_result, CMDQ_LESS_THAN_AND_EQUAL, init_val); cmdq_op_if(handle, cmdq_result, CMDQ_GREATER_THAN, init_val); /* this part of instruction should not execute */ for (i = 0; i < CMDQ_CMD_BUFFER_SIZE / CMDQ_INST_SIZE; i++) cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_end_if(handle); /* add more post instruction */ for (i = 0; i < post_val; i++) cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_continue(handle); cmdq_op_end_while(handle); /* more instructions after end while */ for (i = 0; i < post_val; i++) cmdq_op_add(handle, &cmdq_result, cmdq_result, 1); cmdq_op_backup_CPR(handle, cmdq_result, slot_handle, 0); cmdq_cpu_write_mem(slot_handle, 0, 0); cmdq_op_finalize_command(handle, false); status = _test_flush_task_async(handle); cmdq_pkt_wait_flush_ex_result(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_result); if (status < 0 || test_result != init_val + post_val * 2) { /* test fail */ CMDQ_TEST_FAIL( "%s jump_c while and break, value is 0x%08x not 0x%08x status:%d\n", __func__, test_result, init_val + 1, status); } cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_move_data_between_SRAM(void) { void *p_va_src = NULL; dma_addr_t pa_src = 0; void *p_va_dest = NULL; dma_addr_t pa_dest = 0; u32 buffer_size = 32; size_t free_sram_size = 0; u32 cpr_offset = 0; s32 status = 0; CMDQ_LOG("%s\n", __func__); /* Allocate DRAM memory */ p_va_src = cmdq_core_alloc_hw_buffer(cmdq_dev_get(), buffer_size, &pa_src, GFP_KERNEL); if (!p_va_src) { CMDQ_ERR("p_va_src allocate failed\n"); return; } p_va_dest = cmdq_core_alloc_hw_buffer(cmdq_dev_get(), buffer_size, &pa_dest, GFP_KERNEL); if (!p_va_dest) { CMDQ_ERR("p_va_dest allocate failed\n"); return; } memset(p_va_src, 0xda, buffer_size); memset(p_va_dest, 0xcc, buffer_size); CMDQ_LOG("copy data source:\n"); print_hex_dump(KERN_ERR, "[CMDQ][LOG]", DUMP_PREFIX_ADDRESS, 16, 4, p_va_src, buffer_size, true); /* Allocate SRAM memory */ free_sram_size = cmdq_core_get_free_sram_size(); status = cmdq_core_alloc_sram_buffer(buffer_size, "UT_SRAM", &cpr_offset); if (status < 0 || (free_sram_size - cmdq_core_get_free_sram_size()) != buffer_size) { CMDQ_ERR("%s allocate SRAM failed: before(%zu), after(%zu)\n", __func__, free_sram_size, cmdq_core_get_free_sram_size()); } cmdq_core_dump_sram(); status = cmdq_task_copy_to_sram(pa_src, cpr_offset, buffer_size); if (status < 0) CMDQ_TEST_FAIL("copy to sram API failed:%d", status); status = cmdq_task_copy_from_sram(pa_dest, cpr_offset, buffer_size); if (status < 0) CMDQ_TEST_FAIL("copy from sram API failed:%d", status); if (memcmp(p_va_src, p_va_dest, buffer_size) != 0) { CMDQ_ERR("move data between SRAM failed!\n"); print_hex_dump(KERN_ERR, "[CMDQ][ERR]", DUMP_PREFIX_ADDRESS, 16, 4, p_va_dest, buffer_size, true); } cmdq_core_free_hw_buffer(cmdq_dev_get(), buffer_size, p_va_src, pa_src); cmdq_core_free_hw_buffer(cmdq_dev_get(), buffer_size, p_va_dest, pa_dest); free_sram_size = cmdq_core_get_free_sram_size(); cmdq_core_free_sram_buffer(cpr_offset, buffer_size); if ((cmdq_core_get_free_sram_size() - free_sram_size) != buffer_size) { CMDQ_ERR("%s free SRAM failed: before(%zu), after(%zu)\n", __func__, free_sram_size, cmdq_core_get_free_sram_size()); } cmdq_core_dump_sram(); CMDQ_LOG("%s END\n", __func__); } /* Make sure driver can execute command on SRAM successfully * Coverage: * Cannot start_in_sram with secure task * Cannot flush twice SRAM task * SRAM size should be normal after flush task and destroy task * SRAM execution result should be correct */ static void testcase_run_command_on_SRAM(void) { size_t free_sram_size = 0; s32 status = 0; u32 value = 0; const u32 PATTERN = (1 << 0) | (1 << 2) | (1 << 16); struct cmdqRecStruct *handle; CMDQ_LOG("%s\n", __func__); /* set to 0xFFFFFFFF */ CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_SRAM_LOOP, &handle); cmdq_task_reset(handle); status = cmdq_task_set_secure(handle, true); if (status >= 0) { cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); status = cmdq_task_start_loop_sram(handle, "UT_EXE_SRAM"); if (status >= 0) CMDQ_TEST_FAIL( "SRAM loop command cannot be secure!!!\n"); } cmdq_task_reset(handle); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, PATTERN, ~0); free_sram_size = cmdq_core_get_free_sram_size(); cmdq_task_start_loop_sram(handle, "UT_EXE_SRAM"); cmdq_pkt_dump_command(handle); cmdq_core_dump_sram(); status = cmdq_task_start_loop_sram(handle, "UT_EXE_SRAM"); if (status >= 0) CMDQ_TEST_FAIL("SRAM loop command cannot start twice!!!\n"); cmdq_task_destroy(handle); if ((cmdq_core_get_free_sram_size() - free_sram_size) != 0) { CMDQ_TEST_FAIL( "%s free SRAM failed: before(%zu), after(%zu)\n", __func__, free_sram_size, cmdq_core_get_free_sram_size()); } /* value check */ value = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (value != PATTERN) { /* test fail */ CMDQ_TEST_FAIL("wrote value is 0x%08x not 0x%08x\n", value, PATTERN); } else { CMDQ_LOG("wrote value is 0x%08x\n", value); } CMDQ_LOG("%s END\n", __func__); } static void testcase_read_with_mask(void) { struct cmdqRecStruct *handle; cmdqBackupSlotHandle slot_handle = 0; CMDQ_VARIABLE arg_read = CMDQ_TASK_CPR_INITIAL_VALUE; u32 read_value = 0x00FADE00; u32 read_mask[2] = {0x00FF0000, 0x0000FF00}; u32 backup_read_value = 0; u32 loop = 0; CMDQ_LOG("%s\n", __func__); cmdq_alloc_mem(&slot_handle, 1); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, read_value); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); for (loop = 0; loop < ARRAY_SIZE(read_mask); loop++) { cmdq_task_reset(handle); cmdq_op_read_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, &arg_read, read_mask[loop]); cmdq_op_backup_CPR(handle, arg_read, slot_handle, 0); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &backup_read_value); if (backup_read_value != (read_value & read_mask[loop])) { CMDQ_TEST_FAIL( "read value with mask error:0x%08x expect:0x%08x\n", backup_read_value, read_value & read_mask[loop]); } } cmdq_free_mem(slot_handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } /* * Test Global CPR * 1. initialize and read should be correct * 2. no initialize and read should be correct */ static void testcase_global_variable(void) { s32 status = 0; struct cmdqRecStruct *handle; cmdqBackupSlotHandle slot_handle = 0; u32 cpr_offset = 0; u32 gpr_buffer_size = 2*sizeof(u32); CMDQ_VARIABLE global_x, global_y; u32 test_x = 0, test_y = 0; CMDQ_LOG("%s\n", __func__); /* Allocate SRAM memory */ status = cmdq_core_alloc_sram_buffer(gpr_buffer_size, "UT_ASSIGN_REF", &cpr_offset); if (status < 0) CMDQ_TEST_FAIL("timer loop SRAM buffer allocated failed!!!\n"); cmdq_op_init_global_cpr_variable(&global_x, cpr_offset); cmdq_op_init_global_cpr_variable(&global_y, cpr_offset + 1); cmdq_alloc_mem(&slot_handle, 2); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); /* assign global variable */ cmdq_op_assign(handle, &global_x, 0xaaaabbbb); cmdq_op_assign(handle, &global_y, 0xccccdddd); cmdq_op_backup_CPR(handle, global_x, slot_handle, 0); cmdq_op_backup_CPR(handle, global_y, slot_handle, 1); cmdq_pkt_dump_command(handle); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_x); cmdq_cpu_read_mem(slot_handle, 1, &test_y); /* value check */ if (test_x != 0xaaaabbbb || test_y != 0xccccdddd) { /* test fail */ CMDQ_TEST_FAIL("1. read x:0x%08x read y:0x%08x\n", test_x, test_y); } else { CMDQ_LOG("1. read x:0x%08x read_y:0x%08x\n", test_x, test_y); } /* read again without initialize, should keep the value */ cmdq_task_reset(handle); cmdq_op_backup_CPR(handle, global_x, slot_handle, 0); cmdq_op_backup_CPR(handle, global_y, slot_handle, 1); cmdq_pkt_dump_command(handle); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &test_x); cmdq_cpu_read_mem(slot_handle, 1, &test_y); /* value check */ if (test_x != 0xaaaabbbb || test_y != 0xccccdddd) { /* test fail */ CMDQ_TEST_FAIL("2. read x:0x%08x read y:0x%08x\n", test_x, test_y); } else { CMDQ_LOG("2. read x:0x%08x read_y:0x%08x\n", test_x, test_y); } cmdq_core_free_sram_buffer(cpr_offset, gpr_buffer_size); cmdq_free_mem(slot_handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } /* * Test Efficient Polling * 1. Polling basic function should work * 2. Polling should not block low priority thread */ static void testcase_efficient_polling(void) { struct cmdqRecStruct *h_poll, *h_low; u32 poll_value = 0x00FADE00, poll_mask = 0x00FF0000; s32 result = 0; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, ~0); cmdqCoreClearEvent(CMDQ_SYNC_TOKEN_USER_0); /* create low priority thread to simulate block case*/ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &h_low); cmdq_task_reset(h_low); cmdq_op_wait(h_low, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(h_low, false); /* create polling thread with trigger loop priority */ cmdq_task_create(CMDQ_SCENARIO_TRIGGER_LOOP, &h_poll); cmdq_task_reset(h_poll); cmdq_op_poll(h_poll, CMDQ_TEST_GCE_DUMMY_PA, poll_value, poll_mask); cmdq_op_finalize_command(h_poll, false); _test_flush_async(h_low); msleep_interruptible(500); _test_flush_async(h_poll); cmdq_pkt_dump_command(h_low); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); result = cmdq_pkt_wait_flush_ex_result(h_low); if (result < 0) CMDQ_TEST_FAIL( "Low priority thread is blocked by polling. (%d)\n", result); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, poll_value); result = cmdq_pkt_wait_flush_ex_result(h_poll); if (result < 0) CMDQ_TEST_FAIL( "Poll ability does not execute successfully. (%d)\n", result); cmdq_task_destroy(h_low); cmdq_task_destroy(h_poll); CMDQ_LOG("%s END\n", __func__); } static void testcase_mmsys_performance(s32 test_id) { struct thread_set_event_config config = { .event = CMDQ_SYNC_TOKEN_USER_0, .loop = true, .sleep_ms = 150}; switch (test_id) { case 0: /* test GCE config only in bus idle situation */ testcase_run_set_gce_event((void *)(&config)); msleep_interruptible(500); testcase_while_test_mmsys_bus(); msleep_interruptible(500); testcase_stop_set_gce_event(); break; case 1: /* test GCE config only when * CPU busy configure MMSYS situation */ testcase_run_set_gce_event((void *)(&config)); msleep_interruptible(500); testcase_run_busy_mmsys_config_loop(); msleep_interruptible(500); testcase_while_test_mmsys_bus(); msleep_interruptible(500); testcase_stop_busy_mmsys_config_loop(); msleep_interruptible(500); testcase_stop_set_gce_event(); break; case 2: /* test GCE config only when * CPU busy configure non-MMSYS situation */ testcase_run_set_gce_event((void *)(&config)); msleep_interruptible(500); testcase_run_busy_non_mmsys_config_loop(); msleep_interruptible(500); testcase_while_test_mmsys_bus(); msleep_interruptible(500); testcase_stop_busy_non_mmsys_config_loop(); msleep_interruptible(500); testcase_stop_set_gce_event(); break; default: CMDQ_LOG( "[TESTCASE] mmsys performance testcase Not Found: test_id:%d\n", test_id); break; } } void _testcase_boundary_mem_inst(u32 inst_num) { int i; struct cmdqRecStruct *handle = NULL; u32 data; u32 pattern = 0x0; const unsigned long MMSYS_DUMMY_REG = CMDQ_TEST_MMSYS_DUMMY_VA; CMDQ_REG_SET32(MMSYS_DUMMY_REG, 0xdeaddead); if (CMDQ_REG_GET32(MMSYS_DUMMY_REG) != 0xdeaddead) CMDQ_ERR("%s verify pattern register fail:0x%08x\n", __func__, (u32)CMDQ_REG_GET32(MMSYS_DUMMY_REG)); cmdqRecCreate(CMDQ_SCENARIO_DEBUG, &handle); cmdqRecReset(handle); cmdqRecSetSecure(handle, gCmdqTestSecure); /* Build a buffer with N instructions. */ CMDQ_MSG("%s record inst count:%u size:%u\n", __func__, inst_num, (u32)(inst_num * CMDQ_INST_SIZE)); for (i = 0; i < inst_num; i++) { pattern = i; cmdqRecWrite(handle, CMDQ_TEST_MMSYS_DUMMY_PA, pattern, ~0); } cmdq_op_finalize_command(handle, false); _test_flush_async(handle); cmdq_pkt_dump_command(handle); cmdq_pkt_wait_flush_ex_result(handle); cmdq_task_destroy(handle); /* verify data */ do { if (gCmdqTestSecure) { CMDQ_LOG("%s, timeout case in secure path\n", __func__); break; } data = CMDQ_REG_GET32(CMDQ_TEST_MMSYS_DUMMY_VA); if (pattern != data) { CMDQ_ERR( "TEST FAIL: reg value is 0x%08x not pattern 0x%08x\n", data, pattern); } } while (0); } void testcase_boundary_mem(void) { u32 inst_num = 0; u32 base_inst_num = 0; u32 buffer_num = 0; CMDQ_LOG("%s\n", __func__); /* test cross page from 1 to 3 cases */ for (buffer_num = 1; buffer_num < 4; buffer_num++) { base_inst_num = buffer_num * CMDQ_CMD_BUFFER_SIZE / CMDQ_INST_SIZE; /* * We check 0~4 cases. * Case 0: 3 inst (OP+EOC+JUMP) in last buffer * Case 1: 2 inst (EOC+JUMP) in last buffer * Case 2: last buffer empty, EOC+JUMP at end of previous buf * Case 3: EOC+JUMP+Blank at end of last buffer * Case 4: EOC+JUMP+2 Blank at end of last buffer */ for (inst_num = 0; inst_num < 5; inst_num++) _testcase_boundary_mem_inst(base_inst_num - inst_num); } CMDQ_LOG("%s END\n", __func__); } void testcase_boundary_mem_param(void) { u32 base_inst_num = 0; u32 buffer_num = (u32)gCmdqTestConfig[2]; u32 inst_num = (u32)gCmdqTestConfig[3]; CMDQ_LOG("%s\n", __func__); base_inst_num = buffer_num * CMDQ_CMD_BUFFER_SIZE / CMDQ_INST_SIZE; _testcase_boundary_mem_inst(base_inst_num - inst_num); CMDQ_LOG("%s END\n", __func__); } void _testcase_longloop_inst(u32 inst_num) { int i = 0; int status = 0; u32 data; u32 pattern = 0x0; const unsigned long DUMMY_REG_VA = CMDQ_TEST_GCE_DUMMY_VA; const unsigned long DUMMY_REG_PA = CMDQ_TEST_GCE_DUMMY_PA; CMDQ_REG_SET32(DUMMY_REG_VA, 0xdeaddead); if (CMDQ_REG_GET32(DUMMY_REG_VA) != 0xdeaddead) CMDQ_ERR("%s verify pattern register fail:0x%08x\n", __func__, (u32)CMDQ_REG_GET32(DUMMY_REG_VA)); cmdqRecCreate(CMDQ_SCENARIO_TRIGGER_LOOP, &hLoopReq); cmdqRecReset(hLoopReq); cmdqRecSetSecure(hLoopReq, false); cmdqRecWait(hLoopReq, CMDQ_SYNC_TOKEN_USER_0); cmdqRecWait(hLoopReq, CMDQ_SYNC_TOKEN_USER_0); g_loopIter = 0; cmdq_ttm.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_tltm.test_timer, _testcase_loop_timer_func, 0); mod_timer(&cmdq_tltm.test_timer, jiffies + msecs_to_jiffies(300)); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); /* * Build a buffer with N instructions. * The -2 for wait and clear instruction. */ CMDQ_MSG("%s record inst count:%u size:%u\n", __func__, inst_num, (u32)(inst_num * CMDQ_INST_SIZE)); for (i = 0; i < inst_num - 2; i++) { pattern = i + 1; cmdqRecWrite(hLoopReq, DUMMY_REG_PA, pattern, ~0); } /* should success */ status = cmdqRecStartLoop(hLoopReq); if (status != 0) CMDQ_MSG("TEST FAIL: Unable to start loop\n"); cmdq_pkt_dump_command((struct cmdqRecStruct *)hLoopReq->running_task); /* WAIT */ while (g_loopIter < 5) msleep_interruptible(500); CMDQ_MSG("%s ===== stop timer\n", __func__); cmdq_task_destroy(hLoopReq); del_timer(&cmdq_tltm.test_timer); /* verify data */ do { if (gCmdqTestSecure) { CMDQ_LOG("%s, timeout case in secure path\n", __func__); break; } data = CMDQ_REG_GET32(DUMMY_REG_VA); if (!(data >= 1 && data <= inst_num)) { CMDQ_ERR( "TEST FAIL: reg value is 0x%08x not pattern 1 to 0x%08x\n", data, pattern); } } while (0); } void testcase_longloop(void) { u32 last_inst = 0; u32 page_num = 0; CMDQ_LOG("%s\n", __func__); for (page_num = 1; page_num < 4; page_num++) { for (last_inst = 0; last_inst < 5; last_inst++) _testcase_longloop_inst(CMDQ_CMD_BUFFER_SIZE * page_num / CMDQ_INST_SIZE - last_inst); } CMDQ_LOG("%s\n", __func__); } s32 _testcase_secure_handle(u32 secHandle, enum CMDQ_SCENARIO_ENUM scenario) { #ifdef CMDQ_SECURE_PATH_SUPPORT struct cmdqRecStruct *hReqMDP; const u32 PATTERN_MDP = (1 << 0) | (1 << 2) | (1 << 16); s32 status; cmdq_task_create(scenario, &hReqMDP); cmdq_task_reset(hReqMDP); cmdq_task_set_secure(hReqMDP, true); /* specify use MDP engine */ hReqMDP->engineFlag = (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0); /* enable secure test */ cmdq_task_secure_enable_dapc(hReqMDP, (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0)); cmdq_task_secure_enable_port_security(hReqMDP, (1LL << CMDQ_ENG_MDP_RDMA0) | (1LL << CMDQ_ENG_MDP_WROT0)); /* record command */ cmdq_op_write_reg(hReqMDP, CMDQ_TEST_MMSYS_DUMMY_PA, PATTERN_MDP, ~0); cmdq_op_write_reg_secure(hReqMDP, CMDQ_TEST_MMSYS_DUMMY_PA, CMDQ_SAM_H_2_MVA, secHandle, 0xf000, 0x100, 0, 0); cmdq_append_command(hReqMDP, CMDQ_CODE_EOC, 0, 1, 0, 0); cmdq_append_command(hReqMDP, CMDQ_CODE_JUMP, 0, 8, 0, 0); status = cmdq_task_flush(hReqMDP); cmdq_task_destroy(hReqMDP); CMDQ_LOG("%s END\n", __func__); return status; #else return 0; #endif } void testcase_invalid_handle(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT s32 status; CMDQ_LOG("%s\n", __func__); /* In this case we use an invalid secure handle to check * error handling */ status = _testcase_secure_handle(0xdeaddead, CMDQ_SCENARIO_SUB_DISP); if (status >= 0) CMDQ_ERR( "TEST FAIL: should not success with invalid handle, status:%d\n", status); /* Handle 0 will make SW do not translate to PA. */ status = _testcase_secure_handle(0x0, CMDQ_SCENARIO_DEBUG); if (status >= 0) CMDQ_ERR( "TEST FAIL: should not success with handle 0, status:%d\n", status); CMDQ_LOG("%s END\n", __func__); #else CMDQ_ERR("%s failed since not support secure path\n", __func__); #endif } void _testcase_set_event(struct cmdqRecStruct *task, s32 thread) { cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); } void testcase_engineflag_conflict_dump(void) { struct cmdqRecStruct *handle, *handle2; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle); handle->engineFlag = CMDQ_ENG_MDP_RDMA0 | CMDQ_ENG_MDP_RSZ0; cmdq_op_wait_no_clear(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle, false); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle2); handle->engineFlag = CMDQ_ENG_MDP_RDMA0 | CMDQ_ENG_MDP_WROT0; cmdq_op_wait_no_clear(handle2, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle2, false); cmdq_mdp_flush_async_impl(handle); cmdq_mdp_flush_async_impl(handle2); /* After wait we should get conflict dump in log without crash. */ msleep_interruptible(50); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); cmdq_mdp_wait(handle, NULL); cmdq_mdp_wait(handle2, NULL); cmdq_task_destroy(handle); cmdq_task_destroy(handle2); CMDQ_LOG("%s END\n", __func__); } static void testcase_end_behavior(bool test_prefetch, u32 dummy_size) { cmdqBackupSlotHandle slot_handle = 0; u32 *cmd_end; s32 loop = 0; u32 thread = 1; u32 read_result = 0; u32 cmd_size, last_cmd; u32 *va_base; dma_addr_t pa_base, pa; CMDQ_LOG("%s START with test_prefetch:%d\n", __func__, test_prefetch); /* create command */ cmdq_alloc_mem(&slot_handle, 1); cmdqCoreClearEvent(CMDQ_SYNC_TOKEN_GPR_SET_4); va_base = cmdq_core_alloc_hw_buffer(cmdq_dev_get(), CMDQ_CMD_BUFFER_SIZE, &pa_base, GFP_KERNEL); if (!va_base) return; cmd_end = va_base; cmd_end[1] = (CMDQ_CODE_MOVE << 24) | ((CMDQ_DATA_REG_DEBUG_DST & 0x1f) << 16) | (4 << 21); cmd_end[0] = slot_handle; cmd_end[3] = (CMDQ_CODE_WRITE << 24) | ((CMDQ_DATA_REG_DEBUG_DST & 0x1f) << 16) | (4 << 21); cmd_end[2] = 1; for (loop = 2; loop < dummy_size+2; loop++) { cmd_end[loop*2+1] = (CMDQ_CODE_WRITE << 24) | ((CMDQ_DATA_REG_DEBUG_DST & 0x1f) << 16) | (4 << 21); cmd_end[loop*2] = 1; } cmd_end[loop*2+1] = (CMDQ_CODE_WFE << 24) | CMDQ_SYNC_TOKEN_GPR_SET_4; cmd_end[loop*2] = ((0 << 31) | (1 << 15) | 1); loop++; cmd_end[loop*2+1] = (CMDQ_CODE_WRITE << 24) | ((CMDQ_DATA_REG_DEBUG_DST & 0x1f) << 16) | (4 << 21); cmd_end[loop*2] = 2; last_cmd = loop; loop++; cmd_size = loop * CMDQ_INST_SIZE; CMDQ_LOG("verify END behavior with command size:%d\n", cmd_size); /* start command with 1st END position */ CMDQ_REG_SET32(CMDQ_THR_WARM_RESET(thread), 0x01); while (0x1 == (CMDQ_REG_GET32(CMDQ_THR_WARM_RESET(thread)))) { if (loop > CMDQ_MAX_LOOP_COUNT) CMDQ_ERR("Reset HW thread %d failed\n", thread); loop++; } CMDQ_REG_SET32(CMDQ_THR_INST_CYCLES(thread), 0); CMDQ_REG_SET32(CMDQ_THR_PREFETCH(thread), 0x1); if (test_prefetch) CMDQ_REG_SET32(CMDQ_THR_END_ADDR(thread), CMDQ_REG_SHIFT_ADDR(pa_base + cmd_size)); else { CMDQ_REG_SET32(CMDQ_THR_END_ADDR(thread), CMDQ_REG_SHIFT_ADDR( pa_base + cmd_size - CMDQ_INST_SIZE)); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_GPR_SET_4); } CMDQ_REG_SET32(CMDQ_THR_CURR_ADDR(thread), CMDQ_REG_SHIFT_ADDR(pa_base)); pa = cmdq_core_get_pc(thread); CMDQ_LOG("Step0: PC:%pa\n", &pa); CMDQ_REG_SET32(CMDQ_THR_CFG(thread), 0x6); CMDQ_REG_SET32(CMDQ_THR_IRQ_ENABLE(thread), 0x011); CMDQ_REG_SET32(CMDQ_THR_ENABLE_TASK(thread), 0x01); msleep_interruptible(50); /* check if GCE stop in expected position */ cmdq_cpu_read_mem(slot_handle, 0, &read_result); pa = cmdq_core_get_pc(thread); CMDQ_LOG("Step1: PC:%pa result:%d\n", &pa, read_result); if (read_result != 1) CMDQ_TEST_FAIL("%s read result fail: result:%d\n", __func__, read_result); /* adjust command and reset END addr */ cmd_end[last_cmd*2] = 3; CMDQ_REG_SET32(CMDQ_THR_END_ADDR(thread), CMDQ_REG_SHIFT_ADDR(pa_base + cmd_size)); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_GPR_SET_4); /* check if GCE read correct instruction */ cmdq_cpu_read_mem(slot_handle, 0, &read_result); pa = cmdq_core_get_pc(thread); CMDQ_LOG("Step2: PC:%pa result:%d\n", &pa, read_result); if ((!test_prefetch && read_result != 3) || (test_prefetch && read_result != 2)) CMDQ_TEST_FAIL("%s read result fail: result:%d\n", __func__, read_result); /* stop GCE thread */ pa = cmdq_core_get_pc(thread); CMDQ_LOG("Step3: PC:%pa\n", &pa); CMDQ_REG_SET32(CMDQ_THR_SUSPEND_TASK(thread), 0x01); if ((0x01 & CMDQ_REG_GET32(CMDQ_THR_ENABLE_TASK(thread))) == 0) CMDQ_LOG("[WARNING] thread %d suspend not effective\n", thread); loop = 0; while ((CMDQ_REG_GET32(CMDQ_THR_CURR_STATUS(thread)) & 0x2) == 0x0) { if (loop > CMDQ_MAX_LOOP_COUNT) CMDQ_ERR("Suspend HW thread %d failed\n", thread); loop++; } loop = 0; CMDQ_REG_SET32(CMDQ_THR_WARM_RESET(thread), 0x01); while (0x1 == (CMDQ_REG_GET32(CMDQ_THR_WARM_RESET(thread)))) { if (loop > CMDQ_MAX_LOOP_COUNT) CMDQ_ERR("Reset HW thread %d failed\n", thread); loop++; } CMDQ_REG_SET32(CMDQ_THR_ENABLE_TASK(thread), 0x00); cmdq_free_mem(slot_handle); cmdq_core_free_hw_buffer(cmdq_dev_get(), CMDQ_CMD_BUFFER_SIZE, va_base, pa_base); CMDQ_LOG("%s END\n", __func__); } static void testcase_end_addr_conflict(void) { struct cmdqRecStruct *loop_handle, *submit_handle; u32 index; u32 test_thread = cmdq_get_func()->dispThread( CMDQ_SCENARIO_DEBUG_PREFETCH); u32 test_thread_end = CMDQ_THR_FIX_END_ADDR(test_thread); /* build trigger loop to write END addr value */ cmdq_task_create(CMDQ_SCENARIO_TRIGGER_LOOP, &loop_handle); cmdq_task_reset(loop_handle); cmdq_task_set_secure(loop_handle, gCmdqTestSecure); for (index = 0; index < 48; index++) cmdq_op_write_reg(loop_handle, CMDQ_TEST_GCE_DUMMY_PA, test_thread_end, ~0); cmdq_task_start_loop(loop_handle); /* repeatly submit task to catch error */ cmdq_task_create(CMDQ_SCENARIO_DEBUG_PREFETCH, &submit_handle); for (index = 0; index < 48; index++) { cmdq_task_reset(submit_handle); cmdq_task_set_secure(submit_handle, gCmdqTestSecure); cmdq_op_write_reg(submit_handle, CMDQ_TEST_GCE_DUMMY_PA, test_thread_end, ~0); cmdq_task_flush(submit_handle); CMDQ_LOG("Flush test round #%u\n", index); msleep_interruptible(500); } cmdq_task_stop_loop(loop_handle); cmdq_task_destroy(loop_handle); cmdq_task_destroy(submit_handle); } void testcase_verify_timer(void) { struct cmdqRecStruct *handle = NULL; cmdqBackupSlotHandle slot_handle = 0; u32 start_time = 0, end_time = 0; const u32 tpr_mask = ~0; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TPR_MASK, tpr_mask); cmdq_alloc_mem(&slot_handle, 1); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_reset(handle); cmdq_op_backup_TPR(handle, slot_handle, 0); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &start_time); msleep_interruptible(10); cmdq_task_flush(handle); cmdq_cpu_read_mem(slot_handle, 0, &end_time); if (start_time != end_time) { CMDQ_LOG("TEST SUCCESS: start:%u end:%u dur:%u mask:0x%08x\n", start_time, end_time, end_time - start_time, tpr_mask); } else { CMDQ_TEST_FAIL("start:%u end:%u dur:%u mask:0x%08x\n", start_time, end_time, end_time - start_time, tpr_mask); } cmdq_task_destroy(handle); cmdq_free_mem(slot_handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_remove_by_file_node(void) { struct cmdqRecStruct *handle[2], *handle_conflict; u64 engines[2] = { 1LL << CMDQ_ENG_MDP_RSZ0, 1LL << CMDQ_ENG_MDP_WROT0, }; const u64 node = 0xfffffffcdead0000; s32 status; CMDQ_LOG("%s\n", __func__); cmdqCoreClearEvent(CMDQ_SYNC_TOKEN_USER_0); cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle[0]); cmdq_task_set_secure(handle[0], gCmdqTestSecure); cmdq_task_set_engine(handle[0], engines[0]); cmdq_op_wait(handle[0], CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle[0], false); status = _test_flush_task_async(handle[0]); if (status < 0) { CMDQ_TEST_FAIL("%s flush handle 0 fail:%d\n", __func__, status); return; } cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle[1]); cmdq_task_set_secure(handle[1], gCmdqTestSecure); cmdq_task_set_engine(handle[1], engines[1]); cmdq_op_wait(handle[1], CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle[1], false); status = _test_flush_task_async(handle[1]); if (status < 0) { CMDQ_TEST_FAIL("%s flush handle 1 fail:%d\n", __func__, status); return; } cmdq_task_create(CMDQ_SCENARIO_DEBUG_MDP, &handle_conflict); cmdq_task_set_secure(handle_conflict, gCmdqTestSecure); cmdq_task_set_engine(handle_conflict, engines[0] | engines[1]); cmdq_op_wait(handle_conflict, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_finalize_command(handle_conflict, false); status = _test_flush_task_async(handle_conflict); if (status < 0) { CMDQ_TEST_FAIL("%s flush handle conflict fail:%d\n", __func__, status); return; } handle[0]->node_private = (void *)(unsigned long)node; handle[1]->node_private = (void *)(unsigned long)node; handle_conflict->node_private = (void *)(unsigned long)node; /* all task should be remove and no crash */ cmdq_mdp_release_task_by_file_node((void *)(unsigned long)node); CMDQ_LOG("%s END\n", __func__); } static void testcase_verify_cpr(void) { struct cmdqRecStruct *handle; s32 status; u32 idx, val; const u32 pattern = 0xdeadabcd; CMDQ_VARIABLE var; struct cmdq_pkt_buffer *buf; u32 *va; CMDQ_LOG("%s\n", __func__); cmdq_op_init_global_cpr_variable(&var, 0); cmdq_task_create(CMDQ_SCENARIO_PRIMARY_DISP, &handle); cmdq_op_assign(handle, &var, pattern); cmdq_op_write_reg(handle, CMDQ_TEST_GCE_DUMMY_PA, var, ~0); cmdq_op_finalize_command(handle, false); cmdq_pkt_dump_command(handle); buf = list_first_entry(&handle->pkt->buf, typeof(*buf), list_entry); va = (u32 *)buf->va_base; for (idx = 0; idx < cmdq_core_get_cpr_cnt(); idx++) { CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0); va[1] = (va[1] & 0xFFFF0000) | (CMDQ_CPR_STRAT_ID + idx); status = _test_flush_task(handle); if (status < 0) { CMDQ_TEST_FAIL("%s flush %u fail:%d\n", __func__, idx, status); break; } val = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (val != pattern) { CMDQ_TEST_FAIL( "cpr:0x%x result:0x%08x pattern:0x%08x\n", idx, val, pattern); break; } } cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } static void testcase_simple_run(struct cmdq_pkt *pkt) { const u32 thread_id = 1; struct cmdq_pkt_buffer *buf; u32 cnt = 0; u32 warm_reset = 0; CMDQ_REG_SET32((void *)CMDQ_THR_WARM_RESET(thread_id), 1); if (readl_poll_timeout_atomic((void *)CMDQ_THR_WARM_RESET(thread_id), warm_reset, !(warm_reset & 1), 0, 10)) { CMDQ_LOG("reset GCE thread %d failed\n", thread_id); } buf = list_first_entry(&pkt->buf, typeof(*buf), list_entry); CMDQ_REG_SET32(CMDQ_THR_CURR_ADDR(thread_id), buf->pa_base); CMDQ_REG_SET32(CMDQ_THR_END_ADDR(thread_id), buf->pa_base + pkt->cmd_buf_size); CMDQ_REG_SET32(CMDQ_THR_ENABLE_TASK(thread_id), 0x1); while (CMDQ_REG_GET32(CMDQ_THR_CURR_ADDR(thread_id)) < buf->pa_base + pkt->cmd_buf_size && cnt++ < 200) msleep_interruptible(5); if (CMDQ_REG_GET32(CMDQ_THR_CURR_ADDR(thread_id)) < buf->pa_base + pkt->cmd_buf_size) { CMDQ_ERR("%s fail\n", __func__); } CMDQ_LOG("%s spr:0x%x 0x%x 0x%x 0x%x dummy:0x%x\n", __func__, CMDQ_REG_GET32(CMDQ_THR_SPR0(thread_id)), CMDQ_REG_GET32(CMDQ_THR_SPR1(thread_id)), CMDQ_REG_GET32(CMDQ_THR_SPR2(thread_id)), CMDQ_REG_GET32(CMDQ_THR_SPR3(thread_id)), CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA)); } #define CMDQ_DATA_VAR (CMDQ_BIT_VAR<pkt); cmdq_pkt_dump_command(handle); cmdq_task_destroy(handle); cmdq_cpu_read_mem(slot, 0, &result); if (value != result) CMDQ_ERR("slot 0x%08x should be 0x%08x\n", result, value); else CMDQ_LOG("[SUCCESS]slot = 0x%08x SUCCESS\n", result); cmdq_free_mem(slot); cmdq_dev_enable_gce_clock(false); } static void testcase_write_dma(void) { testcase_write_dma_value(0); testcase_write_dma_value(1); } void testcase_cmdq_trigger_devapc(void) { struct cmdqRecStruct *handle = NULL; u32 PATTERN = 0xdeadabcd; u32 dummy_pa = 0x14009000; /* use CMDQ to set to PATTERN */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_op_write_reg(handle, dummy_pa, PATTERN, ~0); cmdq_task_flush(handle); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); } #ifdef CMDQ_SECURE_PATH_SUPPORT void testcase_read_pq_sec(void) { struct cmdqRecStruct *handle = NULL; const u32 total_read = 1024; u32 i; dma_addr_t out_pa; u32 *out_va; CMDQ_LOG("%s\n", __func__); /* Create Slot */ cmdq_dev_enable_gce_clock(true); out_va = (u32 *)dma_alloc_coherent(cmdq_dev_get(), total_read * 4, &out_pa, GFP_KERNEL); /* assign efault value to detect */ for (i = 0; i < total_read; i++) out_va[i] = 0xdead0000 + i; /* flush empty task to secure world */ cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); cmdq_task_set_secure(handle, true); handle->secData.extension = 0xffff; handle->reg_values = out_va; handle->reg_values_pa = out_pa; handle->reg_count = total_read; cmdq_pkt_jump(handle->pkt, CMDQ_INST_SIZE); _test_flush_task(handle); handle->reg_values = NULL; handle->reg_values_pa = 0; handle->reg_count = 0; CMDQ_LOG("dump results ...\n"); for (i = 0; i < total_read; i += 16) CMDQ_LOG( "%#x:%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x\n", i, out_va[i], out_va[i+1], out_va[i+2], out_va[i+3], out_va[i+4], out_va[i+5], out_va[i+6], out_va[i+7], out_va[i+8], out_va[i+9], out_va[i+10], out_va[i+11], out_va[i+12], out_va[i+13], out_va[i+14], out_va[i+15]); CMDQ_LOG("spr:%#x %#x %#x %#x gpr:%#x %#x %#x\n", CMDQ_REG_GET32((GCE_BASE_VA + 0xF0)), CMDQ_REG_GET32((GCE_BASE_VA + 0xF4)), CMDQ_REG_GET32((GCE_BASE_VA + 0xF8)), CMDQ_REG_GET32((GCE_BASE_VA + 0xFC)), CMDQ_REG_GET32(CMDQ_GPR_R32(5)), CMDQ_REG_GET32(CMDQ_GPR_R32(8)), CMDQ_REG_GET32(CMDQ_GPR_R32(9))); dma_free_coherent(cmdq_dev_get(), total_read * 4, out_va, out_pa); cmdq_task_destroy(handle); cmdq_dev_enable_gce_clock(false); } #endif /* CMDQ driver stress test */ enum ENGINE_POLICY_ENUM { CMDQ_TESTCASE_ENGINE_NOT_SET, CMDQ_TESTCASE_ENGINE_SAME, CMDQ_TESTCASE_ENGINE_RANDOM, }; enum WAIT_POLICY_ENUM { CMDQ_TESTCASE_WAITOP_NOT_SET, CMDQ_TESTCASE_WAITOP_ALWAYS, CMDQ_TESTCASE_WAITOP_RANDOM, CMDQ_TESTCASE_WAITOP_RANDOM_NOTIMEOUT, CMDQ_TESTCASE_WAITOP_BEFORE_END, }; enum BRANCH_POLICY_ENUM { CMDQ_TESTCASE_BRANCH_NONE = 0, CMDQ_TESTCASE_BRANCH_CONTINUE, CMDQ_TESTCASE_BRANCH_BREAK, CMDQ_TESTCASE_BRANCH_MAX, }; enum CONDITION_TEST_POLICY_ENUM { CMDQ_TESTCASE_CONDITION_NONE, CMDQ_TESTCASE_CONDITION_RANDOM, CMDQ_TESTCASE_CONDITION_RANDOM_MORE, }; enum ROUND_LOOP_TIME_POLICY_ENUM { CMDQ_TESTCASE_LOOP_FAST = 4, CMDQ_TESTCASE_LOOP_MEDIUM = 16, CMDQ_TESTCASE_LOOP_SLOW = 60, }; enum POLL_POLICY_ENUM { CMDQ_TESTCASE_POLL_NONE, CMDQ_TESTCASE_POLL_PASS, CMDQ_TESTCASE_POLL_ALL, }; enum TRIGGER_THREAD_POLICY_ENUM { CMDQ_TESTCASE_TRIGGER_RANDOM = 0, CMDQ_TESTCASE_TRIGGER_FAST = 4, CMDQ_TESTCASE_TRIGGER_MEDIUM = 16, CMDQ_TESTCASE_TRIGGER_SLOW = 80, }; enum SECURE_POLICY_ENUM { CMDQ_TESTCASE_NO_SECURE, CMDQ_TESTCASE_SECURE_RANDOM, }; struct stress_policy { enum ENGINE_POLICY_ENUM engines_policy; enum WAIT_POLICY_ENUM wait_policy; enum CONDITION_TEST_POLICY_ENUM condition_policy; enum ROUND_LOOP_TIME_POLICY_ENUM loop_policy; enum POLL_POLICY_ENUM poll_policy; enum TRIGGER_THREAD_POLICY_ENUM trigger_policy; enum SECURE_POLICY_ENUM secure_policy; }; struct thread_param { struct completion cmplt; atomic_t stop; u32 run_count; bool multi_task; struct stress_policy policy; }; struct random_data { struct work_struct release_work; struct thread_param *thread; struct cmdqRecStruct *handle; enum CMDQ_SCENARIO_ENUM scenario; atomic_t *ref_count; cmdqBackupSlotHandle slot; u32 round; u32 *slot_expect_values; u32 slot_reserve_count; u32 slot_count; u32 last_write; u32 mask; u32 inst_count; u32 wait_count; unsigned long dummy_reg_pa; unsigned long dummy_reg_va; u32 expect_result; CMDQ_VARIABLE var_result; bool may_wait; u32 condition_count; u32 poll_count; }; /* trigger thread only set these bits */ #define CMDQ_TEST_POLL_BIT 0xFFFFFFF /* secure task command buffer is limited */ #define CMDQ_MAX_SECURE_INST_COUNT (0x7F00 / CMDQ_INST_SIZE) bool _is_boundary_offset(u32 offset) { u32 offset_idx = offset / CMDQ_INST_SIZE; u32 buffer_inst_count = CMDQ_CMD_BUFFER_SIZE / CMDQ_INST_SIZE; u32 offset_idx_mod = ((offset_idx + (offset_idx / (buffer_inst_count - 1))) % buffer_inst_count); return (offset_idx_mod == 0 || offset_idx_mod == 1); } static bool _append_op_read_mem_to_reg(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { u32 slot_idx = 0; slot_idx = (get_random_int() % data->slot_count) + data->slot_reserve_count; cmdq_op_read_mem_to_reg(handle, data->slot, slot_idx, data->dummy_reg_pa); data->last_write = data->slot_expect_values[slot_idx]; return true; } static bool _append_op_read_reg_to_mem(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { u32 slot_idx = 0; if (data->thread->multi_task) return true; slot_idx = (get_random_int() % data->slot_count) + data->slot_reserve_count; cmdq_op_read_reg_to_mem(handle, data->slot, slot_idx, data->dummy_reg_pa); data->slot_expect_values[slot_idx] = data->last_write; return true; } static bool _append_op_write_reg(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { u32 random_value = get_random_int(); bool use_mask = get_random_int() % 10; if (use_mask) { data->mask = get_random_int(); random_value = (data->last_write & ~data->mask) | (random_value & data->mask); } else { data->mask = ~0; } cmdq_op_write_reg(handle, data->dummy_reg_pa, data->last_write, data->mask); data->last_write = random_value; return true; } static bool _append_op_wait(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { const u32 max_wait_count = 2; const u32 max_wait_bound_count = 5; const unsigned long tokens[] = { CMDQ_SYNC_TOKEN_USER_0, CMDQ_SYNC_TOKEN_USER_1 }; unsigned long token; if (!data->may_wait || data->wait_count > max_wait_bound_count) return true; /* we save few chance to insert wait in boundary case */ if (data->wait_count > max_wait_count && handle->pkt->cmd_buf_size >= CMDQ_CMD_BUFFER_SIZE && !_is_boundary_offset(handle->pkt->cmd_buf_size)) return true; token = tokens[get_random_int() % 2]; data->wait_count++; if (get_random_int() % 8) cmdq_op_wait_no_clear(handle, token); else cmdq_op_wait(handle, token); return true; } static bool _append_op_poll(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { const unsigned long dummy_poll_pa = CMDQ_GPR_R32_PA(CMDQ_GET_GPR_PX2RX_LOW( CMDQ_DATA_REG_2D_SHARPNESS_0_DST)); const u32 max_poll_count = 2; const u32 min_poll_instruction = 18; u32 poll_bit = 0; u32 size_before = handle->pkt->cmd_buf_size; if (data->thread->policy.poll_policy == CMDQ_TESTCASE_POLL_NONE || limit_size < CMDQ_INST_SIZE * min_poll_instruction || data->poll_count >= max_poll_count) return true; CMDQ_MSG("%s limit:%u block size:%zu\n", __func__, limit_size, handle->pkt->cmd_buf_size); data->poll_count++; if (data->thread->policy.poll_policy == CMDQ_TESTCASE_POLL_PASS) poll_bit = 1 << (get_random_int() % 28); else poll_bit = 1 << (get_random_int() % 32); cmdq_op_write_reg(handle, dummy_poll_pa, 0, poll_bit); size_before = handle->pkt->cmd_buf_size; cmdq_op_poll(handle, dummy_poll_pa, poll_bit, poll_bit); CMDQ_LOG( "%s round:%u poll:0x%08x count:%u block size:%zu op size:%zu scenario:%d\n", __func__, data->round, poll_bit, data->poll_count, handle->pkt->cmd_buf_size, handle->pkt->cmd_buf_size - size_before, handle->scenario); return true; } static bool _append_op_add(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { cmdq_op_add(handle, &data->var_result, data->var_result, 1); data->expect_result++; return true; } bool _append_op_while(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size, u32 *op_result) { /* * Pattern of generate op: * while (variable < condition_limit) * variable = variable + 1 (1~N times) * break/continue/empty * variable = variable + 1 (0~N times) * end while */ u32 expect_result = 0; u32 condition_limit, pre_op_count, post_op_count, i; u32 remain_op_count = limit_size / CMDQ_INST_SIZE; enum BRANCH_POLICY_ENUM branch_op; if (data->expect_result >= 0xFFFF) return true; /* condition value accept max 16 bit value */ condition_limit = get_random_int() % (0xFFFF - data->expect_result); pre_op_count = get_random_int() % remain_op_count + 1; post_op_count = remain_op_count > pre_op_count ? get_random_int() % (remain_op_count - pre_op_count) : 0; branch_op = get_random_int() % (u32)CMDQ_TESTCASE_BRANCH_MAX; if (limit_size > 0xFFFF || pre_op_count > 0xFFFF || post_op_count > 0xFFFF) { CMDQ_ERR( "%s start size:%zu limit:%u pre/post:%u/%u op:%u current:%u condition:%u\n", __func__, handle->pkt->cmd_buf_size, limit_size, pre_op_count, post_op_count, branch_op, data->expect_result, condition_limit); return false; } CMDQ_MSG( "%s start size:%zu limit:%u pre/post:%u/%u op:%u current:%u condition:%u\n", __func__, handle->pkt->cmd_buf_size, limit_size, pre_op_count, post_op_count, branch_op, data->expect_result, condition_limit); cmdq_op_while(handle, data->var_result, CMDQ_LESS_THAN, condition_limit + data->expect_result); for (i = 0; i < pre_op_count; i++) cmdq_op_add(handle, &data->var_result, data->var_result, 1); if (data->expect_result < data->expect_result + condition_limit) { switch (branch_op) { case CMDQ_TESTCASE_BRANCH_CONTINUE: cmdq_op_continue(handle); while (expect_result < condition_limit) expect_result += pre_op_count; break; case CMDQ_TESTCASE_BRANCH_BREAK: cmdq_op_break(handle); expect_result = pre_op_count; break; case CMDQ_TESTCASE_BRANCH_NONE: default: while (expect_result < condition_limit) expect_result += pre_op_count + post_op_count; break; } } else { /* content of while loop would not run */ expect_result = 0; } for (i = 0; i < post_op_count; i++) cmdq_op_add(handle, &data->var_result, data->var_result, 1); cmdq_op_end_while(handle); *op_result = expect_result; data->condition_count += 1; CMDQ_MSG("%s result:%u size:%zu\n", __func__, expect_result, handle->pkt->cmd_buf_size); return true; } bool _test_is_condition_match(enum CMDQ_CONDITION_ENUM condition_op, u32 arg_a, u32 arb_b) { bool match; switch (condition_op) { case CMDQ_EQUAL: match = (arg_a == arb_b); break; case CMDQ_NOT_EQUAL: match = (arg_a != arb_b); break; case CMDQ_GREATER_THAN_AND_EQUAL: match = (arg_a >= arb_b); break; case CMDQ_LESS_THAN_AND_EQUAL: match = (arg_a <= arb_b); break; case CMDQ_GREATER_THAN: match = (arg_a > arb_b); break; case CMDQ_LESS_THAN: match = (arg_a < arb_b); break; default: CMDQ_TEST_FAIL("%s cannot recognize IF condition:%u\n", __func__, (s32)condition_op); match = false; break; } return match; } static bool _append_op_if(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size, u32 *op_result) { /* * Pattern of generate op: * if (variable op condition_limit) * variable = variable + 1 (1~N times) * else if (variable op condition_limit) (0~N times) * variable = variable + 1 (1~N times) * else/empty * variable = variable + 1 (1~N times) * end if */ const u32 min_op_count = 2; const u32 reserve_op_count = 2; u32 current_result = 0; u32 ramain_op_count = limit_size / CMDQ_INST_SIZE; u32 op_if_counter = 0; bool already_matched = false; bool terminate = false; enum IF_BRANCH_OP_ENUM { BRANCH_NONE, BRANCH_ELSEIF, BRANCH_ELSE, BRANCH_MAX }; if (data->expect_result >= 0xFFFF) return true; CMDQ_MSG("%s start size:%zu limit:%u\n", __func__, handle->pkt->cmd_buf_size, limit_size); while (ramain_op_count > min_op_count + reserve_op_count && !terminate && data->expect_result + current_result < 0xFFFF) { u32 logic_op_count = 0; u32 condition_limit, i; enum IF_BRANCH_OP_ENUM branch_op; enum CMDQ_CONDITION_ENUM condition_op; /* reserve for if/add+1 */ ramain_op_count -= reserve_op_count; /* condition value accept max 16 bit value */ condition_limit = get_random_int() % (0xFFFF - current_result - data->expect_result); condition_op = get_random_int() % (u32)CMDQ_CONDITION_MAX; if (op_if_counter == 0) { cmdq_op_if(handle, data->var_result, condition_op, condition_limit); branch_op = BRANCH_MAX; } else { branch_op = get_random_int() % (u32)BRANCH_MAX; switch (branch_op) { case BRANCH_ELSEIF: cmdq_op_else_if(handle, data->var_result, condition_op, condition_limit); break; case BRANCH_ELSE: cmdq_op_else(handle); break; case BRANCH_NONE: default: terminate = true; break; } } if (terminate) break; logic_op_count = get_random_int() % ramain_op_count + 1; for (i = 0; i < logic_op_count; i++) cmdq_op_add(handle, &data->var_result, data->var_result, 1); ramain_op_count -= logic_op_count; if (!already_matched) { if (branch_op != BRANCH_ELSE) { already_matched = _test_is_condition_match( condition_op, data->expect_result, condition_limit); } else { already_matched = true; } if (already_matched) current_result += logic_op_count; } if (branch_op == BRANCH_ELSE) terminate = true; CMDQ_MSG( "%s if:%u remaind:%u logic:%u condition:%u branch:%u limit:%u matched:%u\n", __func__, op_if_counter, ramain_op_count, logic_op_count, (u32)condition_op, (s32)branch_op, condition_limit, already_matched); op_if_counter++; } if (op_if_counter > 0) { cmdq_op_end_if(handle); data->condition_count += 1; } *op_result = current_result; CMDQ_MSG("%s result:%u size:%zu\n", __func__, current_result, handle->pkt->cmd_buf_size); return true; } static bool _append_op_condition(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size) { typedef bool(*append_op_func)(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size, u32 *result); const append_op_func op_funcs[] = { _append_op_while, _append_op_if, }; const u32 min_op_count = 4; u32 op_result = 0; u32 limit_op_count; CMDQ_MSG("%s start limit:%u\n", __func__, limit_size); if (data->thread->policy.condition_policy == CMDQ_TESTCASE_CONDITION_NONE) return true; if (data->thread->policy.condition_policy == CMDQ_TESTCASE_CONDITION_RANDOM && data->condition_count >= 1) return true; /* make op count 4~N */ limit_op_count = get_random_int() % (limit_size / CMDQ_INST_SIZE - min_op_count); limit_op_count += min_op_count; if (!op_funcs[get_random_int() % ARRAY_SIZE(op_funcs)](handle, data, limit_op_count * CMDQ_INST_SIZE, &op_result)) return false; data->expect_result += op_result; CMDQ_MSG("%s op result:%u expect:%u\n", __func__, op_result, limit_size); return true; } static bool _append_random_instructions(struct cmdqRecStruct *handle, struct random_data *random_context, u32 limit_size) { typedef bool(*append_op_func)(struct cmdqRecStruct *handle, struct random_data *data, u32 limit_size); const append_op_func op_funcs[] = { _append_op_read_mem_to_reg, _append_op_read_reg_to_mem, _append_op_write_reg, _append_op_wait, _append_op_poll, /* v3 instructions */ _append_op_add, _append_op_condition, }; const u32 min_condition_op_count = 4; while (handle->pkt->cmd_buf_size < limit_size) { u32 total_func = ARRAY_SIZE(op_funcs); s32 op_idx; if (limit_size - handle->pkt->cmd_buf_size <= CMDQ_INST_SIZE * min_condition_op_count) total_func--; op_idx = get_random_int() % total_func; if (!op_funcs[op_idx](handle, random_context, limit_size - handle->pkt->cmd_buf_size)) return false; } return true; } bool _stress_is_ignore_timeout(struct stress_policy *policy) { bool ignore = false; /* exclude some case that timeout is expected */ if (policy->wait_policy != CMDQ_TESTCASE_WAITOP_NOT_SET) { /* insert wait op into testcase will trigger timeout */ ignore = true; } else if (policy->engines_policy == CMDQ_TESTCASE_ENGINE_RANDOM) { /* random engine flag may cause task never able to run, * thus timedout */ ignore = true; } else if (policy->poll_policy != CMDQ_TESTCASE_POLL_NONE) { /* timeout due to poll fail */ ignore = true; } return ignore; } static void _dump_stress_task_result(s32 status, struct random_data *random_context) { u32 result_val = 0, i = 0; bool error_happen = false; do { if (status == -ETIMEDOUT) { error_happen = !_stress_is_ignore_timeout( &random_context->thread->policy); if (!error_happen) { CMDQ_LOG( "Timeout handle:0x%p round:%u skip compare ...\n", random_context->handle, random_context->round); } break; } else if (status == -EINVAL && random_context->handle->thread == CMDQ_INVALID_THREAD && !cmdq_get_func()->isDynamic( random_context->handle->scenario)) { CMDQ_LOG( "Fail to run handle:0x%p round:%u scenario:%d thread:%d skip compare\n", random_context->handle, random_context->round, random_context->handle->scenario, random_context->handle->thread); } else if (status < 0) { error_happen = true; break; } /* register may write by other task, thus only check in * multi-task case */ if (!random_context->thread->multi_task) { result_val = CMDQ_REG_GET32( random_context->dummy_reg_va); if (result_val != random_context->last_write) { CMDQ_TEST_FAIL( "Reg value does not match:0x%08x to 0x%08x round:%u\n", result_val, random_context->last_write, random_context->round); error_happen = true; } } for (i = random_context->slot_reserve_count; i < random_context->slot_reserve_count + random_context->slot_count; i++) { cmdq_cpu_read_mem(random_context->slot, i, &result_val); if (result_val != random_context->slot_expect_values[i]) { CMDQ_TEST_FAIL( "Slot %u value does not match expect:0x%08x reg 0x%08x round:%u\n", i, random_context->slot_expect_values[i], result_val, random_context->round); error_happen = true; } } cmdq_cpu_read_mem(random_context->slot, 1, &result_val); if (result_val != random_context->expect_result) { CMDQ_TEST_FAIL( "Counter value not match expect:0x%08x mem:0x%08x round:%u\n", random_context->expect_result, result_val, random_context->round); error_happen = true; } } while (0); if (error_happen) { char long_msg[CMDQ_LONGSTRING_MAX]; u32 msg_offset; s32 msg_max_size; s32 poll_value = CMDQ_REG_GET32(CMDQ_GPR_R32( CMDQ_GET_GPR_PX2RX_LOW( CMDQ_DATA_REG_2D_SHARPNESS_0_DST))); atomic_set(&random_context->thread->stop, 1); cmdq_long_string_init(true, long_msg, &msg_offset, &msg_max_size); cmdq_long_string(long_msg, &msg_offset, &msg_max_size, "handle:0x%p round:%u size:%zu wait:%d:%d multi:%d engine:%d", random_context->handle, random_context->round, random_context->handle->pkt->cmd_buf_size, random_context->may_wait, random_context->wait_count, random_context->thread->multi_task, random_context->thread->policy.engines_policy); cmdq_long_string(long_msg, &msg_offset, &msg_max_size, " condition:%d conditions:%u status:%d poll:%d reg:0x%08x count:%u exec:%d\n", random_context->thread->policy.condition_policy, random_context->condition_count, status, random_context->thread->policy.poll_policy, poll_value, random_context->poll_count, atomic_read(&random_context->handle->exec)); CMDQ_TEST_FAIL("%s", long_msg); /* wait other threads stop print messages */ msleep_interruptible(10); cmdq_pkt_dump_command(random_context->handle); } } static void _testcase_stress_release_work(struct work_struct *work_item) { struct random_data *random_context = (struct random_data *)container_of( work_item, struct random_data, release_work); s32 status = 0; do { if (!random_context->handle) { CMDQ_TEST_FAIL("Handle not exists, round:%u\n", random_context->round); break; } status = _test_wait_task(random_context->handle); _dump_stress_task_result(status, random_context); } while (0); cmdq_task_destroy(random_context->handle); cmdq_free_mem(random_context->slot); atomic_dec(random_context->ref_count); kfree(random_context->slot_expect_values); kfree(random_context); } void _testcase_stress_submit_release_work(struct work_struct *work_item) { struct random_data *random_context = (struct random_data *)container_of( work_item, struct random_data, release_work); s32 status = 0; do { status = _test_flush_task(random_context->handle); _dump_stress_task_result(status, random_context); } while (0); cmdq_task_destroy(random_context->handle); cmdq_free_mem(random_context->slot); atomic_dec(random_context->ref_count); kfree(random_context->slot_expect_values); kfree(random_context); } void _testcase_on_exec_suspend(struct cmdqRecStruct *task, s32 thread) { const unsigned long tokens[] = { CMDQ_SYNC_TOKEN_USER_0, CMDQ_SYNC_TOKEN_USER_1 }; cmdqCoreSetEvent(tokens[get_random_int() % ARRAY_SIZE(tokens)]); } #define MAX_RELEASE_QUEUE 4 static const u32 stress_engines[] = {CMDQ_ENG_MDP_CAMIN, CMDQ_ENG_MDP_RDMA0, CMDQ_ENG_MDP_RDMA1, CMDQ_ENG_MDP_WROT0, CMDQ_ENG_MDP_WROT1}; static const u32 stress_engines_disp[] = {CMDQ_ENG_DISP_AAL, CMDQ_ENG_DISP_OVL0, CMDQ_ENG_DISP_COLOR0, CMDQ_ENG_DISP_RDMA0}; static const enum CMDQ_SCENARIO_ENUM stress_scene[] = { CMDQ_SCENARIO_DEBUG_MDP, CMDQ_SCENARIO_DEBUG_MDP, CMDQ_SCENARIO_DEBUG_MDP, CMDQ_SCENARIO_DEBUG, CMDQ_SCENARIO_PRIMARY_DISP, CMDQ_SCENARIO_SUB_DISP}; static s32 _testcase_gen_task_thread_each(void *data, u32 task_count, atomic_t *task_ref_count, u32 engine_sel, bool ignore_timeout, struct workqueue_struct *release_queues[]) { struct thread_param *thread_data = (struct thread_param *)data; const unsigned long dummy_reg_va = CMDQ_TEST_GCE_DUMMY_VA; const unsigned long dummy_reg_pa = CMDQ_TEST_GCE_DUMMY_PA; const u32 inst_count_pattern[] = { 1, 2, 3, 4, 5, 6, 7, 8, 32, 64, 128, 256, 512, 1024}; const u32 reserve_slot_count = 2; const u32 max_slot_count = 4; const u32 total_slot = reserve_slot_count + max_slot_count; const u32 max_buffer_count = 4; const u32 *curr_eng = stress_engines; u32 engine_cnt = 0; struct random_data *random_context = NULL; struct cmdqRecStruct *handle = NULL; u32 i = 0; s32 status = 0; #ifdef CMDQ_SECURE_PATH_SUPPORT bool is_secure = (get_random_int() % 20) == 0; #endif random_context = kzalloc(sizeof(*random_context), GFP_KERNEL); random_context->thread = thread_data; random_context->round = task_count; random_context->dummy_reg_pa = dummy_reg_pa; random_context->dummy_reg_va = dummy_reg_va; random_context->ref_count = task_ref_count; random_context->slot_reserve_count = reserve_slot_count; random_context->slot_count = max_slot_count; random_context->slot_expect_values = kcalloc(total_slot, sizeof(*random_context->slot_expect_values), GFP_KERNEL); random_context->scenario = stress_scene[get_random_int() % ARRAY_SIZE(stress_scene)]; if (cmdq_get_func()->isDynamic(random_context->scenario)) { curr_eng = stress_engines; engine_cnt = ARRAY_SIZE(stress_engines); } else { curr_eng = stress_engines_disp; engine_cnt = ARRAY_SIZE(stress_engines_disp); } cmdq_alloc_mem(&random_context->slot, total_slot); for (i = 0; i < reserve_slot_count; i++) { /* * slot 0: Final dummy register value read back. * slot 1: Logic add counter */ cmdq_cpu_write_mem(random_context->slot, i, 0); random_context->slot_expect_values[i] = 0; } for (i = reserve_slot_count; i < total_slot; i++) { cmdq_cpu_write_mem(random_context->slot, i, i); random_context->slot_expect_values[i] = i; } /* if final result is 0xff000000, this task never run */ cmdq_cpu_write_mem(random_context->slot, 0, 0xff000000); cmdq_task_create(random_context->scenario, &handle); cmdq_task_reset(handle); #ifdef CMDQ_SECURE_PATH_SUPPORT cmdq_task_set_secure(handle, is_secure); #endif random_context->handle = handle; /* variable for final result */ cmdq_op_init_variable(&random_context->var_result); switch (thread_data->policy.wait_policy) { case CMDQ_TESTCASE_WAITOP_NOT_SET: case CMDQ_TESTCASE_WAITOP_BEFORE_END: random_context->may_wait = false; break; case CMDQ_TESTCASE_WAITOP_ALWAYS: random_context->may_wait = true; break; case CMDQ_TESTCASE_WAITOP_RANDOM: case CMDQ_TESTCASE_WAITOP_RANDOM_NOTIMEOUT: /* give 1/10 chance the task has wait instructions */ random_context->may_wait = get_random_int() % 10; break; default: random_context->may_wait = get_random_int() % 10; break; } for (i = 0; i < (get_random_int() % max_buffer_count) + 1; i++) { u32 seed = get_random_int() % ARRAY_SIZE(inst_count_pattern); random_context->inst_count += inst_count_pattern[seed]; } #ifdef CMDQ_SECURE_PATH_SUPPORT /* decide if this task can be secure */ if (thread_data->policy.secure_policy == CMDQ_TESTCASE_SECURE_RANDOM && random_context->inst_count < CMDQ_MAX_SECURE_INST_COUNT) { random_context->dummy_reg_pa = CMDQ_TEST_MMSYS_DUMMY_PA; random_context->dummy_reg_va = CMDQ_TEST_MMSYS_DUMMY_VA; } #endif /* set engine flag and priority */ handle->pkt->priority = get_random_int() % 16; if (thread_data->policy.engines_policy == CMDQ_TESTCASE_ENGINE_RANDOM) { u64 rand_eng = 0; engine_sel = get_random_int() % ((1 << engine_cnt) - 1); for (i = 0; i < engine_cnt; i++) { if (((1 << i) & engine_sel) != 0) rand_eng = rand_eng | (1 << curr_eng[i]); } cmdq_task_set_engine(handle, rand_eng); } else { cmdq_task_set_engine(handle, engine_sel); } if (!thread_data->multi_task) { /* clear the reg first */ cmdq_op_write_reg(handle, dummy_reg_pa, 0, ~0); } cmdq_op_assign(handle, &random_context->var_result, 0); /* append random instructions */ if (random_context->inst_count > 4) { if (!_append_random_instructions(handle, random_context, (random_context->inst_count - 4) * CMDQ_INST_SIZE)) { CMDQ_ERR( "%s error during append random instructions\n", __func__); atomic_set(&thread_data->stop, 1); cmdq_free_mem(random_context->slot); kfree(random_context->slot_expect_values); kfree(random_context); return -EINVAL; } } if (!thread_data->multi_task) { cmdqRecBackupRegisterToSlot(handle, random_context->slot, 0, dummy_reg_pa); random_context->slot_expect_values[0] = random_context->last_write; } /* Note: backup to slot 1 which is reserved for register * final value */ cmdq_op_backup_CPR(handle, random_context->var_result, random_context->slot, 1); if (thread_data->policy.wait_policy == CMDQ_TESTCASE_WAITOP_BEFORE_END) { /* make sure task wait before eoc */ cmdq_op_clear_event(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); /* do not queue too much tasks in thread */ if ((u32)atomic_read(task_ref_count) >= 2) cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); } if (get_random_int() % 2) { status = cmdq_op_finalize_command(handle, false); if (status < 0) { CMDQ_ERR("Fail to finalize round:%u status:%d\n", task_count, status); cmdq_free_mem(random_context->slot); kfree(random_context->slot_expect_values); kfree(random_context); return -EINVAL; } /* async submit case, with contains more info * during release */ status = _test_flush_task_async(handle); if (status < 0) { CMDQ_ERR("Fail to submit round:%u status:%d\n", task_count, status); cmdq_free_mem(random_context->slot); kfree(random_context->slot_expect_values); kfree(random_context); return -EINVAL; } INIT_WORK(&random_context->release_work, _testcase_stress_release_work); atomic_inc(task_ref_count); CMDQ_LOG( "Round:%u handle:0x%p pkt:0x%p thread:%d size:%zu ref:%u scenario:%d flag:0x%llx start async\n", task_count, random_context->handle, random_context->handle->pkt, random_context->handle->thread, random_context->handle->pkt->cmd_buf_size, (u32)atomic_read(task_ref_count), random_context->handle->scenario, random_context->handle->engineFlag); } else { /* sync flush case, will blocking worker */ INIT_WORK(&random_context->release_work, _testcase_stress_submit_release_work); atomic_inc(task_ref_count); CMDQ_LOG( "Round:%u handle:0x%p pkt:0x%p size:%zu ref:%u scenario:%d flag:0x%llx start sync\n", task_count, handle, handle->pkt, handle->pkt->cmd_buf_size, (u32)atomic_read(task_ref_count), handle->scenario, random_context->handle->engineFlag); } queue_work(release_queues[get_random_int() % MAX_RELEASE_QUEUE], &random_context->release_work); msleep_interruptible(get_random_int() % (u32)thread_data->policy.loop_policy + 1); return 0; } static int _testcase_gen_task_thread(void *data) { const u32 max_muti_task = CMDQ_MAX_TASK_IN_THREAD + 1; const unsigned long dummy_reg_va = CMDQ_TEST_GCE_DUMMY_VA; struct thread_param *thread_data = (struct thread_param *)data; struct workqueue_struct *release_queues[MAX_RELEASE_QUEUE] = {0}; const bool ignore_timeout = _stress_is_ignore_timeout( &thread_data->policy); u32 task_count = 0; atomic_t task_ref_count; u32 wait_count = 0; u32 engine_sel = 0; u32 i = 0; u32 thd_timeout[CMDQ_MAX_THREAD_COUNT] = {0}; u32 timeout_ms; CMDQ_LOG("%s\n", __func__); for (i = 0; i < MAX_RELEASE_QUEUE; i++) release_queues[i] = create_singlethread_workqueue( "cmdq_random_release"); CMDQ_REG_SET32(dummy_reg_va, 0xdeaddead); if (CMDQ_REG_GET32(dummy_reg_va) != 0xdeaddead) CMDQ_ERR("%s verify pattern register fail:0x%08x\n", __func__, (u32)CMDQ_REG_GET32(dummy_reg_va)); if (thread_data->policy.engines_policy == CMDQ_TESTCASE_ENGINE_SAME) engine_sel = get_random_int() % ((1 << ARRAY_SIZE(stress_engines)) - 1); else engine_sel = 0; if (thread_data->policy.wait_policy == CMDQ_TESTCASE_WAITOP_BEFORE_END) { timeout_ms = 2 * CMDQ_PREDUMP_TIMEOUT_MS; } else if (thread_data->policy.wait_policy == CMDQ_TESTCASE_WAITOP_RANDOM_NOTIMEOUT) timeout_ms = 50 * CMDQ_PREDUMP_TIMEOUT_MS; else timeout_ms = 2 * CMDQ_PREDUMP_TIMEOUT_MS; /* setup thread timeout */ for (i = 0; i < ARRAY_SIZE(thd_timeout); i++) { struct cmdq_client *clt = cmdq_helper_mbox_client(i); if (!clt) continue; thd_timeout[i] = cmdq_mbox_set_thread_timeout( clt->chan, timeout_ms); } atomic_set(&task_ref_count, 0); for (task_count = 0; !atomic_read(&thread_data->stop) && task_count < thread_data->run_count; task_count++) { if (!thread_data->multi_task) { if (atomic_read(&task_ref_count) > 0) { msleep_interruptible(8); task_count--; continue; } } else { if (atomic_read(&task_ref_count) >= max_muti_task) { msleep_interruptible(8); task_count--; continue; } } if (_testcase_gen_task_thread_each(data, task_count, &task_ref_count, engine_sel, ignore_timeout, release_queues) < 0) break; } while (atomic_read(&task_ref_count) > 0) { /* set events to speed up task finish */ cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_1); msleep_interruptible(500); CMDQ_ERR("%s wait for all task done:%u\n", __func__, wait_count++); } CMDQ_LOG("%s END\n", __func__); for (i = 0; i < MAX_RELEASE_QUEUE; i++) destroy_workqueue(release_queues[i]); cmdq_core_reset_first_dump(); /* rollback thread timeout */ for (i = 0; i < ARRAY_SIZE(thd_timeout); i++) { struct cmdq_client *clt = cmdq_helper_mbox_client(i); if (!clt) continue; cmdq_mbox_set_thread_timeout(clt->chan, thd_timeout[i]); } complete(&thread_data->cmplt); return 0; } static int _testcase_trigger_event_thread(void *data) { struct thread_param *thread_data = (struct thread_param *)data; u8 event_idx = 0; const unsigned long tokens[] = { CMDQ_SYNC_TOKEN_USER_0, CMDQ_SYNC_TOKEN_USER_1 }; const unsigned long dummy_poll_va = CMDQ_GPR_R32( CMDQ_GET_GPR_PX2RX_LOW(CMDQ_DATA_REG_2D_SHARPNESS_0_DST)); u32 poll_bit_counter = 0xf; u32 dummy_value = 0; u32 trigger_interval = (u32)thread_data->policy.trigger_policy; CMDQ_LOG("%s\n", __func__); if (!trigger_interval) trigger_interval = get_random_int() % (u32)CMDQ_TESTCASE_TRIGGER_SLOW; if (trigger_interval <= 1) trigger_interval = 2; /* randomly clear/set event */ while (!atomic_read(&thread_data->stop)) { event_idx = get_random_int() % ARRAY_SIZE(tokens); if (get_random_int() % 2) cmdqCoreSetEvent(tokens[event_idx]); else cmdqCoreClearEvent(tokens[event_idx]); dummy_value = CMDQ_REG_GET32(dummy_poll_va); CMDQ_REG_SET32(dummy_poll_va, dummy_value | poll_bit_counter); poll_bit_counter = poll_bit_counter << 4; if (poll_bit_counter > CMDQ_TEST_POLL_BIT) poll_bit_counter = 0xf; msleep_interruptible(get_random_int() % trigger_interval + 1); } CMDQ_LOG("%s END\n", __func__); complete(&thread_data->cmplt); return 0; } static int dummy_dump_smi(const int showSmiDump) { return 0; } static void testcase_gen_random_case(bool multi_task, struct stress_policy policy) { struct task_struct *random_thread_handle; struct task_struct *trigger_thread_handle; struct thread_param random_thread = { {0} }; struct thread_param trigger_thread = { {0} }; const u32 finish_timeout_ms = 1000; u32 timeout_counter = 0; s32 wait_status = 0; struct cmdqCoreFuncStruct *virtual_func = cmdq_get_func(); CmdqDumpSMI dump_smi_func = virtual_func->dumpSMI; CMDQ_LOG( "%s start with multi-task: %s engine:%d wait:%d condition:%d loop:%d timeout: %s\n", __func__, multi_task ? "True" : "False", policy.engines_policy, policy.wait_policy, policy.condition_policy, policy.loop_policy, _stress_is_ignore_timeout(&policy) ? "ignore" : "aee"); /* remove smi dump */ virtual_func->dumpSMI = dummy_dump_smi; cmdq_core_set_aee(!_stress_is_ignore_timeout(&policy)); random_thread.multi_task = multi_task; random_thread.policy = policy; do { init_completion(&random_thread.cmplt); atomic_set(&random_thread.stop, 0); init_completion(&trigger_thread.cmplt); atomic_set(&trigger_thread.stop, 0); random_thread.run_count = 3000; random_thread_handle = kthread_run(_testcase_gen_task_thread, (void *)&random_thread, "cmdq_gen"); if (IS_ERR(random_thread_handle)) { CMDQ_TEST_FAIL("Fail to start gen task thread\n"); break; } trigger_thread_handle = kthread_run( _testcase_trigger_event_thread, (void *)&trigger_thread, "mdq_trigger"); if (IS_ERR(trigger_thread_handle)) { CMDQ_TEST_FAIL("Fail to start trigger event thread\n"); atomic_set(&random_thread.stop, 1); wait_for_completion(&random_thread.cmplt); break; } wait_for_completion(&random_thread.cmplt); atomic_set(&trigger_thread.stop, 1); do { wait_status = wait_for_completion_interruptible_timeout( &trigger_thread.cmplt, msecs_to_jiffies(finish_timeout_ms)); CMDQ_LOG( "wait trigger thread complete count:%u status:%d\n", timeout_counter, wait_status); msleep_interruptible(finish_timeout_ms); timeout_counter++; } while (wait_status <= 0); } while (0); /* restore smi dump */ virtual_func->dumpSMI = dump_smi_func; cmdq_core_set_aee(true); CMDQ_LOG("%s END\n", __func__); } void testcase_stress_basic(void) { struct stress_policy policy = {0}; #ifdef CMDQ_SECURE_PATH_SUPPORT if (gCmdqTestSecure) policy.secure_policy = CMDQ_TESTCASE_SECURE_RANDOM; #endif policy.engines_policy = CMDQ_TESTCASE_ENGINE_NOT_SET; policy.wait_policy = CMDQ_TESTCASE_WAITOP_NOT_SET; policy.condition_policy = CMDQ_TESTCASE_CONDITION_NONE; policy.loop_policy = CMDQ_TESTCASE_LOOP_FAST; policy.trigger_policy = CMDQ_TESTCASE_TRIGGER_SLOW; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.engines_policy = CMDQ_TESTCASE_ENGINE_SAME; policy.wait_policy = CMDQ_TESTCASE_WAITOP_BEFORE_END; policy.trigger_policy = CMDQ_TESTCASE_TRIGGER_MEDIUM; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.wait_policy = CMDQ_TESTCASE_WAITOP_RANDOM; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.engines_policy = CMDQ_TESTCASE_ENGINE_RANDOM; policy.wait_policy = CMDQ_TESTCASE_WAITOP_RANDOM; testcase_gen_random_case(true, policy); } void testcase_stress_condition(void) { struct stress_policy policy = {0}; policy.engines_policy = CMDQ_TESTCASE_ENGINE_NOT_SET; policy.wait_policy = CMDQ_TESTCASE_WAITOP_NOT_SET; policy.condition_policy = CMDQ_TESTCASE_CONDITION_RANDOM; policy.loop_policy = CMDQ_TESTCASE_LOOP_MEDIUM; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.engines_policy = CMDQ_TESTCASE_ENGINE_RANDOM; policy.wait_policy = CMDQ_TESTCASE_WAITOP_RANDOM; policy.loop_policy = CMDQ_TESTCASE_LOOP_FAST; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.engines_policy = CMDQ_TESTCASE_ENGINE_NOT_SET; policy.wait_policy = CMDQ_TESTCASE_WAITOP_NOT_SET; policy.condition_policy = CMDQ_TESTCASE_CONDITION_RANDOM_MORE; policy.loop_policy = CMDQ_TESTCASE_LOOP_MEDIUM; testcase_gen_random_case(true, policy); } void testcase_stress_poll(void) { struct stress_policy policy = {0}; policy.engines_policy = CMDQ_TESTCASE_ENGINE_NOT_SET; policy.wait_policy = CMDQ_TESTCASE_WAITOP_NOT_SET; policy.condition_policy = CMDQ_TESTCASE_CONDITION_RANDOM; policy.loop_policy = CMDQ_TESTCASE_LOOP_MEDIUM; policy.poll_policy = CMDQ_TESTCASE_POLL_PASS; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.poll_policy = CMDQ_TESTCASE_POLL_ALL; testcase_gen_random_case(true, policy); } void testcase_stress_timeout(void) { struct stress_policy policy = {0}; #ifdef CMDQ_SECURE_PATH_SUPPORT if (gCmdqTestSecure) policy.secure_policy = CMDQ_TESTCASE_SECURE_RANDOM; #endif policy.engines_policy = CMDQ_TESTCASE_ENGINE_SAME; policy.wait_policy = CMDQ_TESTCASE_WAITOP_RANDOM; policy.condition_policy = CMDQ_TESTCASE_CONDITION_NONE; policy.loop_policy = CMDQ_TESTCASE_LOOP_MEDIUM; testcase_gen_random_case(true, policy); msleep_interruptible(10); policy.engines_policy = CMDQ_TESTCASE_ENGINE_SAME; testcase_gen_random_case(true, policy); } void testcase_stress_reorder(void) { struct stress_policy policy = {0}; policy.engines_policy = CMDQ_TESTCASE_ENGINE_SAME; policy.wait_policy = CMDQ_TESTCASE_WAITOP_RANDOM_NOTIMEOUT; policy.condition_policy = CMDQ_TESTCASE_CONDITION_NONE; policy.loop_policy = CMDQ_TESTCASE_LOOP_FAST; policy.trigger_policy = CMDQ_TESTCASE_TRIGGER_SLOW; testcase_gen_random_case(true, policy); } #define TESTMBOX_CLT_IDX 14 #define TESTMBOX_CLT_IDX_LOOP 7 void testmbox_write(unsigned long dummy_va, unsigned long dummy_pa, u32 mask) { const u32 pattern = (1 << 0) | (1 << 2) | (1 << 16); const u32 expect_result = pattern & mask; struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; u32 value = 0; CMDQ_LOG("%s va:0x%lx pa:0x%lx mask:0x%08x\n", __func__, dummy_va, dummy_pa, mask); if (!mask) /* set to 0xFFFFFFFF */ CMDQ_REG_SET32((void *)dummy_va, ~0); else CMDQ_REG_SET32((void *)dummy_va, 0); /* use CMDQ to set to PATTERN */ pkt = cmdq_pkt_create(clt); cmdq_pkt_write(pkt, clt_base, dummy_pa, pattern, mask); cmdq_pkt_flush(pkt); /* value check */ value = CMDQ_REG_GET32((void *)dummy_va); if (value != expect_result) { /* test fail */ CMDQ_TEST_FAIL("wrote value is 0x%08x not 0x%08x\n", value, expect_result); cmdq_pkt_dump_buf(pkt, 0); } cmdq_pkt_destroy(pkt); CMDQ_LOG("%s END\n", __func__); } void testmbox_write_gce(void) { unsigned long dummy_va, dummy_pa; if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } testmbox_write(dummy_va, dummy_pa, ~0); } void testmbox_write_with_mask(void) { unsigned long dummy_va, dummy_pa; const u32 mask = (1 << 16); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } testmbox_write(dummy_va, dummy_pa, mask); } void testmbox_loop(void) { struct cmdq_client *clt = cmdq_helper_mbox_client( TESTMBOX_CLT_IDX_LOOP); struct cmdq_pkt *pkt = NULL; struct cmdq_thread *thread = (struct cmdq_thread *)clt->chan->con_priv; s32 err; CMDQ_LOG("%s\n", __func__); pkt = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt, CMDQ_SYNC_TOKEN_USER_0); cmdq_pkt_finalize_loop(pkt); cmdq_dump_pkt(pkt, 0, true); cmdq_tltm.event = CMDQ_SYNC_TOKEN_USER_0; timer_setup(&cmdq_tltm.test_timer, _testcase_loop_timer_func, 0); mod_timer(&cmdq_tltm.test_timer, jiffies + msecs_to_jiffies(300)); CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); g_loopIter = 0; /* should success */ CMDQ_LOG("%s start loop thread:%d pkt:0x%p\n", __func__, thread->idx, pkt); err = cmdq_pkt_flush_async(pkt, NULL, 0); /* WAIT */ while (g_loopIter < 20) msleep_interruptible(2000); CMDQ_LOG("%s stop timer\n", __func__); cmdq_mbox_stop(clt); del_timer(&cmdq_tltm.test_timer); cmdq_pkt_destroy(pkt); CMDQ_LOG("%s end\n", __func__); } void testmbox_dma_access(void) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL, *pkt2 = NULL; const u32 pattern = 0xabcdabcd; const u32 pattern2 = 0xaabbccdd; const u32 pat_default = 0xdeaddead; const u32 pattern3 = 0xdeadbeef; unsigned long dummy_va, dummy_pa; dma_addr_t slot = 0, slot2; u32 *va, value; u32 mem_off = 0xabc; if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(dummy_va, pattern); va = (u32 *)cmdq_mbox_buf_alloc(clt->client.dev, &slot); if (!va || !slot) { CMDQ_TEST_FAIL("%s alloc buffer fail\n", __func__); return; } /* write buffer with default value */ va[0] = pat_default; va[1] = pat_default; va[2] = pattern2; /* use CMDQ to set to PATTERN */ pkt = cmdq_pkt_create(clt); cmdq_pkt_mem_move(pkt, clt_base, dummy_pa, slot, CMDQ_THR_SPR_IDX1); cmdq_pkt_mem_move(pkt, clt_base, slot, slot + 4, CMDQ_THR_SPR_IDX1); cmdq_pkt_mem_move(pkt, clt_base, slot + 8, dummy_pa, CMDQ_THR_SPR_IDX1); cmdq_pkt_flush(pkt); cmdq_pkt_dump_buf(pkt, 0); cmdq_pkt_destroy(pkt); if (va[0] != pattern) CMDQ_TEST_FAIL( "%s move pa to dram value:0x%08x pattern:0x%08x default:0x%08x\n", __func__, va[0], pattern, pat_default); if (va[1] != pattern) CMDQ_TEST_FAIL( "%s move dram to dram value:0x%08x pattern:0x%08x default:0x%08x\n", __func__, va[1], pattern, pat_default); value = CMDQ_REG_GET32(dummy_va); if (va[2] != value) CMDQ_TEST_FAIL( "%s move pa to dram dummy:0x%08x pattern:0x%08x default:0x%08x\n", __func__, value, pattern2, pat_default); /* write pattern and read */ pkt2 = cmdq_pkt_create(clt); cmdq_pkt_jump(pkt2, 8); va[mem_off / 4] = 0; va[mem_off / 4 + 1] = 0; slot2 = slot + mem_off; cmdq_pkt_write_value_addr(pkt2, slot2, 0xdeadbeef, ~0); cmdq_pkt_read_addr(pkt2, slot2, CMDQ_THR_SPR_IDX1); cmdq_pkt_write_reg_addr(pkt2, slot2 + 4, CMDQ_THR_SPR_IDX1, ~0); cmdq_pkt_flush(pkt2); cmdq_pkt_dump_buf(pkt2, 0); cmdq_pkt_destroy(pkt2); if (va[mem_off / 4] != va[mem_off / 4 + 1] || va[mem_off / 4 + 1] != pattern3) { CMDQ_TEST_FAIL( "pa:%pa va:0x%p pattern:0x%08x data:0x%08x 0x%08x\n", &slot2, &va[mem_off / 4], pattern3, va[mem_off / 4], va[mem_off / 4 + 1]); } cmdq_mbox_buf_free(clt->client.dev, va, slot); CMDQ_LOG("%s END\n", __func__); } void testmbox_cmplt_cb_destroy(struct cmdq_cb_data data) { struct cmdq_flush_completion *cmplt = data.data; if (data.err < 0) CMDQ_TEST_FAIL("pkt:0x%p err:%d\n", cmplt->pkt, data.err); cmplt->err = !data.err ? false : true; cmdq_pkt_destroy(cmplt->pkt); complete(&cmplt->cmplt); } void testmbox_cmplt_cb(struct cmdq_cb_data data) { struct cmdq_flush_completion *cmplt = data.data; if (data.err < 0) CMDQ_TEST_FAIL("pkt:0x%p err:%d\n", cmplt->pkt, data.err); cmplt->err = !data.err ? false : true; complete(&cmplt->cmplt); } void testmbox_async_flush(bool threaded) { #define TEST_REQ_COUNT 24 struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_pkt *pkt[TEST_REQ_COUNT] = { 0 }; struct cmdq_flush_completion *cmplt; u32 i, ret; CMDQ_LOG("%s threaded:%s\n", __func__, threaded ? "true" : "false"); cmplt = kcalloc(TEST_REQ_COUNT, sizeof(*cmplt), GFP_KERNEL); cmdq_ttm.event = CMDQ_SYNC_TOKEN_USER_0; test_timer_stop = false; timer_setup(&cmdq_ttm.test_timer, _testcase_sync_token_timer_loop_func, 0); mod_timer(&cmdq_ttm.test_timer, jiffies + msecs_to_jiffies(10)); /* Queue multiple async request */ /* to test dynamic task allocation */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); for (i = 0; i < TEST_REQ_COUNT; i++) { pkt[i] = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt[i], CMDQ_SYNC_TOKEN_USER_0); /* higher priority for later tasks */ pkt[i]->priority = i; init_completion(&cmplt[i].cmplt); cmplt[i].pkt = pkt[i]; if (threaded) cmdq_pkt_flush_threaded(pkt[i], testmbox_cmplt_cb_destroy, &cmplt[i]); else cmdq_pkt_flush_async(pkt[i], testmbox_cmplt_cb, &cmplt[i]); } /* release token and wait them */ for (i = 0; i < TEST_REQ_COUNT; i++) { if (!pkt[i]) { CMDQ_ERR("%s pkt[%d] is NULL\n", __func__, i); continue; } msleep_interruptible(100); CMDQ_MSG("wait pkt[%2d] 0x%p\n", i, pkt[i]); ret = wait_for_completion_timeout(&cmplt[i].cmplt, msecs_to_jiffies(CMDQ_TIMEOUT_DEFAULT)); if (!ret) { CMDQ_TEST_FAIL("wait pkt[%2d] 0x%p timeout\n", i, pkt[i]); continue; } if (!threaded) cmdq_pkt_destroy(pkt[i]); } /* clear token */ CMDQ_REG_SET32(CMDQ_SYNC_TOKEN_UPD, CMDQ_SYNC_TOKEN_USER_0); test_timer_stop = true; del_timer(&cmdq_ttm.test_timer); CMDQ_LOG("%s END\n", __func__); } void testmbox_large_command(void) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; u32 data, i; CMDQ_LOG("%s\n", __func__); CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0xdeaddead); pkt = cmdq_pkt_create(clt); /* build a 64K instruction buffer */ for (i = 0; i < 64 * 1024 / 8; i++) cmdq_pkt_write(pkt, clt_base, CMDQ_TEST_GCE_DUMMY_PA, i, ~0); CMDQ_LOG("pkt:0x%p buf size:%zu size:%zu avail:%zu\n", pkt, pkt->cmd_buf_size, pkt->buf_size, pkt->avail_buf_size); cmdq_pkt_flush(pkt); /* verify data */ data = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (i - 1 != data) CMDQ_TEST_FAIL( "reg value is 0x%08x not idx 0x%08x\n", data, i); cmdq_pkt_destroy(pkt); CMDQ_LOG("%s END\n", __func__); } void testmbox_poll_run(u32 poll_value, u32 poll_mask, bool use_mmsys_dummy) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; struct cmdq_flush_completion cmplt = {0}; u32 value = 0, dst_reg_pa; unsigned long dummy_va; if (gCmdqTestSecure || use_mmsys_dummy) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dst_reg_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dst_reg_pa = CMDQ_TEST_GCE_DUMMY_PA; } CMDQ_REG_SET32(dummy_va, 0); CMDQ_LOG("%s poll value:0x%08x poll mask:0x%08x use mmsys:%s\n", __func__, poll_value, poll_mask, use_mmsys_dummy ? "true" : "false"); pkt = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); cmdq_pkt_poll(pkt, clt_base, poll_value, dst_reg_pa, poll_mask, CMDQ_DATA_REG_DEBUG); cmdq_pkt_set_event(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); init_completion(&cmplt.cmplt); cmplt.pkt = pkt; cmdq_pkt_flush_async(pkt, testmbox_cmplt_cb, &cmplt); cmdq_pkt_dump_buf(pkt, 0); /* Set MMSYS dummy register value after clock is on */ CMDQ_REG_SET32(dummy_va, poll_value); value = CMDQ_REG_GET32(dummy_va); CMDQ_LOG("target value is 0x%08x\n", value); wait_for_completion(&cmplt.cmplt); cmdq_pkt_destroy(pkt); CMDQ_LOG("%s END\n", __func__); } void testmbox_poll(void) { testmbox_poll_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, false); testmbox_poll_run(0xdada1818, 0xFFFFFFFF, false); testmbox_poll_run(0xdada1818 & 0x0000FF00, 0x0000FF00, false); testmbox_poll_run(0x00001818, 0xFFFFFFFF, false); #ifndef CONFIG_FPGA_EARLY_PORTING /* fpga may not ready mmsys */ testmbox_poll_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, true); testmbox_poll_run(0xdada1818, 0xFFFFFFFF, true); testmbox_poll_run(0xdada1818 & 0x0000FF00, 0x0000FF00, true); testmbox_poll_run(0x00001818, 0xFFFFFFFF, true); #endif } void testmbox_verify_cpr(void) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; u32 dummy_pa; unsigned long dummy_va; const u32 pattern = 0xdeadabcd; const u16 var_reg_idx = CMDQ_THR_SPR_IDX3; struct cmdq_pkt_buffer *buf; u32 *va, idx, val; s32 status; CMDQ_LOG("%s END\n", __func__); if (gCmdqTestSecure) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dummy_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dummy_pa = CMDQ_TEST_GCE_DUMMY_PA; } pkt = cmdq_pkt_create(clt); cmdq_pkt_assign_command(pkt, var_reg_idx, pattern); cmdq_pkt_write_indriect(pkt, clt_base, dummy_pa, var_reg_idx, ~0); cmdq_pkt_finalize(pkt); cmdq_pkt_dump_buf(pkt, 0); buf = list_first_entry(&pkt->buf, typeof(*buf), list_entry); va = (u32 *)buf->va_base; for (idx = 0; idx < cmdq_core_get_cpr_cnt(); idx++) { CMDQ_REG_SET32(CMDQ_TEST_GCE_DUMMY_VA, 0); va[1] = (va[1] & 0xFFFF0000) | (CMDQ_CPR_STRAT_ID + idx); va[4] = ((CMDQ_CPR_STRAT_ID + idx) << 16) | (va[5] & 0xFFFF); status = cmdq_pkt_flush(pkt); if (status < 0) { CMDQ_TEST_FAIL("%s flush %u fail:%d\n", __func__, idx, status); break; } val = CMDQ_REG_GET32(CMDQ_TEST_GCE_DUMMY_VA); if (val != pattern) { CMDQ_TEST_FAIL( "cpr:0x%x result:0x%08x pattern:0x%08x\n", idx, val, pattern); cmdq_pkt_dump_buf(pkt, 0); break; } } cmdq_pkt_destroy(pkt); CMDQ_LOG("%s END\n", __func__); } void testmbox_dump_err(void) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_pkt *pkt = NULL; struct cmdq_flush_completion cmplt = {0}; u32 ret; u64 *inst; dma_addr_t pc = 0; CMDQ_LOG("%s\n", __func__); cmdq_clear_event(clt->chan, CMDQ_SYNC_TOKEN_USER_0); pkt = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt, CMDQ_SYNC_TOKEN_USER_0); init_completion(&cmplt.cmplt); cmplt.pkt = pkt; cmdq_pkt_flush_async(pkt, testmbox_cmplt_cb, &cmplt); /* try dump */ cmdq_thread_dump(clt->chan, pkt, &inst, &pc); /* set event and complete pkt */ cmdq_set_event(clt->chan, CMDQ_SYNC_TOKEN_USER_0); ret = wait_for_completion_timeout(&cmplt.cmplt, msecs_to_jiffies(CMDQ_TIMEOUT_DEFAULT)); if (!ret) CMDQ_TEST_FAIL("wait pkt 0x%p timeout inst:%llx pc:%pa\n", pkt, inst ? *inst : 0, &pc); else cmdq_pkt_destroy(pkt); CMDQ_LOG("%s END\n", __func__); } void testmbox_poll_timeout_run(u32 poll_value, u32 poll_mask, bool use_mmsys_dummy, bool timeout) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; struct cmdq_pkt_buffer *buf; struct cmdq_flush_completion cmplt = {0}; u32 value = 0, dst_reg_pa, cost; unsigned long dummy_va; dma_addr_t out_pa; u32 *out_va; u64 cpu_cost; if (gCmdqTestSecure || use_mmsys_dummy) { dummy_va = CMDQ_TEST_MMSYS_DUMMY_VA; dst_reg_pa = CMDQ_TEST_MMSYS_DUMMY_PA; } else { dummy_va = CMDQ_TEST_GCE_DUMMY_VA; dst_reg_pa = CMDQ_TEST_GCE_DUMMY_PA; } CMDQ_REG_SET32(CMDQ_TPR_MASK, 0x80000000); CMDQ_REG_SET32(dummy_va, 0); CMDQ_LOG("%s poll value:0x%08x poll mask:0x%08x use mmsys:%s\n", __func__, poll_value, poll_mask, use_mmsys_dummy ? "true" : "false"); pkt = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); buf = list_last_entry(&pkt->buf, typeof(*buf), list_entry); /* use last 1024 as output buffer */ out_pa = buf->pa_base + 3096; out_va = (u32 *)(buf->va_base + 3096); *out_va = 0; *(out_va + 1) = 0; cmdq_pkt_write_indriect(pkt, clt_base, out_pa, CMDQ_CPR_STRAT_ID, ~0); cmdq_pkt_poll_timeout(pkt, poll_value, SUBSYS_NO_SUPPORT, dst_reg_pa, poll_mask, 100, CMDQ_DATA_REG_DEBUG); cmdq_pkt_write_indriect(pkt, clt_base, out_pa + 4, CMDQ_CPR_STRAT_ID, ~0); cmdq_pkt_set_event(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); init_completion(&cmplt.cmplt); cmplt.pkt = pkt; cpu_cost = sched_clock(); cmdq_pkt_flush_async(pkt, testmbox_cmplt_cb, &cmplt); if (!timeout) { /* Set dummy register value after clock is on */ CMDQ_REG_SET32(dummy_va, poll_value); value = CMDQ_REG_GET32(dummy_va); } wait_for_completion(&cmplt.cmplt); cpu_cost = div_u64(sched_clock() - cpu_cost, 1000000); cmdq_pkt_dump_buf(pkt, 0); /* calculate cost */ if (*out_va <= *(out_va + 1)) cost = *(out_va + 1) - *out_va; else cost = (0xffffffff - *out_va) + *(out_va + 1); /* wait 100 count and each time 5 tick, add 100 for buffer */ if (cost > 600 || cpu_cost > 10) CMDQ_TEST_FAIL( "target value is 0x%08x cost timed out:%d to 600 cpu:%llu to 10ms\n", value, cost, cpu_cost); else CMDQ_LOG("target value is 0x%08x cost time:%d cpu:%llu\n", value, cost, cpu_cost); cmdq_pkt_destroy(pkt); CMDQ_REG_SET32(CMDQ_TPR_MASK, 0); CMDQ_LOG("%s END\n", __func__); } void testmbox_poll_timeout(void) { testmbox_poll_timeout_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, false, false); testmbox_poll_timeout_run(0xdada1818, 0xFFFFFFFF, false, false); testmbox_poll_timeout_run(0xdada1818 & 0x0000FF00, 0x0000FF00, false, false); testmbox_poll_timeout_run(0x00001818, 0xFFFFFFFF, false, false); testmbox_poll_timeout_run(0x1, 0xFFFFFFFF, false, true); #ifndef CONFIG_FPGA_EARLY_PORTING /* fpga may not ready mmsys */ testmbox_poll_timeout_run(0xdada1818 & 0xFF00FF00, 0xFF00FF00, true, false); testmbox_poll_timeout_run(0xdada1818, 0xFFFFFFFF, true, false); testmbox_poll_timeout_run(0xdada1818 & 0x0000FF00, 0x0000FF00, true, false); testmbox_poll_timeout_run(0x00001818, 0xFFFFFFFF, true, false); #endif } void testmbox_gpr_timer(void) { #define CMDQ_TPR_TIMEOUT_EN 0xDC struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_thread *thread = (struct cmdq_thread *)clt->chan->con_priv; const u32 timeout_en = thread->gce_pa + CMDQ_TPR_TIMEOUT_EN; struct cmdq_pkt *pkt = NULL; struct cmdq_pkt_buffer *buf; const u16 reg_gpr = CMDQ_DATA_REG_DEBUG; const u32 tpr_en = 1 << reg_gpr; const u16 event = (u16)CMDQ_EVENT_GPR_TIMER + reg_gpr; struct cmdq_operand lop = {.reg = true, .idx = CMDQ_TPR_ID}; struct cmdq_operand rop = {.reg = false, .value = 100}; u32 cost; dma_addr_t out_pa; u32 *out_va; u64 cpu_cost; CMDQ_REG_SET32(CMDQ_TPR_MASK, 0x80000000); CMDQ_REG_SET32(CMDQ_TPR_GPR_TIMER, tpr_en); CMDQ_LOG("%s GCE PA:%pa\n", __func__, &thread->gce_pa); pkt = cmdq_pkt_create(clt); cmdq_pkt_write(pkt, clt_base, timeout_en, tpr_en, tpr_en); cmdq_pkt_clear_event(pkt, event); buf = list_last_entry(&pkt->buf, typeof(*buf), list_entry); CMDQ_LOG("pkt:0x%p buf:0x%p\n", pkt, buf); if (!buf) { CMDQ_ERR("%s no buf in pkt\n", __func__); return; } /* use last 1024 as output buffer */ out_pa = buf->pa_base + 3096; out_va = (u32 *)(buf->va_base + 3096); *out_va = 0; *(out_va + 1) = 0; CMDQ_LOG("use gce write tpr to:0x%p(%pa) and 0x%p\n", out_va, &out_pa, out_va + 1); cmdq_pkt_write_indriect(pkt, clt_base, out_pa, CMDQ_TPR_ID, ~0); cmdq_pkt_logic_command(pkt, CMDQ_LOGIC_ADD, CMDQ_GPR_CNT_ID + reg_gpr, &lop, &rop); cmdq_pkt_wfe(pkt, event); cmdq_pkt_write_indriect(pkt, clt_base, out_pa + 4, CMDQ_TPR_ID, ~0); cpu_cost = sched_clock(); cmdq_pkt_flush(pkt); cpu_cost = div_u64(sched_clock() - cpu_cost, 1000000); cmdq_pkt_dump_buf(pkt, 0); /* calculate cost */ if (*out_va <= *(out_va + 1)) cost = *(out_va + 1) - *out_va; else cost = (0xffffffff - *out_va) + *(out_va + 1); /* wait 100 count and each time 5 tick, add 100 for buffer */ if (cost > 600 || cpu_cost > 10000) CMDQ_TEST_FAIL( "%s sleep cost timedout:%u (%u %u) to 600 cpu:%lluus to 10ms\n", __func__, cost, *out_va, *(out_va + 1), cpu_cost); else CMDQ_LOG("%s sleep cost time:%u (%u %u) cpu:%llu\n", __func__, cost, *out_va, *(out_va + 1), cpu_cost); cmdq_pkt_destroy(pkt); CMDQ_REG_SET32(CMDQ_TPR_MASK, 0); CMDQ_LOG("%s END\n", __func__); } void testmbox_sleep(void) { struct cmdq_client *clt = cmdq_helper_mbox_client(TESTMBOX_CLT_IDX); struct cmdq_base *clt_base = cmdq_helper_mbox_base(); struct cmdq_pkt *pkt = NULL; struct cmdq_pkt_buffer *buf; u32 cost; dma_addr_t out_pa; u32 *out_va; u64 cpu_cost; CMDQ_REG_SET32(CMDQ_TPR_MASK, 0x80000000); CMDQ_REG_SET32(CMDQ_TPR_GPR_TIMER, 1 << CMDQ_DATA_REG_DEBUG); CMDQ_LOG("%s\n", __func__); pkt = cmdq_pkt_create(clt); cmdq_pkt_wfe(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); buf = list_last_entry(&pkt->buf, typeof(*buf), list_entry); CMDQ_LOG("pkt:0x%p buf:0x%p\n", pkt, buf); if (!buf) { CMDQ_ERR("%s no buf in pkt\n", __func__); return; } /* use last 1024 as output buffer */ out_pa = buf->pa_base + 3096; out_va = (u32 *)(buf->va_base + 3096); CMDQ_LOG("use gce write tpr to:0x%p(%pa) and 0x%p\n", out_va, &out_pa, out_va + 1); *out_va = 0; *(out_va + 1) = 0; cmdq_pkt_write_indriect(pkt, clt_base, out_pa, CMDQ_TPR_ID, ~0); cmdq_pkt_sleep(pkt, 100, CMDQ_DATA_REG_DEBUG); cmdq_pkt_write_indriect(pkt, clt_base, out_pa + 4, CMDQ_TPR_ID, ~0); cmdq_pkt_set_event(pkt, CMDQ_SYNC_TOKEN_GPR_SET_4); cmdq_pkt_write_indriect(pkt, clt_base, out_pa + 8, CMDQ_GPR_CNT_ID + CMDQ_DATA_REG_DEBUG, ~0); cpu_cost = sched_clock(); cmdq_pkt_flush(pkt); cpu_cost = div_u64(sched_clock() - cpu_cost, 1000000); cmdq_pkt_dump_buf(pkt, 0); /* calculate cost */ if (*out_va <= *(out_va + 1)) cost = *(out_va + 1) - *out_va; else cost = (0xffffffff - *out_va) + *(out_va + 1); /* wait 100 count and each time 5 tick, add 100 for buffer */ if (cost > 600 || cpu_cost > 10000) CMDQ_TEST_FAIL( "sleep cost timedout:%u (%u %u %u) to 600 cpu:%lluus to 10ms\n", cost, *out_va, *(out_va + 1), *(out_va + 2), cpu_cost); else CMDQ_LOG("sleep cost time:%u (%u %u %u) cpu:%llu\n", cost, *out_va, *(out_va + 1), *(out_va + 2), cpu_cost); cmdq_pkt_destroy(pkt); CMDQ_REG_SET32(CMDQ_TPR_MASK, 0); CMDQ_LOG("%s END\n", __func__); } void testcase_sec_dapc_protect(void) { #ifdef CMDQ_SECURE_PATH_SUPPORT struct cmdqRecStruct *handle = NULL; void *wrot_va = (void *)cmdq_mdp_get_module_base_VA_MDP_WROT0() + 0xF00; const u32 pattern = 0xdeadbeef; u32 val; CMDQ_LOG("%s\n", __func__); cmdq_task_create(CMDQ_SCENARIO_DEBUG, &handle); handle->engineFlag = 0x1LL << CMDQ_ENG_MDP_WROT0; cmdq_task_secure_enable_dapc(handle, 1LL << CMDQ_ENG_MDP_WROT0); cmdq_task_secure_enable_port_security(handle, 1LL << CMDQ_ENG_MDP_WROT0); cmdq_task_set_secure(handle, true); cmdq_op_clear_event(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_op_wait(handle, CMDQ_SYNC_TOKEN_USER_0); cmdq_task_flush_async(handle); CMDQ_REG_SET32(wrot_va, pattern); val = CMDQ_REG_GET32(wrot_va); if (val == pattern) CMDQ_TEST_FAIL("dapc protect fail addr:0x%p\n", wrot_va); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_USER_0); cmdq_task_destroy(handle); CMDQ_LOG("%s END\n", __func__); #endif } enum CMDQ_TESTCASE_ENUM { CMDQ_TESTCASE_DEFAULT = 0, CMDQ_TESTCASE_BASIC = 1, CMDQ_TESTCASE_ERROR = 2, CMDQ_TESTCASE_FPGA = 3, /* user request get some registers' value when task execution */ CMDQ_TESTCASE_READ_REG_REQUEST, CMDQ_TESTCASE_GPR, CMDQ_TESTCASE_SW_TIMEOUT_HANDLE, CMDQ_TESTCASE_END, /* always at the end */ }; static void testcase_general_handling(s32 testID) { u32 i; switch (testID) { case 510: testmbox_gpr_timer(); testmbox_sleep(); break; case 509: testmbox_poll_timeout(); break; case 508: testmbox_dump_err(); break; case 507: testmbox_verify_cpr(); break; case 506: testmbox_poll(); break; case 505: testmbox_large_command(); break; case 504: testmbox_async_flush(false); testmbox_async_flush(true); break; case 503: testmbox_dma_access(); break; case 502: testmbox_loop(); break; case 501: testmbox_write_gce(); testmbox_write_with_mask(); break; case 500: testmbox_write_gce(); testmbox_write_with_mask(); testmbox_loop(); testmbox_dma_access(); testmbox_async_flush(false); testmbox_async_flush(true); testmbox_large_command(); testmbox_poll(); testmbox_verify_cpr(); break; case 304: testcase_stress_reorder(); break; case 303: testcase_stress_timeout(); break; case 302: testcase_stress_poll(); break; case 301: testcase_stress_condition(); break; case 300: testcase_stress_basic(); break; #ifdef CMDQ_SECURE_PATH_SUPPORT case 164: testcase_read_pq_sec(); break; #endif case 163: testcase_cmdq_trigger_devapc(); break; case 162: testcase_write_dma(); break; case 161: testcase_sec_dapc_protect(); break; case 160: testcase_error_irq_for_secure(); break; case 159: testcase_cross_4k_buffer(); break; case 158: testcase_verify_cpr(); break; case 157: testcase_remove_by_file_node(); break; case 156: testcase_verify_timer(); break; case 154: testcase_end_behavior(true, 0); testcase_end_behavior(false, 0); for (i = 0; i < 500; i++) testcase_end_behavior(false, get_random_int() % 50 + 1); break; case 152: testcase_end_addr_conflict(); break; case 151: testcase_engineflag_conflict_dump(); break; case 149: testcase_global_variable(); break; case 148: testcase_read_with_mask(); break; case 147: testcase_efficient_polling(); break; case 143: testcase_run_command_on_SRAM(); break; case 142: testcase_move_data_between_SRAM(); break; case 141: testcase_track_task_cb(); break; case 139: testcase_invalid_handle(); break; case 130: testcase_longloop(); break; case 129: testcase_boundary_mem(); break; case 128: testcase_boundary_mem_param(); break; case 125: testcase_do_while_continue(); testcase_jump_c(); testcase_jump_c_do_while(); testcase_long_jump_c(); break; case 124: testcase_basic_logic(); testcase_basic_jumpc(); break; case 121: testcase_prefetch_from_DTS(); break; case 120: testcase_notify_and_delay_submit(16); break; case 119: testcase_check_dts_correctness(); break; case 118: testcase_error_irq(); break; case 117: testcase_timeout_reorder_test(); break; case 116: testcase_timeout_wait_early_test(); break; case 115: testcase_manual_suspend_resume_test(); break; case 114: testcase_append_task_verify(); break; case 113: testcase_trigger_engine_dispatch_check(); break; case 112: testcase_complicated_engine_thread(); break; case 111: testcase_module_full_mdp_engine(); break; case 110: testcase_nonsuspend_irq(); break; case 107: testcase_prefetch_multiple_command(); break; case 106: testcase_concurrency_for_normal_path_and_secure_path(); break; case 104: testcase_submit_after_error_happened(); break; case 103: testcase_secure_meta_data(); break; case 102: testcase_secure_disp_scenario(); break; case 101: testcase_write_stress_test(); break; case 100: testcase_secure_basic(); break; case 99: testcase_write(); testcase_write_with_mask(); break; case 98: testcase_errors(); break; case 97: testcase_scenario(); break; case 96: testcase_sync_token(); break; case 95: testcase_write_address(); break; case 94: testcase_async_request(); break; case 93: testcase_async_suspend_resume(); break; case 92: testcase_async_request_partial_engine(); break; case 91: testcase_prefetch_scenarios(); break; case 90: testcase_loop(); break; case 89: testcase_trigger_thread(); break; case 88: testcase_multiple_async_request(); break; case 87: testcase_get_result(); break; case 86: testcase_read_to_data_reg(); break; case 85: testcase_dram_access(); break; case 84: testcase_backup_register(); break; case 83: testcase_fire_and_forget(); break; case 82: testcase_sync_token_threaded(); break; case 81: testcase_long_command(); break; case 80: cmdq_dev_enable_gce_clock(false); testcase_clkmgr(); cmdq_dev_enable_gce_clock(true); break; case 79: testcase_perisys_apb(); break; case 78: testcase_backup_reg_to_slot(); break; case 77: testcase_thread_dispatch(); break; case 75: testcase_full_thread_array(); break; case 74: testcase_module_full_dump(); break; case 73: testcase_write_from_data_reg(); break; case 72: testcase_update_value_to_slot(); break; case 71: testcase_poll(); break; case 70: testcase_write_reg_from_slot(); break; case CMDQ_TESTCASE_FPGA: CMDQ_LOG("FPGA Verify Start!\n"); testcase_write(); testcase_write_with_mask(); testcase_poll(); testcase_scenario(); testcase_prefetch_multiple_command(); testcase_write_stress_test(); testcase_async_suspend_resume(); testcase_async_request_partial_engine(); testcase_prefetch_scenarios(); testcase_loop(); testcase_trigger_thread(); testcase_multiple_async_request(); testcase_get_result(); testcase_dram_access(); testcase_backup_register(); testcase_fire_and_forget(); testcase_long_command(); testcase_backup_reg_to_slot(); testcase_thread_dispatch(); testcase_write_from_data_reg(); testcase_update_value_to_slot(); testcase_verify_cpr(); testcase_basic_logic(); testcase_do_while_continue(); testcase_jump_c(); testcase_jump_c_do_while(); testcase_long_jump_c(); CMDQ_LOG("FPGA Verify Done!\n"); break; case CMDQ_TESTCASE_ERROR: testcase_errors(); testcase_async_request(); testcase_module_full_dump(); break; case CMDQ_TESTCASE_BASIC: testcase_write(); testcase_write_with_mask(); testcase_poll(); testcase_scenario(); break; case CMDQ_TESTCASE_READ_REG_REQUEST: testcase_get_result(); break; case CMDQ_TESTCASE_GPR: testcase_read_to_data_reg(); /* must verify! */ testcase_dram_access(); testcase_verify_cpr(); break; case CMDQ_TESTCASE_DEFAULT: testcase_multiple_async_request(); testcase_read_to_data_reg(); testcase_get_result(); testcase_scenario(); testcase_write(); testcase_poll(); testcase_write_address(); testcase_async_suspend_resume(); testcase_async_request_partial_engine(); testcase_prefetch_scenarios(); testcase_loop(); testcase_trigger_thread(); testcase_prefetch(); testcase_long_command(); testcase_dram_access(); testcase_backup_register(); testcase_fire_and_forget(); testcase_backup_reg_to_slot(); testcase_thread_dispatch(); testcase_full_thread_array(); testcase_verify_cpr(); testcase_basic_logic(); testcase_do_while_continue(); testcase_jump_c(); testcase_jump_c_do_while(); testcase_long_jump_c(); break; default: CMDQ_LOG( "[TESTCASE]CONFIG Not Found: gCmdqTestSecure:%d testType:%lld\n", gCmdqTestSecure, gCmdqTestConfig[0]); break; } } ssize_t cmdq_test_proc(struct file *fp, char __user *u, size_t s, loff_t *l) { s64 testParameter[CMDQ_TESTCASE_PARAMETER_MAX]; mutex_lock(&gCmdqTestProcLock); /* make sure the following section is protected */ smp_mb(); CMDQ_LOG("[TESTCASE]CONFIG: gCmdqTestSecure:%d testType:%lld\n", gCmdqTestSecure, gCmdqTestConfig[0]); CMDQ_LOG("[TESTCASE]CONFIG PARAMETER: [1]:%lld [2]:%lld [3]:%lld\n", gCmdqTestConfig[1], gCmdqTestConfig[2], gCmdqTestConfig[3]); memcpy(testParameter, gCmdqTestConfig, sizeof(testParameter)); gCmdqTestConfig[0] = 0LL; gCmdqTestConfig[1] = -1LL; mutex_unlock(&gCmdqTestProcLock); /* trigger test case here */ CMDQ_MSG("//\n//\n//\ncmdq_test_proc\n"); #ifndef CONFIG_FPGA_EARLY_PORTING /* Turn on GCE clock to make sure GPR is always alive */ cmdq_dev_enable_gce_clock(true); #else cmdq_core_reset_gce(); #endif cmdq_get_func()->testSetup(); switch (testParameter[0]) { case CMDQ_TEST_TYPE_NORMAL: case CMDQ_TEST_TYPE_SECURE: case CMDQ_TEST_TYPE_SECURE_MTEE: testcase_general_handling((s32)testParameter[1]); break; case CMDQ_TEST_TYPE_MONITOR_EVENT: /* (wait type, event ID or back register) */ testcase_monitor_trigger((u32)testParameter[1], (u64)testParameter[2]); break; case CMDQ_TEST_TYPE_MONITOR_POLL: /* (poll register, poll value, poll mask) */ testcase_poll_monitor_trigger((u64)testParameter[1], (u64)testParameter[2], (u64)testParameter[3]); break; case CMDQ_TEST_TYPE_DUMP_DTS: cmdq_core_dump_dts_setting(); break; case CMDQ_TEST_TYPE_MMSYS_PERFORMANCE: testcase_mmsys_performance((s32)testParameter[1]); break; default: break; } cmdq_get_func()->testCleanup(); #ifndef CONFIG_FPGA_EARLY_PORTING /* Turn off GCE clock */ cmdq_dev_enable_gce_clock(false); #endif CMDQ_MSG("%s ended\n", __func__); return 0; } static ssize_t cmdq_write_test_proc_config(struct file *file, const char __user *userBuf, size_t count, loff_t *data) { char desc[50]; long long testConfig[CMDQ_TESTCASE_PARAMETER_MAX]; u64 len = 0ULL; do { /* copy user input */ len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, userBuf, len)) { CMDQ_MSG("TEST_CONFIG: data fail, length:%d\n", len); break; } desc[len] = '\0'; /* Set initial test config value */ memset(testConfig, -1, sizeof(testConfig)); /* process and update config */ if (sscanf(desc, "%lld %lld %lld %lld", &testConfig[0], &testConfig[1], &testConfig[2], &testConfig[3]) <= 0) { /* sscanf returns the number of items in argument * list successfully filled. */ CMDQ_MSG("TEST_CONFIG: sscanf failed, len:%d\n", len); break; } CMDQ_MSG("TEST_CONFIG:%lld, %lld, %lld, %lld\n", testConfig[0], testConfig[1], testConfig[2], testConfig[3]); if ((testConfig[0] < 0) || (testConfig[0] >= CMDQ_TEST_TYPE_MAX)) { CMDQ_MSG( "TEST_CONFIG: testType:%lld, newTestSuit:%lld\n", testConfig[0], testConfig[1]); break; } mutex_lock(&gCmdqTestProcLock); /* set memory barrier for lock */ smp_mb(); memcpy(&gCmdqTestConfig, &testConfig, sizeof(testConfig)); gCmdqTestSecure = testConfig[0]; if (testConfig[0] == CMDQ_TEST_TYPE_SECURE_MTEE) gCmdqTestSecure = -1; mutex_unlock(&gCmdqTestProcLock); } while (0); return count; } void cmdq_test_init_setting(void) { memset(&(gEventMonitor), 0x0, sizeof(gEventMonitor)); memset(&(gPollMonitor), 0x0, sizeof(gPollMonitor)); } static int cmdq_test_open(struct inode *pInode, struct file *pFile) { return 0; } static const struct file_operations cmdq_fops = { .owner = THIS_MODULE, .open = cmdq_test_open, .read = cmdq_test_proc, .write = cmdq_write_test_proc_config, }; static int __init cmdq_test_init(void) { #ifdef CMDQ_TEST_PROC CMDQ_MSG("%s\n", __func__); /* Initial value */ gCmdqTestSecure = false; gCmdqTestConfig[0] = 0LL; gCmdqTestConfig[1] = -1LL; /* Mout proc entry for debug */ gCmdqTestProcEntry = proc_mkdir("cmdq_test", NULL); if (gCmdqTestProcEntry != NULL) { if (proc_create("test", 0660, gCmdqTestProcEntry, &cmdq_fops) == NULL) { /* cmdq_test_init failed */ CMDQ_ERR("%s failed\n", __func__); } } #endif return 0; } static void __exit cmdq_test_exit(void) { #ifdef CMDQ_TEST_PROC CMDQ_MSG("%s\n", __func__); if (gCmdqTestProcEntry != NULL) { proc_remove(gCmdqTestProcEntry); gCmdqTestProcEntry = NULL; } #endif } module_init(cmdq_test_init); module_exit(cmdq_test_exit); MODULE_LICENSE("GPL"); #endif /* CMDQ_TEST */