/* linux/drivers/iommu/exynos_iommu-reg.h * * Copyright (c) 2017 Samsung Electronics Co., Ltd. * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include "exynos-iommu.h" #include #ifdef CONFIG_SEC_DEBUG_AUTO_COMMENT #include #endif #define is_secure_info_fail(x) ((((x) >> 16) & 0xffff) == 0xdead) static inline u32 __secure_info_read(unsigned int addr) { u32 ret; ret = exynos_smc(SMC_DRM_SEC_SMMU_INFO, (unsigned long)addr, 0, SEC_SMMU_SFR_READ); if (is_secure_info_fail(ret)) pr_err("Invalid value returned, %#x\n", ret); return ret; } static inline void __sysmmu_tlb_invalidate_all(void __iomem *sfrbase) { writel(0x1, sfrbase + REG_MMU_FLUSH); } static inline void __sysmmu_tlb_invalidate(struct sysmmu_drvdata *drvdata, dma_addr_t iova, size_t size) { void * __iomem sfrbase = drvdata->sfrbase; __raw_writel(iova, sfrbase + REG_FLUSH_RANGE_START); __raw_writel(size - 1 + iova, sfrbase + REG_FLUSH_RANGE_END); writel(0x1, sfrbase + REG_MMU_FLUSH_RANGE); SYSMMU_EVENT_LOG_TLB_INV_RANGE(SYSMMU_DRVDATA_TO_LOG(drvdata), iova, iova + size); } static inline void __sysmmu_set_ptbase(struct sysmmu_drvdata *drvdata, phys_addr_t pfn_pgtable) { void * __iomem sfrbase = drvdata->sfrbase; writel_relaxed(pfn_pgtable, sfrbase + REG_PT_BASE_PPN); __sysmmu_tlb_invalidate_all(sfrbase); SYSMMU_EVENT_LOG_TLB_INV_ALL( SYSMMU_DRVDATA_TO_LOG(drvdata)); } static inline 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))) { pr_auto(ASL4, "[%02d][%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n", idx_way, idx_set, __raw_readl(sfrbase + REG_TLB_VPN), __raw_readl(sfrbase + REG_TLB_PPN), __raw_readl(sfrbase + REG_TLB_ATTR)); return 1; } return 0; } static inline void sysmmu_tlb_compare(phys_addr_t pgtable, int idx_sub, u32 vpn, u32 ppn) { sysmmu_pte_t *entry; unsigned long vaddr = MMU_VADDR_FROM_TLB((unsigned long)vpn, idx_sub); unsigned long paddr = MMU_PADDR_FROM_TLB((unsigned long)ppn); unsigned long phys = 0; if (!pgtable) return; entry = section_entry(phys_to_virt(pgtable), vaddr); if (lv1ent_section(entry)) { phys = section_phys(entry); } else if (lv1ent_page(entry)) { entry = page_entry(entry, vaddr); if (lv2ent_large(entry)) phys = lpage_phys(entry); else if (lv2ent_small(entry)) phys = spage_phys(entry); } else { pr_auto(ASL4, ">> Invalid address detected! entry: %#lx", (unsigned long)*entry); return; } if (paddr != phys) { pr_auto(ASL4, ">> TLB mismatch detected!\n"); pr_auto(ASL4, " TLB: %#010lx, PT entry: %#010lx\n", paddr, phys); } } static inline unsigned int dump_tlb_entry_port_type(void __iomem *sfrbase, phys_addr_t pgtable, int idx_way, int idx_set, int idx_sub) { if (MMU_TLB_ENTRY_VALID(__raw_readl(sfrbase + REG_CAPA1_TLB_ATTR))) { u32 vpn, ppn, attr; vpn = __raw_readl(sfrbase + REG_CAPA1_TLB_VPN); ppn = __raw_readl(sfrbase + REG_CAPA1_TLB_PPN); attr = __raw_readl(sfrbase + REG_CAPA1_TLB_ATTR); pr_auto(ASL4, "[%02d][%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n", idx_way, idx_set, vpn, ppn, attr); sysmmu_tlb_compare(pgtable, idx_sub, vpn, ppn); return 1; } return 0; } #define MMU_NUM_TLB_SUBLINE 4 static inline void dump_sysmmu_tlb_way(struct sysmmu_drvdata *drvdata) { int i, j, k; u32 capa; unsigned int cnt; void __iomem *sfrbase = drvdata->sfrbase; capa = __raw_readl(sfrbase + REG_MMU_CAPA0_V7); pr_auto(ASL4, "TLB has %d way, %d set. SBB has %d entries.\n", MMU_CAPA_NUM_TLB_WAY(capa), (1 << MMU_CAPA_NUM_TLB_SET(capa)), (1 << MMU_CAPA_NUM_SBB_ENTRY(capa))); pr_auto(ASL4, "------------- TLB[WAY][SET][ENTRY] -------------\n"); for (i = 0, cnt = 0; i < MMU_CAPA_NUM_TLB_WAY(capa); i++) { for (j = 0; j < (1 << MMU_CAPA_NUM_TLB_SET(capa)); j++) { 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_tlb_entry_way_type(sfrbase, i, j); } } } if (!cnt) pr_auto(ASL4, ">> No Valid TLB Entries\n"); pr_auto(ASL4, "--- SBB(Second-Level Page Table Base Address Buffer ---\n"); for (i = 0, cnt = 0; i < (1 << MMU_CAPA_NUM_SBB_ENTRY(capa)); i++) { __raw_writel(i, sfrbase + REG_SBB_READ); if (MMU_SBB_ENTRY_VALID(__raw_readl(sfrbase + REG_SBB_VPN))) { pr_auto(ASL4, "[%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n", i, __raw_readl(sfrbase + REG_SBB_VPN), __raw_readl(sfrbase + REG_SBB_LINK), __raw_readl(sfrbase + REG_SBB_ATTR)); cnt++; } } if (!cnt) pr_auto(ASL4, ">> No Valid SBB Entries\n"); } static inline void sysmmu_sbb_compare(u32 sbb_vpn, u32 sbb_link, phys_addr_t pgtable) { sysmmu_pte_t *entry; unsigned long vaddr = MMU_VADDR_FROM_SBB((unsigned long)sbb_vpn); unsigned long paddr = MMU_PADDR_FROM_SBB((unsigned long)sbb_link); unsigned long phys = 0; if (!pgtable) return; entry = section_entry(phys_to_virt(pgtable), vaddr); if (lv1ent_page(entry)) { phys = lv2table_base(entry); if (paddr != phys) { pr_auto(ASL4, ">> SBB mismatch detected!\n"); pr_auto(ASL4, " entry addr: %lx / SBB addr %lx\n", paddr, phys); } } else { pr_auto(ASL4, ">> Invalid address detected! entry: %#lx", (unsigned long)*entry); } } static inline void dump_sysmmu_tlb_port(struct sysmmu_drvdata *drvdata, phys_addr_t pgtable) { int t, i, j, k; u32 capa0, capa1, info; u32 sbb_vpn, sbb_link; 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, pgtable, i, j, k); } } } } if (!cnt) pr_auto(ASL4, ">> 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))) { sbb_vpn = __raw_readl(sfrbase + REG_CAPA1_SBB_VPN); sbb_link = __raw_readl(sfrbase + REG_CAPA1_SBB_LINK); pr_crit("[%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n", i, sbb_vpn, sbb_link, __raw_readl(sfrbase + REG_CAPA1_SBB_ATTR)); sysmmu_sbb_compare(sbb_vpn, sbb_link, pgtable); cnt++; } } if (!cnt) pr_auto(ASL4, ">> No Valid SBB Entries\n"); } static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "PTW ACCESS FAULT", "PAGE FAULT", "L1TLB MULTI-HIT FAULT", "ACCESS FAULT", "SECURITY FAULT", "UNKNOWN FAULT" }; static inline void dump_sysmmu_status(struct sysmmu_drvdata *drvdata, phys_addr_t pgtable) { int info; pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; phys_addr_t phys; void __iomem *sfrbase = drvdata->sfrbase; pgd = pgd_offset_k((unsigned long)sfrbase); if (!pgd) { pr_auto(ASL4, "Invalid virtual address %p\n", sfrbase); return; } pud = pud_offset(pgd, (unsigned long)sfrbase); if (!pud) { pr_auto(ASL4, "Invalid virtual address %p\n", sfrbase); return; } pmd = pmd_offset(pud, (unsigned long)sfrbase); if (!pmd) { pr_auto(ASL4, "Invalid virtual address %p\n", sfrbase); return; } pte = pte_offset_kernel(pmd, (unsigned long)sfrbase); if (!pte) { pr_auto(ASL4, "Invalid virtual address %p\n", sfrbase); return; } info = MMU_RAW_VER(__raw_readl(sfrbase + REG_MMU_VERSION)); phys = pte_pfn(*pte) << PAGE_SHIFT; pr_auto(ASL4, "ADDR: %pa(VA: %p), MMU_CTRL: %#010x, PT_BASE: %#010x\n", &phys, sfrbase, __raw_readl(sfrbase + REG_MMU_CTRL), __raw_readl(sfrbase + REG_PT_BASE_PPN)); pr_auto(ASL4, "VERSION %d.%d.%d, MMU_CFG: %#010x, MMU_STATUS: %#010x\n", MMU_MAJ_VER(info), MMU_MIN_VER(info), MMU_REV_VER(info), __raw_readl(sfrbase + REG_MMU_CFG), __raw_readl(sfrbase + REG_MMU_STATUS)); if (IS_TLB_WAY_TYPE(drvdata)) dump_sysmmu_tlb_way(drvdata); else if(IS_TLB_PORT_TYPE(drvdata)) dump_sysmmu_tlb_port(drvdata, pgtable); } static inline void show_secure_fault_information(struct sysmmu_drvdata *drvdata, int flags, unsigned long fault_addr) { unsigned int info; phys_addr_t pgtable; int fault_id = SYSMMU_FAULT_ID(flags); unsigned int sfrbase = drvdata->securebase; const char *port_name = NULL; #ifdef CONFIG_SEC_DEBUG_EXTRA_INFO char temp_buf[SZ_128]; #endif pgtable = __secure_info_read(sfrbase + REG_PT_BASE_PPN); pgtable <<= PAGE_SHIFT; info = __secure_info_read(sfrbase + REG_FAULT_TRANS_INFO); of_property_read_string(drvdata->sysmmu->of_node, "port-name", &port_name); pr_auto_once(4); pr_auto(ASL4, "----------------------------------------------------------\n"); pr_auto(ASL4, "From [%s], SysMMU %s %s at %#010lx (page table @ %pa)\n", port_name ? port_name : dev_name(drvdata->sysmmu), (flags & IOMMU_FAULT_WRITE) ? "WRITE" : "READ", sysmmu_fault_name[fault_id], fault_addr, &pgtable); #ifdef CONFIG_SEC_DEBUG_EXTRA_INFO snprintf(temp_buf, SZ_128, "%s %s %s at %#010lx (%pa)", port_name ? port_name : dev_name(drvdata->sysmmu), (flags & IOMMU_FAULT_WRITE) ? "WRITE" : "READ", sysmmu_fault_name[fault_id], fault_addr, &pgtable); sec_debug_set_extra_info_sysmmu(temp_buf); #endif if (fault_id == SYSMMU_FAULT_UNKNOWN) { pr_auto(ASL4, "The fault is not caused by this System MMU.\n"); pr_auto(ASL4, "Please check IRQ and SFR base address.\n"); goto finish; } pr_auto(ASL4, "AxID: %#x, AxLEN: %#x\n", info & 0xFFFF, (info >> 16) & 0xF); if (fault_id == SYSMMU_FAULT_PTW_ACCESS) pr_auto(ASL4, "System MMU has failed to access page table\n"); if (!pfn_valid(pgtable >> PAGE_SHIFT)) { pr_auto(ASL4, "Page table base is not in a valid memory region\n"); } else { sysmmu_pte_t *ent; ent = section_entry(phys_to_virt(pgtable), fault_addr); pr_auto(ASL4, "Lv1 entry: %#010x\n", *ent); if (lv1ent_page(ent)) { ent = page_entry(ent, fault_addr); pr_auto(ASL4, "Lv2 entry: %#010x\n", *ent); } } info = MMU_RAW_VER(__secure_info_read(sfrbase + REG_MMU_VERSION)); pr_auto(ASL4, "ADDR: %#x, MMU_CTRL: %#010x, PT_BASE: %#010x\n", sfrbase, __secure_info_read(sfrbase + REG_MMU_CTRL), __secure_info_read(sfrbase + REG_PT_BASE_PPN)); pr_auto(ASL4, "VERSION %d.%d.%d, MMU_CFG: %#010x, MMU_STATUS: %#010x\n", MMU_MAJ_VER(info), MMU_MIN_VER(info), MMU_REV_VER(info), __secure_info_read(sfrbase + REG_MMU_CFG), __secure_info_read(sfrbase + REG_MMU_STATUS)); finish: pr_auto(ASL4, "----------------------------------------------------------\n"); pr_auto_disable(4); } static inline int show_fault_information(struct sysmmu_drvdata *drvdata, int flags, unsigned long fault_addr) { unsigned int info; phys_addr_t pgtable; int fault_id = SYSMMU_FAULT_ID(flags); const char *port_name = NULL; static int ptw_count = 0; #ifdef CONFIG_SEC_DEBUG_EXTRA_INFO char temp_buf[SZ_128]; #endif pgtable = __raw_readl(drvdata->sfrbase + REG_PT_BASE_PPN); pgtable <<= PAGE_SHIFT; info = __raw_readl(drvdata->sfrbase + REG_FAULT_TRANS_INFO); of_property_read_string(drvdata->sysmmu->of_node, "port-name", &port_name); pr_auto_once(4); pr_auto(ASL4, "----------------------------------------------------------\n"); pr_auto(ASL4, "From [%s], SysMMU %s %s at %#010lx (page table @ %pa)\n", port_name ? port_name : dev_name(drvdata->sysmmu), (flags & IOMMU_FAULT_WRITE) ? "WRITE" : "READ", sysmmu_fault_name[fault_id], fault_addr, &pgtable); #ifdef CONFIG_SEC_DEBUG_EXTRA_INFO snprintf(temp_buf, SZ_128, "%s %s %s at %#010lx (%pa)", port_name ? port_name : dev_name(drvdata->sysmmu), (flags & IOMMU_FAULT_WRITE) ? "WRITE" : "READ", sysmmu_fault_name[fault_id], fault_addr, &pgtable); sec_debug_set_extra_info_sysmmu(temp_buf); #endif if (fault_id == SYSMMU_FAULT_UNKNOWN) { pr_auto(ASL4, "The fault is not caused by this System MMU.\n"); pr_auto(ASL4, "Please check IRQ and SFR base address.\n"); goto finish; } pr_auto(ASL4, "AxID: %#x, AxLEN: %#x\n", info & 0xFFFF, (info >> 16) & 0xF); if (pgtable != drvdata->pgtable) pr_auto(ASL4, "Page table base of driver: %pa\n", &drvdata->pgtable); if (!pfn_valid(pgtable >> PAGE_SHIFT)) { pr_auto(ASL4, "Page table base is not in a valid memory region\n"); pgtable = 0; } else { sysmmu_pte_t *ent; ent = section_entry(phys_to_virt(pgtable), fault_addr); pr_auto(ASL4, "Lv1 entry: %#010x\n", *ent); if (lv1ent_page(ent)) { ent = page_entry(ent, fault_addr); pr_auto(ASL4, "Lv2 entry: %#010x\n", *ent); } } if (fault_id == SYSMMU_FAULT_PTW_ACCESS) { ptw_count++; pr_auto(ASL4, "System MMU has failed to access page table, %d\n", ptw_count); pgtable = 0; if (ptw_count > 3) panic("Unrecoverable System MMU PTW fault"); writel(0x1, drvdata->sfrbase + REG_INT_CLEAR); return -EAGAIN; } dump_sysmmu_status(drvdata, pgtable); finish: pr_auto(ASL4, "----------------------------------------------------------\n"); pr_auto_disable(4); return 0; } static inline void __sysmmu_disable_nocount(struct sysmmu_drvdata *drvdata) { writel_relaxed(0, drvdata->sfrbase + REG_MMU_CFG); writel_relaxed(CTRL_BLOCK_DISABLE, drvdata->sfrbase + REG_MMU_CTRL); BUG_ON(readl_relaxed(drvdata->sfrbase + REG_MMU_CTRL) != CTRL_BLOCK_DISABLE); clk_disable(drvdata->clk); SYSMMU_EVENT_LOG_DISABLE(SYSMMU_DRVDATA_TO_LOG(drvdata)); } static inline void __sysmmu_set_public_way(struct sysmmu_drvdata *drvdata, unsigned int public_cfg) { u32 cfg = __raw_readl(drvdata->sfrbase + REG_PUBLIC_WAY_CFG); cfg &= ~MMU_PUBLIC_WAY_MASK; cfg |= public_cfg; writel_relaxed(cfg, drvdata->sfrbase + REG_PUBLIC_WAY_CFG); dev_dbg(drvdata->sysmmu, "public_cfg : %#x\n", cfg); } static inline void __sysmmu_set_private_way_id(struct sysmmu_drvdata *drvdata, unsigned int way_idx) { struct tlb_priv_id *priv_cfg = drvdata->tlb_props.way_props.priv_id_cfg; u32 cfg = __raw_readl(drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); cfg &= ~MMU_PRIVATE_WAY_MASK; cfg |= MMU_WAY_CFG_ID_MATCHING | MMU_WAY_CFG_PRIVATE_ENABLE | priv_cfg[way_idx].cfg; writel_relaxed(cfg, drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); writel_relaxed(priv_cfg[way_idx].id, drvdata->sfrbase + REG_PRIVATE_ID(way_idx)); dev_dbg(drvdata->sysmmu, "priv ID way[%d] cfg : %#x, id : %#x\n", way_idx, cfg, priv_cfg[way_idx].id); } static inline void __sysmmu_set_private_way_addr(struct sysmmu_drvdata *drvdata, unsigned int priv_addr_idx) { struct tlb_priv_addr *priv_cfg = drvdata->tlb_props.way_props.priv_addr_cfg; unsigned int way_idx = drvdata->tlb_props.way_props.priv_id_cnt + priv_addr_idx; u32 cfg = __raw_readl(drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); cfg &= ~MMU_PRIVATE_WAY_MASK; cfg |= MMU_WAY_CFG_ADDR_MATCHING | MMU_WAY_CFG_PRIVATE_ENABLE | priv_cfg[priv_addr_idx].cfg; writel_relaxed(cfg, drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); dev_dbg(drvdata->sysmmu, "priv ADDR way[%d] cfg : %#x\n", way_idx, cfg); } static inline void __sysmmu_set_tlb_way_type(struct sysmmu_drvdata *drvdata) { 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; unsigned int i; int priv_id_cnt = tlb_props->way_props.priv_id_cnt; int priv_addr_cnt = tlb_props->way_props.priv_addr_cnt; if (tlb_props->flags & TLB_WAY_PUBLIC) __sysmmu_set_public_way(drvdata, tlb_props->way_props.public_cfg); if (tlb_props->flags & TLB_WAY_PRIVATE_ID) { for (i = 0; i < priv_id_cnt && set_cnt < tlb_way_num; i++, set_cnt++) __sysmmu_set_private_way_id(drvdata, i); } if (tlb_props->flags & TLB_WAY_PRIVATE_ADDR) { for (i = 0; i < priv_addr_cnt && set_cnt < tlb_way_num; i++, set_cnt++) __sysmmu_set_private_way_addr(drvdata, i); } if (priv_id_cnt + priv_addr_cnt > tlb_way_num) { dev_warn(drvdata->sysmmu, "Too many values than TLB way count %d," " so ignored!\n", tlb_way_num); dev_warn(drvdata->sysmmu, "Number of private way id/addr = %d/%d\n", priv_id_cnt, priv_addr_cnt); } } static inline void __sysmmu_set_tlb_port(struct sysmmu_drvdata *drvdata, unsigned int port_idx) { struct tlb_port_cfg *port_cfg = drvdata->tlb_props.port_props.port_cfg; writel_relaxed(MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg), drvdata->sfrbase + REG_MMU_TLB_CFG(port_idx)); /* port_idx 0 is default port. */ if (port_idx == 0) { dev_dbg(drvdata->sysmmu, "port[%d] cfg : %#x for common\n", port_idx, MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg)); return; } writel_relaxed(MMU_TLB_MATCH_CFG_MASK(port_cfg[port_idx].cfg), drvdata->sfrbase + REG_MMU_TLB_MATCH_CFG(port_idx)); writel_relaxed(port_cfg[port_idx].id, drvdata->sfrbase + REG_MMU_TLB_MATCH_ID(port_idx)); dev_dbg(drvdata->sysmmu, "port[%d] cfg : %#x, match : %#x, id : %#x\n", port_idx, MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg), MMU_TLB_MATCH_CFG_MASK(port_cfg[port_idx].cfg), port_cfg[port_idx].id); } static inline void __sysmmu_set_tlb_port_type(struct sysmmu_drvdata *drvdata) { u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA1_V7); u32 tlb_num = MMU_CAPA1_NUM_TLB(cfg); struct tlb_props *tlb_props = &drvdata->tlb_props; unsigned int i; int port_id_cnt = tlb_props->port_props.port_id_cnt; int slot_cnt = tlb_props->port_props.slot_cnt; if (port_id_cnt > tlb_num) { dev_warn(drvdata->sysmmu, "Too many values %d than TLB count %d," " so ignored!\n", port_id_cnt, tlb_num); port_id_cnt = tlb_num; } for (i = 0; i < port_id_cnt; i++) __sysmmu_set_tlb_port(drvdata, i); for (i = 0; i < slot_cnt; i++) writel_relaxed(tlb_props->port_props.slot_cfg[i], drvdata->sfrbase + REG_SLOT_RSV(i)); } static inline void __sysmmu_init_config(struct sysmmu_drvdata *drvdata) { unsigned long cfg = 0; if (IS_TLB_WAY_TYPE(drvdata)) __sysmmu_set_tlb_way_type(drvdata); else if (IS_TLB_PORT_TYPE(drvdata)) __sysmmu_set_tlb_port_type(drvdata); if (drvdata->qos != DEFAULT_QOS_VALUE) cfg |= CFG_QOS_OVRRIDE | CFG_QOS(drvdata->qos); cfg |= __raw_readl(drvdata->sfrbase + REG_MMU_CFG) & ~CFG_MASK; writel_relaxed(cfg, drvdata->sfrbase + REG_MMU_CFG); } static inline void __sysmmu_enable_nocount(struct sysmmu_drvdata *drvdata) { clk_enable(drvdata->clk); __sysmmu_init_config(drvdata); __sysmmu_set_ptbase(drvdata, drvdata->pgtable / PAGE_SIZE); writel(CTRL_ENABLE, drvdata->sfrbase + REG_MMU_CTRL); SYSMMU_EVENT_LOG_ENABLE(SYSMMU_DRVDATA_TO_LOG(drvdata)); } static inline u32 __sysmmu_get_intr_status(struct sysmmu_drvdata *drvdata, bool is_secure) { if (is_secure) return __secure_info_read(drvdata->securebase + REG_INT_STATUS); else return __raw_readl(drvdata->sfrbase + REG_INT_STATUS); } static inline u32 __sysmmu_get_fault_address(struct sysmmu_drvdata *drvdata, bool is_secure) { if (is_secure) return __secure_info_read(drvdata->securebase + REG_FAULT_ADDR); else return __raw_readl(drvdata->sfrbase + REG_FAULT_ADDR); } static inline u32 __sysmmu_get_fault_trans_info(struct sysmmu_drvdata *drvdata, bool is_secure) { if (is_secure) return __secure_info_read( drvdata->securebase + REG_FAULT_TRANS_INFO); else return __raw_readl(drvdata->sfrbase + REG_FAULT_TRANS_INFO); } static inline u32 __sysmmu_get_hw_version(struct sysmmu_drvdata *data) { return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); } static inline bool __sysmmu_has_capa1(struct sysmmu_drvdata *data) { return MMU_CAPA1_EXIST(__raw_readl(data->sfrbase + REG_MMU_CAPA0_V7)); } static inline u32 __sysmmu_get_capa_type(struct sysmmu_drvdata *data) { return MMU_CAPA1_TYPE(__raw_readl(data->sfrbase + REG_MMU_CAPA1_V7)); }