From b74b780dcfa5a4f92b16fb9ac67f2c46e883b914 Mon Sep 17 00:00:00 2001 From: Janghyuck Kim Date: Mon, 31 Oct 2016 17:03:09 +0900 Subject: [PATCH] [COMMON] iommu/exynos: add TLB match type flags TLB match type can be different by the supported version. This patch reads supported feature from H/W, and calls handling function for each type. Change-Id: Id674fa9687c4a3839c2dcfa04061c2c8386a4c34 Signed-off-by: Janghyuck Kim --- drivers/iommu/exynos-iommu.c | 125 ++++++++++++++++++++++++++++++----- drivers/iommu/exynos-iommu.h | 33 ++++++++- 2 files changed, 141 insertions(+), 17 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9e10598bfa37..11508ba82c21 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -193,7 +193,7 @@ void exynos_sysmmu_tlb_invalidate(struct iommu_domain *iommu_domain, } -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))) { @@ -207,12 +207,29 @@ static unsigned int dump_set_associative_tlb(void __iomem *sfrbase, 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), @@ -224,7 +241,7 @@ static void dump_sysmmu_tlb(void __iomem *sfrbase) 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); } } } @@ -246,6 +263,61 @@ static void dump_sysmmu_tlb(void __iomem *sfrbase) 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", @@ -255,7 +327,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "UNKNOWN FAULT" }; -void dump_sysmmu_status(void __iomem *sfrbase) +void dump_sysmmu_status(struct sysmmu_drvdata *drvdata) { int info; pgd_t *pgd; @@ -263,6 +335,7 @@ void dump_sysmmu_status(void __iomem *sfrbase) pmd_t *pmd; pte_t *pte; phys_addr_t phys; + void __iomem *sfrbase = drvdata->sfrbase; pgd = pgd_offset_k((unsigned long)sfrbase); if (!pgd) { @@ -300,7 +373,10 @@ void dump_sysmmu_status(void __iomem *sfrbase) __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, @@ -422,7 +498,7 @@ static void show_fault_information(struct sysmmu_drvdata *drvdata, } } - dump_sysmmu_status(drvdata->sfrbase); + dump_sysmmu_status(drvdata); finish: pr_crit("----------------------------------------------------------\n"); @@ -515,19 +591,32 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) 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, @@ -751,7 +840,11 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) 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) { @@ -891,7 +984,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *drvdata) 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; @@ -1593,7 +1686,7 @@ void exynos_sysmmu_show_status(struct device *master) } dev_info(drvdata->sysmmu, "Dumping status.\n"); - dump_sysmmu_status(drvdata->sfrbase); + dump_sysmmu_status(drvdata); spin_unlock_irqrestore(&drvdata->lock, flags); } diff --git a/drivers/iommu/exynos-iommu.h b/drivers/iommu/exynos-iommu.h index 287ce6366b83..b215ddbe8f34 100644 --- a/drivers/iommu/exynos-iommu.h +++ b/drivers/iommu/exynos-iommu.h @@ -145,7 +145,8 @@ typedef u32 sysmmu_pte_t; #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)) @@ -162,6 +163,23 @@ typedef u32 sysmmu_pte_t; #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) @@ -248,6 +266,19 @@ struct tlb_priv_id { 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) -- 2.20.1