}
-static unsigned int dump_set_associative_tlb(void __iomem *sfrbase,
+static unsigned int dump_tlb_entry_way_type(void __iomem *sfrbase,
int idx_way, int idx_set)
{
if (MMU_TLB_ENTRY_VALID(__raw_readl(sfrbase + REG_TLB_ATTR))) {
return 0;
}
+static unsigned int dump_tlb_entry_port_type(void __iomem *sfrbase,
+ int idx_way, int idx_set)
+{
+ if (MMU_TLB_ENTRY_VALID(__raw_readl(sfrbase + REG_CAPA1_TLB_ATTR))) {
+ pr_crit("[%02d][%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n",
+ idx_way, idx_set,
+ __raw_readl(sfrbase + REG_CAPA1_TLB_VPN),
+ __raw_readl(sfrbase + REG_CAPA1_TLB_PPN),
+ __raw_readl(sfrbase + REG_CAPA1_TLB_ATTR));
+ return 1;
+ }
+ return 0;
+}
+
#define MMU_NUM_TLB_SUBLINE 4
-static void dump_sysmmu_tlb(void __iomem *sfrbase)
+static void dump_sysmmu_tlb_way(struct sysmmu_drvdata *drvdata)
{
int i, j, k;
- u32 capa = __raw_readl(sfrbase + REG_MMU_CAPA_V7);
+ u32 capa;
unsigned int cnt;
+ void __iomem *sfrbase = drvdata->sfrbase;
+
+ capa = __raw_readl(sfrbase + REG_MMU_CAPA0_V7);
pr_crit("TLB has %d way, %d set. SBB has %d entries.\n",
MMU_CAPA_NUM_TLB_WAY(capa),
for (k = 0; k < MMU_NUM_TLB_SUBLINE; k++) {
__raw_writel(MMU_SET_TLB_READ_ENTRY(j, i, k),
sfrbase + REG_TLB_READ);
- cnt += dump_set_associative_tlb(sfrbase, i, j);
+ cnt += dump_tlb_entry_way_type(sfrbase, i, j);
}
}
}
pr_crit(">> No Valid SBB Entries\n");
}
+static void dump_sysmmu_tlb_port(struct sysmmu_drvdata *drvdata)
+{
+ int t, i, j, k;
+ u32 capa0, capa1, info;
+ unsigned int cnt;
+ int num_tlb, num_port, num_sbb;
+ void __iomem *sfrbase = drvdata->sfrbase;
+
+ capa0 = __raw_readl(sfrbase + REG_MMU_CAPA0_V7);
+ capa1 = __raw_readl(sfrbase + REG_MMU_CAPA1_V7);
+
+ num_tlb = MMU_CAPA1_NUM_TLB(capa1);
+ num_port = MMU_CAPA1_NUM_PORT(capa1);
+ num_sbb = 1 << MMU_CAPA_NUM_SBB_ENTRY(capa0);
+
+ pr_crit("SysMMU has %d TLBs, %d ports, %d sbb entries\n",
+ num_tlb, num_port, num_sbb);
+
+ for (t = 0; t < num_tlb; t++) {
+ int num_set, num_way;
+
+ info = __raw_readl(sfrbase + MMU_TLB_INFO(t));
+ num_way = MMU_CAPA1_NUM_TLB_WAY(info);
+ num_set = MMU_CAPA1_NUM_TLB_SET(info);
+
+ pr_crit("TLB.%d has %d way, %d set.\n", t, num_way, num_set);
+ pr_crit("------------- TLB[WAY][SET][ENTRY] -------------\n");
+ for (i = 0, cnt = 0; i < num_way; i++) {
+ for (j = 0; j < num_set; j++) {
+ for (k = 0; k < MMU_NUM_TLB_SUBLINE; k++) {
+ __raw_writel(MMU_CAPA1_SET_TLB_READ_ENTRY(t, j, i, k),
+ sfrbase + REG_CAPA1_TLB_READ);
+ cnt += dump_tlb_entry_port_type(sfrbase, i, j);
+ }
+ }
+ }
+ }
+ if (!cnt)
+ pr_crit(">> No Valid TLB Entries\n");
+
+ pr_crit("--- SBB(Second-Level Page Table Base Address Buffer ---\n");
+ for (i = 0, cnt = 0; i < num_sbb; i++) {
+ __raw_writel(i, sfrbase + REG_CAPA1_SBB_READ);
+ if (MMU_SBB_ENTRY_VALID(__raw_readl(sfrbase + REG_CAPA1_SBB_VPN))) {
+ pr_crit("[%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n",
+ i, __raw_readl(sfrbase + REG_CAPA1_SBB_VPN),
+ __raw_readl(sfrbase + REG_CAPA1_SBB_LINK),
+ __raw_readl(sfrbase + REG_CAPA1_SBB_ATTR));
+ cnt++;
+ }
+ }
+ if (!cnt)
+ pr_crit(">> No Valid SBB Entries\n");
+}
+
static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
"PTW ACCESS FAULT",
"PAGE FAULT",
"UNKNOWN FAULT"
};
-void dump_sysmmu_status(void __iomem *sfrbase)
+void dump_sysmmu_status(struct sysmmu_drvdata *drvdata)
{
int info;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
phys_addr_t phys;
+ void __iomem *sfrbase = drvdata->sfrbase;
pgd = pgd_offset_k((unsigned long)sfrbase);
if (!pgd) {
__raw_readl(sfrbase + REG_MMU_CFG),
__raw_readl(sfrbase + REG_MMU_STATUS));
- dump_sysmmu_tlb(sfrbase);
+ if (IS_TLB_WAY_TYPE(drvdata))
+ dump_sysmmu_tlb_way(drvdata);
+ else if(IS_TLB_PORT_TYPE(drvdata))
+ dump_sysmmu_tlb_port(drvdata);
}
static void show_secure_fault_information(struct sysmmu_drvdata *drvdata,
}
}
- dump_sysmmu_status(drvdata->sfrbase);
+ dump_sysmmu_status(drvdata);
finish:
pr_crit("----------------------------------------------------------\n");
return IRQ_HANDLED;
}
-static int get_hw_version(struct device *dev, void __iomem *sfrbase)
+static int sysmmu_get_hw_info(struct sysmmu_drvdata *data)
{
int ret;
+ void __iomem *sfrbase = data->sfrbase;
+ struct tlb_props *tlb_props = &data->tlb_props;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(data->sysmmu);
if (ret < 0) {
- dev_err(dev, "Failed to runtime pm get(%d)\n", ret);
+ dev_err(data->sysmmu, "Failed to runtime pm get(%d)\n", ret);
return ret;
}
- ret = MMU_RAW_VER(__raw_readl(sfrbase + REG_MMU_VERSION));
- pm_runtime_put(dev);
- return ret;
+ data->version = MMU_RAW_VER(__raw_readl(sfrbase + REG_MMU_VERSION));
+
+ /*
+ * If CAPA1 doesn't exist, sysmmu uses TLB way dedication.
+ * If CAPA1[31:28] is zero, sysmmu uses TLB port dedication.
+ */
+ if (!(__raw_readl(sfrbase + REG_MMU_CAPA0_V7) & (1 << 11)))
+ tlb_props->flags |= TLB_TYPE_WAY;
+ else if (!(__raw_readl(sfrbase + REG_MMU_CAPA1_V7) & (0xF << 28)))
+ tlb_props->flags |= TLB_TYPE_PORT;
+
+ pm_runtime_put(data->sysmmu);
+
+ return 0;
}
static int __init __sysmmu_secure_irq_init(struct device *sysmmu,
pm_runtime_enable(dev);
- data->version = get_hw_version(dev, data->sfrbase);
+ ret = sysmmu_get_hw_info(data);
+ if (ret) {
+ dev_err(dev, "Failed to get h/w info\n");
+ return ret;
+ }
ret = sysmmu_parse_dt(data->sysmmu, data);
if (ret) {
cfg |= CFG_QOS_OVRRIDE | CFG_QOS(drvdata->qos);
if (MMU_MAJ_VER(drvdata->version) >= 7) {
- u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA_V7);
+ u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA0_V7);
u32 tlb_way_num = MMU_CAPA_NUM_TLB_WAY(cfg);
u32 set_cnt = 0;
struct tlb_props *tlb_props = &drvdata->tlb_props;
}
dev_info(drvdata->sysmmu, "Dumping status.\n");
- dump_sysmmu_status(drvdata->sfrbase);
+ dump_sysmmu_status(drvdata);
spin_unlock_irqrestore(&drvdata->lock, flags);
}
#define REG_L2TLB_CFG 0x200
/* For SysMMU v7.x */
-#define REG_MMU_CAPA_V7 0x870
+#define REG_MMU_CAPA0_V7 0x870
+#define REG_MMU_CAPA1_V7 0x874
#define REG_PUBLIC_WAY_CFG 0x120
#define REG_PRIVATE_WAY_CFG(n) (0x200 + ((n) * 0x10))
#define REG_PRIVATE_ADDR_START(n) (0x204 + ((n) * 0x10))
#define REG_SBB_LINK 0x1108
#define REG_SBB_ATTR 0x110C
+/* For SysMMU v7.1 */
+#define MMU_CAPA1_NUM_TLB(reg) ((reg >> 4) & 0xFF)
+#define MMU_CAPA1_NUM_PORT(reg) ((reg) & 0xF)
+#define MMU_TLB_INFO(n) (0x2000 + ((n) * 0x20))
+#define MMU_CAPA1_NUM_TLB_SET(reg) ((reg >> 16) & 0xFF)
+#define MMU_CAPA1_NUM_TLB_WAY(reg) ((reg) & 0xFF)
+#define REG_CAPA1_TLB_READ 0x8000
+#define REG_CAPA1_TLB_VPN 0x8004
+#define REG_CAPA1_TLB_PPN 0x8008
+#define REG_CAPA1_TLB_ATTR 0x800C
+#define REG_CAPA1_SBB_READ 0x8020
+#define REG_CAPA1_SBB_VPN 0x8024
+#define REG_CAPA1_SBB_LINK 0x8028
+#define REG_CAPA1_SBB_ATTR 0x802C
+#define MMU_CAPA1_SET_TLB_READ_ENTRY(tid, set, way, line) \
+ ((set) | ((way) << 8) | ((line) << 16) | ((tid) << 20))
+
#define MMU_CAPA_NUM_SBB_ENTRY(reg) ((reg >> 12) & 0xF)
#define MMU_CAPA_NUM_TLB_SET(reg) ((reg >> 8) & 0xF)
#define MMU_CAPA_NUM_TLB_WAY(reg) ((reg) & 0xFF)
unsigned int id;
};
+/*
+ * flags[7:4] specifies TLB matching types.
+ * 0x1 : TLB way dedication
+ * 0x2 : TLB port dedication
+ */
+#define TLB_TYPE_MASK(x) ((x) & (0xF << 4))
+#define TLB_TYPE_WAY (0x1 << 4)
+#define TLB_TYPE_PORT (0x2 << 4)
+#define IS_TLB_WAY_TYPE(data) (TLB_TYPE_MASK((data)->tlb_props.flags) \
+ == TLB_TYPE_WAY)
+#define IS_TLB_PORT_TYPE(data) (TLB_TYPE_MASK((data)->tlb_props.flags) \
+ == TLB_TYPE_PORT)
+
#define TLB_WAY_PRIVATE_ID (1 << 0)
#define TLB_WAY_PRIVATE_ADDR (1 << 1)
#define TLB_WAY_PUBLIC (1 << 2)