mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2024-11-19 13:27:49 +00:00
1882 lines
48 KiB
C
1882 lines
48 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/mailbox_controller.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "cmdq-util.h"
|
|
#include "cmdq-sec.h"
|
|
#include "cmdq-sec-mailbox.h"
|
|
|
|
#define CMDQ_THR_SPR3(base, id) ((base) + (0x80 * (id)) + 0x16c)
|
|
#define CMDQ_GPR_R32(base, id) ((base) + (0x4 * (id)) + 0x80)
|
|
#define CMDQ_SYNC_TOKEN_UPD(base) ((base) + 0x68)
|
|
#define CMDQ_TPR_MASK(base) ((base) + 0xd0)
|
|
#define CMDQ_TPR_TIMEOUT_EN(base) ((base) + 0xdc)
|
|
|
|
#define CMDQ_TEST_CNT 8
|
|
|
|
#define CMDQ_GPR_DEBUG_TIMER CMDQ_GPR_R14
|
|
#define CMDQ_GPR_DEBUG_DUMMY CMDQ_GPR_R15
|
|
|
|
enum {
|
|
CMDQ_TEST_SUBSYS_GCE,
|
|
CMDQ_TEST_SUBSYS_MMSYS,
|
|
CMDQ_TEST_SUBSYS_NR,
|
|
CMDQ_TEST_SUBSYS_ERR = 99
|
|
};
|
|
|
|
enum CMDQ_SECURE_STATE_ENUM {
|
|
CMDQ_MTEE_STATE = -1,
|
|
CMDQ_NORMAL_STATE = 0,
|
|
CMDQ_TEE_STATE = 1,
|
|
};
|
|
|
|
struct test_node {
|
|
struct device *dev;
|
|
void __iomem *va;
|
|
phys_addr_t pa;
|
|
struct clk *clk;
|
|
struct clk *clk_timer;
|
|
};
|
|
|
|
struct cmdq_test {
|
|
struct device *dev;
|
|
struct mutex lock;
|
|
struct test_node gce;
|
|
struct test_node mmsys;
|
|
struct cmdq_client *clt;
|
|
struct cmdq_client *loop;
|
|
struct cmdq_client *sec;
|
|
u32 iter;
|
|
u32 subsys[CMDQ_TEST_SUBSYS_NR];
|
|
struct dentry *fs;
|
|
|
|
bool tick;
|
|
struct timer_list timer;
|
|
|
|
u16 token_user0;
|
|
u16 token_gpr_set4;
|
|
};
|
|
|
|
static struct cmdq_test *gtest;
|
|
|
|
static void cmdq_test_mbox_cb(struct cmdq_cb_data data)
|
|
{
|
|
struct cmdq_flush_completion *cmplt = data.data;
|
|
|
|
if (data.err < 0)
|
|
cmdq_err("pkt:%p err:%d", cmplt->pkt, data.err);
|
|
cmplt->err = !data.err ? false : true;
|
|
complete(&cmplt->cmplt);
|
|
}
|
|
|
|
static void cmdq_test_mbox_cb_destroy(struct cmdq_cb_data data)
|
|
{
|
|
struct cmdq_pkt *pkt = (struct cmdq_pkt *)data.data;
|
|
|
|
if (data.err < 0)
|
|
cmdq_err("pkt:%p err:%d", pkt, data.err);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
cmdq_pkt_destroy(pkt);
|
|
cmdq_msg("%s: pkt:%p", __func__, pkt);
|
|
}
|
|
|
|
static void cmdq_test_mbox_cb_dump(struct cmdq_cb_data data)
|
|
{
|
|
cmdq_msg("pkt:0x%p err:%d done", data.data, data.err);
|
|
}
|
|
|
|
static void cmdq_test_mbox_cb_dump_err(struct cmdq_cb_data data)
|
|
{
|
|
cmdq_err("pkt:0x%p err:%d during err", data.data, data.err);
|
|
}
|
|
|
|
static int cmdq_test_no_aee(struct cmdq_cb_data data)
|
|
{
|
|
struct cmdq_pkt *pkt = (struct cmdq_pkt *)data.data;
|
|
|
|
cmdq_err("pkt:0x%p err:%d during err", pkt, data.err);
|
|
return CMDQ_NO_AEE;
|
|
}
|
|
|
|
static void cmdq_test_mbox_err_dump(struct cmdq_test *test, const bool sec, const bool aee)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_flush_completion cmplt;
|
|
s32 ret;
|
|
u64 *inst;
|
|
dma_addr_t pc;
|
|
struct cmdq_client *clt;
|
|
size_t wfe_offset;
|
|
|
|
if (sec && !test->sec) {
|
|
cmdq_err("no test->sec");
|
|
return;
|
|
}
|
|
clt = sec ? test->sec : test->clt;
|
|
|
|
cmdq_msg("%s sec[%d] aee[%d]", __func__, sec, aee);
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
cmdq_clear_event(clt->chan, test->token_user0);
|
|
pkt = cmdq_pkt_create(clt);
|
|
|
|
if (!aee)
|
|
pkt->aee_cb = cmdq_test_no_aee;
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (sec)
|
|
cmdq_sec_pkt_set_data(pkt, 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#endif
|
|
|
|
cmdq_pkt_wfe(pkt, test->token_user0);
|
|
wfe_offset = pkt->cmd_buf_size - CMDQ_INST_SIZE;
|
|
|
|
init_completion(&cmplt.cmplt);
|
|
cmplt.pkt = pkt;
|
|
cmdq_pkt_flush_async(pkt, cmdq_test_mbox_cb, &cmplt);
|
|
|
|
cmdq_thread_dump(clt->chan, pkt, &inst, &pc);
|
|
cmdq_set_event(clt->chan, test->token_user0);
|
|
|
|
ret = cmdq_pkt_wait_complete(pkt);
|
|
cmdq_msg("wait complete pkt:0x%p ret:%d", pkt, ret);
|
|
|
|
ret = wait_for_completion_timeout(
|
|
&cmplt.cmplt, msecs_to_jiffies(CMDQ_TIMEOUT_DEFAULT));
|
|
if (!ret)
|
|
cmdq_err(
|
|
"wait_for_completion_timeout pkt:0x%p ret:%d inst:0x%016llx pc:%pa",
|
|
pkt, ret, inst ? *inst : 0, &pc);
|
|
else
|
|
cmdq_msg("%s round 1 done", __func__);
|
|
|
|
/* second round, use flush async ex with wait, pre-dump and timeout */
|
|
pkt->err_cb.cb = cmdq_test_mbox_cb_dump_err;
|
|
pkt->err_cb.data = pkt;
|
|
cmdq_clear_event(clt->chan, test->token_user0);
|
|
ret = cmdq_pkt_flush_async(pkt, cmdq_test_mbox_cb_dump, (void *)pkt);
|
|
cmdq_msg("flush pkt:0x%p ret:%d", pkt, ret);
|
|
ret = cmdq_pkt_wait_complete(pkt);
|
|
if (wfe_offset == pkt->err_data.offset)
|
|
cmdq_msg("pkt:0x%p right wfe_offset:%zu = err_offset:%zu",
|
|
pkt, wfe_offset, pkt->err_data.offset);
|
|
else
|
|
cmdq_err("pkt:0x%p wrong wfe_offset:%zu, err_offset:%zu",
|
|
pkt, wfe_offset, pkt->err_data.offset);
|
|
cmdq_msg("wait complete pkt:0x%p ret:%d", pkt, ret);
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
cmdq_msg("%s done", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_gpr_sleep(struct cmdq_test *test, const bool sleep)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_pkt_buffer *buf;
|
|
struct cmdq_operand l_op = {.reg = true, .idx = CMDQ_TPR_ID};
|
|
struct cmdq_operand r_op = {.reg = false, .idx = 100};
|
|
dma_addr_t out_pa;
|
|
u32 *out_va, gce_time;
|
|
u64 cpu_time;
|
|
const u16 event =
|
|
(u16)CMDQ_EVENT_GPR_TIMER + CMDQ_GPR_DEBUG_TIMER;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
if (clk_prepare_enable(test->gce.clk_timer)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(0x80000000, (void *)CMDQ_TPR_MASK(test->gce.va));
|
|
writel(1 << CMDQ_GPR_DEBUG_TIMER,
|
|
(void *)CMDQ_TPR_TIMEOUT_EN(test->gce.va));
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
if (!sleep) {
|
|
cmdq_pkt_write(pkt, NULL, CMDQ_TPR_TIMEOUT_EN(test->gce.pa),
|
|
1 << CMDQ_GPR_DEBUG_TIMER, 1 << CMDQ_GPR_DEBUG_TIMER);
|
|
cmdq_pkt_clear_event(pkt, event);
|
|
} else
|
|
cmdq_pkt_wfe(pkt, test->token_gpr_set4);
|
|
|
|
buf = list_last_entry(&pkt->buf, typeof(*buf), list_entry);
|
|
out_pa = CMDQ_BUF_ADDR(buf) + 3096;
|
|
out_va = (u32 *)(buf->va_base + 3096);
|
|
*out_va = 0;
|
|
*(out_va + 1) = 0;
|
|
*(out_va + 2) = 0;
|
|
|
|
if (!sleep) {
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa, CMDQ_TPR_ID, ~0);
|
|
cmdq_pkt_logic_command(pkt, CMDQ_LOGIC_ADD,
|
|
CMDQ_GPR_CNT_ID + CMDQ_GPR_DEBUG_TIMER, &l_op, &r_op);
|
|
cmdq_pkt_wfe(pkt, event);
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa + 4, CMDQ_TPR_ID, ~0);
|
|
} else {
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa, CMDQ_TPR_ID, ~0);
|
|
cmdq_pkt_sleep(pkt, 100, CMDQ_GPR_DEBUG_TIMER);
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa + 4, CMDQ_TPR_ID, ~0);
|
|
cmdq_pkt_set_event(pkt, test->token_gpr_set4);
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa + 8,
|
|
CMDQ_GPR_CNT_ID + CMDQ_GPR_DEBUG_TIMER, ~0);
|
|
}
|
|
cmdq_pkt_dump_buf(pkt, 0);
|
|
|
|
cpu_time = sched_clock();
|
|
cmdq_pkt_flush(pkt);
|
|
cpu_time = div_u64(sched_clock() - cpu_time, 1000000);
|
|
|
|
if (*out_va <= *(out_va + 1))
|
|
gce_time = *(out_va + 1) - *out_va;
|
|
else
|
|
gce_time = 0xffffffff - *out_va + *(out_va + 1);
|
|
|
|
if (cpu_time > 100 || gce_time > 300)
|
|
cmdq_err("sleep:%d cpu:%llu gce:%u out:%u %u %u",
|
|
sleep, cpu_time, gce_time, *out_va, *(out_va + 1),
|
|
*(out_va + 2));
|
|
else
|
|
cmdq_msg("sleep:%d cpu:%llu gce:%u out:%u %u %u",
|
|
sleep, cpu_time, gce_time, *out_va, *(out_va + 1),
|
|
*(out_va + 2));
|
|
cmdq_pkt_destroy(pkt);
|
|
writel(0, (void *)CMDQ_TPR_MASK(test->gce.va));
|
|
|
|
clk_disable_unprepare(test->gce.clk_timer);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
}
|
|
|
|
static void cmdq_test_mbox_cpr(struct cmdq_test *test)
|
|
{
|
|
unsigned long va = (unsigned long)CMDQ_GPR_R32(test->gce.va,
|
|
CMDQ_GPR_DEBUG_DUMMY);
|
|
unsigned long pa = CMDQ_GPR_R32(test->gce.pa,
|
|
CMDQ_GPR_DEBUG_DUMMY);
|
|
u32 pttn = 0xdeaddead, *buf_va, mark_assign, mark_write;
|
|
s32 i, ret;
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_pkt_buffer *buf;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
mark_assign = pkt->cmd_buf_size >> 2;
|
|
cmdq_pkt_assign_command(pkt, CMDQ_THR_SPR_IDX3, pttn);
|
|
mark_write = (pkt->cmd_buf_size + CMDQ_INST_SIZE) >> 2;
|
|
cmdq_pkt_write_indriect(pkt, NULL, pa, CMDQ_THR_SPR_IDX3, ~0);
|
|
cmdq_pkt_finalize(pkt);
|
|
cmdq_pkt_dump_buf(pkt, 0);
|
|
|
|
buf = list_first_entry(&pkt->buf, typeof(*buf), list_entry);
|
|
buf_va = (u32 *)buf->va_base;
|
|
|
|
/* (1024 - 256 - 4 * 24 - 16) * 2 = 1312 */
|
|
for (i = 0; i < 1312; i++) { // CPR_CNT
|
|
writel(0, (void *)va);
|
|
buf_va[mark_assign + 1] =
|
|
(buf_va[1] & 0xffff0000) | (CMDQ_CPR_STRAT_ID + i);
|
|
buf_va[mark_write] =
|
|
((CMDQ_CPR_STRAT_ID + i) << 16) | (buf_va[5] & 0xffff);
|
|
|
|
ret = cmdq_pkt_flush(pkt);
|
|
if (ret)
|
|
cmdq_err("cmdq_pkt_flush failed:%d i:%d", ret, i);
|
|
|
|
ret = readl((void *)va);
|
|
if (ret != pttn)
|
|
cmdq_err("ret:%#x not equal to pttn:%#x for idx:%d",
|
|
ret, pttn, i + CMDQ_CPR_STRAT_ID);
|
|
else
|
|
cmdq_msg("ret:%#x equals to pttn:%#x for idx:%d",
|
|
ret, pttn, i + CMDQ_CPR_STRAT_ID);
|
|
}
|
|
cmdq_pkt_destroy(pkt);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
u32 *cmdq_test_mbox_polling_timeout_unit(struct cmdq_pkt *pkt,
|
|
const unsigned long pa, const u32 pttn, const u32 mask, const bool aee)
|
|
{
|
|
struct cmdq_pkt_buffer *buf;
|
|
u32 *out_va;
|
|
dma_addr_t out_pa;
|
|
|
|
buf = list_last_entry(&pkt->buf, typeof(*buf), list_entry);
|
|
// last 1k as output buffer
|
|
out_pa = CMDQ_BUF_ADDR(buf) + 3096 + gce_mminfra;
|
|
out_va = (u32 *)(buf->va_base + 3096);
|
|
*out_va = 0;
|
|
*(out_va + 1) = 0;
|
|
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa, CMDQ_CPR_STRAT_ID, ~0);
|
|
cmdq_pkt_poll_timeout(pkt, pttn & mask,
|
|
SUBSYS_NO_SUPPORT, pa, mask, aee ? U16_MAX : 100,
|
|
CMDQ_GPR_DEBUG_TIMER);
|
|
cmdq_pkt_write_indriect(pkt, NULL, out_pa + 4, CMDQ_CPR_STRAT_ID, ~0);
|
|
return out_va;
|
|
}
|
|
|
|
void cmdq_test_mbox_polling(
|
|
struct cmdq_test *test, const s32 secure, const bool timeout,
|
|
const bool aee)
|
|
{
|
|
unsigned long va = (unsigned long)(secure ?
|
|
CMDQ_THR_SPR3(test->gce.va, 3) :
|
|
CMDQ_GPR_R32(test->gce.va, CMDQ_GPR_DEBUG_DUMMY));
|
|
unsigned long pa = secure ? CMDQ_THR_SPR3(test->gce.pa, 3) :
|
|
CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
const u32 pttn[CMDQ_TEST_CNT] = {
|
|
0xdada1818, 0xdada1818, 0xdada1818, 0x00001818};
|
|
const u32 mask[CMDQ_TEST_CNT] = {
|
|
0xff00ff00, 0xffffffff, 0x0000ff00, 0xffffffff};
|
|
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt[CMDQ_TEST_CNT];
|
|
u64 cpu_time;
|
|
u32 i, val = 0, *out_va, gce_time;
|
|
|
|
cmdq_msg("%s: secure:%d timeout:%d va:%#lx pa:%#lx",
|
|
__func__, secure, timeout, va, pa);
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
if (clk_prepare_enable(test->gce.clk_timer)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(0, (void *)va);
|
|
|
|
for (i = 0; i < CMDQ_TEST_CNT && pttn[i]; i++) {
|
|
if (timeout)
|
|
writel(0x80000000, (void *)CMDQ_TPR_MASK(test->gce.va));
|
|
|
|
pkt[i] = cmdq_pkt_create(clt);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure)
|
|
cmdq_sec_pkt_set_data(pkt[i], 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#endif
|
|
|
|
cmdq_pkt_wfe(pkt[i], test->token_gpr_set4);
|
|
if (timeout)
|
|
out_va = cmdq_test_mbox_polling_timeout_unit(
|
|
pkt[i], pa, pttn[i], mask[i], aee);
|
|
else
|
|
cmdq_pkt_poll(pkt[i], NULL, pttn[i] & mask[i], pa,
|
|
mask[i], CMDQ_GPR_DEBUG_TIMER);
|
|
|
|
cmdq_pkt_set_event(pkt[i], test->token_gpr_set4);
|
|
|
|
cpu_time = sched_clock();
|
|
cmdq_pkt_flush_async(pkt[i], NULL, NULL);
|
|
|
|
if (!timeout) {
|
|
writel(pttn[i] & mask[i], (void *)va);
|
|
val = readl((void *)va);
|
|
}
|
|
cmdq_pkt_wait_complete(pkt[i]);
|
|
cpu_time = div_u64(sched_clock() - cpu_time, 1000000);
|
|
|
|
if (!timeout)
|
|
gce_time = 0;
|
|
else if (*out_va <= *(out_va + 1))
|
|
gce_time = *(out_va + 1) - *out_va;
|
|
else
|
|
gce_time = 0xffffffff - *out_va + *(out_va + 1);
|
|
|
|
cmdq_msg("%d: pkt:%p pttn:%#x mask:%#x val:%#x cpu:%llu gce:%u",
|
|
i, pkt[i], pttn[i], mask[i], val, cpu_time, gce_time);
|
|
cmdq_pkt_dump_buf(pkt[i], 0);
|
|
cmdq_pkt_destroy(pkt[i]);
|
|
|
|
if (timeout)
|
|
writel(0, (void *)CMDQ_TPR_MASK(test->gce.va));
|
|
}
|
|
|
|
clk_disable_unprepare(test->gce.clk_timer);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_large_cmd(struct cmdq_test *test, const u32 count)
|
|
{
|
|
unsigned long va = (unsigned long)(CMDQ_GPR_R32(
|
|
test->gce.va, CMDQ_GPR_DEBUG_DUMMY));
|
|
unsigned long pa = CMDQ_GPR_R32(
|
|
test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
|
|
struct cmdq_pkt *pkt, *pkt2;
|
|
s32 i, val;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(0xdeaddead, (void *)va);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
for (i = 0; i < count; i++)
|
|
cmdq_pkt_write(pkt, NULL, pa, i, ~0);
|
|
|
|
pkt2 = cmdq_pkt_create(test->loop);
|
|
cmdq_pkt_copy(pkt2, pkt);
|
|
cmdq_pkt_write(pkt2, NULL, pa, i, ~0);
|
|
|
|
cmdq_msg("%s: pkt:%p before flush", __func__, pkt);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_msg("%s: pkt:%p after flush", __func__, pkt);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
cmdq_msg("%s: pkt2:%p before flush", __func__, pkt2);
|
|
cmdq_dump_pkt(pkt2, 0, true);
|
|
cmdq_pkt_flush(pkt2);
|
|
cmdq_msg("%s: pkt2:%p after flush", __func__, pkt2);
|
|
cmdq_dump_pkt(pkt2, 0, true);
|
|
cmdq_pkt_destroy(pkt2);
|
|
|
|
val = readl((void *)va);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
if (val != i)
|
|
cmdq_err("val:%#x not equal to i:%#x", val, i);
|
|
else
|
|
cmdq_msg("val:%#x equals to i:%#x", val, i);
|
|
}
|
|
|
|
static void cmdq_test_mbox_sync_token_loop_iter(struct timer_list *t)
|
|
{
|
|
struct cmdq_test *test = from_timer(test, t, timer);
|
|
|
|
if (!test->tick)
|
|
del_timer(&test->timer);
|
|
else {
|
|
mod_timer(&test->timer, jiffies + msecs_to_jiffies(300));
|
|
test->iter += 1;
|
|
}
|
|
}
|
|
|
|
static void cmdq_test_mbox_loop(struct cmdq_test *test)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_thread *thread =
|
|
(struct cmdq_thread *)test->loop->chan->con_priv;
|
|
s32 ret;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
pkt = cmdq_pkt_create(test->loop);
|
|
cmdq_pkt_wfe(pkt, test->token_user0);
|
|
cmdq_pkt_finalize_loop(pkt);
|
|
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
|
|
test->iter = 0;
|
|
test->tick = true;
|
|
timer_setup(&test->timer, cmdq_test_mbox_sync_token_loop_iter,
|
|
0);
|
|
mod_timer(&test->timer, jiffies + msecs_to_jiffies(300));
|
|
|
|
writel(test->token_user0,
|
|
(void *)CMDQ_SYNC_TOKEN_UPD(test->gce.va));
|
|
|
|
ret = cmdq_pkt_flush_async(pkt, NULL, 0);
|
|
while (test->iter < CMDQ_TEST_CNT) {
|
|
cmdq_msg("loop thrd-idx:%u pkt:%p iter:%u",
|
|
thread->idx, pkt, test->iter);
|
|
msleep_interruptible(1000);
|
|
}
|
|
|
|
cmdq_mbox_stop(test->loop);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
test->tick = false;
|
|
del_timer(&test->timer);
|
|
}
|
|
|
|
static void cmdq_test_mbox_dma_access(struct cmdq_test *test, const s32 secure)
|
|
{
|
|
unsigned long va = (unsigned long)(secure ?
|
|
CMDQ_THR_SPR3(test->gce.va, 3) :
|
|
CMDQ_GPR_R32(test->gce.va, CMDQ_GPR_DEBUG_DUMMY));
|
|
unsigned long pa = secure ? CMDQ_THR_SPR3(test->gce.pa, 3) :
|
|
CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
const u32 ofst = 0xabc, pttn[CMDQ_TEST_CNT] = {
|
|
0xabcdabcd, 0xaabbccdd, 0xdeaddead};
|
|
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt;
|
|
|
|
u32 *dma_va;
|
|
dma_addr_t dma_pa;
|
|
u32 val;
|
|
|
|
dma_va = cmdq_mbox_buf_alloc(clt, &dma_pa);
|
|
if (!dma_va || !dma_pa) {
|
|
cmdq_err("cmdq_mbox_buf_alloc failed");
|
|
return;
|
|
}
|
|
dma_va[0] = pttn[0];
|
|
dma_va[1] = pttn[0];
|
|
dma_va[2] = pttn[1];
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(pttn[2], (void *)va);
|
|
|
|
pkt = cmdq_pkt_create(clt);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure)
|
|
cmdq_sec_pkt_set_data(pkt, 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#endif
|
|
|
|
cmdq_pkt_mem_move(pkt, NULL, pa, dma_pa, CMDQ_THR_SPR_IDX1);
|
|
cmdq_pkt_mem_move(pkt, NULL, dma_pa, dma_pa + 4, CMDQ_THR_SPR_IDX1);
|
|
cmdq_pkt_mem_move(pkt, NULL, dma_pa + 8, pa, CMDQ_THR_SPR_IDX1);
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_dump_buf(pkt, 0);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
if (dma_va[0] != pttn[2])
|
|
cmdq_err("move pa:%#x to dma-0:%#x dft:%#x",
|
|
pttn[2], dma_va[0], pttn[0]);
|
|
if (dma_va[1] != pttn[2])
|
|
cmdq_err("move dma-0:%#x to dma-1:%#x dft:%#x",
|
|
pttn[2], dma_va[1], pttn[0]);
|
|
val = readl((void *)va);
|
|
if (dma_va[2] != val)
|
|
cmdq_err("move dma-2:%#x to pa:%#x dft:%#x",
|
|
dma_va[2], val, pttn[2]);
|
|
|
|
pkt = cmdq_pkt_create(clt);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure)
|
|
cmdq_sec_pkt_set_data(pkt, 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#endif
|
|
|
|
cmdq_pkt_jump(pkt, 8);
|
|
cmdq_pkt_write_value_addr(pkt, dma_pa + ofst, pttn[2], ~0);
|
|
cmdq_pkt_read_addr(pkt, dma_pa + ofst, CMDQ_THR_SPR_IDX1);
|
|
cmdq_pkt_write_reg_addr(pkt, dma_pa + ofst + 4, CMDQ_THR_SPR_IDX1, ~0);
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_dump_buf(pkt, 2);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
if (dma_va[ofst / 4] != dma_va[ofst / 4 + 1] ||
|
|
dma_va[ofst / 4 + 1] != pttn[2])
|
|
cmdq_err("pa:%pa offset:%#x va:%p val:%#x %#x pttn:%#x",
|
|
&dma_pa, ofst, &dma_va[ofst / 4], dma_va[ofst / 4],
|
|
dma_va[ofst / 4 + 1], pttn[2]);
|
|
|
|
clk_disable_unprepare(test->gce.clk);
|
|
cmdq_mbox_buf_free(clt, dma_va, dma_pa);
|
|
cmdq_msg("%s done", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_write_dma(
|
|
struct cmdq_test *test, const s32 secure, u32 cnt)
|
|
{
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt;
|
|
u32 *dma_va;
|
|
dma_addr_t dma_pa = 0;
|
|
s32 i;
|
|
|
|
cmdq_msg("%s in", __func__);
|
|
dma_va = cmdq_mbox_buf_alloc(clt, &dma_pa);
|
|
pkt = cmdq_pkt_create(clt);
|
|
for (i = 0; i < cnt; i++)
|
|
cmdq_pkt_write(pkt, NULL, dma_pa + ((i * 4) % PAGE_SIZE), i, ~0);
|
|
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_destroy(pkt);
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_write_dma_cpr(
|
|
struct cmdq_test *test, const s32 secure, u32 cnt)
|
|
{
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt;
|
|
u32 *dma_va;
|
|
dma_addr_t dma_pa = 0;
|
|
s32 i;
|
|
const u32 pattern = 0xbeef0000;
|
|
|
|
cmdq_msg("%s in", __func__);
|
|
dma_va = cmdq_mbox_buf_alloc(clt, &dma_pa);
|
|
cmdq_msg("dma pa %#lx", (unsigned long)dma_pa);
|
|
|
|
if (!dma_va) {
|
|
cmdq_err("%s buf alloc null", __func__);
|
|
return;
|
|
}
|
|
|
|
pkt = cmdq_pkt_create(clt);
|
|
for (i = 0; i < cnt; i++) {
|
|
cmdq_pkt_assign_command(pkt, CMDQ_THR_SPR_IDX3, pattern + i);
|
|
cmdq_pkt_assign_command(pkt, CMDQ_CPR_STRAT_ID + i * 2, (u32)dma_pa + i * 4);
|
|
cmdq_pkt_assign_command(pkt, CMDQ_CPR_STRAT_ID + i * 2 + 1, (u32)(dma_pa >> 32));
|
|
cmdq_pkt_write_reg_indriect(pkt, CMDQ_CPR_STRAT_ID + CMDQ_CPR64 + i,
|
|
CMDQ_THR_SPR_IDX3, U32_MAX);
|
|
*(dma_va + i) = 0xdead0000 + i;
|
|
}
|
|
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
if (*(dma_va + i) != pattern + i)
|
|
cmdq_err("data %u: %#x fail", i, *(dma_va + i));
|
|
else
|
|
cmdq_msg("data %u: %#x match", i, *(dma_va + i));
|
|
}
|
|
|
|
cmdq_mbox_buf_free(test->clt, dma_va, dma_pa);
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_sync_token_flush(struct timer_list *t)
|
|
{
|
|
u32 val;
|
|
struct cmdq_test *test = from_timer(test, t, timer);
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel((1L << 16) | test->token_user0,
|
|
(void *)CMDQ_SYNC_TOKEN_UPD(test->gce.va));
|
|
val = readl((void *)CMDQ_SYNC_TOKEN_UPD(test->gce.va));
|
|
cmdq_log("data:%#hx event:%#x val:%#x",
|
|
test->token_user0, (1 << 16), val);
|
|
|
|
if (!test->tick)
|
|
del_timer(&test->timer);
|
|
else
|
|
mod_timer(&test->timer, jiffies + msecs_to_jiffies(10));
|
|
|
|
clk_disable_unprepare(test->gce.clk);
|
|
}
|
|
|
|
void cmdq_test_mbox_flush(
|
|
struct cmdq_test *test, const s32 secure, const bool threaded)
|
|
{
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt[CMDQ_TEST_CNT] = {0};
|
|
s32 i, err;
|
|
|
|
cmdq_msg("%s sec:%d threaded:%d", __func__, secure, threaded);
|
|
|
|
test->tick = true;
|
|
timer_setup(&test->timer, cmdq_test_mbox_sync_token_flush,
|
|
0);
|
|
mod_timer(&test->timer, jiffies + msecs_to_jiffies(10));
|
|
|
|
for (i = 0; i < CMDQ_TEST_CNT; i++) {
|
|
pkt[i] = cmdq_pkt_create(clt);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure) {
|
|
cmdq_sec_pkt_set_data(pkt[i], 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#ifdef CMDQ_SECURE_MTEE_SUPPORT
|
|
if (!~secure)
|
|
cmdq_sec_pkt_set_mtee(pkt[i], true);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
cmdq_pkt_wfe(pkt[i], test->token_user0);
|
|
pkt[i]->priority = i;
|
|
|
|
if (!threaded)
|
|
cmdq_pkt_flush_async(pkt[i], NULL, NULL);
|
|
else
|
|
cmdq_pkt_flush_threaded(pkt[i],
|
|
cmdq_test_mbox_cb_destroy, (void *)pkt[i]);
|
|
}
|
|
|
|
for (i = 0; i < CMDQ_TEST_CNT; i++) {
|
|
if (!pkt[i]) {
|
|
cmdq_err("NULL pkt:%d", i);
|
|
continue;
|
|
}
|
|
msleep_interruptible(100);
|
|
if (!threaded)
|
|
err = cmdq_pkt_wait_complete(pkt[i]);
|
|
else
|
|
err = 0;
|
|
if (err < 0) {
|
|
cmdq_err("wait complete pkt[%d]:%p err:%d",
|
|
i, pkt[i], err);
|
|
continue;
|
|
}
|
|
if (!threaded)
|
|
cmdq_pkt_destroy(pkt[i]);
|
|
}
|
|
|
|
test->tick = false;
|
|
del_timer(&test->timer);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_write(
|
|
struct cmdq_test *test, const s32 secure, const bool need_mask)
|
|
{
|
|
const u32 mask = need_mask ? (1 << 16) : ~0;
|
|
const u32 pttn = (1 << 0) | (1 << 2) | (1 << 16);
|
|
unsigned long va = (unsigned long)(secure ?
|
|
CMDQ_THR_SPR3(test->gce.va, 3) :
|
|
CMDQ_GPR_R32(test->gce.va, CMDQ_GPR_DEBUG_DUMMY));
|
|
unsigned long pa = secure ? CMDQ_THR_SPR3(test->gce.pa, 3) :
|
|
CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
|
|
struct cmdq_client *clt = secure ? test->sec : test->clt;
|
|
struct cmdq_pkt *pkt;
|
|
s32 val;
|
|
|
|
cmdq_msg("sec:%d va:%#lx pa:%#lx pttn:%#x mask:%#x clt:%p",
|
|
secure, va, pa, pttn, mask, clt);
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(0, (void *)va);
|
|
|
|
pkt = cmdq_pkt_create(clt);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure) {
|
|
cmdq_sec_pkt_set_data(pkt, 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_NONE);
|
|
#ifdef CMDQ_SECURE_MTEE_SUPPORT
|
|
if (!~secure)
|
|
cmdq_sec_pkt_set_mtee(pkt, true);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
cmdq_pkt_write(pkt, NULL, pa, pttn, mask);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
cmdq_pkt_flush(pkt);
|
|
|
|
val = readl((void *)va);
|
|
if (val != (pttn & mask)) {
|
|
cmdq_err("wrong val:%#x ans:%#x", val, pttn & mask);
|
|
cmdq_pkt_dump_buf(pkt, 0);
|
|
} else
|
|
cmdq_msg("right val:%#x ans:%#x", val, pttn & mask);
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_handshake_event(struct cmdq_test *test)
|
|
{
|
|
struct cmdq_client *clt1 = test->clt, *clt2 = test->loop;
|
|
struct cmdq_pkt *pkt_wait, *pkt_shake;
|
|
int ret;
|
|
|
|
if (cmdq_mbox_get_base_pa(clt1->chan) ==
|
|
cmdq_mbox_get_base_pa(clt2->chan)) {
|
|
cmdq_msg("no handshake for 2 same client");
|
|
return;
|
|
}
|
|
|
|
pkt_wait = cmdq_pkt_create(clt1);
|
|
cmdq_pkt_wfe(pkt_wait, CMDQ_EVENT_HANDSHAKE);
|
|
cmdq_pkt_handshake_event(pkt_wait, CMDQ_EVENT_HANDSHAKE + 1);
|
|
|
|
pkt_shake = cmdq_pkt_create(clt2);
|
|
cmdq_pkt_handshake_event(pkt_shake, CMDQ_EVENT_HANDSHAKE);
|
|
cmdq_pkt_wfe(pkt_shake, CMDQ_EVENT_HANDSHAKE + 1);
|
|
|
|
cmdq_pkt_flush_async(pkt_wait, NULL, NULL);
|
|
ret = cmdq_pkt_flush(pkt_shake);
|
|
|
|
cmdq_pkt_wait_complete(pkt_wait);
|
|
|
|
if (ret < 0)
|
|
cmdq_err("shake event fail:%d", ret);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_access_sub_impl(struct cmdq_test *test,
|
|
struct cmdq_client *clt, const char *tag)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
u32 *regs, count, *va, i;
|
|
dma_addr_t pa;
|
|
u8 swap_reg = CMDQ_THR_SPR_IDX1;
|
|
u32 pat = 0xadceabce, pat_init = 0xdeaddead, pat_src = 0xbeefbeef;
|
|
void __iomem *va_base;
|
|
u32 val = 0;
|
|
u32 pa_base;
|
|
|
|
cmdq_msg("%s in", __func__);
|
|
|
|
count = cmdq_util_test_get_subsys_list(®s);
|
|
if (count <= 0) {
|
|
cmdq_err("invalid count:%d", count);
|
|
return;
|
|
}
|
|
|
|
va = cmdq_mbox_buf_alloc(clt, &pa);
|
|
if (!va) {
|
|
cmdq_err("cmdq_mbox_buf_alloc failed");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
va[0] = pat_init;
|
|
|
|
cmdq_msg("%s idx:%d, addr:%#x", __func__, i, regs[i]);
|
|
pa_base = regs[i];
|
|
va_base = ioremap(pa_base, 0x1000);
|
|
writel(pat, va_base);
|
|
val = readl(va_base);
|
|
if (val != pat)
|
|
cmdq_msg("%s AP write, addr:%#x = %#x, pat:%#x",
|
|
__func__, regs[i], val, pat);
|
|
writel(0, va_base);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_write_value_addr(pkt, regs[i], pat_src, ~0);
|
|
cmdq_pkt_mem_move(pkt, NULL, regs[i], pa, swap_reg);
|
|
cmdq_pkt_flush(pkt);
|
|
|
|
if (va[0] != pat_src)
|
|
cmdq_err(
|
|
"%s access reg fail addr:%#x val:%#x should be:%#x",
|
|
tag, regs[i], va[0], pat_src);
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
}
|
|
|
|
cmdq_mbox_buf_free(test->clt, va, pa);
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_subsys_access(struct cmdq_test *test)
|
|
{
|
|
cmdq_access_sub_impl(test, test->clt, "clt");
|
|
cmdq_access_sub_impl(test, test->loop, "loop");
|
|
}
|
|
|
|
static void cmdq_test_err_irq(struct cmdq_test *test)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_pkt_buffer *buf;
|
|
u64 *inst;
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_jump(pkt, 0);
|
|
|
|
buf = list_first_entry(&pkt->buf, typeof(*buf), list_entry);
|
|
inst = (u64 *)buf->va_base;
|
|
*inst = 0xffffbeefdeadbeef;
|
|
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_devapc_vio(struct cmdq_test *test)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
int ret;
|
|
|
|
cmdq_msg("%s", __func__);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_read(pkt, NULL, 0x14000000, CMDQ_THR_SPR_IDX3);
|
|
ret = cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
cmdq_msg("%s ret:%d end", __func__, ret);
|
|
}
|
|
|
|
static void cmdq_test_mbox_stop(struct cmdq_test *test)
|
|
{
|
|
struct cmdq_pkt *pkt[3];
|
|
int i;
|
|
|
|
cmdq_msg("%s", __func__);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pkt); i++) {
|
|
pkt[i] = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_wfe(pkt[i], test->token_user0);
|
|
cmdq_pkt_flush_async(pkt[i], NULL, NULL);
|
|
}
|
|
|
|
cmdq_msg("%s stop channel", __func__);
|
|
cmdq_mbox_channel_stop(test->clt->chan);
|
|
|
|
cmdq_msg("%s still wait it", __func__);
|
|
for (i = 0; i < ARRAY_SIZE(pkt); i++) {
|
|
cmdq_pkt_wait_complete(pkt[i]);
|
|
cmdq_pkt_destroy(pkt[i]);
|
|
}
|
|
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_show_events(struct cmdq_test *test)
|
|
{
|
|
u32 i;
|
|
|
|
cmdq_msg("%s scan all active event ...", __func__);
|
|
cmdq_mbox_enable(test->clt->chan);
|
|
|
|
for (i = 0; i < CMDQ_EVENT_MAX; i++)
|
|
if (cmdq_get_event(test->clt->chan, i))
|
|
cmdq_msg("event set:%u", i);
|
|
cmdq_mbox_disable(test->clt->chan);
|
|
cmdq_msg("%s end", __func__);
|
|
}
|
|
|
|
static void cmdq_test_mbox_reuse_buf_va(struct cmdq_test *test)
|
|
{
|
|
unsigned long va = (unsigned long)(CMDQ_GPR_R32(
|
|
test->gce.va, CMDQ_GPR_DEBUG_DUMMY));
|
|
unsigned long pa = CMDQ_GPR_R32(
|
|
test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
|
|
struct cmdq_pkt *pkt;
|
|
s32 i, j = 0, val;
|
|
struct cmdq_reuse *reuse;
|
|
const u32 ans = (CMDQ_INST_SIZE * CMDQ_INST_SIZE - 1) * CMDQ_INST_SIZE;
|
|
|
|
reuse = kcalloc(CMDQ_INST_SIZE, sizeof(*reuse), GFP_KERNEL);
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
writel(0xdeaddead, (void *)va);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
for (i = 0; i < CMDQ_INST_SIZE * CMDQ_INST_SIZE; i++) {
|
|
if (i % CMDQ_INST_SIZE != CMDQ_INST_SIZE - 1)
|
|
cmdq_pkt_write(pkt, NULL, pa, i, ~0);
|
|
else {
|
|
cmdq_pkt_write_value_addr_reuse(
|
|
pkt, pa, i, ~0, &reuse[j]);
|
|
reuse[j].val = i;
|
|
cmdq_msg("%s: reuse:%d va:%p val:%#x inst:%#llx",
|
|
__func__, j,
|
|
reuse[j].va, reuse[j].val, *reuse[j].va);
|
|
j += 1;
|
|
}
|
|
}
|
|
cmdq_pkt_flush(pkt);
|
|
|
|
for (i = 0; i < j; i++)
|
|
reuse[i].val *= CMDQ_INST_SIZE;
|
|
cmdq_pkt_reuse_buf_va(pkt, reuse, CMDQ_INST_SIZE);
|
|
cmdq_pkt_flush(pkt);
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
val = readl((void *)va);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
if (val != ans)
|
|
cmdq_err("val:%#x not equal to ans:%#x", val, ans);
|
|
else
|
|
cmdq_msg("val:%#x equals to ans:%#x", val, ans);
|
|
}
|
|
|
|
struct cmdq_test_mbox_routine {
|
|
struct cmdq_pkt *pkt;
|
|
u16 mod;
|
|
u16 pipe;
|
|
u32 pa;
|
|
u32 pas;
|
|
u16 spr;
|
|
const u32 *reg;
|
|
size_t sz;
|
|
const u32 *ext_reg;
|
|
size_t ext_sz;
|
|
u64 *inst;
|
|
s32 mark;
|
|
u32 aid_sel;
|
|
bool secure;
|
|
};
|
|
|
|
static void cmdq_test_mbox_prebuilt_routine(struct cmdq_test_mbox_routine r)
|
|
{
|
|
s32 i;
|
|
|
|
if (r.inst)
|
|
*r.inst |= CMDQ_REG_SHIFT_ADDR(
|
|
(s32)r.pkt->cmd_buf_size - r.mark - CMDQ_INST_SIZE);
|
|
|
|
cmdq_pkt_write_value_addr(r.pkt, r.pa + 0x000, 0x1, 0x00000001);
|
|
if (r.mod == CMDQ_PREBUILT_MML)
|
|
cmdq_pkt_write_value_addr(r.pkt, r.pa + 0x024, 0x1, UINT_MAX);
|
|
for (i = 0; i < r.sz; i++)
|
|
cmdq_pkt_write_reg_addr(r.pkt, r.pa + r.reg[i],
|
|
CMDQ_CPR_PREBUILT(r.mod, r.pipe, i), UINT_MAX);
|
|
for (i = 0; i < r.ext_sz; i++) {
|
|
if (r.ext_reg[i] == 0x000 || r.ext_reg[i] == 0x024)
|
|
continue;
|
|
cmdq_pkt_write_reg_addr(r.pkt, r.pa + r.ext_reg[i],
|
|
CMDQ_CPR_PREBUILT_EXT(r.mod, r.pipe, i), UINT_MAX);
|
|
}
|
|
cmdq_pkt_write_value_addr(r.pkt, r.pa + 0x038,
|
|
r.secure ? (1 << 18) : 0, 1 << 18);
|
|
cmdq_pkt_write_value_addr(r.pkt, r.pas + 0xfa8,
|
|
r.secure ? r.aid_sel : 0, r.aid_sel);
|
|
}
|
|
|
|
static void cmdq_test_mbox_prebuilt_instr_ext_table(struct cmdq_test *test,
|
|
struct cmdq_test_mbox_routine r, const u16 event,
|
|
const u32 pa0, const u32 pa1, const u32 pas, const bool secure)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_pkt_buffer *buf;
|
|
struct cmdq_operand lop, rop;
|
|
u64 *inst[6];
|
|
s32 mark[6] = {0}, i;
|
|
|
|
memset(inst, 0, sizeof(inst));
|
|
|
|
cmdq_msg("%s: mod:%hu event:%hu pa0:%#x pa1:%#x pas:%#x",
|
|
__func__, r.mod, event, pa0, pa1, pas);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_wfe(pkt, event);
|
|
|
|
/* conditional jumps */
|
|
lop.reg = true;
|
|
lop.idx = CMDQ_CPR_PREBUILT_PIPE(r.mod);
|
|
rop.reg = false;
|
|
if (pa0 && pa1) {
|
|
rop.value = 1;
|
|
mark[0] = pkt->cmd_buf_size;
|
|
inst[0] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_assign_command(pkt, r.spr, 0);
|
|
cmdq_pkt_cond_jump(pkt, r.spr, &lop, &rop, CMDQ_EQUAL);
|
|
}
|
|
if (pa0 && secure) {
|
|
rop.value = 2;
|
|
mark[1] = pkt->cmd_buf_size;
|
|
inst[1] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_assign_command(pkt, r.spr, 0);
|
|
cmdq_pkt_cond_jump(pkt, r.spr, &lop, &rop, CMDQ_EQUAL);
|
|
}
|
|
if (pa1 && secure) {
|
|
rop.value = 3;
|
|
mark[2] = pkt->cmd_buf_size;
|
|
inst[2] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_assign_command(pkt, r.spr, 0);
|
|
cmdq_pkt_cond_jump(pkt, r.spr, &lop, &rop, CMDQ_EQUAL);
|
|
}
|
|
|
|
/* routines */
|
|
r.pkt = pkt;
|
|
r.pas = pas;
|
|
if (pa0) {
|
|
/* case 0: normal pipe 0 */
|
|
r.pa = pa0;
|
|
r.aid_sel = 0x15;
|
|
cmdq_test_mbox_prebuilt_routine(r);
|
|
|
|
if (pa1 || secure) {
|
|
mark[3] = pkt->cmd_buf_size;
|
|
inst[3] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_jump(pkt, 0);
|
|
}
|
|
}
|
|
if (pa1) {
|
|
/* case 1: normal pipe 1 */
|
|
r.pipe = 1;
|
|
r.pa = pa1;
|
|
r.inst = inst[0];
|
|
r.mark = mark[0];
|
|
r.aid_sel = 0x2a;
|
|
cmdq_test_mbox_prebuilt_routine(r);
|
|
|
|
if (secure) {
|
|
mark[4] = pkt->cmd_buf_size;
|
|
inst[4] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_jump(pkt, 0);
|
|
}
|
|
}
|
|
if (pa1 && secure) {
|
|
/* case 3: secure pipe 1 */
|
|
r.inst = inst[2];
|
|
r.mark = mark[2];
|
|
r.secure = true;
|
|
cmdq_test_mbox_prebuilt_routine(r);
|
|
|
|
if (pa0) {
|
|
mark[5] = pkt->cmd_buf_size;
|
|
inst[5] = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_jump(pkt, 0);
|
|
}
|
|
}
|
|
if (pa0 && secure) {
|
|
/* case 2: secure pipe 0 */
|
|
r.pipe = 0;
|
|
r.pa = pa0;
|
|
r.inst = inst[1];
|
|
r.mark = mark[1];
|
|
r.aid_sel = 0x15;
|
|
r.secure = true;
|
|
cmdq_test_mbox_prebuilt_routine(r);
|
|
}
|
|
|
|
if (inst[3])
|
|
*inst[3] |= CMDQ_REG_SHIFT_ADDR((s32)pkt->cmd_buf_size - mark[3]);
|
|
if (inst[4])
|
|
*inst[4] |= CMDQ_REG_SHIFT_ADDR((s32)pkt->cmd_buf_size - mark[4]);
|
|
if (inst[5])
|
|
*inst[5] |= CMDQ_REG_SHIFT_ADDR((s32)pkt->cmd_buf_size - mark[5]);
|
|
cmdq_pkt_set_event(pkt, event + 1);
|
|
cmdq_pkt_finalize_loop(pkt);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
|
|
buf = list_first_entry_or_null(&pkt->buf, typeof(*buf), list_entry);
|
|
if (!buf) {
|
|
cmdq_pkt_destroy(pkt);
|
|
return;
|
|
}
|
|
|
|
cmdq_msg("%s: pkt:%p pa:%#lx cmd_buf_size:%#lx pc:%#lx end:%#lx",
|
|
__func__, pkt, (unsigned long)buf->pa_base, pkt->cmd_buf_size,
|
|
CMDQ_REG_SHIFT_ADDR((unsigned long)buf->pa_base),
|
|
CMDQ_REG_SHIFT_ADDR((unsigned long)buf->pa_base +
|
|
pkt->cmd_buf_size));
|
|
|
|
for (i = 0; i < pkt->cmd_buf_size / CMDQ_INST_SIZE; i++)
|
|
cmdq_msg(",%d,%#llx,", i, *((u64 *)buf->va_base + i));
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
}
|
|
|
|
static void cmdq_test_mbox_prebuilt_instr_ext(struct cmdq_test *test,
|
|
const u16 mod, const u16 event)
|
|
{
|
|
/* configure */
|
|
const u16 spr = CMDQ_THR_SPR_IDX3;
|
|
static const u32 reg[] = {
|
|
0x118, 0x120, 0x128, 0x148, 0x150, 0x200,
|
|
0xf00, 0xf08, 0xf10, 0xf20, 0xf28, 0xf30, 0xf34,
|
|
0xf38, 0xf3c, 0xf40, 0xf44, 0xf48, 0xf4c, 0xf50};
|
|
static const u32 mdp_reg[] = {
|
|
0x000, 0x020, 0x024, 0x028, 0x030, 0x038,
|
|
0x060, 0x068, 0x070, 0x078, 0x080, 0x090, 0x098,
|
|
0x240, 0x244, 0x248, 0x250, 0x254, 0x258, 0x260, 0x264,
|
|
0x268, 0x270, 0x274, 0x278, 0x280, 0x284, 0x288, 0x290, 0x2a0};
|
|
static const u32 mml_reg[] = {
|
|
0x020, 0x028, 0x030, 0x038,
|
|
0x060, 0x068, 0x070, 0x078, 0x080, 0x090, 0x098,
|
|
0x240, 0x244, 0x248, 0x250, 0x254, 0x258, 0x260, 0x264,
|
|
0x268, 0x270, 0x274, 0x278, 0x280, 0x284, 0x288, 0x290, 0x2a0};
|
|
struct cmdq_test_mbox_routine r = {0};
|
|
|
|
r.mod = mod;
|
|
r.spr = spr;
|
|
r.reg = reg;
|
|
r.sz = ARRAY_SIZE(reg);
|
|
|
|
if (mod == CMDQ_PREBUILT_MDP) {
|
|
r.ext_reg = mdp_reg;
|
|
r.ext_sz = ARRAY_SIZE(mdp_reg);
|
|
|
|
cmdq_test_mbox_prebuilt_instr_ext_table(test, r, event,
|
|
0x1f003000,
|
|
0x1f004000,
|
|
0x1f000000, true);
|
|
cmdq_test_mbox_prebuilt_instr_ext_table(test, r, event,
|
|
0,
|
|
0x1f004000,
|
|
0x1f000000, true);
|
|
cmdq_test_mbox_prebuilt_instr_ext_table(test, r, event,
|
|
0x1f003000,
|
|
0,
|
|
0x1f000000, false);
|
|
} else if (mod == CMDQ_PREBUILT_MML) {
|
|
r.ext_reg = mml_reg;
|
|
r.ext_sz = ARRAY_SIZE(mml_reg);
|
|
|
|
cmdq_test_mbox_prebuilt_instr_ext_table(test, r, event,
|
|
0x1f803000,
|
|
0x1f804000,
|
|
0x1f800000, true);
|
|
cmdq_test_mbox_prebuilt_instr_ext_table(test, r, event,
|
|
0x1f003000,
|
|
0,
|
|
0x1f000000, true);
|
|
}
|
|
}
|
|
|
|
static void cmdq_test_mbox_prebuilt_instr(struct cmdq_test *test,
|
|
const u16 mod, const u16 event)
|
|
{
|
|
struct cmdq_pkt *pkt;
|
|
struct cmdq_pkt_buffer *buf;
|
|
struct cmdq_operand lop, rop;
|
|
u64 *inst, *inst2;
|
|
s32 mark, mark2, i;
|
|
/* configure */
|
|
const u16 spr = CMDQ_THR_SPR_IDX3;
|
|
unsigned long pa0, pa1, reg[] = {
|
|
0x118, 0x120, 0x128, 0x148, 0x150, 0x200,
|
|
0xf00, 0xf08, 0xf10, 0xf20, 0xf28, 0xf30, 0xf34,
|
|
0xf38, 0xf3c, 0xf40, 0xf44, 0xf48, 0xf4c, 0xf50};
|
|
|
|
if (mod == CMDQ_PREBUILT_MDP) {
|
|
pa0 = 0x1f003000;
|
|
pa1 = 0x1f004000;
|
|
} else if (mod == CMDQ_PREBUILT_MML) {
|
|
pa0 = 0x1f803000;
|
|
pa1 = 0x1f804000;
|
|
} else if (mod == CMDQ_PREBUILT_VFMT) {
|
|
pa0 = 0x16005000;
|
|
pa1 = 0x16007000;
|
|
} else {
|
|
pa0 = CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_TIMER);
|
|
pa1 = CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
memset(reg, 0, sizeof(reg));
|
|
}
|
|
cmdq_msg("%s: mod:%hu event:%hu pa0:%#lx pa1:%#lx",
|
|
__func__, mod, event, pa0, pa1);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_wfe(pkt, event);
|
|
|
|
/* conditional jump */
|
|
mark = pkt->cmd_buf_size;
|
|
inst = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_assign_command(pkt, spr, 0);
|
|
|
|
lop.reg = true;
|
|
lop.idx = CMDQ_CPR_PREBUILT_PIPE(mod);
|
|
rop.reg = false;
|
|
rop.value = 1;
|
|
cmdq_pkt_cond_jump(pkt, spr, &lop, &rop, CMDQ_EQUAL);
|
|
|
|
/* pipe 0 */
|
|
for (i = 0; i < ARRAY_SIZE(reg); i++)
|
|
cmdq_pkt_write_reg_addr(pkt, pa0 + reg[i],
|
|
CMDQ_CPR_PREBUILT(mod, 0, i), UINT_MAX);
|
|
|
|
mark2 = pkt->cmd_buf_size;
|
|
inst2 = cmdq_pkt_get_curr_buf_va(pkt);
|
|
cmdq_pkt_jump(pkt, 0);
|
|
|
|
/* pipe 1 */
|
|
*inst |= CMDQ_REG_SHIFT_ADDR(
|
|
(s32)pkt->cmd_buf_size - mark - CMDQ_INST_SIZE);
|
|
for (i = 0; i < ARRAY_SIZE(reg); i++)
|
|
cmdq_pkt_write_reg_addr(pkt, pa1 + reg[i],
|
|
CMDQ_CPR_PREBUILT(mod, 1, i), UINT_MAX);
|
|
|
|
*inst2 |= CMDQ_REG_SHIFT_ADDR((s32)pkt->cmd_buf_size - mark2);
|
|
cmdq_pkt_set_event(pkt, event + 1);
|
|
cmdq_pkt_finalize_loop(pkt);
|
|
cmdq_dump_pkt(pkt, 0, true);
|
|
|
|
buf = list_first_entry_or_null(&pkt->buf, typeof(*buf), list_entry);
|
|
if (!buf) {
|
|
cmdq_pkt_destroy(pkt);
|
|
return;
|
|
}
|
|
|
|
cmdq_msg("%s: pkt:%p pa:%#lx cmd_buf_size:%#lx pc:%#lx end:%#lx",
|
|
__func__, pkt, (unsigned long)buf->pa_base, pkt->cmd_buf_size,
|
|
CMDQ_REG_SHIFT_ADDR((unsigned long)buf->pa_base),
|
|
CMDQ_REG_SHIFT_ADDR((unsigned long)buf->pa_base +
|
|
pkt->cmd_buf_size));
|
|
|
|
for (i = 0; i < pkt->cmd_buf_size / CMDQ_INST_SIZE; i++)
|
|
cmdq_msg(",%d,%#llx,", i, *((u64 *)buf->va_base + i));
|
|
|
|
cmdq_pkt_destroy(pkt);
|
|
}
|
|
|
|
static void cmdq_test_mbox_prebuilt(struct cmdq_test *test, const u16 mod,
|
|
const bool pipe, const bool timeout)
|
|
{
|
|
unsigned long va = (unsigned long)
|
|
CMDQ_GPR_R32(test->gce.va, CMDQ_GPR_DEBUG_DUMMY);
|
|
unsigned long pa =
|
|
CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
struct cmdq_pkt *pkt;
|
|
const u32 ans = pipe ? 0xbeaf : 0xfeed, dead = 0xdead;
|
|
u16 event = 689, cpr[3];
|
|
u32 init, cpu[2], val;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
event = CMDQ_PREBUILT_MDP ? 680 : 689;
|
|
cpr[0] = CMDQ_PREBUILT_MDP ? 0x8003 : 0x8006;
|
|
cpr[1] = CMDQ_PREBUILT_MDP ? 0x8023 : 0x809b;
|
|
cpr[2] = CMDQ_PREBUILT_MDP ? 0x8037 : 0x80af;
|
|
|
|
writel(0x12345678, (void *)va);
|
|
|
|
init = cmdq_get_event(test->clt->chan, event);
|
|
cmdq_msg(
|
|
"%s: mod:%hu pipe:%d timeout:%d event:%hu init:%x cpr:%#hx:%#hx:%#hx",
|
|
__func__, mod, pipe, timeout,
|
|
event, init, cpr[0], cpr[1], cpr[2]);
|
|
|
|
pkt = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_assign_command(pkt, cpr[0], pipe ? 0x1 : 0x0);
|
|
cmdq_pkt_assign_command(pkt, cpr[1], !pipe ? ans : dead);
|
|
cmdq_pkt_assign_command(pkt, cpr[2], pipe ? ans : dead);
|
|
if (!timeout)
|
|
cmdq_pkt_set_event(pkt, event);
|
|
cmdq_pkt_wfe(pkt, event + 1);
|
|
cmdq_pkt_flush(pkt);
|
|
cmdq_pkt_destroy(pkt);
|
|
|
|
cpu[0] = cmdq_get_event(test->clt->chan, event);
|
|
cpu[1] = cmdq_get_event(test->clt->chan, event + 1);
|
|
val = readl((void *)va);
|
|
clk_disable_unprepare(test->gce.clk);
|
|
|
|
cmdq_msg("%s: event:%hu cpu:%u:%u pa:%#lx val:%#x ans:%#x",
|
|
__func__, event, cpu[0], cpu[1], pa, val, ans);
|
|
}
|
|
|
|
static void cmdq_test_mbox_tzmp(struct cmdq_test *test, const s32 secure,
|
|
const bool timeout)
|
|
{
|
|
unsigned long va = (unsigned long)
|
|
CMDQ_GPR_R32(test->gce.va, CMDQ_GPR_DEBUG_DUMMY);
|
|
unsigned long pa =
|
|
CMDQ_GPR_R32(test->gce.pa, CMDQ_GPR_DEBUG_DUMMY);
|
|
const u32 ans = 0xbeafdead, event = 678;
|
|
struct cmdq_client *clt = test->clt, *sec = test->sec;
|
|
struct cmdq_pkt *pkt, *pkt2;
|
|
s32 val, i;
|
|
|
|
if (clk_prepare_enable(test->gce.clk)) {
|
|
cmdq_err("clk fail");
|
|
return;
|
|
}
|
|
|
|
/* trigger secure loop */
|
|
pkt2 = cmdq_pkt_create(sec);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (secure) {
|
|
cmdq_sec_pkt_set_data(pkt2, 0, 0, CMDQ_SEC_DEBUG,
|
|
CMDQ_METAEX_TZMP);
|
|
#ifdef CMDQ_SECURE_MTEE_SUPPORT
|
|
if (!~secure)
|
|
cmdq_sec_pkt_set_mtee(pkt2, true);
|
|
#endif
|
|
}
|
|
#endif
|
|
cmdq_pkt_finalize_loop(pkt2);
|
|
cmdq_pkt_flush_threaded(pkt2, NULL, (void *)pkt2);
|
|
|
|
/* trigger normal */
|
|
for (i = 0; i < 3; i++) {
|
|
writel(0x12345678, (void *)va);
|
|
|
|
pkt = cmdq_pkt_create(clt);
|
|
cmdq_pkt_write(pkt, NULL, pa, ans + i, UINT_MAX);
|
|
if (!timeout)
|
|
cmdq_pkt_set_event(pkt, event);
|
|
cmdq_pkt_wfe(pkt, event + 1);
|
|
cmdq_pkt_flush(pkt);
|
|
|
|
val = readl((void *)va);
|
|
cmdq_msg("%s: val:%#x ans:%#x", __func__, val, ans + i);
|
|
|
|
cmdq_pkt_dump_buf(pkt, 0);
|
|
cmdq_pkt_destroy(pkt);
|
|
}
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
cmdq_sec_mbox_stop(sec);
|
|
#endif
|
|
clk_disable_unprepare(test->gce.clk);
|
|
}
|
|
|
|
static void cmdq_test_mbox_vcp(struct cmdq_test *test, const bool reuse)
|
|
{
|
|
struct cmdq_pkt *pkt1 = cmdq_pkt_create(test->clt);
|
|
struct cmdq_pkt *pkt2 = cmdq_pkt_create(test->loop);
|
|
struct cmdq_pkt *pkt1_reuse;
|
|
struct cmdq_pkt *pkt2_reuse;
|
|
struct cmdq_reuse reuse1, reuse2;
|
|
struct cmdq_poll_reuse poll_reuse1, poll_reuse2;
|
|
dma_addr_t iova;
|
|
void *va = cmdq_get_vcp_buf(CMDQ_VCP_ENG_MDP_HDR0, &iova);
|
|
u32 val[4], i, j;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
writel(0xdeaddead, (void *)va + i * 4);
|
|
|
|
cmdq_vcp_enable(true);
|
|
for (i = 0; i < 4; i++) {
|
|
if (!i) {
|
|
cmdq_pkt_readback(pkt1, CMDQ_VCP_ENG_MDP_HDR0, 0, 0,
|
|
CMDQ_GPR_DEBUG_DUMMY, &reuse1, &poll_reuse1);
|
|
cmdq_pkt_readback(pkt2, CMDQ_VCP_ENG_MDP_HDR1, 4, 0,
|
|
CMDQ_GPR_DEBUG_TIMER, &reuse2, &poll_reuse2);
|
|
} else {
|
|
pkt1_reuse = cmdq_pkt_create(test->clt);
|
|
cmdq_pkt_copy(pkt1_reuse, pkt1);
|
|
reuse1.va = cmdq_pkt_get_va_by_offset(pkt1_reuse, reuse1.offset);
|
|
reuse1.val = cmdq_pkt_vcp_reuse_val(
|
|
CMDQ_VCP_ENG_MDP_HDR0, 0, i);
|
|
cmdq_pkt_reuse_buf_va(pkt1_reuse, &reuse1, 1);
|
|
cmdq_pkt_reuse_poll(pkt1_reuse, &poll_reuse1);
|
|
pkt2_reuse = cmdq_pkt_create(test->loop);
|
|
cmdq_pkt_copy(pkt2_reuse, pkt2);
|
|
reuse2.va = cmdq_pkt_get_va_by_offset(pkt2_reuse, reuse2.offset);
|
|
reuse2.val = cmdq_pkt_vcp_reuse_val(
|
|
CMDQ_VCP_ENG_MDP_HDR1, 4, i);
|
|
cmdq_pkt_reuse_buf_va(pkt2_reuse, &reuse2, 1);
|
|
cmdq_pkt_reuse_poll(pkt2_reuse, &poll_reuse2);
|
|
}
|
|
|
|
if (reuse) {
|
|
if (!i) {
|
|
cmdq_pkt_dump_buf(pkt1, 0);
|
|
cmdq_pkt_dump_buf(pkt2, 0);
|
|
cmdq_pkt_flush(pkt1);
|
|
cmdq_pkt_flush(pkt2);
|
|
} else {
|
|
cmdq_pkt_refinalize(pkt1_reuse);
|
|
cmdq_pkt_dump_buf(pkt1_reuse, 0);
|
|
cmdq_pkt_flush(pkt1_reuse);
|
|
cmdq_pkt_destroy(pkt1_reuse);
|
|
|
|
cmdq_pkt_refinalize(pkt2_reuse);
|
|
cmdq_pkt_dump_buf(pkt2_reuse, 0);
|
|
cmdq_pkt_flush(pkt2_reuse);
|
|
cmdq_pkt_destroy(pkt2_reuse);
|
|
}
|
|
|
|
for (j = 0; j < 4; j++)
|
|
val[j] = readl(va + j * 4);
|
|
cmdq_msg("%s: i:%d va:%p iova:%pa val:%#x %#x %#x %#x",
|
|
__func__, i, va, &iova,
|
|
val[0], val[1], val[2], val[3]);
|
|
} else {
|
|
cmdq_pkt_flush_threaded(
|
|
pkt1, cmdq_test_mbox_cb_destroy, (void *)pkt1);
|
|
cmdq_pkt_flush_threaded(
|
|
pkt2, cmdq_test_mbox_cb_destroy, (void *)pkt2);
|
|
msleep(1000);
|
|
break;
|
|
}
|
|
}
|
|
cmdq_vcp_enable(false);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
val[i] = readl(va + i * 4);
|
|
cmdq_msg("%s: va:%p iova:%pa", __func__, va, &iova);
|
|
cmdq_msg("%s: val:%#x %#x %#x %#x",
|
|
__func__, val[0], val[1], val[2], val[3]);
|
|
}
|
|
|
|
static void
|
|
cmdq_test_trigger(struct cmdq_test *test, enum CMDQ_SECURE_STATE_ENUM sec, const s32 id)
|
|
{
|
|
if (sec < CMDQ_MTEE_STATE || sec > CMDQ_TEE_STATE) {
|
|
cmdq_err("invalid input");
|
|
return;
|
|
}
|
|
#ifndef CMDQ_SECURE_SUPPORT
|
|
if (sec) {
|
|
cmdq_err("CMDQ_SECURE not support");
|
|
return;
|
|
}
|
|
#endif
|
|
#ifndef CMDQ_GP_SUPPORT
|
|
if (sec == CMDQ_TEE_STATE) {
|
|
cmdq_err("%s sec:%d, don't support cmdq tee driver", __func__, sec);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
cmdq_mbox_enable(test->clt->chan);
|
|
if (test->loop)
|
|
cmdq_mbox_enable(test->loop->chan);
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (test->sec)
|
|
cmdq_sec_mbox_enable(test->sec->chan);
|
|
#endif
|
|
|
|
switch (id) {
|
|
case 0:
|
|
cmdq_test_mbox_write(test, sec, false);
|
|
cmdq_test_mbox_write(test, sec, true);
|
|
cmdq_test_mbox_flush(test, sec, false);
|
|
cmdq_test_mbox_flush(test, sec, true);
|
|
cmdq_test_mbox_polling(test, sec, false, false);
|
|
cmdq_test_mbox_dma_access(test, sec);
|
|
cmdq_test_mbox_gpr_sleep(test, false);
|
|
cmdq_test_mbox_gpr_sleep(test, true);
|
|
cmdq_test_mbox_loop(test);
|
|
cmdq_test_mbox_large_cmd(test, 239);
|
|
cmdq_test_mbox_cpr(test);
|
|
break;
|
|
case 1:
|
|
cmdq_test_mbox_write(test, sec, false);
|
|
cmdq_test_mbox_write(test, sec, true);
|
|
break;
|
|
case 2:
|
|
cmdq_test_mbox_flush(test, sec, false);
|
|
cmdq_test_mbox_flush(test, sec, true);
|
|
break;
|
|
case 3:
|
|
cmdq_test_mbox_polling(test, sec, false, false);
|
|
cmdq_test_mbox_polling(test, sec, true, false);
|
|
break;
|
|
case 4:
|
|
cmdq_test_mbox_dma_access(test, sec);
|
|
break;
|
|
case 5:
|
|
cmdq_test_mbox_gpr_sleep(test, false);
|
|
cmdq_test_mbox_gpr_sleep(test, true);
|
|
break;
|
|
case 6:
|
|
cmdq_test_mbox_loop(test);
|
|
break;
|
|
case 7:
|
|
cmdq_test_mbox_large_cmd(test, 237);
|
|
break;
|
|
case 8:
|
|
cmdq_test_mbox_cpr(test);
|
|
break;
|
|
case 9:
|
|
cmdq_test_mbox_err_dump(test, sec, true);
|
|
cmdq_test_mbox_err_dump(test, sec, false);
|
|
break;
|
|
case 10:
|
|
if (test->loop)
|
|
cmdq_test_mbox_handshake_event(test);
|
|
break;
|
|
case 11:
|
|
cmdq_test_mbox_subsys_access(test);
|
|
break;
|
|
case 12:
|
|
cmdq_test_err_irq(test);
|
|
break;
|
|
case 13:
|
|
cmdq_test_devapc_vio(test);
|
|
break;
|
|
case 14:
|
|
cmdq_test_mbox_polling(test, sec, true, true);
|
|
break;
|
|
case 15:
|
|
cmdq_test_mbox_stop(test);
|
|
break;
|
|
case 16:
|
|
cmdq_test_show_events(test);
|
|
break;
|
|
case 17:
|
|
cmdq_test_mbox_reuse_buf_va(test);
|
|
break;
|
|
case 18:
|
|
cmdq_test_mbox_prebuilt_instr_ext(test,
|
|
CMDQ_PREBUILT_MDP, CMDQ_TOKEN_PREBUILT_MDP_WAIT);
|
|
cmdq_test_mbox_prebuilt_instr_ext(test,
|
|
CMDQ_PREBUILT_MML, CMDQ_TOKEN_PREBUILT_MML_WAIT);
|
|
cmdq_test_mbox_prebuilt_instr(test,
|
|
CMDQ_PREBUILT_VFMT, CMDQ_TOKEN_PREBUILT_VFMT_WAIT);
|
|
break;
|
|
case 19:
|
|
cmdq_test_mbox_prebuilt(test, CMDQ_PREBUILT_DISP, 0, false);
|
|
cmdq_test_mbox_prebuilt(test, CMDQ_PREBUILT_DISP, 1, false);
|
|
cmdq_test_mbox_prebuilt(test, CMDQ_PREBUILT_DISP, 1, true);
|
|
break;
|
|
case 20:
|
|
if (test->sec)
|
|
cmdq_test_mbox_tzmp(test, sec, false);
|
|
break;
|
|
case 21:
|
|
cmdq_util_test_set_ostd();
|
|
cmdq_test_mbox_write_dma(test, sec, 10);
|
|
break;
|
|
case 22:
|
|
cmdq_test_mbox_vcp(test, false);
|
|
cmdq_test_mbox_vcp(test, true);
|
|
break;
|
|
case 23:
|
|
cmdq_test_mbox_write_dma_cpr(test, sec, 3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
if (test->sec)
|
|
cmdq_sec_mbox_disable(test->sec->chan);
|
|
#endif
|
|
cmdq_mbox_disable(test->clt->chan);
|
|
if (test->loop) {
|
|
struct cmdq_thread *thread =
|
|
(struct cmdq_thread *)test->loop->chan->con_priv;
|
|
s32 backup = cmdq_thread_timeout_backup(thread, CMDQ_NO_TIMEOUT);
|
|
|
|
cmdq_mbox_disable(test->loop->chan);
|
|
cmdq_thread_timeout_restore(thread, backup);
|
|
}
|
|
}
|
|
|
|
#define MAX_SCAN 30
|
|
|
|
static ssize_t
|
|
cmdq_test_write(struct file *filp, const char *buf, size_t count, loff_t *offp)
|
|
{
|
|
struct cmdq_test *test = (struct cmdq_test *)filp->f_inode->i_private;
|
|
char str[MAX_SCAN] = {0};
|
|
s32 sec, id = 0;
|
|
u32 len;
|
|
|
|
len = (count < MAX_SCAN - 1) ? count : (MAX_SCAN - 1);
|
|
if (copy_from_user(str, buf, len)) {
|
|
cmdq_err("copy_from_user failed len:%d", len);
|
|
return count;
|
|
}
|
|
str[len] = '\0';
|
|
|
|
if (sscanf(str, "%d %d", &sec, &id) != 2) {
|
|
cmdq_err("sscanf failed str:%s sec:%d id:%d", str, sec, id);
|
|
return count;
|
|
}
|
|
cmdq_msg("test:%p len:%d sec:%d id:%d str:%s", test, len, sec, id, str);
|
|
|
|
mutex_lock(&test->lock);
|
|
cmdq_test_trigger(test, sec, id);
|
|
mutex_unlock(&test->lock);
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations cmdq_test_fops = {
|
|
.write = cmdq_test_write,
|
|
};
|
|
|
|
static int cmdq_test_probe(struct platform_device *pdev)
|
|
{
|
|
struct cmdq_test *test;
|
|
struct device_node *np;
|
|
struct platform_device *np_pdev;
|
|
struct resource res;
|
|
struct dentry *dir;
|
|
s32 i, ret;
|
|
|
|
test = devm_kzalloc(&pdev->dev, sizeof(*test), GFP_KERNEL);
|
|
if (!test)
|
|
return -ENOMEM;
|
|
test->dev = &pdev->dev;
|
|
mutex_init(&test->lock);
|
|
test->tick = false;
|
|
gtest = test;
|
|
|
|
// gce
|
|
np = of_parse_phandle(pdev->dev.of_node, "mediatek,gce", 0);
|
|
if (!np) {
|
|
cmdq_err("of_parse_phandle mediatek,gce failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
np_pdev = of_find_device_by_node(np);
|
|
of_node_put(np);
|
|
if (!np_pdev)
|
|
return -EINVAL;
|
|
test->gce.dev = &np_pdev->dev;
|
|
|
|
test->gce.va = of_iomap(np_pdev->dev.of_node, 0);
|
|
if (!test->gce.va)
|
|
return -EINVAL;
|
|
|
|
ret = of_address_to_resource(np_pdev->dev.of_node, 0, &res);
|
|
if (ret)
|
|
return ret;
|
|
test->gce.pa = res.start;
|
|
|
|
test->gce.clk = devm_clk_get(&np_pdev->dev, "gce");
|
|
if (IS_ERR(test->gce.clk)) {
|
|
cmdq_err("devm_clk_get gce clk failed:%ld",
|
|
PTR_ERR(test->gce.clk));
|
|
test->gce.clk = NULL;
|
|
}
|
|
|
|
test->gce.clk_timer = devm_clk_get(&np_pdev->dev, "gce-timer");
|
|
if (IS_ERR(test->gce.clk_timer)) {
|
|
cmdq_err("devm_clk_get gce clk_timer failed:%ld",
|
|
PTR_ERR(test->gce.clk_timer));
|
|
test->gce.clk_timer = NULL;
|
|
}
|
|
cmdq_msg("gce dev:%p va:%p pa:%pa",
|
|
test->gce.dev, test->gce.va, &test->gce.pa);
|
|
|
|
// clt
|
|
test->clt = cmdq_mbox_create(&pdev->dev, 0);
|
|
if (IS_ERR(test->clt) || !test->clt) {
|
|
if (!test->clt)
|
|
return -ENXIO;
|
|
}
|
|
|
|
test->loop = cmdq_mbox_create(&pdev->dev, 1);
|
|
/*
|
|
if (IS_ERR(test->loop) || !test->loop) {
|
|
if (!test->loop)
|
|
return -ENXIO;
|
|
}
|
|
*/
|
|
|
|
#ifdef CMDQ_SECURE_SUPPORT
|
|
test->sec = cmdq_mbox_create(&pdev->dev, 2);
|
|
if (IS_ERR(test->sec) || !test->sec) {
|
|
if (!test->sec)
|
|
cmdq_err("no test->sec");
|
|
}
|
|
#endif
|
|
cmdq_msg("%s test:%p dev:%p clt:%p loop:%p sec:%p",
|
|
__func__, test, test->dev, test->clt, test->loop, test->sec);
|
|
|
|
// subsys
|
|
i = of_property_count_u32_elems(
|
|
pdev->dev.of_node, "mediatek,gce-subsys");
|
|
if (i < 0) {
|
|
cmdq_err("of_property_count_u32_elems gce-subsys failed:%d", i);
|
|
for (i = 0; i < CMDQ_TEST_SUBSYS_NR; i++)
|
|
test->subsys[i] = CMDQ_TEST_SUBSYS_ERR;
|
|
} else {
|
|
ret = of_property_read_u32_array(pdev->dev.of_node,
|
|
"mediatek,gce-subsys", test->subsys, i);
|
|
if (ret) {
|
|
cmdq_err("of_property_read_u32_array failed:%d", ret);
|
|
for (i = 0; i < CMDQ_TEST_SUBSYS_NR; i++)
|
|
test->subsys[i] = CMDQ_TEST_SUBSYS_ERR;
|
|
}
|
|
}
|
|
for (i = 0; i < CMDQ_TEST_SUBSYS_NR; i++)
|
|
cmdq_msg("subsys[%d]:%u", i, test->subsys[i]);
|
|
|
|
ret = of_property_read_u16(pdev->dev.of_node, "token_user0",
|
|
&test->token_user0);
|
|
if (ret < 0) {
|
|
cmdq_err("no token_user0 err:%d", ret);
|
|
test->token_user0 = CMDQ_EVENT_MAX;
|
|
}
|
|
|
|
ret = of_property_read_u16(pdev->dev.of_node, "token_gpr_set4",
|
|
&test->token_gpr_set4);
|
|
if (ret < 0) {
|
|
cmdq_err("no token_gpr_set4 err:%d", ret);
|
|
test->token_gpr_set4 = CMDQ_EVENT_MAX;
|
|
}
|
|
|
|
// fs
|
|
dir = debugfs_lookup("cmdq", NULL);
|
|
if (!dir) {
|
|
dir = debugfs_create_dir("cmdq", NULL);
|
|
if (IS_ERR(dir) && PTR_ERR(dir) != -EEXIST) {
|
|
cmdq_err("debugfs_create_dir cmdq failed:%ld", PTR_ERR(dir));
|
|
return PTR_ERR(dir);
|
|
}
|
|
}
|
|
|
|
test->fs = debugfs_create_file(
|
|
"cmdq-test", 0444, dir, test, &cmdq_test_fops);
|
|
if (IS_ERR(test->fs)) {
|
|
cmdq_err("debugfs_create_file cmdq-test failed:%ld",
|
|
PTR_ERR(test->fs));
|
|
return PTR_ERR(test->fs);
|
|
}
|
|
|
|
platform_set_drvdata(pdev, test);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmdq_test_remove(struct platform_device *pdev)
|
|
{
|
|
struct cmdq_test *test = (struct cmdq_test *)platform_get_drvdata(pdev);
|
|
|
|
cmdq_mbox_destroy(test->clt);
|
|
cmdq_mbox_destroy(test->loop);
|
|
cmdq_mbox_destroy(test->sec);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id cmdq_test_of_ids[] = {
|
|
{
|
|
.compatible = "mediatek,cmdq-test",
|
|
},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, cmdq_test_of_ids);
|
|
|
|
static struct platform_driver cmdq_test_drv = {
|
|
.probe = cmdq_test_probe,
|
|
.remove = cmdq_test_remove,
|
|
.driver = {
|
|
.name = "cmdq-test",
|
|
.of_match_table = cmdq_test_of_ids,
|
|
},
|
|
};
|
|
module_platform_driver(cmdq_test_drv);
|
|
|
|
MODULE_LICENSE("GPL v2");
|