kernel_samsung_a34x-permissive/drivers/misc/mediatek/iommu/m4u_debug.c
2024-04-28 15:51:13 +02:00

529 lines
15 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#define pr_fmt(fmt) "[m4u_debug] " fmt
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/export.h>
#ifdef CONFIG_MTK_AEE_FEATURE
#include <aee.h>
#endif
#include "m4u_debug.h"
#define MTK_M4U_ID(larb, port) (((larb) << 5) | (port))
#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0xf)
/* PortID within the local arbiter */
#define MTK_M4U_TO_PORT(id) ((id) & 0x1f)
#define ERROR_LARB_PORT_ID 0xFFFF
#define F_MMU_INT_TF_MSK GENMASK(11, 2)
#ifdef CONFIG_MTK_AEE_FEATURE
#define m4u_aee_print(string, args...) do {\
char m4u_name[100];\
snprintf(m4u_name, 100, "[M4U]"string, ##args); \
aee_kernel_warning_api(__FILE__, __LINE__, \
DB_OPT_MMPROFILE_BUFFER | DB_OPT_DUMP_DISPLAY, \
m4u_name, "[M4U] error"string, ##args); \
pr_err("[M4U] error:"string, ##args); \
} while (0)
#else
#define m4u_aee_print(string, args...) do {\
char m4u_name[100];\
snprintf(m4u_name, 100, "[M4U]"string, ##args); \
pr_err("[M4U] error:"string, ##args); \
} while (0)
#endif
#define M4U0_PORT_INIT(name, slave, larb_id, smi_select_larb_id, port) {\
name, 0, slave, larb_id, port, \
(((smi_select_larb_id)<<7)|((port)<<2)), 1\
}
#define M4U1_PORT_INIT(name, slave, larb_id, smi_select_larb_id, port) {\
name, 1, slave, larb_id, port, \
(((smi_select_larb_id)<<7)|((port)<<2)), 1\
}
#define mmu_translation_log_format \
"CRDISPATCH_KEY:M4U_%s\ntranslation fault:port=%s,mva=0x%x,pa=0x%x\n"
struct mtk_iommu_cb {
mtk_iommu_fault_callback_t fault_fn;
void *fault_data;
};
struct mtk_m4u_data {
struct device *dev;
struct dentry *debug_root;
struct mtk_iommu_cb *m4u_cb;
const struct mtk_m4u_plat_data *plat_data;
};
struct iova_info {
struct device *dev;
dma_addr_t iova;
size_t size;
struct list_head list_node;
};
struct mtk_iommu_port {
char *name;
unsigned m4u_id: 2;
unsigned m4u_slave: 2;
unsigned larb_id: 4;
unsigned larb_port: 8;
unsigned tf_id: 12; /* 12 bits */
bool enable_tf;
};
struct mtk_m4u_plat_data {
const struct mtk_iommu_port *port;
u32 port_nr;
};
struct iova_buf_list {
struct list_head head;
struct mutex lock;
};
static const struct mtk_iommu_port iommu_port_mt6779[] = {
/* larb0 -MMSYS-9 */
M4U0_PORT_INIT("DISP_POSTMASK0", 0, 0, 0, 0),
M4U0_PORT_INIT("DISP_OVL0_HDR", 0, 0, 0, 1),
M4U0_PORT_INIT("DISP_OVL1_HDR", 0, 0, 0, 2),
M4U0_PORT_INIT("DISP_OVL0", 0, 0, 0, 3),
M4U0_PORT_INIT("DISP_OVL1", 0, 0, 0, 4),
M4U0_PORT_INIT("DISP_PVRIC0", 0, 0, 0, 5),
M4U0_PORT_INIT("DISP_RDMA0", 0, 0, 0, 6),
M4U0_PORT_INIT("DISP_WDMA0", 0, 0, 0, 7),
M4U0_PORT_INIT("DISP_FAKE0", 0, 0, 0, 8),
/*larb1-MMSYS-14*/
M4U0_PORT_INIT("DISP_OVL0_2L_HDR", 0, 1, 4, 0),
M4U0_PORT_INIT("DISP_OVL1_2L_HDR", 0, 1, 4, 1),
M4U0_PORT_INIT("DISP_OVL0_2L", 0, 1, 4, 2),
M4U0_PORT_INIT("DISP_OVL1_2L", 0, 1, 4, 3),
M4U0_PORT_INIT("DISP_RDMA1", 0, 1, 4, 4),
M4U0_PORT_INIT("MDP_PVRIC0", 0, 1, 4, 5),
M4U0_PORT_INIT("MDP_PVRIC1", 0, 1, 4, 6),
M4U0_PORT_INIT("MDP_RDMA0", 0, 1, 4, 7),
M4U0_PORT_INIT("MDP_RDMA1", 0, 1, 4, 8),
M4U0_PORT_INIT("MDP_WROT0_R", 0, 1, 4, 9),
M4U0_PORT_INIT("MDP_WROT0_W", 0, 1, 4, 10),
M4U0_PORT_INIT("MDP_WROT1_R", 0, 1, 4, 11),
M4U0_PORT_INIT("MDP_WROT1_W", 0, 1, 4, 12),
M4U0_PORT_INIT("DISP_FAKE1", 0, 1, 4, 13),
/*larb2-VDEC-12*/
M4U0_PORT_INIT("VDEC_MC_EXT", 0, 2, 8, 0),
M4U0_PORT_INIT("VDEC_UFO_EXT", 0, 2, 8, 1),
M4U0_PORT_INIT("VDEC_PP_EXT", 0, 2, 8, 2),
M4U0_PORT_INIT("VDEC_PRED_RD_EXT", 0, 2, 8, 3),
M4U0_PORT_INIT("VDEC_PRED_WR_EXT", 0, 2, 8, 4),
M4U0_PORT_INIT("VDEC_PPWRAP_EXT", 0, 2, 8, 5),
M4U0_PORT_INIT("VDEC_TILE_EXT", 0, 2, 8, 6),
M4U0_PORT_INIT("VDEC_VLD_EXT", 0, 2, 8, 7),
M4U0_PORT_INIT("VDEC_VLD2_EXT", 0, 2, 8, 8),
M4U0_PORT_INIT("VDEC_AVC_MV_EXT", 0, 2, 8, 9),
M4U0_PORT_INIT("VDEC_UFO_ENC_EXT", 0, 2, 8, 10),
M4U0_PORT_INIT("VDEC_RG_CTRL_DMA_EXT", 0, 2, 8, 11),
/*larb3-VENC-19*/
M4U0_PORT_INIT("VENC_RCPU", 0, 3, 12, 0),
M4U0_PORT_INIT("VENC_REC", 0, 3, 12, 1),
M4U0_PORT_INIT("VENC_BSDMA", 0, 3, 12, 2),
M4U0_PORT_INIT("VENC_SV_COMV", 0, 3, 12, 3),
M4U0_PORT_INIT("VENC_RD_COMV", 0, 3, 12, 4),
M4U0_PORT_INIT("VENC_NBM_RDMA", 0, 3, 12, 5),
M4U0_PORT_INIT("VENC_NBM_RDMA_LITE", 0, 3, 12, 6),
M4U0_PORT_INIT("JPGENC_Y_RDMA", 0, 3, 12, 7),
M4U0_PORT_INIT("JPGENC_C_RDMA", 0, 3, 12, 8),
M4U0_PORT_INIT("JPGENC_Q_TABLE", 0, 3, 12, 9),
M4U0_PORT_INIT("JPGENC_BSDMA", 0, 3, 12, 10),
M4U0_PORT_INIT("JPGEDC_WDMA", 0, 3, 12, 11),
M4U0_PORT_INIT("JPGEDC_BSDMA", 0, 3, 12, 12),
M4U0_PORT_INIT("VENC_NBM_WDMA", 0, 3, 12, 13),
M4U0_PORT_INIT("VENC_NBM_WDMA_LITE", 0, 3, 12, 14),
M4U0_PORT_INIT("VENC_CUR_LUMA", 0, 3, 12, 15),
M4U0_PORT_INIT("VENC_CUR_CHROMA", 0, 3, 12, 16),
M4U0_PORT_INIT("VENC_REF_LUMA", 0, 3, 12, 17),
M4U0_PORT_INIT("VENC_REF_CHROMA", 0, 3, 12, 18),
/*larb4-dummy*/
/*larb5-IMG-26*/
M4U0_PORT_INIT("IMGI_D1", 0, 5, 16, 0),
M4U0_PORT_INIT("IMGBI_D1", 0, 5, 16, 1),
M4U0_PORT_INIT("DMGI_D1", 0, 5, 16, 2),
M4U0_PORT_INIT("DEPI_D1", 0, 5, 16, 3),
M4U0_PORT_INIT("LCEI_D1", 0, 5, 16, 4),
M4U0_PORT_INIT("SMTI_D1", 0, 5, 16, 5),
M4U0_PORT_INIT("SMTO_D2", 0, 5, 16, 6),
M4U0_PORT_INIT("SMTO_D1", 0, 5, 16, 7),
M4U0_PORT_INIT("CRZO_D1", 0, 5, 16, 8),
M4U0_PORT_INIT("IMG3O_D1", 0, 5, 16, 9),
M4U0_PORT_INIT("VIPI_D1", 0, 5, 16, 10),
M4U0_PORT_INIT("WPE_A_RDMA1", 0, 5, 16, 11),
M4U0_PORT_INIT("WPE_A_RDMA0", 0, 5, 16, 12),
M4U0_PORT_INIT("WPE_A_WDMA", 0, 5, 16, 13),
M4U0_PORT_INIT("TIMGO_D1", 0, 5, 16, 14),
M4U0_PORT_INIT("MFB_RDMA0", 0, 5, 16, 15),
M4U0_PORT_INIT("MFB_RDMA1", 0, 5, 16, 16),
M4U0_PORT_INIT("MFB_RDMA2", 0, 5, 16, 17),
M4U0_PORT_INIT("MFB_RDMA3", 0, 5, 16, 18),
M4U0_PORT_INIT("MFB_WDMA", 0, 5, 16, 19),
M4U0_PORT_INIT("RESERVED1", 0, 5, 16, 20),
M4U0_PORT_INIT("RESERVED2", 0, 5, 16, 21),
M4U0_PORT_INIT("RESERVED3", 0, 5, 16, 22),
M4U0_PORT_INIT("RESERVED4", 0, 5, 16, 23),
M4U0_PORT_INIT("RESERVED5", 0, 5, 16, 24),
M4U0_PORT_INIT("RESERVED6", 0, 5, 16, 25),
/*larb7-IPESYS-4*/
M4U0_PORT_INIT("DVS_RDMA", 0, 7, 20, 0),
M4U0_PORT_INIT("DVS_WDMA", 0, 7, 20, 1),
M4U0_PORT_INIT("DVP_RDMA,", 0, 7, 20, 2),
M4U0_PORT_INIT("DVP_WDMA,", 0, 7, 20, 3),
/*larb8-IPESYS-10*/
M4U0_PORT_INIT("FDVT_RDA", 0, 8, 21, 0),
M4U0_PORT_INIT("FDVT_RDB", 0, 8, 21, 1),
M4U0_PORT_INIT("FDVT_WRA", 0, 8, 21, 2),
M4U0_PORT_INIT("FDVT_WRB", 0, 8, 21, 3),
M4U0_PORT_INIT("FE_RD0", 0, 8, 21, 4),
M4U0_PORT_INIT("FE_RD1", 0, 8, 21, 5),
M4U0_PORT_INIT("FE_WR0", 0, 8, 21, 6),
M4U0_PORT_INIT("FE_WR1", 0, 8, 21, 7),
M4U0_PORT_INIT("RSC_RDMA0", 0, 8, 21, 8),
M4U0_PORT_INIT("RSC_WDMA", 0, 8, 21, 9),
/*larb9-CAM-24*/
M4U0_PORT_INIT("CAM_IMGO_R1_C", 0, 9, 28, 0),
M4U0_PORT_INIT("CAM_RRZO_R1_C", 0, 9, 28, 1),
M4U0_PORT_INIT("CAM_LSCI_R1_C", 0, 9, 28, 2),
M4U0_PORT_INIT("CAM_BPCI_R1_C", 0, 9, 28, 3),
M4U0_PORT_INIT("CAM_YUVO_R1_C", 0, 9, 28, 4),
M4U0_PORT_INIT("CAM_UFDI_R2_C", 0, 9, 28, 5),
M4U0_PORT_INIT("CAM_RAWI_R2_C", 0, 9, 28, 6),
M4U0_PORT_INIT("CAM_RAWI_R5_C", 0, 9, 28, 7),
M4U0_PORT_INIT("CAM_CAMSV_1", 0, 9, 28, 8),
M4U0_PORT_INIT("CAM_CAMSV_2", 0, 9, 28, 9),
M4U0_PORT_INIT("CAM_CAMSV_3", 0, 9, 28, 10),
M4U0_PORT_INIT("CAM_CAMSV_4", 0, 9, 28, 11),
M4U0_PORT_INIT("CAM_CAMSV_5", 0, 9, 28, 12),
M4U0_PORT_INIT("CAM_CAMSV_6", 0, 9, 28, 13),
M4U0_PORT_INIT("CAM_AAO_R1_C", 0, 9, 28, 14),
M4U0_PORT_INIT("CAM_AFO_R1_C", 0, 9, 28, 15),
M4U0_PORT_INIT("CAM_FLKO_R1_C", 0, 9, 28, 16),
M4U0_PORT_INIT("CAM_LCESO_R1_C", 0, 9, 28, 17),
M4U0_PORT_INIT("CAM_CRZO_R1_C", 0, 9, 28, 18),
M4U0_PORT_INIT("CAM_LTMSO_R1_C", 0, 9, 28, 19),
M4U0_PORT_INIT("CAM_RSSO_R1_C", 0, 9, 28, 20),
M4U0_PORT_INIT("CAM_CCUI", 0, 9, 28, 21),
M4U0_PORT_INIT("CAM_CCUO", 0, 9, 28, 22),
M4U0_PORT_INIT("CAM_FAKE", 0, 9, 28, 23),
/*larb10-CAM-31*/
M4U0_PORT_INIT("CAM_IMGO_R1_A", 0, 10, 25, 0),
M4U0_PORT_INIT("CAM_RRZO_R1_A", 0, 10, 25, 1),
M4U0_PORT_INIT("CAM_LSCI_R1_A", 0, 10, 25, 2),
M4U0_PORT_INIT("CAM_BPCI_R1_A", 0, 10, 25, 3),
M4U0_PORT_INIT("CAM_YUVO_R1_A", 0, 10, 25, 4),
M4U0_PORT_INIT("CAM_UFDI_R2_A", 0, 10, 25, 5),
M4U0_PORT_INIT("CAM_RAWI_R2_A", 0, 10, 25, 6),
M4U0_PORT_INIT("CAM_RAWI_R5_A", 0, 10, 25, 7),
M4U0_PORT_INIT("CAM_IMGO_R1_B", 0, 10, 25, 8),
M4U0_PORT_INIT("CAM_RRZO_R1_B", 0, 10, 25, 9),
M4U0_PORT_INIT("CAM_LSCI_R1_B", 0, 10, 25, 10),
M4U0_PORT_INIT("CAM_BPCI_R1_B", 0, 10, 25, 11),
M4U0_PORT_INIT("CAM_YUVO_R1_B,", 0, 10, 25, 12),
M4U0_PORT_INIT("CAM_UFDI_R2_B", 0, 10, 25, 13),
M4U0_PORT_INIT("CAM_RAWI_R2_B", 0, 10, 25, 14),
M4U0_PORT_INIT("CAM_RAWI_R5_B", 0, 10, 25, 15),
M4U0_PORT_INIT("CAM_CAMSV_0", 0, 10, 25, 16),
M4U0_PORT_INIT("CAM_AAO_R1_A", 0, 10, 25, 17),
M4U0_PORT_INIT("CAM_AFO_R1_A", 0, 10, 25, 18),
M4U0_PORT_INIT("CAM_FLKO_R1_A", 0, 10, 25, 19),
M4U0_PORT_INIT("CAM_LCESO_R1_A", 0, 10, 25, 20),
M4U0_PORT_INIT("CAM_CRZO_R1_A", 0, 10, 25, 21),
M4U0_PORT_INIT("CAM_AAO_R1_B", 0, 10, 25, 22),
M4U0_PORT_INIT("CAM_AFO_R1_B", 0, 10, 25, 23),
M4U0_PORT_INIT("CAM_FLKO_R1_B", 0, 10, 25, 24),
M4U0_PORT_INIT("CAM_LCESO_R1_B", 0, 10, 25, 25),
M4U0_PORT_INIT("CAM_CRZO_R1_B", 0, 10, 25, 26),
M4U0_PORT_INIT("CAM_LTMSO_R1_A", 0, 10, 25, 27),
M4U0_PORT_INIT("CAM_RSSO_R1_A", 0, 10, 25, 28),
M4U0_PORT_INIT("CAM_LTMSO_R1_B", 0, 10, 25, 29),
M4U0_PORT_INIT("CAM_RSSO_R1_B", 0, 10, 25, 30),
M4U0_PORT_INIT("CCU0", 0, 9, 24, 0),
M4U0_PORT_INIT("CCU1", 0, 9, 24, 1),
M4U1_PORT_INIT("VPU", 0, 0, 0, 0),
M4U0_PORT_INIT("UNKNOWN", 0, 0, 0, 0)
};
static struct mtk_m4u_data *m4u_data;
static struct iova_buf_list iova_list;
void mtk_iova_dbg_alloc(struct device *dev, dma_addr_t iova, size_t size)
{
struct iova_info *iova_buf;
iova_buf = kzalloc(sizeof(*iova_buf), GFP_KERNEL);
if (!iova_buf)
return;
iova_buf->dev = dev;
iova_buf->iova = iova;
iova_buf->size = size;
mutex_lock(&iova_list.lock);
list_add(&iova_buf->list_node, &iova_list.head);
mutex_unlock(&iova_list.lock);
}
void mtk_iova_dbg_free(dma_addr_t iova, size_t size)
{
struct iova_info *plist;
struct iova_info *tmp_plist;
mutex_lock(&iova_list.lock);
list_for_each_entry_safe(plist, tmp_plist,
&iova_list.head, list_node) {
if (plist->iova == iova && plist->size == size) {
list_del(&plist->list_node);
kfree(plist);
break;
}
}
mutex_unlock(&iova_list.lock);
}
void mtk_iova_dbg_dump(struct seq_file *s)
{
struct iova_info *plist = NULL;
struct iova_info *n = NULL;
mutex_lock(&iova_list.lock);
if (s)
seq_printf(s, "%18s %8s %18s\n", "iova", "size", "dev");
else
pr_info("%18s %8s %18s\n", "iova", "size", "dev");
list_for_each_entry_safe(plist, n, &iova_list.head,
list_node) {
if (s)
seq_printf(s, "%pa %8zu %18s\n",
&plist->iova,
plist->size,
dev_name(plist->dev));
else
pr_info("%pa %8zu %18s\n",
&plist->iova,
plist->size,
dev_name(plist->dev));
}
mutex_unlock(&iova_list.lock);
}
static inline int mtk_iommu_get_tf_port_idx(int tf_id)
{
int i;
tf_id &= F_MMU_INT_TF_MSK;
for (i = 0; i < m4u_data->plat_data->port_nr; i++) {
if (m4u_data->plat_data->port[i].tf_id == tf_id)
return i;
}
return ERROR_LARB_PORT_ID;
}
static inline int mtk_iommu_port_idx(int id)
{
int i;
for (i = 0; i < m4u_data->plat_data->port_nr; i++) {
if ((m4u_data->plat_data->port[i].larb_id ==
MTK_M4U_TO_LARB(id)) &&
(m4u_data->plat_data->port[i].larb_port ==
MTK_M4U_TO_PORT(id)))
return i;
}
return ERROR_LARB_PORT_ID;
}
bool report_custom_iommu_fault(
u32 fault_iova,
u32 fault_pa,
u32 fault_id, bool is_vpu)
{
int idx;
int port;
if (is_vpu) {
m4u_aee_print(mmu_translation_log_format, "VPU",
"VPU", fault_iova, fault_pa);
return true;
}
idx = mtk_iommu_get_tf_port_idx(fault_id);
if (idx >= m4u_data->plat_data->port_nr) {
pr_err("fail,iova:0x%x, port:0x%x\n",
fault_iova, fault_id);
return false;
}
port = MTK_M4U_ID(m4u_data->plat_data->port[idx].larb_id,
m4u_data->plat_data->port[idx].larb_port);
if (m4u_data->plat_data->port[idx].enable_tf &&
m4u_data->m4u_cb[idx].fault_fn)
m4u_data->m4u_cb[idx].fault_fn(port,
fault_iova, m4u_data->m4u_cb[idx].fault_data);
m4u_aee_print(mmu_translation_log_format,
m4u_data->plat_data->port[idx].name,
m4u_data->plat_data->port[idx].name, fault_iova,
fault_pa);
return true;
}
int mtk_iommu_register_fault_callback(int port,
mtk_iommu_fault_callback_t fn,
void *cb_data)
{
int idx = mtk_iommu_port_idx(port);
if (idx >= m4u_data->plat_data->port_nr) {
pr_info("%s fail, port=%d\n", __func__, port);
return -1;
}
m4u_data->m4u_cb[idx].fault_fn = fn;
m4u_data->m4u_cb[idx].fault_data = cb_data;
return 0;
}
EXPORT_SYMBOL(mtk_iommu_register_fault_callback);
int mtk_iommu_unregister_fault_callback(int port)
{
int idx = mtk_iommu_port_idx(port);
if (idx >= m4u_data->plat_data->port_nr) {
pr_info("%s fail, port=%d\n", __func__, port);
return -1;
}
m4u_data->m4u_cb[idx].fault_fn = NULL;
m4u_data->m4u_cb[idx].fault_data = NULL;
return 0;
}
EXPORT_SYMBOL(mtk_iommu_unregister_fault_callback);
static int m4u_debug_set(void *data, u64 val)
{
pr_info("%s:val=%llu\n", __func__, val);
switch (val) {
case 1: /* translation fault test */
{
report_custom_iommu_fault(0, 0, 0x500000f, false);
break;
}
case 2: /* dump iova info */
{
mtk_iova_dbg_dump(NULL);
break;
}
default:
pr_err("%s error,val=%llu\n", __func__, val);
}
return 0;
}
static int m4u_debug_get(void *data, u64 *val)
{
*val = 0;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(m4u_debug_fops, m4u_debug_get, m4u_debug_set, "%llu\n");
static int m4u_debug_init(struct mtk_m4u_data *data)
{
struct dentry *debug_file;
data->debug_root = debugfs_create_dir("m4u", NULL);
if (IS_ERR_OR_NULL(data->debug_root))
pr_err("failed to create debug dir.\n");
debug_file = debugfs_create_file("debug",
0644, data->debug_root, NULL, &m4u_debug_fops);
if (IS_ERR_OR_NULL(debug_file))
pr_err("failed to create debug files 2.\n");
return 0;
}
static int mtk_m4u_dbg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
m4u_data = devm_kzalloc(dev, sizeof(struct mtk_m4u_data), GFP_KERNEL);
if (!m4u_data)
return -ENOMEM;
m4u_data->dev = dev;
m4u_data->plat_data = of_device_get_match_data(dev);
m4u_data->m4u_cb = devm_kzalloc(dev, m4u_data->plat_data->port_nr *
sizeof(struct mtk_iommu_cb), GFP_KERNEL);
if (!m4u_data->m4u_cb)
return -ENOMEM;
m4u_debug_init(m4u_data);
mutex_init(&iova_list.lock);
INIT_LIST_HEAD(&iova_list.head);
return 0;
}
static const struct mtk_m4u_plat_data mt6779_data = {
.port = iommu_port_mt6779,
.port_nr = ARRAY_SIZE(iommu_port_mt6779),
};
static const struct of_device_id mtk_m4u_dbg_of_ids[] = {
{ .compatible = "mediatek,mt6779-m4u-debug", .data = &mt6779_data},
{}
};
static struct platform_driver mtk_m4u_dbg_drv = {
.probe = mtk_m4u_dbg_probe,
.driver = {
.name = "mtk-m4u-debug",
.of_match_table = of_match_ptr(mtk_m4u_dbg_of_ids),
}
};
module_platform_driver(mtk_m4u_dbg_drv);