kernel-brax3-ubuntu-touch/drivers/misc/mediatek/sda/btm/v1/bus_tracer_v1.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

519 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/circ_buf.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include "../bus_tracer_interface.h"
#include "bus_tracer_v1.h"
static int start(struct bus_tracer_plt *plt)
{
struct device_node *node;
int ret, num_tracer, i;
/* u32 args[3]; */
if (!plt) {
pr_notice("%s:%d: plt == NULL\n", __func__, __LINE__);
return -1;
}
node = of_find_compatible_node(NULL, NULL, "mediatek,bus_tracer-v1");
if (!node) {
pr_notice("can't find compatible node for bus_tracer\n");
return -1;
}
if (of_property_read_u32(node, "mediatek,err-flag",
&plt->err_flag) != 0)
plt->err_flag = 0xFFF8FFFF;
if (of_property_read_u32(node, "mediatek,num-tracer",
&num_tracer) != 0) {
pr_notice("can't find property \"mediatek,num-tracer\"\n");
return -1;
}
if (num_tracer <= 0) {
pr_notice("[bus tracer] fatal error: num-tracer <= 0\n");
return -1;
}
plt->dem_base = of_iomap(node, 0);
plt->dbgao_base = of_iomap(node, 1);
plt->funnel_base = of_iomap(node, 2);
plt->etb_base = of_iomap(node, 3);
plt->num_tracer = num_tracer;
plt->tracer = kcalloc(num_tracer, sizeof(struct tracer), GFP_KERNEL);
if (!plt->tracer)
return -ENOMEM;
for (i = 0; i <= num_tracer-1; ++i) {
if (of_property_read_u32_index(node, "mediatek,enabled-tracer",
i, &ret) == 0)
plt->tracer[i].enabled = ret & 0x1;
else
plt->tracer[i].enabled = 0;
if (of_property_read_u32_index(node, "mediatek,at-id", i,
&ret) == 0)
plt->tracer[i].at_id = ret;
plt->tracer[i].base = of_iomap(node, 4+i);
plt->tracer[i].recording = 0;
}
return 0;
}
static void enable_etb_cm7(void __iomem *base)
{
unsigned int depth, i;
if (!base)
return;
CS_UNLOCK(base);
writel(0x0, base + ETB_CTRL);
writel(0x0, base + ETB_REG28);
writel(0x0, base + ETB_REG110);
writel(0x0, base + ETB_TRIGGERCOUNT);
writel(0x0, base + ETB_TRIGGERCOUNT);
writel(0x323, base + ETB_REG304);
depth = readl(base + ETB_DEPTH);
writel(0x0, base + ETB_WRITEADDR);
for (i = 0; i < depth; ++i)
writel(0x0, base + ETB_RWD);
writel(0x0, base + ETB_WRITEADDR);
writel(0x0, base + ETB_READADDR);
writel(0x1, base + ETB_CTRL);
CS_LOCK(base);
dsb(sy);
}
static void enable_etb(void __iomem *base)
{
unsigned int depth, i;
if (!base)
return;
CS_UNLOCK(base);
depth = readl(base + ETB_DEPTH);
writel(0x0, base + ETB_WRITEADDR);
for (i = 0; i < depth; ++i)
writel(0x0, base + ETB_RWD);
writel(0x0, base + ETB_WRITEADDR);
writel(0x0, base + ETB_READADDR);
writel(0x1, base + ETB_CTRL);
CS_LOCK(base);
dsb(sy);
}
static int dump(struct bus_tracer_plt *plt, char *buf, int len)
{
return 0;
}
/* force_enable=0 to enable all the tracers with enabled=1 */
static int enable(struct bus_tracer_plt *plt, unsigned char force_enable,
unsigned int tracer_id)
{
int i, j;
unsigned long ret;
if (!plt->tracer) {
pr_notice("%s:%d: plt->tracer == NULL\n", __func__, __LINE__);
return -1;
}
if (!plt->dem_base) {
pr_notice("%s:%d: dem_base == NULL\n", __func__, __LINE__);
return -1;
}
if (!plt->dbgao_base) {
pr_notice("%s:%d: dbgao_base == NULL\n", __func__, __LINE__);
return -1;
}
if (!plt->funnel_base) {
pr_notice("%s:%d: funnel_base == NULL\n", __func__, __LINE__);
return -1;
}
if (force_enable) {
for (j = 0; j <= plt->num_tracer - 1; ++j)
plt->tracer[j].enabled = 1;
} else {
j = 0;
for (i = 0; i <= plt->num_tracer - 1; ++i)
j += plt->tracer[i].enabled;
if (j == 0) {
pr_debug("%s:%d: all the tracers are disabled\n",
__func__, __LINE__);
return -2;
}
}
/* enable ATB CG */
writel(0x3, plt->dem_base + DEM_ATB_CG);
dsb(sy);
/* enable ATB clk */
writel(0x1, plt->dbgao_base + DEM_ATB_CLK);
dsb(sy);
writel(0x1, plt->dem_base + DEM_ATB_CLK);
dsb(sy);
/* set dem_ao unlock */
CS_UNLOCK(plt->dem_base);
dsb(sy);
/* set DBGRST_ALL to 0 */
writel(0x0, plt->dem_base + DEM_DBGRST_ALL);
dsb(sy);
CS_LOCK(plt->dem_base);
dsb(sy);
/* timestamp per 10us; 1: 76.923ns; 13: 1us */
writel(0x8201, plt->dem_base + INSERT_TS0);
//writel(0x0D01, plt->dem_base + INSERT_TS0);
writel(plt->err_flag, plt->dbgao_base + DBG_ERR_FLAG_IRQ_POLARITY);
dsb(sy);
writel(0x0, plt->dbgao_base + DBG_ERR_FLAG_CON);
dsb(sy);
writel(0x3, plt->dbgao_base + DBG_ERR_FLAG_CON);
dsb(sy);
/* replicator 1 setup */
CS_UNLOCK(plt->funnel_base + REPLICATOR1_BASE);
writel(0x00, plt->funnel_base + REPLICATOR1_BASE +
REPLICATOR_IDFILTER0);
writel(0x00, plt->funnel_base + REPLICATOR1_BASE +
REPLICATOR_IDFILTER1);
CS_LOCK(plt->funnel_base + REPLICATOR1_BASE);
dsb(sy);
/* funnel setup */
CS_UNLOCK(plt->funnel_base);
writel(0xff, plt->funnel_base + FUNNEL_CTRL_REG);
CS_LOCK(plt->funnel_base);
dsb(sy);
for (i = 0; i <= plt->num_tracer - 1; ++i) {
/* enable ETB */
enable_etb(plt->etb_base);
/* set ID */
writel(plt->tracer[i].at_id, plt->tracer[i].base +
BUS_TRACE_ATID);
writel(0x1, plt->tracer[i].base + BUS_TRACE_CON_SYNC_SET);
ret = readl(plt->tracer[i].base + BUS_TRACE_CON_SYNC_STATUS);
while (0 == (ret & 0x1))
ret = readl(plt->tracer[i].base + BUS_TRACE_CON_SYNC_STATUS);
/* enable tracer */
if (plt->tracer[i].enabled) {
ret = readl(plt->tracer[i].base + BUS_TRACE_CON);
writel(ret|BUS_TRACE_EN|WDT_RST_EN|SW_RST_B,
plt->tracer[i].base + BUS_TRACE_CON);
plt->tracer[i].recording = 1;
}
dsb(sy);
}
return 0;
}
static int set_recording(struct bus_tracer_plt *plt, unsigned char pause)
{
int i;
unsigned long ret;
if (!plt->tracer) {
pr_notice("%s:%d: plt->tracer == NULL\n", __func__, __LINE__);
return -1;
}
for (i = 0; i <= plt->num_tracer - 1; ++i) {
/* only pause/resume tracers that are enabled*/
if (!plt->tracer[i].enabled)
continue;
if (pause) {
/* disable tracer */
ret = readl(plt->tracer[i].base);
writel(ret & ~(BUS_TRACE_EN), plt->tracer[i].base +
BUS_TRACE_CON);
dsb(sy);
/* disable etb */
/* disable_etb(plt->etb_base); */
plt->tracer[i].recording = 0;
} else {
/* enable ETB */
enable_etb(plt->etb_base);
/* enable tracer */
ret = readl(plt->tracer[i].base);
writel(ret|BUS_TRACE_EN, plt->tracer[i].base +
BUS_TRACE_CON);
dsb(sy);
plt->tracer[i].recording = 1;
}
}
return 0;
}
static int dump_setting(struct bus_tracer_plt *plt, char *buf, int len)
{
int i;
if (!plt->tracer) {
pr_notice("%s:%d: plt->tracer == NULL\n", __func__, __LINE__);
return -1;
}
for (i = 0; i <= plt->num_tracer-1; ++i) {
buf += sprintf(buf, "== dump setting of tracer %d ==\n", i);
buf += sprintf(buf, "enabled = %x\ntrace recording = %x\n",
plt->tracer[i].enabled, plt->tracer[i].recording);
buf += sprintf(buf, "==== register dump ====\n");
buf += sprintf(buf, "0x0 = 0x%lx\n0x10 = 0x%lx\n0x14 = 0x%lx\n"
"0x18 = 0x%lx\n",
(unsigned long) readl(plt->tracer[i].base),
(unsigned long) readl(plt->tracer[i].base + 0x10),
(unsigned long) readl(plt->tracer[i].base + 0x14),
(unsigned long) readl(plt->tracer[i].base + 0x18));
}
buf += sprintf(buf, "== DBG_ERR_FLAG register dump ==\n");
buf += sprintf(buf, "0x80 = 0x%lx\n0x84 = 0x%lx\n0x88 = 0x%lx\n"
"0x8c = 0x%lx\n",
(unsigned long) readl(plt->dbgao_base + 0x80),
(unsigned long) readl(plt->dbgao_base + 0x84),
(unsigned long) readl(plt->dbgao_base + 0x88),
(unsigned long) readl(plt->dbgao_base + 0x8c));
buf += sprintf(buf, "0x90 = 0x%lx\n0x94 = 0x%lx\n0x98 = 0x%lx\n"
"0x9c = 0x%lx\n",
(unsigned long) readl(plt->dbgao_base + 0x90),
(unsigned long) readl(plt->dbgao_base + 0x94),
(unsigned long) readl(plt->dbgao_base + 0x98),
(unsigned long) readl(plt->dbgao_base + 0x9c));
return 0;
}
static int resume(struct bus_tracer_plt *plt, struct platform_device *pdev)
{
return 0;
}
static struct bus_tracer_plt_operations bus_tracer_ops = {
.dump = dump,
.start = start,
.enable = enable,
.set_recording = set_recording,
/* .set_watchpoint_filter = set_watchpoint_filter, */
/* .set_bypass_filter = set_bypass_filter, */
/* .set_id_filter = set_id_filter, */
/* .set_rw_filter = set_rw_filter, */
.dump_setting = dump_setting,
.resume = resume,
};
struct bus_tracer_plt *plt;
EXPORT_SYMBOL(plt);
#define CIRC_CNT(head, tail, size) (((head) - (tail)) & ((size)-1))
static ssize_t tracer_dbgfs_etb_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
unsigned int depth, etb_rp, etb_wp, ret, offset = 0;
unsigned int nr_words;
unsigned int i;
unsigned char *etb_data;
if (buf == NULL)
return -ENOMEM;
if (!plt)
return -ENOMEM;
for (i = 0; i <= plt->num_tracer - 1; ++i) {
if (plt->tracer[i].enabled) {
pr_notice("%s:%d:[ETB] Only support GPU DEBUG\n", __func__, __LINE__);
return 0;
}
}
CS_UNLOCK(plt->etb_base);
ret = readl(plt->etb_base + ETB_STATUS);
etb_rp = readl(plt->etb_base + ETB_READADDR);
etb_wp = readl(plt->etb_base + ETB_WRITEADDR);
/* depth is counted in byte */
if (ret & 0x1)
depth = readl(plt->etb_base + ETB_DEPTH) << 2;
else
depth = CIRC_CNT(etb_wp, etb_rp, readl(plt->etb_base + ETB_DEPTH) << 2);
pr_notice("%s:%d:[ETB] etb read siez=%ld\n", __func__, __LINE__, (unsigned long)size);
pr_notice("%s:%d:[ETB] depth = 0x%lx bytes\n", __func__, __LINE__, depth);
if (depth == 0) {
/* enable ETB after dump */
writel(0x1, plt->etb_base + ETB_CTRL);
CS_LOCK(plt->etb_base);
return 0;
}
if (size < depth)
depth = size;
/* disable ETB before dump */
writel(0x0, plt->etb_base + ETB_CTRL);
etb_data = kmalloc(depth, GFP_KERNEL);
if (etb_data == NULL) {
/* enable ETB after dump */
writel(0x1, plt->etb_base + ETB_CTRL);
CS_LOCK(plt->etb_base);
return -ENOMEM;
}
nr_words = depth / 4;
for (i = 0; i < nr_words; ++i) {
ret = readl(plt->etb_base + ETB_READMEM);
memcpy(etb_data + offset, (unsigned char *)(&ret), 4);
offset += 4;
}
ret = copy_to_user(buf, (void *)etb_data, depth);
if (ret)
pr_notice("%s:%d:[ETB] copy_to_user fail=%lx.\n", __func__, __LINE__, ret);
kfree(etb_data);
/* enable ETB after dump */
writel(0x1, plt->etb_base + ETB_CTRL);
CS_LOCK(plt->etb_base);
pr_notice("%s:%d:[ETB] *ppos=0x%llx.\n", __func__, __LINE__, *ppos);
*ppos += depth;
return depth;
}
void enable_etb_for_gpu_mcu(void)
{
unsigned int i = 0, ret = 0;
if (!plt->tracer) {
pr_notice("%s:%d:[ETB] plt->tracer == NULL\n", __func__, __LINE__);
return;
}
for (i = 0; i <= plt->num_tracer - 1; ++i) {
if (!plt->tracer[i].enabled)
continue;
plt->tracer[i].enabled = 0;
/* disable tracer */
ret = readl(plt->tracer[i].base);
writel(ret & ~(BUS_TRACE_EN), plt->tracer[i].base +
BUS_TRACE_CON);
dsb(sy);
plt->tracer[i].recording = 0;
pr_notice("%s:%d:[ETB] disable trace[%d]\n", __func__, __LINE__, i);
}
pr_notice("%s:%d:[ETB]: GPU/Cotrex-M7\n", __func__, __LINE__);
/* funnel setup */
CS_UNLOCK(plt->funnel_base);
writel(0x320, plt->funnel_base + FUNNEL_CTRL_REG);
CS_LOCK(plt->funnel_base);
dsb(sy);
/* enable ETB for Cotrex-M7 */
enable_etb_cm7(plt->etb_base);
}
EXPORT_SYMBOL(enable_etb_for_gpu_mcu);
void disable_etb_capture(void)
{
CS_UNLOCK(plt->etb_base);
writel(0x0, plt->etb_base + ETB_CTRL);
pr_notice("%s:%d:[ETB]: Disable ETB before DFD trig or BUG_ON\n", __func__, __LINE__);
CS_LOCK(plt->etb_base);
}
EXPORT_SYMBOL(disable_etb_capture);
static const struct file_operations tracer_dbgfs_etb_fops = {
.read = tracer_dbgfs_etb_read,
.llseek = generic_file_llseek,
};
static struct dentry *g_p_debug_fs_dir;
static struct dentry *g_p_debug_fs_etb;
static int __init bus_tracer_init(void)
{
plt = kzalloc(sizeof(struct bus_tracer_plt), GFP_KERNEL);
if (!plt)
return -ENOMEM;
plt->ops = &bus_tracer_ops;
plt->min_buf_len = 8192; /* 8K */
g_p_debug_fs_dir = debugfs_create_dir("tracer", NULL);
if (g_p_debug_fs_dir) {
/* Create debugfs files. */
g_p_debug_fs_etb = debugfs_create_file("etb",
0444,
g_p_debug_fs_dir,
NULL,
&tracer_dbgfs_etb_fops);
}
//set_sram_flag_timestamp();
return 0;
}
static void __exit bus_tracer_exit(void)
{
debugfs_remove(g_p_debug_fs_etb);
debugfs_remove(g_p_debug_fs_dir);
kfree(plt);
}
module_init(bus_tracer_init);
module_exit(bus_tracer_exit);
MODULE_LICENSE("GPL v2");