445 lines
13 KiB
C
445 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
/**
|
|
* @file gpueb_debug.c
|
|
* @brief Debug mechanism for GPU-DVFS
|
|
*/
|
|
|
|
/**
|
|
* ===============================================
|
|
* Include
|
|
* ===============================================
|
|
*/
|
|
#include <linux/platform_device.h>
|
|
#include <linux/io.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/arm-smccc.h>
|
|
#include <linux/soc/mediatek/mtk_sip_svc.h>
|
|
|
|
#include "gpueb_helper.h"
|
|
#include "gpueb_debug.h"
|
|
#include "gpueb_ipi.h"
|
|
|
|
/**
|
|
* ===============================================
|
|
* Local Variable Definition
|
|
* ===============================================
|
|
*/
|
|
enum gpueb_smc_op {
|
|
GPUEB_SMC_OP_TRIGGER_WDT = 0,
|
|
GPUEB_SMC_OP_SET_RGX_BUS_SECURE = 1,
|
|
GPUEB_SMC_OP_NUMBER = 2,
|
|
};
|
|
|
|
static void __iomem *g_gpueb_base;
|
|
static void __iomem *g_gpueb_gpr_base;
|
|
static void __iomem *g_gpueb_reg_base;
|
|
static void __iomem *g_gpueb_mbox_ipi;
|
|
static void __iomem *g_gpueb_mbox_sw_int;
|
|
|
|
/**
|
|
* ===============================================
|
|
* Function Definition
|
|
* ===============================================
|
|
*/
|
|
void gpueb_dump_status(void)
|
|
{
|
|
gpueb_pr_info("@%s: == [GPUEB STATUS] ==\n", __func__);
|
|
|
|
if (g_gpueb_reg_base) {
|
|
/* 0x13C60300 */
|
|
gpueb_pr_info("@%s: GPUEB_INTC_IRQ_EN (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x300), readl(g_gpueb_reg_base + 0x300));
|
|
/* 0x13C60338 */
|
|
gpueb_pr_info("@%s: GPUEB_INTC_IRQ_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x338), readl(g_gpueb_reg_base + 0x338));
|
|
/* 0x13C6033C */
|
|
gpueb_pr_info("@%s: GPUEB_INTC_IRQ_RAW_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x33C), readl(g_gpueb_reg_base + 0x33C));
|
|
/* 0x13C60348 */
|
|
gpueb_pr_info("@%s: GPUEB_INTC_IRQ_GRP2_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x348), readl(g_gpueb_reg_base + 0x348));
|
|
|
|
/* 0x13C6060C */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_AXI_CFG (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x60C), readl(g_gpueb_reg_base + 0x60C));
|
|
/* 0x13C60610 */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_AXI_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x610), readl(g_gpueb_reg_base + 0x610));
|
|
/* 0x13C60618 */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_WDT_CON (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x618), readl(g_gpueb_reg_base + 0x618));
|
|
/* 0x13C6061C */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_WDT_KICK (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x61C), readl(g_gpueb_reg_base + 0x61C));
|
|
/* 0x13C60634 */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_MDSP_CFG (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x634), readl(g_gpueb_reg_base + 0x634));
|
|
/* 0x13C6071C */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_DBG_APB_PC (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x71C), readl(g_gpueb_reg_base + 0x71C));
|
|
/* 0x13C60720 */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_DBG_APB_LR (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x720), readl(g_gpueb_reg_base + 0x720));
|
|
/* 0x13C60724 */
|
|
gpueb_pr_info("@%s: GPUEB_CFGREG_DBG_APB_SP (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x724), readl(g_gpueb_reg_base + 0x724));
|
|
} else
|
|
gpueb_pr_info("@%s: skip null g_gpueb_reg_base\n", __func__);
|
|
|
|
if (g_gpueb_gpr_base) {
|
|
gpueb_pr_info("@%s: LOW_POWER_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x14));
|
|
gpueb_pr_info("@%s: GPUFREQ_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x44));
|
|
gpueb_pr_info("@%s: GPUEB_DRAM_RES_STA_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x40));
|
|
} else
|
|
gpueb_pr_info("@%s: skip null g_gpueb_gpr_base\n", __func__);
|
|
|
|
if (g_gpueb_mbox_ipi)
|
|
/* 0x13C62000 */
|
|
gpueb_pr_info("@%s: GPUEB_MBOX_IPI_GPUEB (0x%x): 0x%08x\n", __func__,
|
|
(0x13C62000), readl(g_gpueb_mbox_ipi));
|
|
else
|
|
gpueb_pr_info("@%s: skip null g_gpueb_mbox_ipi\n", __func__);
|
|
|
|
if (g_gpueb_mbox_sw_int)
|
|
/* 0x13C62078 */
|
|
gpueb_pr_info("@%s: GPUEB_MBOX_SW_INT_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C62078), readl(g_gpueb_mbox_sw_int));
|
|
else
|
|
gpueb_pr_info("@%s: skip null g_gpueb_mbox_sw_int\n", __func__);
|
|
}
|
|
EXPORT_SYMBOL(gpueb_dump_status);
|
|
|
|
void gpueb_dump_footprint(void)
|
|
{
|
|
gpueb_pr_info("@%s: == [GPUEB FOOTPRINT] ==\n", __func__);
|
|
|
|
if (g_gpueb_gpr_base) {
|
|
gpueb_pr_info("@%s: LOW_POWER_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x14));
|
|
gpueb_pr_info("@%s: GPUFREQ_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x44));
|
|
gpueb_pr_info("@%s: GPUEB_DRAM_RES_STA_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x40));
|
|
} else
|
|
gpueb_pr_info("@%s: skip null g_gpueb_gpr_base\n", __func__);
|
|
}
|
|
EXPORT_SYMBOL(gpueb_dump_footprint);
|
|
|
|
void gpueb_trigger_wdt(const char *name)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
gpueb_pr_info("@%s: GPUEB WDT is triggered by %s\n", __func__, name);
|
|
|
|
arm_smccc_smc(
|
|
MTK_SIP_KERNEL_GPUEB_CONTROL, /* a0 */
|
|
GPUEB_SMC_OP_TRIGGER_WDT, /* a1 */
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
}
|
|
EXPORT_SYMBOL(gpueb_trigger_wdt);
|
|
|
|
void gpu_set_rgx_bus_secure(void)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(
|
|
MTK_SIP_KERNEL_GPUEB_CONTROL, /* a0 */
|
|
GPUEB_SMC_OP_SET_RGX_BUS_SECURE, /* a1 */
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
}
|
|
EXPORT_SYMBOL(gpu_set_rgx_bus_secure);
|
|
|
|
#if defined(CONFIG_PROC_FS)
|
|
/* PROCFS: show current gpueb status */
|
|
static int gpueb_status_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_puts(m, "[GPUEB-DEBUG] Current Status of GPUEB\n");
|
|
|
|
if (g_gpueb_reg_base) {
|
|
/* 0x13C60300 */
|
|
seq_printf(m, "@%s: GPUEB_INTC_IRQ_EN (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x300), readl(g_gpueb_reg_base + 0x300));
|
|
/* 0x13C60338 */
|
|
seq_printf(m, "@%s: GPUEB_INTC_IRQ_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x338), readl(g_gpueb_reg_base + 0x338));
|
|
/* 0x13C6033C */
|
|
seq_printf(m, "@%s: GPUEB_INTC_IRQ_RAW_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x33C), readl(g_gpueb_reg_base + 0x33C));
|
|
/* 0x13C60348 */
|
|
seq_printf(m, "@%s: GPUEB_INTC_IRQ_GRP2_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x348), readl(g_gpueb_reg_base + 0x348));
|
|
|
|
/* 0x13C6060C */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_AXI_CFG (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x60C), readl(g_gpueb_reg_base + 0x60C));
|
|
/* 0x13C60610 */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_AXI_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x610), readl(g_gpueb_reg_base + 0x610));
|
|
/* 0x13C60618 */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_WDT_CON (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x618), readl(g_gpueb_reg_base + 0x618));
|
|
/* 0x13C6061C */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_WDT_KICK (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x61C), readl(g_gpueb_reg_base + 0x61C));
|
|
/* 0x13C60634 */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_MDSP_CFG (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x634), readl(g_gpueb_reg_base + 0x634));
|
|
/* 0x13C6071C */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_DBG_APB_PC (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x71C), readl(g_gpueb_reg_base + 0x71C));
|
|
/* 0x13C60720 */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_DBG_APB_LR (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x720), readl(g_gpueb_reg_base + 0x720));
|
|
/* 0x13C60724 */
|
|
seq_printf(m, "@%s: GPUEB_CFGREG_DBG_APB_SP (0x%x): 0x%08x\n", __func__,
|
|
(0x13C60000 + 0x724), readl(g_gpueb_reg_base + 0x724));
|
|
} else
|
|
seq_printf(m, "@%s: skip null g_gpueb_reg_base\n", __func__);
|
|
|
|
if (g_gpueb_gpr_base) {
|
|
seq_printf(m, "@%s: LOW_POWER_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x14));
|
|
seq_printf(m, "@%s: GPUFREQ_FOOTPRINT_GPR: 0x%08x\n", __func__,
|
|
readl(g_gpueb_gpr_base + 0x44));
|
|
} else
|
|
seq_printf(m, "@%s: skip null g_gpueb_gpr_base\n", __func__);
|
|
|
|
if (g_gpueb_mbox_ipi)
|
|
/* 0x13C62000 */
|
|
seq_printf(m, "@%s: GPUEB_MBOX_IPI_GPUEB (0x%x): 0x%08x\n", __func__,
|
|
(0x13C62000), readl(g_gpueb_mbox_ipi));
|
|
else
|
|
seq_printf(m, "@%s: skip null g_gpueb_mbox_ipi\n", __func__);
|
|
|
|
if (g_gpueb_mbox_sw_int)
|
|
/* 0x13C62078 */
|
|
seq_printf(m, "@%s: GPUEB_MBOX_SW_INT_STA (0x%x): 0x%08x\n", __func__,
|
|
(0x13C62078), readl(g_gpueb_mbox_sw_int));
|
|
else
|
|
seq_printf(m, "@%s: skip null g_gpueb_mbox_sw_int\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* PROCFS: show current gpueb dram user status */
|
|
static int gpueb_dram_user_status_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
unsigned int dram_res = 0;
|
|
int user_id = 0;
|
|
|
|
if (g_gpueb_gpr_base) {
|
|
dram_res = readl(g_gpueb_gpr_base + 0x40);
|
|
seq_printf(m, "@%s: GPUEB_DRAM_RES_STA_GPR: 0x%08x\n",
|
|
__func__, dram_res);
|
|
|
|
for (user_id = 0; user_id < ARRAY_SIZE(gpueb_dram_user_name); user_id++) {
|
|
seq_printf(m, "%s:%d ", gpueb_dram_user_name[user_id],
|
|
(dram_res & (0x1 << user_id)) ? 1 : 0);
|
|
}
|
|
seq_puts(m, "\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* PROCFS: trigger GPUEB WDT */
|
|
static int force_trigger_wdt_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_puts(m, "[GPUEB-DEBUG] Force trigger GPUEB WDT\n");
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t force_trigger_wdt_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
int ret = 0;
|
|
char buf[64];
|
|
unsigned int len = 0;
|
|
|
|
len = (count < (sizeof(buf) - 1)) ? count : (sizeof(buf) - 1);
|
|
if (copy_from_user(buf, buffer, len)) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
buf[len] = '\0';
|
|
|
|
if (sysfs_streq(buf, WDT_EXCEPTION_EN))
|
|
gpueb_trigger_wdt("GPUEB_DEBUG");
|
|
|
|
done:
|
|
return (ret < 0) ? ret : count;
|
|
}
|
|
|
|
#if IPI_TEST
|
|
/* PROCFS: GPUEB ipi test */
|
|
static int gpueb_ipi_test_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
int i, ret;
|
|
int send_msg;
|
|
|
|
seq_puts(m, "[GPUEB-TEST] Test AP <-> EB IPI\n");
|
|
|
|
for (i = 0; i < gpueb_get_send_pin_count(); i++) {
|
|
send_msg = i * 10 + 1;
|
|
ret = gpueb_ipi_send_compl_test(i, send_msg);
|
|
if (ret < 0)
|
|
seq_printf(m, "ipi #%d send_compl failed, ret=%d\n", i, ret);
|
|
else
|
|
seq_printf(m, "ipi #%d, send %d, recv %d\n", i, send_msg, ret);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t gpueb_ipi_test_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
int ret = 0;
|
|
char buf[64];
|
|
unsigned int len = 0;
|
|
int ipi_id, send_msg;
|
|
|
|
len = (count < (sizeof(buf) - 1)) ? count : (sizeof(buf) - 1);
|
|
if (copy_from_user(buf, buffer, len)) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
buf[len] = '\0';
|
|
|
|
if (sscanf(buf, "%d %d", &ipi_id, &send_msg) == 2) {
|
|
ret = gpueb_ipi_send_compl_test(ipi_id, send_msg);
|
|
if (ret < 0)
|
|
gpueb_pr_info("ipi send failed, ret=%d", ret);
|
|
else
|
|
gpueb_pr_info("ipi send %d, recv %d", send_msg, ret);
|
|
}
|
|
|
|
done:
|
|
return (ret < 0) ? ret : count;
|
|
}
|
|
#endif
|
|
|
|
/* PROCFS : initialization */
|
|
PROC_FOPS_RO(gpueb_status);
|
|
PROC_FOPS_RO(gpueb_dram_user_status);
|
|
PROC_FOPS_RW(force_trigger_wdt);
|
|
#if IPI_TEST
|
|
PROC_FOPS_RW(gpueb_ipi_test);
|
|
#endif
|
|
|
|
static int gpueb_create_procfs(void)
|
|
{
|
|
struct proc_dir_entry *dir = NULL;
|
|
int i = 0;
|
|
|
|
struct pentry {
|
|
const char *name;
|
|
const struct proc_ops *fops;
|
|
};
|
|
|
|
const struct pentry default_entries[] = {
|
|
PROC_ENTRY(gpueb_status),
|
|
PROC_ENTRY(gpueb_dram_user_status),
|
|
PROC_ENTRY(force_trigger_wdt),
|
|
#if IPI_TEST
|
|
PROC_ENTRY(gpueb_ipi_test)
|
|
#endif
|
|
};
|
|
|
|
dir = proc_mkdir("gpueb", NULL);
|
|
if (!dir) {
|
|
gpueb_pr_info("@%s: fail to create /proc/gpueb (ENOMEM)\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(default_entries); i++) {
|
|
if (!proc_create(default_entries[i].name, 0660,
|
|
dir, default_entries[i].fops))
|
|
gpueb_pr_info("@%s: fail to create /proc/gpueb/%s\n",
|
|
__func__, default_entries[i].name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
void gpueb_debug_init(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct device *gpueb_dev = &pdev->dev;
|
|
struct resource *res = NULL;
|
|
|
|
#if defined(CONFIG_PROC_FS)
|
|
ret = gpueb_create_procfs();
|
|
if (ret)
|
|
gpueb_pr_info("@%s: fail to create procfs (%d)\n", __func__, ret);
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
if (unlikely(!gpueb_dev)) {
|
|
gpueb_pr_info("@%s: fail to find gpueb device\n", __func__);
|
|
return;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpueb_base");
|
|
if (unlikely(!res)) {
|
|
gpueb_pr_info("@%s: fail to get resource GPUEB_BASE\n", __func__);
|
|
return;
|
|
}
|
|
g_gpueb_base = devm_ioremap(gpueb_dev, res->start, resource_size(res));
|
|
if (unlikely(!g_gpueb_base)) {
|
|
gpueb_pr_info("@%s: fail to ioremap GPUEB_BASE: 0x%llx\n", __func__, res->start);
|
|
return;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpueb_gpr_base");
|
|
if (unlikely(!res)) {
|
|
gpueb_pr_info("@%s: fail to get resource GPUEB_GPR_BASE\n", __func__);
|
|
return;
|
|
}
|
|
g_gpueb_gpr_base = devm_ioremap(gpueb_dev, res->start, resource_size(res));
|
|
if (unlikely(!g_gpueb_gpr_base)) {
|
|
gpueb_pr_info("@%s: fail to ioremap gpr base: 0x%llx\n", __func__, res->start);
|
|
return;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpueb_reg_base");
|
|
if (unlikely(!res)) {
|
|
gpueb_pr_info("@%s: fail to get resource GPUEB_REG_BASE\n", __func__);
|
|
return;
|
|
}
|
|
g_gpueb_reg_base = devm_ioremap(gpueb_dev, res->start, resource_size(res));
|
|
if (unlikely(!g_gpueb_reg_base)) {
|
|
gpueb_pr_info("@%s: fail to ioremap reg base: 0x%llx\n", __func__, res->start);
|
|
return;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbox0_send");
|
|
if (unlikely(!res)) {
|
|
gpueb_pr_info("@%s: fail to get resource MBOX0_SEND\n", __func__);
|
|
return;
|
|
}
|
|
g_gpueb_mbox_ipi = devm_ioremap(gpueb_dev, res->start, resource_size(res));
|
|
if (unlikely(!g_gpueb_mbox_ipi)) {
|
|
gpueb_pr_info("@%s: fail to ioremap MBOX0_SEND: 0x%llx\n", __func__, res->start);
|
|
return;
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbox0_recv");
|
|
if (unlikely(!res)) {
|
|
gpueb_pr_info("@%s: fail to get resource MBOX0_RECV\n", __func__);
|
|
return;
|
|
}
|
|
g_gpueb_mbox_sw_int = devm_ioremap(gpueb_dev, res->start, resource_size(res));
|
|
if (unlikely(!g_gpueb_mbox_sw_int)) {
|
|
gpueb_pr_info("@%s: fail to ioremap MBOX0_RECV: 0x%llx\n", __func__, res->start);
|
|
return;
|
|
}
|
|
}
|