11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b17336c5SHonghui Zhang /* 3d4cf5bbdSPaul Gortmaker * IOMMU API for MTK architected m4u v1 implementations 4d4cf5bbdSPaul Gortmaker * 5b17336c5SHonghui Zhang * Copyright (c) 2015-2016 MediaTek Inc. 6b17336c5SHonghui Zhang * Author: Honghui Zhang <honghui.zhang@mediatek.com> 7b17336c5SHonghui Zhang * 8b17336c5SHonghui Zhang * Based on driver/iommu/mtk_iommu.c 9b17336c5SHonghui Zhang */ 1057c8a661SMike Rapoport #include <linux/memblock.h> 11b17336c5SHonghui Zhang #include <linux/bug.h> 12b17336c5SHonghui Zhang #include <linux/clk.h> 13b17336c5SHonghui Zhang #include <linux/component.h> 14b17336c5SHonghui Zhang #include <linux/device.h> 15745b6e74SArnd Bergmann #include <linux/dma-mapping.h> 16b17336c5SHonghui Zhang #include <linux/err.h> 17b17336c5SHonghui Zhang #include <linux/interrupt.h> 18b17336c5SHonghui Zhang #include <linux/io.h> 19b17336c5SHonghui Zhang #include <linux/iommu.h> 20b17336c5SHonghui Zhang #include <linux/iopoll.h> 21b17336c5SHonghui Zhang #include <linux/list.h> 228de000cfSYong Wu #include <linux/module.h> 23b17336c5SHonghui Zhang #include <linux/of_address.h> 24b17336c5SHonghui Zhang #include <linux/of_irq.h> 25b17336c5SHonghui Zhang #include <linux/of_platform.h> 26b17336c5SHonghui Zhang #include <linux/platform_device.h> 27b17336c5SHonghui Zhang #include <linux/slab.h> 28b17336c5SHonghui Zhang #include <linux/spinlock.h> 29b17336c5SHonghui Zhang #include <asm/barrier.h> 30b17336c5SHonghui Zhang #include <asm/dma-iommu.h> 31d4cf5bbdSPaul Gortmaker #include <linux/init.h> 32b17336c5SHonghui Zhang #include <dt-bindings/memory/mt2701-larb-port.h> 33b17336c5SHonghui Zhang #include <soc/mediatek/smi.h> 34b17336c5SHonghui Zhang #include "mtk_iommu.h" 35b17336c5SHonghui Zhang 36b17336c5SHonghui Zhang #define REG_MMU_PT_BASE_ADDR 0x000 37b17336c5SHonghui Zhang 38b17336c5SHonghui Zhang #define F_ALL_INVLD 0x2 39b17336c5SHonghui Zhang #define F_MMU_INV_RANGE 0x1 40b17336c5SHonghui Zhang #define F_INVLD_EN0 BIT(0) 41b17336c5SHonghui Zhang #define F_INVLD_EN1 BIT(1) 42b17336c5SHonghui Zhang 43b17336c5SHonghui Zhang #define F_MMU_FAULT_VA_MSK 0xfffff000 44b17336c5SHonghui Zhang #define MTK_PROTECT_PA_ALIGN 128 45b17336c5SHonghui Zhang 46b17336c5SHonghui Zhang #define REG_MMU_CTRL_REG 0x210 47b17336c5SHonghui Zhang #define F_MMU_CTRL_COHERENT_EN BIT(8) 48b17336c5SHonghui Zhang #define REG_MMU_IVRP_PADDR 0x214 49b17336c5SHonghui Zhang #define REG_MMU_INT_CONTROL 0x220 50b17336c5SHonghui Zhang #define F_INT_TRANSLATION_FAULT BIT(0) 51b17336c5SHonghui Zhang #define F_INT_MAIN_MULTI_HIT_FAULT BIT(1) 52b17336c5SHonghui Zhang #define F_INT_INVALID_PA_FAULT BIT(2) 53b17336c5SHonghui Zhang #define F_INT_ENTRY_REPLACEMENT_FAULT BIT(3) 54b17336c5SHonghui Zhang #define F_INT_TABLE_WALK_FAULT BIT(4) 55b17336c5SHonghui Zhang #define F_INT_TLB_MISS_FAULT BIT(5) 56b17336c5SHonghui Zhang #define F_INT_PFH_DMA_FIFO_OVERFLOW BIT(6) 57b17336c5SHonghui Zhang #define F_INT_MISS_DMA_FIFO_OVERFLOW BIT(7) 58b17336c5SHonghui Zhang 59b17336c5SHonghui Zhang #define F_MMU_TF_PROTECT_SEL(prot) (((prot) & 0x3) << 5) 60b17336c5SHonghui Zhang #define F_INT_CLR_BIT BIT(12) 61b17336c5SHonghui Zhang 62b17336c5SHonghui Zhang #define REG_MMU_FAULT_ST 0x224 63b17336c5SHonghui Zhang #define REG_MMU_FAULT_VA 0x228 64b17336c5SHonghui Zhang #define REG_MMU_INVLD_PA 0x22C 65b17336c5SHonghui Zhang #define REG_MMU_INT_ID 0x388 66b17336c5SHonghui Zhang #define REG_MMU_INVALIDATE 0x5c0 67b17336c5SHonghui Zhang #define REG_MMU_INVLD_START_A 0x5c4 68b17336c5SHonghui Zhang #define REG_MMU_INVLD_END_A 0x5c8 69b17336c5SHonghui Zhang 70b17336c5SHonghui Zhang #define REG_MMU_INV_SEL 0x5d8 71b17336c5SHonghui Zhang #define REG_MMU_STANDARD_AXI_MODE 0x5e8 72b17336c5SHonghui Zhang 73b17336c5SHonghui Zhang #define REG_MMU_DCM 0x5f0 74b17336c5SHonghui Zhang #define F_MMU_DCM_ON BIT(1) 75b17336c5SHonghui Zhang #define REG_MMU_CPE_DONE 0x60c 76b17336c5SHonghui Zhang #define F_DESC_VALID 0x2 77b17336c5SHonghui Zhang #define F_DESC_NONSEC BIT(3) 78b17336c5SHonghui Zhang #define MT2701_M4U_TF_LARB(TF) (6 - (((TF) >> 13) & 0x7)) 79b17336c5SHonghui Zhang #define MT2701_M4U_TF_PORT(TF) (((TF) >> 8) & 0xF) 80b17336c5SHonghui Zhang /* MTK generation one iommu HW only support 4K size mapping */ 81b17336c5SHonghui Zhang #define MT2701_IOMMU_PAGE_SHIFT 12 82b17336c5SHonghui Zhang #define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT) 83b17336c5SHonghui Zhang 84b17336c5SHonghui Zhang /* 85b17336c5SHonghui Zhang * MTK m4u support 4GB iova address space, and only support 4K page 86b17336c5SHonghui Zhang * mapping. So the pagetable size should be exactly as 4M. 87b17336c5SHonghui Zhang */ 88b17336c5SHonghui Zhang #define M2701_IOMMU_PGT_SIZE SZ_4M 89b17336c5SHonghui Zhang 90b17336c5SHonghui Zhang struct mtk_iommu_domain { 91b17336c5SHonghui Zhang spinlock_t pgtlock; /* lock for page table */ 92b17336c5SHonghui Zhang struct iommu_domain domain; 93b17336c5SHonghui Zhang u32 *pgt_va; 94b17336c5SHonghui Zhang dma_addr_t pgt_pa; 95b17336c5SHonghui Zhang struct mtk_iommu_data *data; 96b17336c5SHonghui Zhang }; 97b17336c5SHonghui Zhang 98b17336c5SHonghui Zhang static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom) 99b17336c5SHonghui Zhang { 100b17336c5SHonghui Zhang return container_of(dom, struct mtk_iommu_domain, domain); 101b17336c5SHonghui Zhang } 102b17336c5SHonghui Zhang 103b17336c5SHonghui Zhang static const int mt2701_m4u_in_larb[] = { 104b17336c5SHonghui Zhang LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, 105b17336c5SHonghui Zhang LARB2_PORT_OFFSET, LARB3_PORT_OFFSET 106b17336c5SHonghui Zhang }; 107b17336c5SHonghui Zhang 108b17336c5SHonghui Zhang static inline int mt2701_m4u_to_larb(int id) 109b17336c5SHonghui Zhang { 110b17336c5SHonghui Zhang int i; 111b17336c5SHonghui Zhang 112b17336c5SHonghui Zhang for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--) 113b17336c5SHonghui Zhang if ((id) >= mt2701_m4u_in_larb[i]) 114b17336c5SHonghui Zhang return i; 115b17336c5SHonghui Zhang 116b17336c5SHonghui Zhang return 0; 117b17336c5SHonghui Zhang } 118b17336c5SHonghui Zhang 119b17336c5SHonghui Zhang static inline int mt2701_m4u_to_port(int id) 120b17336c5SHonghui Zhang { 121b17336c5SHonghui Zhang int larb = mt2701_m4u_to_larb(id); 122b17336c5SHonghui Zhang 123b17336c5SHonghui Zhang return id - mt2701_m4u_in_larb[larb]; 124b17336c5SHonghui Zhang } 125b17336c5SHonghui Zhang 126b17336c5SHonghui Zhang static void mtk_iommu_tlb_flush_all(struct mtk_iommu_data *data) 127b17336c5SHonghui Zhang { 128b17336c5SHonghui Zhang writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, 129b17336c5SHonghui Zhang data->base + REG_MMU_INV_SEL); 130b17336c5SHonghui Zhang writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); 131b17336c5SHonghui Zhang wmb(); /* Make sure the tlb flush all done */ 132b17336c5SHonghui Zhang } 133b17336c5SHonghui Zhang 134b17336c5SHonghui Zhang static void mtk_iommu_tlb_flush_range(struct mtk_iommu_data *data, 135b17336c5SHonghui Zhang unsigned long iova, size_t size) 136b17336c5SHonghui Zhang { 137b17336c5SHonghui Zhang int ret; 138b17336c5SHonghui Zhang u32 tmp; 139b17336c5SHonghui Zhang 140b17336c5SHonghui Zhang writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, 141b17336c5SHonghui Zhang data->base + REG_MMU_INV_SEL); 142b17336c5SHonghui Zhang writel_relaxed(iova & F_MMU_FAULT_VA_MSK, 143b17336c5SHonghui Zhang data->base + REG_MMU_INVLD_START_A); 144b17336c5SHonghui Zhang writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK, 145b17336c5SHonghui Zhang data->base + REG_MMU_INVLD_END_A); 146b17336c5SHonghui Zhang writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); 147b17336c5SHonghui Zhang 148b17336c5SHonghui Zhang ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, 149b17336c5SHonghui Zhang tmp, tmp != 0, 10, 100000); 150b17336c5SHonghui Zhang if (ret) { 151b17336c5SHonghui Zhang dev_warn(data->dev, 152b17336c5SHonghui Zhang "Partial TLB flush timed out, falling back to full flush\n"); 153b17336c5SHonghui Zhang mtk_iommu_tlb_flush_all(data); 154b17336c5SHonghui Zhang } 155b17336c5SHonghui Zhang /* Clear the CPE status */ 156b17336c5SHonghui Zhang writel_relaxed(0, data->base + REG_MMU_CPE_DONE); 157b17336c5SHonghui Zhang } 158b17336c5SHonghui Zhang 159b17336c5SHonghui Zhang static irqreturn_t mtk_iommu_isr(int irq, void *dev_id) 160b17336c5SHonghui Zhang { 161b17336c5SHonghui Zhang struct mtk_iommu_data *data = dev_id; 162b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = data->m4u_dom; 163b17336c5SHonghui Zhang u32 int_state, regval, fault_iova, fault_pa; 164b17336c5SHonghui Zhang unsigned int fault_larb, fault_port; 165b17336c5SHonghui Zhang 166b17336c5SHonghui Zhang /* Read error information from registers */ 167b17336c5SHonghui Zhang int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST); 168b17336c5SHonghui Zhang fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA); 169b17336c5SHonghui Zhang 170b17336c5SHonghui Zhang fault_iova &= F_MMU_FAULT_VA_MSK; 171b17336c5SHonghui Zhang fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA); 172b17336c5SHonghui Zhang regval = readl_relaxed(data->base + REG_MMU_INT_ID); 173b17336c5SHonghui Zhang fault_larb = MT2701_M4U_TF_LARB(regval); 174b17336c5SHonghui Zhang fault_port = MT2701_M4U_TF_PORT(regval); 175b17336c5SHonghui Zhang 176b17336c5SHonghui Zhang /* 177b17336c5SHonghui Zhang * MTK v1 iommu HW could not determine whether the fault is read or 178b17336c5SHonghui Zhang * write fault, report as read fault. 179b17336c5SHonghui Zhang */ 180b17336c5SHonghui Zhang if (report_iommu_fault(&dom->domain, data->dev, fault_iova, 181b17336c5SHonghui Zhang IOMMU_FAULT_READ)) 182b17336c5SHonghui Zhang dev_err_ratelimited(data->dev, 183b17336c5SHonghui Zhang "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n", 184b17336c5SHonghui Zhang int_state, fault_iova, fault_pa, 185b17336c5SHonghui Zhang fault_larb, fault_port); 186b17336c5SHonghui Zhang 187b17336c5SHonghui Zhang /* Interrupt clear */ 188b17336c5SHonghui Zhang regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL); 189b17336c5SHonghui Zhang regval |= F_INT_CLR_BIT; 190b17336c5SHonghui Zhang writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); 191b17336c5SHonghui Zhang 192b17336c5SHonghui Zhang mtk_iommu_tlb_flush_all(data); 193b17336c5SHonghui Zhang 194b17336c5SHonghui Zhang return IRQ_HANDLED; 195b17336c5SHonghui Zhang } 196b17336c5SHonghui Zhang 197b17336c5SHonghui Zhang static void mtk_iommu_config(struct mtk_iommu_data *data, 198b17336c5SHonghui Zhang struct device *dev, bool enable) 199b17336c5SHonghui Zhang { 200b17336c5SHonghui Zhang struct mtk_smi_larb_iommu *larb_mmu; 201b17336c5SHonghui Zhang unsigned int larbid, portid; 202a9bf2eecSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 20384672f19SRobin Murphy int i; 204b17336c5SHonghui Zhang 20584672f19SRobin Murphy for (i = 0; i < fwspec->num_ids; ++i) { 20684672f19SRobin Murphy larbid = mt2701_m4u_to_larb(fwspec->ids[i]); 20784672f19SRobin Murphy portid = mt2701_m4u_to_port(fwspec->ids[i]); 2081ee9feb2SYong Wu larb_mmu = &data->larb_imu[larbid]; 209b17336c5SHonghui Zhang 210b17336c5SHonghui Zhang dev_dbg(dev, "%s iommu port: %d\n", 211b17336c5SHonghui Zhang enable ? "enable" : "disable", portid); 212b17336c5SHonghui Zhang 213b17336c5SHonghui Zhang if (enable) 214b17336c5SHonghui Zhang larb_mmu->mmu |= MTK_SMI_MMU_EN(portid); 215b17336c5SHonghui Zhang else 216b17336c5SHonghui Zhang larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid); 217b17336c5SHonghui Zhang } 218b17336c5SHonghui Zhang } 219b17336c5SHonghui Zhang 220b17336c5SHonghui Zhang static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data) 221b17336c5SHonghui Zhang { 222b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = data->m4u_dom; 223b17336c5SHonghui Zhang 224b17336c5SHonghui Zhang spin_lock_init(&dom->pgtlock); 225b17336c5SHonghui Zhang 226750afb08SLuis Chamberlain dom->pgt_va = dma_alloc_coherent(data->dev, M2701_IOMMU_PGT_SIZE, 227b17336c5SHonghui Zhang &dom->pgt_pa, GFP_KERNEL); 228b17336c5SHonghui Zhang if (!dom->pgt_va) 229b17336c5SHonghui Zhang return -ENOMEM; 230b17336c5SHonghui Zhang 231b17336c5SHonghui Zhang writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR); 232b17336c5SHonghui Zhang 233b17336c5SHonghui Zhang dom->data = data; 234b17336c5SHonghui Zhang 235b17336c5SHonghui Zhang return 0; 236b17336c5SHonghui Zhang } 237b17336c5SHonghui Zhang 238b17336c5SHonghui Zhang static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type) 239b17336c5SHonghui Zhang { 240b17336c5SHonghui Zhang struct mtk_iommu_domain *dom; 241b17336c5SHonghui Zhang 242b17336c5SHonghui Zhang if (type != IOMMU_DOMAIN_UNMANAGED) 243b17336c5SHonghui Zhang return NULL; 244b17336c5SHonghui Zhang 245b17336c5SHonghui Zhang dom = kzalloc(sizeof(*dom), GFP_KERNEL); 246b17336c5SHonghui Zhang if (!dom) 247b17336c5SHonghui Zhang return NULL; 248b17336c5SHonghui Zhang 249b17336c5SHonghui Zhang return &dom->domain; 250b17336c5SHonghui Zhang } 251b17336c5SHonghui Zhang 252b17336c5SHonghui Zhang static void mtk_iommu_domain_free(struct iommu_domain *domain) 253b17336c5SHonghui Zhang { 254b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = to_mtk_domain(domain); 255b17336c5SHonghui Zhang struct mtk_iommu_data *data = dom->data; 256b17336c5SHonghui Zhang 257b17336c5SHonghui Zhang dma_free_coherent(data->dev, M2701_IOMMU_PGT_SIZE, 258b17336c5SHonghui Zhang dom->pgt_va, dom->pgt_pa); 259b17336c5SHonghui Zhang kfree(to_mtk_domain(domain)); 260b17336c5SHonghui Zhang } 261b17336c5SHonghui Zhang 262b17336c5SHonghui Zhang static int mtk_iommu_attach_device(struct iommu_domain *domain, 263b17336c5SHonghui Zhang struct device *dev) 264b17336c5SHonghui Zhang { 2653524b559SJoerg Roedel struct mtk_iommu_data *data = dev_iommu_priv_get(dev); 266b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = to_mtk_domain(domain); 2678bbe13f5SYong Wu struct dma_iommu_mapping *mtk_mapping; 268b17336c5SHonghui Zhang int ret; 269b17336c5SHonghui Zhang 2708bbe13f5SYong Wu /* Only allow the domain created internally. */ 27158960172SJoerg Roedel mtk_mapping = data->mapping; 2728bbe13f5SYong Wu if (mtk_mapping->domain != domain) 2738bbe13f5SYong Wu return 0; 274b17336c5SHonghui Zhang 275b17336c5SHonghui Zhang if (!data->m4u_dom) { 276b17336c5SHonghui Zhang data->m4u_dom = dom; 277b17336c5SHonghui Zhang ret = mtk_iommu_domain_finalise(data); 278b17336c5SHonghui Zhang if (ret) { 279b17336c5SHonghui Zhang data->m4u_dom = NULL; 280b17336c5SHonghui Zhang return ret; 281b17336c5SHonghui Zhang } 282b17336c5SHonghui Zhang } 283b17336c5SHonghui Zhang 284b17336c5SHonghui Zhang mtk_iommu_config(data, dev, true); 285b17336c5SHonghui Zhang return 0; 286b17336c5SHonghui Zhang } 287b17336c5SHonghui Zhang 288b17336c5SHonghui Zhang static void mtk_iommu_detach_device(struct iommu_domain *domain, 289b17336c5SHonghui Zhang struct device *dev) 290b17336c5SHonghui Zhang { 2913524b559SJoerg Roedel struct mtk_iommu_data *data = dev_iommu_priv_get(dev); 292b17336c5SHonghui Zhang 293b17336c5SHonghui Zhang mtk_iommu_config(data, dev, false); 294b17336c5SHonghui Zhang } 295b17336c5SHonghui Zhang 296b17336c5SHonghui Zhang static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, 297781ca2deSTom Murphy phys_addr_t paddr, size_t size, int prot, gfp_t gfp) 298b17336c5SHonghui Zhang { 299b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = to_mtk_domain(domain); 300b17336c5SHonghui Zhang unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT; 301b17336c5SHonghui Zhang unsigned long flags; 302b17336c5SHonghui Zhang unsigned int i; 303b17336c5SHonghui Zhang u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT); 304b17336c5SHonghui Zhang u32 pabase = (u32)paddr; 305b17336c5SHonghui Zhang int map_size = 0; 306b17336c5SHonghui Zhang 307b17336c5SHonghui Zhang spin_lock_irqsave(&dom->pgtlock, flags); 308b17336c5SHonghui Zhang for (i = 0; i < page_num; i++) { 309b17336c5SHonghui Zhang if (pgt_base_iova[i]) { 310b17336c5SHonghui Zhang memset(pgt_base_iova, 0, i * sizeof(u32)); 311b17336c5SHonghui Zhang break; 312b17336c5SHonghui Zhang } 313b17336c5SHonghui Zhang pgt_base_iova[i] = pabase | F_DESC_VALID | F_DESC_NONSEC; 314b17336c5SHonghui Zhang pabase += MT2701_IOMMU_PAGE_SIZE; 315b17336c5SHonghui Zhang map_size += MT2701_IOMMU_PAGE_SIZE; 316b17336c5SHonghui Zhang } 317b17336c5SHonghui Zhang 318b17336c5SHonghui Zhang spin_unlock_irqrestore(&dom->pgtlock, flags); 319b17336c5SHonghui Zhang 320b17336c5SHonghui Zhang mtk_iommu_tlb_flush_range(dom->data, iova, size); 321b17336c5SHonghui Zhang 322b17336c5SHonghui Zhang return map_size == size ? 0 : -EEXIST; 323b17336c5SHonghui Zhang } 324b17336c5SHonghui Zhang 325b17336c5SHonghui Zhang static size_t mtk_iommu_unmap(struct iommu_domain *domain, 32656f8af5eSWill Deacon unsigned long iova, size_t size, 32756f8af5eSWill Deacon struct iommu_iotlb_gather *gather) 328b17336c5SHonghui Zhang { 329b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = to_mtk_domain(domain); 330b17336c5SHonghui Zhang unsigned long flags; 331b17336c5SHonghui Zhang u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT); 332b17336c5SHonghui Zhang unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT; 333b17336c5SHonghui Zhang 334b17336c5SHonghui Zhang spin_lock_irqsave(&dom->pgtlock, flags); 335b17336c5SHonghui Zhang memset(pgt_base_iova, 0, page_num * sizeof(u32)); 336b17336c5SHonghui Zhang spin_unlock_irqrestore(&dom->pgtlock, flags); 337b17336c5SHonghui Zhang 338b17336c5SHonghui Zhang mtk_iommu_tlb_flush_range(dom->data, iova, size); 339b17336c5SHonghui Zhang 340b17336c5SHonghui Zhang return size; 341b17336c5SHonghui Zhang } 342b17336c5SHonghui Zhang 343b17336c5SHonghui Zhang static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, 344b17336c5SHonghui Zhang dma_addr_t iova) 345b17336c5SHonghui Zhang { 346b17336c5SHonghui Zhang struct mtk_iommu_domain *dom = to_mtk_domain(domain); 347b17336c5SHonghui Zhang unsigned long flags; 348b17336c5SHonghui Zhang phys_addr_t pa; 349b17336c5SHonghui Zhang 350b17336c5SHonghui Zhang spin_lock_irqsave(&dom->pgtlock, flags); 351b17336c5SHonghui Zhang pa = *(dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT)); 352b17336c5SHonghui Zhang pa = pa & (~(MT2701_IOMMU_PAGE_SIZE - 1)); 353b17336c5SHonghui Zhang spin_unlock_irqrestore(&dom->pgtlock, flags); 354b17336c5SHonghui Zhang 355b17336c5SHonghui Zhang return pa; 356b17336c5SHonghui Zhang } 357b17336c5SHonghui Zhang 358b65f5016SArvind Yadav static const struct iommu_ops mtk_iommu_ops; 35984672f19SRobin Murphy 360b17336c5SHonghui Zhang /* 361b17336c5SHonghui Zhang * MTK generation one iommu HW only support one iommu domain, and all the client 362b17336c5SHonghui Zhang * sharing the same iova address space. 363b17336c5SHonghui Zhang */ 364b17336c5SHonghui Zhang static int mtk_iommu_create_mapping(struct device *dev, 365b17336c5SHonghui Zhang struct of_phandle_args *args) 366b17336c5SHonghui Zhang { 367a9bf2eecSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 36884672f19SRobin Murphy struct mtk_iommu_data *data; 369b17336c5SHonghui Zhang struct platform_device *m4updev; 370b17336c5SHonghui Zhang struct dma_iommu_mapping *mtk_mapping; 371b17336c5SHonghui Zhang int ret; 372b17336c5SHonghui Zhang 373b17336c5SHonghui Zhang if (args->args_count != 1) { 374b17336c5SHonghui Zhang dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n", 375b17336c5SHonghui Zhang args->args_count); 376b17336c5SHonghui Zhang return -EINVAL; 377b17336c5SHonghui Zhang } 378b17336c5SHonghui Zhang 379a9bf2eecSJoerg Roedel if (!fwspec) { 38084672f19SRobin Murphy ret = iommu_fwspec_init(dev, &args->np->fwnode, &mtk_iommu_ops); 38184672f19SRobin Murphy if (ret) 38284672f19SRobin Murphy return ret; 383a9bf2eecSJoerg Roedel fwspec = dev_iommu_fwspec_get(dev); 384a9bf2eecSJoerg Roedel } else if (dev_iommu_fwspec_get(dev)->ops != &mtk_iommu_ops) { 38584672f19SRobin Murphy return -EINVAL; 38684672f19SRobin Murphy } 38784672f19SRobin Murphy 3883524b559SJoerg Roedel if (!dev_iommu_priv_get(dev)) { 389b17336c5SHonghui Zhang /* Get the m4u device */ 390b17336c5SHonghui Zhang m4updev = of_find_device_by_node(args->np); 391b17336c5SHonghui Zhang if (WARN_ON(!m4updev)) 392b17336c5SHonghui Zhang return -EINVAL; 393b17336c5SHonghui Zhang 3943524b559SJoerg Roedel dev_iommu_priv_set(dev, platform_get_drvdata(m4updev)); 395b17336c5SHonghui Zhang } 396b17336c5SHonghui Zhang 39784672f19SRobin Murphy ret = iommu_fwspec_add_ids(dev, args->args, 1); 39884672f19SRobin Murphy if (ret) 39984672f19SRobin Murphy return ret; 400b17336c5SHonghui Zhang 4013524b559SJoerg Roedel data = dev_iommu_priv_get(dev); 40258960172SJoerg Roedel mtk_mapping = data->mapping; 403b17336c5SHonghui Zhang if (!mtk_mapping) { 404b17336c5SHonghui Zhang /* MTK iommu support 4GB iova address space. */ 405b17336c5SHonghui Zhang mtk_mapping = arm_iommu_create_mapping(&platform_bus_type, 406b17336c5SHonghui Zhang 0, 1ULL << 32); 40784672f19SRobin Murphy if (IS_ERR(mtk_mapping)) 40884672f19SRobin Murphy return PTR_ERR(mtk_mapping); 40984672f19SRobin Murphy 41058960172SJoerg Roedel data->mapping = mtk_mapping; 411b17336c5SHonghui Zhang } 412b17336c5SHonghui Zhang 413b17336c5SHonghui Zhang return 0; 414b17336c5SHonghui Zhang } 415b17336c5SHonghui Zhang 4168bbe13f5SYong Wu static int mtk_iommu_def_domain_type(struct device *dev) 4178bbe13f5SYong Wu { 4188bbe13f5SYong Wu return IOMMU_DOMAIN_UNMANAGED; 4198bbe13f5SYong Wu } 4208bbe13f5SYong Wu 42157dbf81fSJoerg Roedel static struct iommu_device *mtk_iommu_probe_device(struct device *dev) 422b17336c5SHonghui Zhang { 423a9bf2eecSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 424b17336c5SHonghui Zhang struct of_phandle_args iommu_spec; 4256f66ea09SJoerg Roedel struct mtk_iommu_data *data; 426*635319a4SYong Wu int err, idx = 0, larbid, larbidx; 427*635319a4SYong Wu struct device_link *link; 428*635319a4SYong Wu struct device *larbdev; 429b17336c5SHonghui Zhang 430822a2ed8SYong Wu /* 431822a2ed8SYong Wu * In the deferred case, free the existed fwspec. 432822a2ed8SYong Wu * Always initialize the fwspec internally. 433822a2ed8SYong Wu */ 434822a2ed8SYong Wu if (fwspec) { 435822a2ed8SYong Wu iommu_fwspec_free(dev); 436822a2ed8SYong Wu fwspec = dev_iommu_fwspec_get(dev); 437822a2ed8SYong Wu } 438822a2ed8SYong Wu 439f90a9a85SYong Wu while (!of_parse_phandle_with_args(dev->of_node, "iommus", 440f90a9a85SYong Wu "#iommu-cells", 441f90a9a85SYong Wu idx, &iommu_spec)) { 442b17336c5SHonghui Zhang 443f90a9a85SYong Wu err = mtk_iommu_create_mapping(dev, &iommu_spec); 444f90a9a85SYong Wu of_node_put(iommu_spec.np); 445f90a9a85SYong Wu if (err) 446f90a9a85SYong Wu return ERR_PTR(err); 447da5d2748SJoerg Roedel 448da5d2748SJoerg Roedel /* dev->iommu_fwspec might have changed */ 449da5d2748SJoerg Roedel fwspec = dev_iommu_fwspec_get(dev); 450f90a9a85SYong Wu idx++; 451b17336c5SHonghui Zhang } 452b17336c5SHonghui Zhang 453a9bf2eecSJoerg Roedel if (!fwspec || fwspec->ops != &mtk_iommu_ops) 45457dbf81fSJoerg Roedel return ERR_PTR(-ENODEV); /* Not a iommu client device */ 455b17336c5SHonghui Zhang 45657dbf81fSJoerg Roedel data = dev_iommu_priv_get(dev); 457b17336c5SHonghui Zhang 458*635319a4SYong Wu /* Link the consumer device with the smi-larb device(supplier) */ 459*635319a4SYong Wu larbid = mt2701_m4u_to_larb(fwspec->ids[0]); 460*635319a4SYong Wu for (idx = 1; idx < fwspec->num_ids; idx++) { 461*635319a4SYong Wu larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]); 462*635319a4SYong Wu if (larbid != larbidx) { 463*635319a4SYong Wu dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n", 464*635319a4SYong Wu larbid, larbidx); 465*635319a4SYong Wu return ERR_PTR(-EINVAL); 466*635319a4SYong Wu } 467*635319a4SYong Wu } 468*635319a4SYong Wu 469*635319a4SYong Wu larbdev = data->larb_imu[larbid].dev; 470*635319a4SYong Wu link = device_link_add(dev, larbdev, 471*635319a4SYong Wu DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); 472*635319a4SYong Wu if (!link) 473*635319a4SYong Wu dev_err(dev, "Unable to link %s\n", dev_name(larbdev)); 474*635319a4SYong Wu 47557dbf81fSJoerg Roedel return &data->iommu; 47657dbf81fSJoerg Roedel } 47757dbf81fSJoerg Roedel 47857dbf81fSJoerg Roedel static void mtk_iommu_probe_finalize(struct device *dev) 47957dbf81fSJoerg Roedel { 48057dbf81fSJoerg Roedel struct dma_iommu_mapping *mtk_mapping; 48157dbf81fSJoerg Roedel struct mtk_iommu_data *data; 48257dbf81fSJoerg Roedel int err; 483f3e827d7SYong Wu 4843524b559SJoerg Roedel data = dev_iommu_priv_get(dev); 48558960172SJoerg Roedel mtk_mapping = data->mapping; 48657dbf81fSJoerg Roedel 487f3e827d7SYong Wu err = arm_iommu_attach_device(dev, mtk_mapping); 48857dbf81fSJoerg Roedel if (err) 48957dbf81fSJoerg Roedel dev_err(dev, "Can't create IOMMU mapping - DMA-OPS will not work\n"); 490f3e827d7SYong Wu } 491f3e827d7SYong Wu 49257dbf81fSJoerg Roedel static void mtk_iommu_release_device(struct device *dev) 493b17336c5SHonghui Zhang { 494a9bf2eecSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 495*635319a4SYong Wu struct mtk_iommu_data *data; 496*635319a4SYong Wu struct device *larbdev; 497*635319a4SYong Wu unsigned int larbid; 4986f66ea09SJoerg Roedel 499a9bf2eecSJoerg Roedel if (!fwspec || fwspec->ops != &mtk_iommu_ops) 500b17336c5SHonghui Zhang return; 501b17336c5SHonghui Zhang 502*635319a4SYong Wu data = dev_iommu_priv_get(dev); 503*635319a4SYong Wu larbid = mt2701_m4u_to_larb(fwspec->ids[0]); 504*635319a4SYong Wu larbdev = data->larb_imu[larbid].dev; 505*635319a4SYong Wu device_link_remove(dev, larbdev); 506*635319a4SYong Wu 50784672f19SRobin Murphy iommu_fwspec_free(dev); 508b17336c5SHonghui Zhang } 509b17336c5SHonghui Zhang 510b17336c5SHonghui Zhang static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) 511b17336c5SHonghui Zhang { 512b17336c5SHonghui Zhang u32 regval; 513b17336c5SHonghui Zhang int ret; 514b17336c5SHonghui Zhang 515b17336c5SHonghui Zhang ret = clk_prepare_enable(data->bclk); 516b17336c5SHonghui Zhang if (ret) { 517b17336c5SHonghui Zhang dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret); 518b17336c5SHonghui Zhang return ret; 519b17336c5SHonghui Zhang } 520b17336c5SHonghui Zhang 521b17336c5SHonghui Zhang regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2); 522b17336c5SHonghui Zhang writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); 523b17336c5SHonghui Zhang 524b17336c5SHonghui Zhang regval = F_INT_TRANSLATION_FAULT | 525b17336c5SHonghui Zhang F_INT_MAIN_MULTI_HIT_FAULT | 526b17336c5SHonghui Zhang F_INT_INVALID_PA_FAULT | 527b17336c5SHonghui Zhang F_INT_ENTRY_REPLACEMENT_FAULT | 528b17336c5SHonghui Zhang F_INT_TABLE_WALK_FAULT | 529b17336c5SHonghui Zhang F_INT_TLB_MISS_FAULT | 530b17336c5SHonghui Zhang F_INT_PFH_DMA_FIFO_OVERFLOW | 531b17336c5SHonghui Zhang F_INT_MISS_DMA_FIFO_OVERFLOW; 532b17336c5SHonghui Zhang writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); 533b17336c5SHonghui Zhang 534b17336c5SHonghui Zhang /* protect memory,hw will write here while translation fault */ 535b17336c5SHonghui Zhang writel_relaxed(data->protect_base, 536b17336c5SHonghui Zhang data->base + REG_MMU_IVRP_PADDR); 537b17336c5SHonghui Zhang 538b17336c5SHonghui Zhang writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM); 539b17336c5SHonghui Zhang 540b17336c5SHonghui Zhang if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, 541b17336c5SHonghui Zhang dev_name(data->dev), (void *)data)) { 542b17336c5SHonghui Zhang writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR); 543b17336c5SHonghui Zhang clk_disable_unprepare(data->bclk); 544b17336c5SHonghui Zhang dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq); 545b17336c5SHonghui Zhang return -ENODEV; 546b17336c5SHonghui Zhang } 547b17336c5SHonghui Zhang 548b17336c5SHonghui Zhang return 0; 549b17336c5SHonghui Zhang } 550b17336c5SHonghui Zhang 551b65f5016SArvind Yadav static const struct iommu_ops mtk_iommu_ops = { 552b17336c5SHonghui Zhang .domain_alloc = mtk_iommu_domain_alloc, 553b17336c5SHonghui Zhang .domain_free = mtk_iommu_domain_free, 554b17336c5SHonghui Zhang .attach_dev = mtk_iommu_attach_device, 555b17336c5SHonghui Zhang .detach_dev = mtk_iommu_detach_device, 556b17336c5SHonghui Zhang .map = mtk_iommu_map, 557b17336c5SHonghui Zhang .unmap = mtk_iommu_unmap, 558b17336c5SHonghui Zhang .iova_to_phys = mtk_iommu_iova_to_phys, 55957dbf81fSJoerg Roedel .probe_device = mtk_iommu_probe_device, 56057dbf81fSJoerg Roedel .probe_finalize = mtk_iommu_probe_finalize, 56157dbf81fSJoerg Roedel .release_device = mtk_iommu_release_device, 5628bbe13f5SYong Wu .def_domain_type = mtk_iommu_def_domain_type, 56357dbf81fSJoerg Roedel .device_group = generic_device_group, 564b17336c5SHonghui Zhang .pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT, 5658de000cfSYong Wu .owner = THIS_MODULE, 566b17336c5SHonghui Zhang }; 567b17336c5SHonghui Zhang 568b17336c5SHonghui Zhang static const struct of_device_id mtk_iommu_of_ids[] = { 569b17336c5SHonghui Zhang { .compatible = "mediatek,mt2701-m4u", }, 570b17336c5SHonghui Zhang {} 571b17336c5SHonghui Zhang }; 572b17336c5SHonghui Zhang 573b17336c5SHonghui Zhang static const struct component_master_ops mtk_iommu_com_ops = { 574b17336c5SHonghui Zhang .bind = mtk_iommu_bind, 575b17336c5SHonghui Zhang .unbind = mtk_iommu_unbind, 576b17336c5SHonghui Zhang }; 577b17336c5SHonghui Zhang 578b17336c5SHonghui Zhang static int mtk_iommu_probe(struct platform_device *pdev) 579b17336c5SHonghui Zhang { 580b17336c5SHonghui Zhang struct mtk_iommu_data *data; 581b17336c5SHonghui Zhang struct device *dev = &pdev->dev; 582b17336c5SHonghui Zhang struct resource *res; 583b17336c5SHonghui Zhang struct component_match *match = NULL; 584b17336c5SHonghui Zhang void *protect; 585f90a9a85SYong Wu int larb_nr, ret, i; 586b17336c5SHonghui Zhang 587b17336c5SHonghui Zhang data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 588b17336c5SHonghui Zhang if (!data) 589b17336c5SHonghui Zhang return -ENOMEM; 590b17336c5SHonghui Zhang 591b17336c5SHonghui Zhang data->dev = dev; 592b17336c5SHonghui Zhang 593b17336c5SHonghui Zhang /* Protect memory. HW will access here while translation fault.*/ 594b17336c5SHonghui Zhang protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, 595b17336c5SHonghui Zhang GFP_KERNEL | GFP_DMA); 596b17336c5SHonghui Zhang if (!protect) 597b17336c5SHonghui Zhang return -ENOMEM; 598b17336c5SHonghui Zhang data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN); 599b17336c5SHonghui Zhang 600b17336c5SHonghui Zhang res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 601b17336c5SHonghui Zhang data->base = devm_ioremap_resource(dev, res); 602b17336c5SHonghui Zhang if (IS_ERR(data->base)) 603b17336c5SHonghui Zhang return PTR_ERR(data->base); 604b17336c5SHonghui Zhang 605b17336c5SHonghui Zhang data->irq = platform_get_irq(pdev, 0); 606b17336c5SHonghui Zhang if (data->irq < 0) 607b17336c5SHonghui Zhang return data->irq; 608b17336c5SHonghui Zhang 609b17336c5SHonghui Zhang data->bclk = devm_clk_get(dev, "bclk"); 610b17336c5SHonghui Zhang if (IS_ERR(data->bclk)) 611b17336c5SHonghui Zhang return PTR_ERR(data->bclk); 612b17336c5SHonghui Zhang 613f90a9a85SYong Wu larb_nr = of_count_phandle_with_args(dev->of_node, 614f90a9a85SYong Wu "mediatek,larbs", NULL); 615f90a9a85SYong Wu if (larb_nr < 0) 616f90a9a85SYong Wu return larb_nr; 617f90a9a85SYong Wu 618f90a9a85SYong Wu for (i = 0; i < larb_nr; i++) { 619f90a9a85SYong Wu struct device_node *larbnode; 620b17336c5SHonghui Zhang struct platform_device *plarbdev; 621b17336c5SHonghui Zhang 622f90a9a85SYong Wu larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i); 623f90a9a85SYong Wu if (!larbnode) 624f90a9a85SYong Wu return -EINVAL; 625f90a9a85SYong Wu 626f90a9a85SYong Wu if (!of_device_is_available(larbnode)) { 627f90a9a85SYong Wu of_node_put(larbnode); 628b17336c5SHonghui Zhang continue; 629f90a9a85SYong Wu } 630b17336c5SHonghui Zhang 631f90a9a85SYong Wu plarbdev = of_find_device_by_node(larbnode); 632b17336c5SHonghui Zhang if (!plarbdev) { 633f90a9a85SYong Wu of_node_put(larbnode); 6342fb0feedSYong Wu return -ENODEV; 635b17336c5SHonghui Zhang } 6367d09aaf8SYong Wu if (!plarbdev->dev.driver) { 6377d09aaf8SYong Wu of_node_put(larbnode); 6387d09aaf8SYong Wu return -EPROBE_DEFER; 6397d09aaf8SYong Wu } 640f90a9a85SYong Wu data->larb_imu[i].dev = &plarbdev->dev; 641b17336c5SHonghui Zhang 64200c7c81fSRussell King component_match_add_release(dev, &match, release_of, 643f90a9a85SYong Wu compare_of, larbnode); 644b17336c5SHonghui Zhang } 645b17336c5SHonghui Zhang 646b17336c5SHonghui Zhang platform_set_drvdata(pdev, data); 647b17336c5SHonghui Zhang 648b17336c5SHonghui Zhang ret = mtk_iommu_hw_init(data); 649b17336c5SHonghui Zhang if (ret) 650b17336c5SHonghui Zhang return ret; 651b17336c5SHonghui Zhang 6526f66ea09SJoerg Roedel ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL, 6536f66ea09SJoerg Roedel dev_name(&pdev->dev)); 6546f66ea09SJoerg Roedel if (ret) 6556f66ea09SJoerg Roedel return ret; 6566f66ea09SJoerg Roedel 6572d471b20SRobin Murphy ret = iommu_device_register(&data->iommu, &mtk_iommu_ops, dev); 6586f66ea09SJoerg Roedel if (ret) 659ac304c07SYong Wu goto out_sysfs_remove; 660ac304c07SYong Wu 661ac304c07SYong Wu if (!iommu_present(&platform_bus_type)) { 662ac304c07SYong Wu ret = bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); 663ac304c07SYong Wu if (ret) 664ac304c07SYong Wu goto out_dev_unreg; 665ac304c07SYong Wu } 666ac304c07SYong Wu 667ac304c07SYong Wu ret = component_master_add_with_match(dev, &mtk_iommu_com_ops, match); 668ac304c07SYong Wu if (ret) 669ac304c07SYong Wu goto out_bus_set_null; 6706f66ea09SJoerg Roedel return ret; 6716f66ea09SJoerg Roedel 672ac304c07SYong Wu out_bus_set_null: 673ac304c07SYong Wu bus_set_iommu(&platform_bus_type, NULL); 674ac304c07SYong Wu out_dev_unreg: 675ac304c07SYong Wu iommu_device_unregister(&data->iommu); 676ac304c07SYong Wu out_sysfs_remove: 677ac304c07SYong Wu iommu_device_sysfs_remove(&data->iommu); 678ac304c07SYong Wu return ret; 679b17336c5SHonghui Zhang } 680b17336c5SHonghui Zhang 681b17336c5SHonghui Zhang static int mtk_iommu_remove(struct platform_device *pdev) 682b17336c5SHonghui Zhang { 683b17336c5SHonghui Zhang struct mtk_iommu_data *data = platform_get_drvdata(pdev); 684b17336c5SHonghui Zhang 6856f66ea09SJoerg Roedel iommu_device_sysfs_remove(&data->iommu); 6866f66ea09SJoerg Roedel iommu_device_unregister(&data->iommu); 6876f66ea09SJoerg Roedel 688b17336c5SHonghui Zhang if (iommu_present(&platform_bus_type)) 689b17336c5SHonghui Zhang bus_set_iommu(&platform_bus_type, NULL); 690b17336c5SHonghui Zhang 691b17336c5SHonghui Zhang clk_disable_unprepare(data->bclk); 692b17336c5SHonghui Zhang devm_free_irq(&pdev->dev, data->irq, data); 693b17336c5SHonghui Zhang component_master_del(&pdev->dev, &mtk_iommu_com_ops); 694b17336c5SHonghui Zhang return 0; 695b17336c5SHonghui Zhang } 696b17336c5SHonghui Zhang 697b17336c5SHonghui Zhang static int __maybe_unused mtk_iommu_suspend(struct device *dev) 698b17336c5SHonghui Zhang { 699b17336c5SHonghui Zhang struct mtk_iommu_data *data = dev_get_drvdata(dev); 700b17336c5SHonghui Zhang struct mtk_iommu_suspend_reg *reg = &data->reg; 701b17336c5SHonghui Zhang void __iomem *base = data->base; 702b17336c5SHonghui Zhang 703b17336c5SHonghui Zhang reg->standard_axi_mode = readl_relaxed(base + 704b17336c5SHonghui Zhang REG_MMU_STANDARD_AXI_MODE); 705b17336c5SHonghui Zhang reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM); 706b17336c5SHonghui Zhang reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG); 707b17336c5SHonghui Zhang reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL); 708b17336c5SHonghui Zhang return 0; 709b17336c5SHonghui Zhang } 710b17336c5SHonghui Zhang 711b17336c5SHonghui Zhang static int __maybe_unused mtk_iommu_resume(struct device *dev) 712b17336c5SHonghui Zhang { 713b17336c5SHonghui Zhang struct mtk_iommu_data *data = dev_get_drvdata(dev); 714b17336c5SHonghui Zhang struct mtk_iommu_suspend_reg *reg = &data->reg; 715b17336c5SHonghui Zhang void __iomem *base = data->base; 716b17336c5SHonghui Zhang 717b17336c5SHonghui Zhang writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR); 718b17336c5SHonghui Zhang writel_relaxed(reg->standard_axi_mode, 719b17336c5SHonghui Zhang base + REG_MMU_STANDARD_AXI_MODE); 720b17336c5SHonghui Zhang writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM); 721b17336c5SHonghui Zhang writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); 722b17336c5SHonghui Zhang writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL); 723b17336c5SHonghui Zhang writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); 724b17336c5SHonghui Zhang return 0; 725b17336c5SHonghui Zhang } 726b17336c5SHonghui Zhang 727131bc8ebSJoerg Roedel static const struct dev_pm_ops mtk_iommu_pm_ops = { 728b17336c5SHonghui Zhang SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) 729b17336c5SHonghui Zhang }; 730b17336c5SHonghui Zhang 731b17336c5SHonghui Zhang static struct platform_driver mtk_iommu_driver = { 732b17336c5SHonghui Zhang .probe = mtk_iommu_probe, 733b17336c5SHonghui Zhang .remove = mtk_iommu_remove, 734b17336c5SHonghui Zhang .driver = { 735395df08dSMatthias Brugger .name = "mtk-iommu-v1", 736b17336c5SHonghui Zhang .of_match_table = mtk_iommu_of_ids, 737b17336c5SHonghui Zhang .pm = &mtk_iommu_pm_ops, 738b17336c5SHonghui Zhang } 739b17336c5SHonghui Zhang }; 7408de000cfSYong Wu module_platform_driver(mtk_iommu_driver); 741b17336c5SHonghui Zhang 7428de000cfSYong Wu MODULE_DESCRIPTION("IOMMU API for MediaTek M4U v1 implementations"); 7438de000cfSYong Wu MODULE_LICENSE("GPL v2"); 744