18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2016 MediaTek Inc. 48c2ecf20Sopenharmony_ci * Author: Yong Wu <yong.wu@mediatek.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/bug.h> 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/iommu.h> 158c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/of_iommu.h> 208c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 268c2ecf20Sopenharmony_ci#include <linux/soc/mediatek/infracfg.h> 278c2ecf20Sopenharmony_ci#include <asm/barrier.h> 288c2ecf20Sopenharmony_ci#include <soc/mediatek/smi.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "mtk_iommu.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define REG_MMU_PT_BASE_ADDR 0x000 338c2ecf20Sopenharmony_ci#define MMU_PT_ADDR_MASK GENMASK(31, 7) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define REG_MMU_INVALIDATE 0x020 368c2ecf20Sopenharmony_ci#define F_ALL_INVLD 0x2 378c2ecf20Sopenharmony_ci#define F_MMU_INV_RANGE 0x1 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define REG_MMU_INVLD_START_A 0x024 408c2ecf20Sopenharmony_ci#define REG_MMU_INVLD_END_A 0x028 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define REG_MMU_INV_SEL_GEN2 0x02c 438c2ecf20Sopenharmony_ci#define REG_MMU_INV_SEL_GEN1 0x038 448c2ecf20Sopenharmony_ci#define F_INVLD_EN0 BIT(0) 458c2ecf20Sopenharmony_ci#define F_INVLD_EN1 BIT(1) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define REG_MMU_MISC_CTRL 0x048 488c2ecf20Sopenharmony_ci#define F_MMU_IN_ORDER_WR_EN_MASK (BIT(1) | BIT(17)) 498c2ecf20Sopenharmony_ci#define F_MMU_STANDARD_AXI_MODE_MASK (BIT(3) | BIT(19)) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define REG_MMU_DCM_DIS 0x050 528c2ecf20Sopenharmony_ci#define REG_MMU_WR_LEN_CTRL 0x054 538c2ecf20Sopenharmony_ci#define F_MMU_WR_THROT_DIS_MASK (BIT(5) | BIT(21)) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define REG_MMU_CTRL_REG 0x110 568c2ecf20Sopenharmony_ci#define F_MMU_TF_PROT_TO_PROGRAM_ADDR (2 << 4) 578c2ecf20Sopenharmony_ci#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4) 588c2ecf20Sopenharmony_ci#define F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173 (2 << 5) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define REG_MMU_IVRP_PADDR 0x114 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define REG_MMU_VLD_PA_RNG 0x118 638c2ecf20Sopenharmony_ci#define F_MMU_VLD_PA_RNG(EA, SA) (((EA) << 8) | (SA)) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define REG_MMU_INT_CONTROL0 0x120 668c2ecf20Sopenharmony_ci#define F_L2_MULIT_HIT_EN BIT(0) 678c2ecf20Sopenharmony_ci#define F_TABLE_WALK_FAULT_INT_EN BIT(1) 688c2ecf20Sopenharmony_ci#define F_PREETCH_FIFO_OVERFLOW_INT_EN BIT(2) 698c2ecf20Sopenharmony_ci#define F_MISS_FIFO_OVERFLOW_INT_EN BIT(3) 708c2ecf20Sopenharmony_ci#define F_PREFETCH_FIFO_ERR_INT_EN BIT(5) 718c2ecf20Sopenharmony_ci#define F_MISS_FIFO_ERR_INT_EN BIT(6) 728c2ecf20Sopenharmony_ci#define F_INT_CLR_BIT BIT(12) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define REG_MMU_INT_MAIN_CONTROL 0x124 758c2ecf20Sopenharmony_ci /* mmu0 | mmu1 */ 768c2ecf20Sopenharmony_ci#define F_INT_TRANSLATION_FAULT (BIT(0) | BIT(7)) 778c2ecf20Sopenharmony_ci#define F_INT_MAIN_MULTI_HIT_FAULT (BIT(1) | BIT(8)) 788c2ecf20Sopenharmony_ci#define F_INT_INVALID_PA_FAULT (BIT(2) | BIT(9)) 798c2ecf20Sopenharmony_ci#define F_INT_ENTRY_REPLACEMENT_FAULT (BIT(3) | BIT(10)) 808c2ecf20Sopenharmony_ci#define F_INT_TLB_MISS_FAULT (BIT(4) | BIT(11)) 818c2ecf20Sopenharmony_ci#define F_INT_MISS_TRANSACTION_FIFO_FAULT (BIT(5) | BIT(12)) 828c2ecf20Sopenharmony_ci#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT (BIT(6) | BIT(13)) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define REG_MMU_CPE_DONE 0x12C 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define REG_MMU_FAULT_ST1 0x134 878c2ecf20Sopenharmony_ci#define F_REG_MMU0_FAULT_MASK GENMASK(6, 0) 888c2ecf20Sopenharmony_ci#define F_REG_MMU1_FAULT_MASK GENMASK(13, 7) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define REG_MMU0_FAULT_VA 0x13c 918c2ecf20Sopenharmony_ci#define F_MMU_FAULT_VA_WRITE_BIT BIT(1) 928c2ecf20Sopenharmony_ci#define F_MMU_FAULT_VA_LAYER_BIT BIT(0) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define REG_MMU0_INVLD_PA 0x140 958c2ecf20Sopenharmony_ci#define REG_MMU1_FAULT_VA 0x144 968c2ecf20Sopenharmony_ci#define REG_MMU1_INVLD_PA 0x148 978c2ecf20Sopenharmony_ci#define REG_MMU0_INT_ID 0x150 988c2ecf20Sopenharmony_ci#define REG_MMU1_INT_ID 0x154 998c2ecf20Sopenharmony_ci#define F_MMU_INT_ID_COMM_ID(a) (((a) >> 9) & 0x7) 1008c2ecf20Sopenharmony_ci#define F_MMU_INT_ID_SUB_COMM_ID(a) (((a) >> 7) & 0x3) 1018c2ecf20Sopenharmony_ci#define F_MMU_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7) 1028c2ecf20Sopenharmony_ci#define F_MMU_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define MTK_PROTECT_PA_ALIGN 256 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Get the local arbiter ID and the portid within the larb arbiter 1088c2ecf20Sopenharmony_ci * from mtk_m4u_id which is defined by MTK_M4U_ID. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0xf) 1118c2ecf20Sopenharmony_ci#define MTK_M4U_TO_PORT(id) ((id) & 0x1f) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define HAS_4GB_MODE BIT(0) 1148c2ecf20Sopenharmony_ci/* HW will use the EMI clock if there isn't the "bclk". */ 1158c2ecf20Sopenharmony_ci#define HAS_BCLK BIT(1) 1168c2ecf20Sopenharmony_ci#define HAS_VLD_PA_RNG BIT(2) 1178c2ecf20Sopenharmony_ci#define RESET_AXI BIT(3) 1188c2ecf20Sopenharmony_ci#define OUT_ORDER_WR_EN BIT(4) 1198c2ecf20Sopenharmony_ci#define HAS_SUB_COMM BIT(5) 1208c2ecf20Sopenharmony_ci#define WR_THROT_EN BIT(6) 1218c2ecf20Sopenharmony_ci#define HAS_LEGACY_IVRP_PADDR BIT(7) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define MTK_IOMMU_HAS_FLAG(pdata, _x) \ 1248c2ecf20Sopenharmony_ci ((((pdata)->flags) & (_x)) == (_x)) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct mtk_iommu_domain { 1278c2ecf20Sopenharmony_ci struct io_pgtable_cfg cfg; 1288c2ecf20Sopenharmony_ci struct io_pgtable_ops *iop; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci struct iommu_domain domain; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct iommu_ops mtk_iommu_ops; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * In M4U 4GB mode, the physical address is remapped as below: 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * CPU Physical address: 1398c2ecf20Sopenharmony_ci * ==================== 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * 0 1G 2G 3G 4G 5G 1428c2ecf20Sopenharmony_ci * |---A---|---B---|---C---|---D---|---E---| 1438c2ecf20Sopenharmony_ci * +--I/O--+------------Memory-------------+ 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * IOMMU output physical address: 1468c2ecf20Sopenharmony_ci * ============================= 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * 4G 5G 6G 7G 8G 1498c2ecf20Sopenharmony_ci * |---E---|---B---|---C---|---D---| 1508c2ecf20Sopenharmony_ci * +------------Memory-------------+ 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * The Region 'A'(I/O) can NOT be mapped by M4U; For Region 'B'/'C'/'D', the 1538c2ecf20Sopenharmony_ci * bit32 of the CPU physical address always is needed to set, and for Region 1548c2ecf20Sopenharmony_ci * 'E', the CPU physical address keep as is. 1558c2ecf20Sopenharmony_ci * Additionally, The iommu consumers always use the CPU phyiscal address. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci#define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x140000000UL 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic LIST_HEAD(m4ulist); /* List all the M4U HWs */ 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list) 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * There may be 1 or 2 M4U HWs, But we always expect they are in the same domain 1658c2ecf20Sopenharmony_ci * for the performance. 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * Here always return the mtk_iommu_data of the first probed M4U where the 1688c2ecf20Sopenharmony_ci * iommu domain information is recorded. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic struct mtk_iommu_data *mtk_iommu_get_m4u_data(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct mtk_iommu_data *data; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for_each_m4u(data) 1758c2ecf20Sopenharmony_ci return data; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return NULL; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci return container_of(dom, struct mtk_iommu_domain, domain); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void mtk_iommu_tlb_flush_all(void *cookie) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = cookie; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for_each_m4u(data) { 1908c2ecf20Sopenharmony_ci writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, 1918c2ecf20Sopenharmony_ci data->base + data->plat_data->inv_sel_reg); 1928c2ecf20Sopenharmony_ci writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); 1938c2ecf20Sopenharmony_ci wmb(); /* Make sure the tlb flush all done */ 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void mtk_iommu_tlb_flush_range_sync(unsigned long iova, size_t size, 1988c2ecf20Sopenharmony_ci size_t granule, void *cookie) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = cookie; 2018c2ecf20Sopenharmony_ci unsigned long flags; 2028c2ecf20Sopenharmony_ci int ret; 2038c2ecf20Sopenharmony_ci u32 tmp; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for_each_m4u(data) { 2068c2ecf20Sopenharmony_ci spin_lock_irqsave(&data->tlb_lock, flags); 2078c2ecf20Sopenharmony_ci writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, 2088c2ecf20Sopenharmony_ci data->base + data->plat_data->inv_sel_reg); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A); 2118c2ecf20Sopenharmony_ci writel_relaxed(iova + size - 1, 2128c2ecf20Sopenharmony_ci data->base + REG_MMU_INVLD_END_A); 2138c2ecf20Sopenharmony_ci writel_relaxed(F_MMU_INV_RANGE, 2148c2ecf20Sopenharmony_ci data->base + REG_MMU_INVALIDATE); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* tlb sync */ 2178c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, 2188c2ecf20Sopenharmony_ci tmp, tmp != 0, 10, 1000); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_warn(data->dev, 2218c2ecf20Sopenharmony_ci "Partial TLB flush timed out, falling back to full flush\n"); 2228c2ecf20Sopenharmony_ci mtk_iommu_tlb_flush_all(cookie); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci /* Clear the CPE status */ 2258c2ecf20Sopenharmony_ci writel_relaxed(0, data->base + REG_MMU_CPE_DONE); 2268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&data->tlb_lock, flags); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void mtk_iommu_tlb_flush_page_nosync(struct iommu_iotlb_gather *gather, 2318c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, 2328c2ecf20Sopenharmony_ci void *cookie) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = cookie; 2358c2ecf20Sopenharmony_ci struct iommu_domain *domain = &data->m4u_dom->domain; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci iommu_iotlb_gather_add_page(domain, gather, iova, granule); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops mtk_iommu_flush_ops = { 2418c2ecf20Sopenharmony_ci .tlb_flush_all = mtk_iommu_tlb_flush_all, 2428c2ecf20Sopenharmony_ci .tlb_flush_walk = mtk_iommu_tlb_flush_range_sync, 2438c2ecf20Sopenharmony_ci .tlb_flush_leaf = mtk_iommu_tlb_flush_range_sync, 2448c2ecf20Sopenharmony_ci .tlb_add_page = mtk_iommu_tlb_flush_page_nosync, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic irqreturn_t mtk_iommu_isr(int irq, void *dev_id) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = dev_id; 2508c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = data->m4u_dom; 2518c2ecf20Sopenharmony_ci u32 int_state, regval, fault_iova, fault_pa; 2528c2ecf20Sopenharmony_ci unsigned int fault_larb, fault_port, sub_comm = 0; 2538c2ecf20Sopenharmony_ci bool layer, write; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Read error info from registers */ 2568c2ecf20Sopenharmony_ci int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1); 2578c2ecf20Sopenharmony_ci if (int_state & F_REG_MMU0_FAULT_MASK) { 2588c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU0_INT_ID); 2598c2ecf20Sopenharmony_ci fault_iova = readl_relaxed(data->base + REG_MMU0_FAULT_VA); 2608c2ecf20Sopenharmony_ci fault_pa = readl_relaxed(data->base + REG_MMU0_INVLD_PA); 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU1_INT_ID); 2638c2ecf20Sopenharmony_ci fault_iova = readl_relaxed(data->base + REG_MMU1_FAULT_VA); 2648c2ecf20Sopenharmony_ci fault_pa = readl_relaxed(data->base + REG_MMU1_INVLD_PA); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT; 2678c2ecf20Sopenharmony_ci write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT; 2688c2ecf20Sopenharmony_ci fault_port = F_MMU_INT_ID_PORT_ID(regval); 2698c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_SUB_COMM)) { 2708c2ecf20Sopenharmony_ci fault_larb = F_MMU_INT_ID_COMM_ID(regval); 2718c2ecf20Sopenharmony_ci sub_comm = F_MMU_INT_ID_SUB_COMM_ID(regval); 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci fault_larb = F_MMU_INT_ID_LARB_ID(regval); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci fault_larb = data->plat_data->larbid_remap[fault_larb][sub_comm]; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (report_iommu_fault(&dom->domain, data->dev, fault_iova, 2788c2ecf20Sopenharmony_ci write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) { 2798c2ecf20Sopenharmony_ci dev_err_ratelimited( 2808c2ecf20Sopenharmony_ci data->dev, 2818c2ecf20Sopenharmony_ci "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d layer=%d %s\n", 2828c2ecf20Sopenharmony_ci int_state, fault_iova, fault_pa, fault_larb, fault_port, 2838c2ecf20Sopenharmony_ci layer, write ? "write" : "read"); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Interrupt clear */ 2878c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL0); 2888c2ecf20Sopenharmony_ci regval |= F_INT_CLR_BIT; 2898c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mtk_iommu_tlb_flush_all(data); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void mtk_iommu_config(struct mtk_iommu_data *data, 2978c2ecf20Sopenharmony_ci struct device *dev, bool enable) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct mtk_smi_larb_iommu *larb_mmu; 3008c2ecf20Sopenharmony_ci unsigned int larbid, portid; 3018c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 3028c2ecf20Sopenharmony_ci int i; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = 0; i < fwspec->num_ids; ++i) { 3058c2ecf20Sopenharmony_ci larbid = MTK_M4U_TO_LARB(fwspec->ids[i]); 3068c2ecf20Sopenharmony_ci portid = MTK_M4U_TO_PORT(fwspec->ids[i]); 3078c2ecf20Sopenharmony_ci larb_mmu = &data->larb_imu[larbid]; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dev_dbg(dev, "%s iommu port: %d\n", 3108c2ecf20Sopenharmony_ci enable ? "enable" : "disable", portid); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (enable) 3138c2ecf20Sopenharmony_ci larb_mmu->mmu |= MTK_SMI_MMU_EN(portid); 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci dom->cfg = (struct io_pgtable_cfg) { 3248c2ecf20Sopenharmony_ci .quirks = IO_PGTABLE_QUIRK_ARM_NS | 3258c2ecf20Sopenharmony_ci IO_PGTABLE_QUIRK_NO_PERMS | 3268c2ecf20Sopenharmony_ci IO_PGTABLE_QUIRK_TLBI_ON_MAP | 3278c2ecf20Sopenharmony_ci IO_PGTABLE_QUIRK_ARM_MTK_EXT, 3288c2ecf20Sopenharmony_ci .pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap, 3298c2ecf20Sopenharmony_ci .ias = 32, 3308c2ecf20Sopenharmony_ci .oas = 34, 3318c2ecf20Sopenharmony_ci .tlb = &mtk_iommu_flush_ops, 3328c2ecf20Sopenharmony_ci .iommu_dev = data->dev, 3338c2ecf20Sopenharmony_ci }; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data); 3368c2ecf20Sopenharmony_ci if (!dom->iop) { 3378c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to alloc io pgtable\n"); 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Update our support page sizes bitmap */ 3428c2ecf20Sopenharmony_ci dom->domain.pgsize_bitmap = dom->cfg.pgsize_bitmap; 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic struct iommu_domain *mtk_iommu_domain_alloc(unsigned type) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (type != IOMMU_DOMAIN_DMA) 3518c2ecf20Sopenharmony_ci return NULL; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci dom = kzalloc(sizeof(*dom), GFP_KERNEL); 3548c2ecf20Sopenharmony_ci if (!dom) 3558c2ecf20Sopenharmony_ci return NULL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (iommu_get_dma_cookie(&dom->domain)) 3588c2ecf20Sopenharmony_ci goto free_dom; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (mtk_iommu_domain_finalise(dom)) 3618c2ecf20Sopenharmony_ci goto put_dma_cookie; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci dom->domain.geometry.aperture_start = 0; 3648c2ecf20Sopenharmony_ci dom->domain.geometry.aperture_end = DMA_BIT_MASK(32); 3658c2ecf20Sopenharmony_ci dom->domain.geometry.force_aperture = true; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return &dom->domain; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ciput_dma_cookie: 3708c2ecf20Sopenharmony_ci iommu_put_dma_cookie(&dom->domain); 3718c2ecf20Sopenharmony_cifree_dom: 3728c2ecf20Sopenharmony_ci kfree(dom); 3738c2ecf20Sopenharmony_ci return NULL; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void mtk_iommu_domain_free(struct iommu_domain *domain) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = to_mtk_domain(domain); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci free_io_pgtable_ops(dom->iop); 3818c2ecf20Sopenharmony_ci iommu_put_dma_cookie(domain); 3828c2ecf20Sopenharmony_ci kfree(to_mtk_domain(domain)); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int mtk_iommu_attach_device(struct iommu_domain *domain, 3868c2ecf20Sopenharmony_ci struct device *dev) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = dev_iommu_priv_get(dev); 3898c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = to_mtk_domain(domain); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!data) 3928c2ecf20Sopenharmony_ci return -ENODEV; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Update the pgtable base address register of the M4U HW */ 3958c2ecf20Sopenharmony_ci if (!data->m4u_dom) { 3968c2ecf20Sopenharmony_ci data->m4u_dom = dom; 3978c2ecf20Sopenharmony_ci writel(dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK, 3988c2ecf20Sopenharmony_ci data->base + REG_MMU_PT_BASE_ADDR); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mtk_iommu_config(data, dev, true); 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void mtk_iommu_detach_device(struct iommu_domain *domain, 4068c2ecf20Sopenharmony_ci struct device *dev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = dev_iommu_priv_get(dev); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!data) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mtk_iommu_config(data, dev, false); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, 4178c2ecf20Sopenharmony_ci phys_addr_t paddr, size_t size, int prot, gfp_t gfp) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = to_mtk_domain(domain); 4208c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* The "4GB mode" M4U physically can not use the lower remap of Dram. */ 4238c2ecf20Sopenharmony_ci if (data->enable_4GB) 4248c2ecf20Sopenharmony_ci paddr |= BIT_ULL(32); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Synchronize with the tlb_lock */ 4278c2ecf20Sopenharmony_ci return dom->iop->map(dom->iop, iova, paddr, size, prot, gfp); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic size_t mtk_iommu_unmap(struct iommu_domain *domain, 4318c2ecf20Sopenharmony_ci unsigned long iova, size_t size, 4328c2ecf20Sopenharmony_ci struct iommu_iotlb_gather *gather) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = to_mtk_domain(domain); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return dom->iop->unmap(dom->iop, iova, size, gather); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void mtk_iommu_flush_iotlb_all(struct iommu_domain *domain) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci mtk_iommu_tlb_flush_all(mtk_iommu_get_m4u_data()); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void mtk_iommu_iotlb_sync(struct iommu_domain *domain, 4458c2ecf20Sopenharmony_ci struct iommu_iotlb_gather *gather) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); 4488c2ecf20Sopenharmony_ci size_t length = gather->end - gather->start + 1; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (gather->start == ULONG_MAX) 4518c2ecf20Sopenharmony_ci return; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mtk_iommu_tlb_flush_range_sync(gather->start, length, gather->pgsize, 4548c2ecf20Sopenharmony_ci data); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, 4588c2ecf20Sopenharmony_ci dma_addr_t iova) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct mtk_iommu_domain *dom = to_mtk_domain(domain); 4618c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); 4628c2ecf20Sopenharmony_ci phys_addr_t pa; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci pa = dom->iop->iova_to_phys(dom->iop, iova); 4658c2ecf20Sopenharmony_ci if (data->enable_4GB && pa >= MTK_IOMMU_4GB_MODE_REMAP_BASE) 4668c2ecf20Sopenharmony_ci pa &= ~BIT_ULL(32); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return pa; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic struct iommu_device *mtk_iommu_probe_device(struct device *dev) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 4748c2ecf20Sopenharmony_ci struct mtk_iommu_data *data; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &mtk_iommu_ops) 4778c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); /* Not a iommu client device */ 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci data = dev_iommu_priv_get(dev); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return &data->iommu; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic void mtk_iommu_release_device(struct device *dev) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &mtk_iommu_ops) 4898c2ecf20Sopenharmony_ci return; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci iommu_fwspec_free(dev); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic struct iommu_group *mtk_iommu_device_group(struct device *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!data) 4998c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* All the client devices are in the same m4u iommu-group */ 5028c2ecf20Sopenharmony_ci if (!data->m4u_group) { 5038c2ecf20Sopenharmony_ci data->m4u_group = iommu_group_alloc(); 5048c2ecf20Sopenharmony_ci if (IS_ERR(data->m4u_group)) 5058c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate M4U IOMMU group\n"); 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci iommu_group_ref_get(data->m4u_group); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci return data->m4u_group; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct platform_device *m4updev; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (args->args_count != 1) { 5178c2ecf20Sopenharmony_ci dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n", 5188c2ecf20Sopenharmony_ci args->args_count); 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!dev_iommu_priv_get(dev)) { 5238c2ecf20Sopenharmony_ci /* Get the m4u device */ 5248c2ecf20Sopenharmony_ci m4updev = of_find_device_by_node(args->np); 5258c2ecf20Sopenharmony_ci if (WARN_ON(!m4updev)) 5268c2ecf20Sopenharmony_ci return -EINVAL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, platform_get_drvdata(m4updev)); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return iommu_fwspec_add_ids(dev, args->args, 1); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct iommu_ops mtk_iommu_ops = { 5358c2ecf20Sopenharmony_ci .domain_alloc = mtk_iommu_domain_alloc, 5368c2ecf20Sopenharmony_ci .domain_free = mtk_iommu_domain_free, 5378c2ecf20Sopenharmony_ci .attach_dev = mtk_iommu_attach_device, 5388c2ecf20Sopenharmony_ci .detach_dev = mtk_iommu_detach_device, 5398c2ecf20Sopenharmony_ci .map = mtk_iommu_map, 5408c2ecf20Sopenharmony_ci .unmap = mtk_iommu_unmap, 5418c2ecf20Sopenharmony_ci .flush_iotlb_all = mtk_iommu_flush_iotlb_all, 5428c2ecf20Sopenharmony_ci .iotlb_sync = mtk_iommu_iotlb_sync, 5438c2ecf20Sopenharmony_ci .iova_to_phys = mtk_iommu_iova_to_phys, 5448c2ecf20Sopenharmony_ci .probe_device = mtk_iommu_probe_device, 5458c2ecf20Sopenharmony_ci .release_device = mtk_iommu_release_device, 5468c2ecf20Sopenharmony_ci .device_group = mtk_iommu_device_group, 5478c2ecf20Sopenharmony_ci .of_xlate = mtk_iommu_of_xlate, 5488c2ecf20Sopenharmony_ci .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M, 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int mtk_iommu_hw_init(const struct mtk_iommu_data *data) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci u32 regval; 5548c2ecf20Sopenharmony_ci int ret; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = clk_prepare_enable(data->bclk); 5578c2ecf20Sopenharmony_ci if (ret) { 5588c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret); 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (data->plat_data->m4u_plat == M4U_MT8173) { 5638c2ecf20Sopenharmony_ci regval = F_MMU_PREFETCH_RT_REPLACE_MOD | 5648c2ecf20Sopenharmony_ci F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173; 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU_CTRL_REG); 5678c2ecf20Sopenharmony_ci regval |= F_MMU_TF_PROT_TO_PROGRAM_ADDR; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci regval = F_L2_MULIT_HIT_EN | 5728c2ecf20Sopenharmony_ci F_TABLE_WALK_FAULT_INT_EN | 5738c2ecf20Sopenharmony_ci F_PREETCH_FIFO_OVERFLOW_INT_EN | 5748c2ecf20Sopenharmony_ci F_MISS_FIFO_OVERFLOW_INT_EN | 5758c2ecf20Sopenharmony_ci F_PREFETCH_FIFO_ERR_INT_EN | 5768c2ecf20Sopenharmony_ci F_MISS_FIFO_ERR_INT_EN; 5778c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci regval = F_INT_TRANSLATION_FAULT | 5808c2ecf20Sopenharmony_ci F_INT_MAIN_MULTI_HIT_FAULT | 5818c2ecf20Sopenharmony_ci F_INT_INVALID_PA_FAULT | 5828c2ecf20Sopenharmony_ci F_INT_ENTRY_REPLACEMENT_FAULT | 5838c2ecf20Sopenharmony_ci F_INT_TLB_MISS_FAULT | 5848c2ecf20Sopenharmony_ci F_INT_MISS_TRANSACTION_FIFO_FAULT | 5858c2ecf20Sopenharmony_ci F_INT_PRETETCH_TRANSATION_FIFO_FAULT; 5868c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_LEGACY_IVRP_PADDR)) 5898c2ecf20Sopenharmony_ci regval = (data->protect_base >> 1) | (data->enable_4GB << 31); 5908c2ecf20Sopenharmony_ci else 5918c2ecf20Sopenharmony_ci regval = lower_32_bits(data->protect_base) | 5928c2ecf20Sopenharmony_ci upper_32_bits(data->protect_base); 5938c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (data->enable_4GB && 5968c2ecf20Sopenharmony_ci MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_VLD_PA_RNG)) { 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * If 4GB mode is enabled, the validate PA range is from 5998c2ecf20Sopenharmony_ci * 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30]. 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci regval = F_MMU_VLD_PA_RNG(7, 4); 6028c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_VLD_PA_RNG); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci writel_relaxed(0, data->base + REG_MMU_DCM_DIS); 6058c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, WR_THROT_EN)) { 6068c2ecf20Sopenharmony_ci /* write command throttling mode */ 6078c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU_WR_LEN_CTRL); 6088c2ecf20Sopenharmony_ci regval &= ~F_MMU_WR_THROT_DIS_MASK; 6098c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_WR_LEN_CTRL); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, RESET_AXI)) { 6138c2ecf20Sopenharmony_ci /* The register is called STANDARD_AXI_MODE in this case */ 6148c2ecf20Sopenharmony_ci regval = 0; 6158c2ecf20Sopenharmony_ci } else { 6168c2ecf20Sopenharmony_ci regval = readl_relaxed(data->base + REG_MMU_MISC_CTRL); 6178c2ecf20Sopenharmony_ci regval &= ~F_MMU_STANDARD_AXI_MODE_MASK; 6188c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, OUT_ORDER_WR_EN)) 6198c2ecf20Sopenharmony_ci regval &= ~F_MMU_IN_ORDER_WR_EN_MASK; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci writel_relaxed(regval, data->base + REG_MMU_MISC_CTRL); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, 6248c2ecf20Sopenharmony_ci dev_name(data->dev), (void *)data)) { 6258c2ecf20Sopenharmony_ci writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR); 6268c2ecf20Sopenharmony_ci clk_disable_unprepare(data->bclk); 6278c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq); 6288c2ecf20Sopenharmony_ci return -ENODEV; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic const struct component_master_ops mtk_iommu_com_ops = { 6358c2ecf20Sopenharmony_ci .bind = mtk_iommu_bind, 6368c2ecf20Sopenharmony_ci .unbind = mtk_iommu_unbind, 6378c2ecf20Sopenharmony_ci}; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int mtk_iommu_probe(struct platform_device *pdev) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct mtk_iommu_data *data; 6428c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6438c2ecf20Sopenharmony_ci struct resource *res; 6448c2ecf20Sopenharmony_ci resource_size_t ioaddr; 6458c2ecf20Sopenharmony_ci struct component_match *match = NULL; 6468c2ecf20Sopenharmony_ci struct regmap *infracfg; 6478c2ecf20Sopenharmony_ci void *protect; 6488c2ecf20Sopenharmony_ci int i, larb_nr, ret; 6498c2ecf20Sopenharmony_ci u32 val; 6508c2ecf20Sopenharmony_ci char *p; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 6538c2ecf20Sopenharmony_ci if (!data) 6548c2ecf20Sopenharmony_ci return -ENOMEM; 6558c2ecf20Sopenharmony_ci data->dev = dev; 6568c2ecf20Sopenharmony_ci data->plat_data = of_device_get_match_data(dev); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Protect memory. HW will access here while translation fault.*/ 6598c2ecf20Sopenharmony_ci protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL); 6608c2ecf20Sopenharmony_ci if (!protect) 6618c2ecf20Sopenharmony_ci return -ENOMEM; 6628c2ecf20Sopenharmony_ci data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_4GB_MODE)) { 6658c2ecf20Sopenharmony_ci switch (data->plat_data->m4u_plat) { 6668c2ecf20Sopenharmony_ci case M4U_MT2712: 6678c2ecf20Sopenharmony_ci p = "mediatek,mt2712-infracfg"; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci case M4U_MT8173: 6708c2ecf20Sopenharmony_ci p = "mediatek,mt8173-infracfg"; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci default: 6738c2ecf20Sopenharmony_ci p = NULL; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci infracfg = syscon_regmap_lookup_by_compatible(p); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (IS_ERR(infracfg)) 6798c2ecf20Sopenharmony_ci return PTR_ERR(infracfg); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci ret = regmap_read(infracfg, REG_INFRA_MISC, &val); 6828c2ecf20Sopenharmony_ci if (ret) 6838c2ecf20Sopenharmony_ci return ret; 6848c2ecf20Sopenharmony_ci data->enable_4GB = !!(val & F_DDR_4GB_SUPPORT_EN); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6888c2ecf20Sopenharmony_ci data->base = devm_ioremap_resource(dev, res); 6898c2ecf20Sopenharmony_ci if (IS_ERR(data->base)) 6908c2ecf20Sopenharmony_ci return PTR_ERR(data->base); 6918c2ecf20Sopenharmony_ci ioaddr = res->start; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci data->irq = platform_get_irq(pdev, 0); 6948c2ecf20Sopenharmony_ci if (data->irq < 0) 6958c2ecf20Sopenharmony_ci return data->irq; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (MTK_IOMMU_HAS_FLAG(data->plat_data, HAS_BCLK)) { 6988c2ecf20Sopenharmony_ci data->bclk = devm_clk_get(dev, "bclk"); 6998c2ecf20Sopenharmony_ci if (IS_ERR(data->bclk)) 7008c2ecf20Sopenharmony_ci return PTR_ERR(data->bclk); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci larb_nr = of_count_phandle_with_args(dev->of_node, 7048c2ecf20Sopenharmony_ci "mediatek,larbs", NULL); 7058c2ecf20Sopenharmony_ci if (larb_nr < 0) 7068c2ecf20Sopenharmony_ci return larb_nr; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci for (i = 0; i < larb_nr; i++) { 7098c2ecf20Sopenharmony_ci struct device_node *larbnode; 7108c2ecf20Sopenharmony_ci struct platform_device *plarbdev; 7118c2ecf20Sopenharmony_ci u32 id; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i); 7148c2ecf20Sopenharmony_ci if (!larbnode) 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (!of_device_is_available(larbnode)) { 7188c2ecf20Sopenharmony_ci of_node_put(larbnode); 7198c2ecf20Sopenharmony_ci continue; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = of_property_read_u32(larbnode, "mediatek,larb-id", &id); 7238c2ecf20Sopenharmony_ci if (ret)/* The id is consecutive if there is no this property */ 7248c2ecf20Sopenharmony_ci id = i; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci plarbdev = of_find_device_by_node(larbnode); 7278c2ecf20Sopenharmony_ci if (!plarbdev) { 7288c2ecf20Sopenharmony_ci of_node_put(larbnode); 7298c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci data->larb_imu[id].dev = &plarbdev->dev; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci component_match_add_release(dev, &match, release_of, 7348c2ecf20Sopenharmony_ci compare_of, larbnode); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ret = mtk_iommu_hw_init(data); 7408c2ecf20Sopenharmony_ci if (ret) 7418c2ecf20Sopenharmony_ci return ret; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ret = iommu_device_sysfs_add(&data->iommu, dev, NULL, 7448c2ecf20Sopenharmony_ci "mtk-iommu.%pa", &ioaddr); 7458c2ecf20Sopenharmony_ci if (ret) 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci iommu_device_set_ops(&data->iommu, &mtk_iommu_ops); 7498c2ecf20Sopenharmony_ci iommu_device_set_fwnode(&data->iommu, &pdev->dev.of_node->fwnode); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = iommu_device_register(&data->iommu); 7528c2ecf20Sopenharmony_ci if (ret) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci spin_lock_init(&data->tlb_lock); 7568c2ecf20Sopenharmony_ci list_add_tail(&data->list, &m4ulist); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (!iommu_present(&platform_bus_type)) 7598c2ecf20Sopenharmony_ci bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return component_master_add_with_match(dev, &mtk_iommu_com_ops, match); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int mtk_iommu_remove(struct platform_device *pdev) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = platform_get_drvdata(pdev); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci iommu_device_sysfs_remove(&data->iommu); 7698c2ecf20Sopenharmony_ci iommu_device_unregister(&data->iommu); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci list_del(&data->list); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci clk_disable_unprepare(data->bclk); 7748c2ecf20Sopenharmony_ci devm_free_irq(&pdev->dev, data->irq, data); 7758c2ecf20Sopenharmony_ci component_master_del(&pdev->dev, &mtk_iommu_com_ops); 7768c2ecf20Sopenharmony_ci return 0; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_iommu_suspend(struct device *dev) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = dev_get_drvdata(dev); 7828c2ecf20Sopenharmony_ci struct mtk_iommu_suspend_reg *reg = &data->reg; 7838c2ecf20Sopenharmony_ci void __iomem *base = data->base; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci reg->wr_len_ctrl = readl_relaxed(base + REG_MMU_WR_LEN_CTRL); 7868c2ecf20Sopenharmony_ci reg->misc_ctrl = readl_relaxed(base + REG_MMU_MISC_CTRL); 7878c2ecf20Sopenharmony_ci reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM_DIS); 7888c2ecf20Sopenharmony_ci reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG); 7898c2ecf20Sopenharmony_ci reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0); 7908c2ecf20Sopenharmony_ci reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL); 7918c2ecf20Sopenharmony_ci reg->ivrp_paddr = readl_relaxed(base + REG_MMU_IVRP_PADDR); 7928c2ecf20Sopenharmony_ci reg->vld_pa_rng = readl_relaxed(base + REG_MMU_VLD_PA_RNG); 7938c2ecf20Sopenharmony_ci clk_disable_unprepare(data->bclk); 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_iommu_resume(struct device *dev) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct mtk_iommu_data *data = dev_get_drvdata(dev); 8008c2ecf20Sopenharmony_ci struct mtk_iommu_suspend_reg *reg = &data->reg; 8018c2ecf20Sopenharmony_ci struct mtk_iommu_domain *m4u_dom = data->m4u_dom; 8028c2ecf20Sopenharmony_ci void __iomem *base = data->base; 8038c2ecf20Sopenharmony_ci int ret; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ret = clk_prepare_enable(data->bclk); 8068c2ecf20Sopenharmony_ci if (ret) { 8078c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret); 8088c2ecf20Sopenharmony_ci return ret; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL); 8118c2ecf20Sopenharmony_ci writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL); 8128c2ecf20Sopenharmony_ci writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS); 8138c2ecf20Sopenharmony_ci writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); 8148c2ecf20Sopenharmony_ci writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0); 8158c2ecf20Sopenharmony_ci writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL); 8168c2ecf20Sopenharmony_ci writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR); 8178c2ecf20Sopenharmony_ci writel_relaxed(reg->vld_pa_rng, base + REG_MMU_VLD_PA_RNG); 8188c2ecf20Sopenharmony_ci if (m4u_dom) 8198c2ecf20Sopenharmony_ci writel(m4u_dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK, 8208c2ecf20Sopenharmony_ci base + REG_MMU_PT_BASE_ADDR); 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mtk_iommu_pm_ops = { 8258c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) 8268c2ecf20Sopenharmony_ci}; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic const struct mtk_iommu_plat_data mt2712_data = { 8298c2ecf20Sopenharmony_ci .m4u_plat = M4U_MT2712, 8308c2ecf20Sopenharmony_ci .flags = HAS_4GB_MODE | HAS_BCLK | HAS_VLD_PA_RNG, 8318c2ecf20Sopenharmony_ci .inv_sel_reg = REG_MMU_INV_SEL_GEN1, 8328c2ecf20Sopenharmony_ci .larbid_remap = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}}, 8338c2ecf20Sopenharmony_ci}; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic const struct mtk_iommu_plat_data mt6779_data = { 8368c2ecf20Sopenharmony_ci .m4u_plat = M4U_MT6779, 8378c2ecf20Sopenharmony_ci .flags = HAS_SUB_COMM | OUT_ORDER_WR_EN | WR_THROT_EN, 8388c2ecf20Sopenharmony_ci .inv_sel_reg = REG_MMU_INV_SEL_GEN2, 8398c2ecf20Sopenharmony_ci .larbid_remap = {{0}, {1}, {2}, {3}, {5}, {7, 8}, {10}, {9}}, 8408c2ecf20Sopenharmony_ci}; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic const struct mtk_iommu_plat_data mt8167_data = { 8438c2ecf20Sopenharmony_ci .m4u_plat = M4U_MT8167, 8448c2ecf20Sopenharmony_ci .flags = RESET_AXI | HAS_LEGACY_IVRP_PADDR, 8458c2ecf20Sopenharmony_ci .inv_sel_reg = REG_MMU_INV_SEL_GEN1, 8468c2ecf20Sopenharmony_ci .larbid_remap = {{0}, {1}, {2}}, /* Linear mapping. */ 8478c2ecf20Sopenharmony_ci}; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic const struct mtk_iommu_plat_data mt8173_data = { 8508c2ecf20Sopenharmony_ci .m4u_plat = M4U_MT8173, 8518c2ecf20Sopenharmony_ci .flags = HAS_4GB_MODE | HAS_BCLK | RESET_AXI | 8528c2ecf20Sopenharmony_ci HAS_LEGACY_IVRP_PADDR, 8538c2ecf20Sopenharmony_ci .inv_sel_reg = REG_MMU_INV_SEL_GEN1, 8548c2ecf20Sopenharmony_ci .larbid_remap = {{0}, {1}, {2}, {3}, {4}, {5}}, /* Linear mapping. */ 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic const struct mtk_iommu_plat_data mt8183_data = { 8588c2ecf20Sopenharmony_ci .m4u_plat = M4U_MT8183, 8598c2ecf20Sopenharmony_ci .flags = RESET_AXI, 8608c2ecf20Sopenharmony_ci .inv_sel_reg = REG_MMU_INV_SEL_GEN1, 8618c2ecf20Sopenharmony_ci .larbid_remap = {{0}, {4}, {5}, {6}, {7}, {2}, {3}, {1}}, 8628c2ecf20Sopenharmony_ci}; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_iommu_of_ids[] = { 8658c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt2712-m4u", .data = &mt2712_data}, 8668c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt6779-m4u", .data = &mt6779_data}, 8678c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt8167-m4u", .data = &mt8167_data}, 8688c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data}, 8698c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data}, 8708c2ecf20Sopenharmony_ci {} 8718c2ecf20Sopenharmony_ci}; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic struct platform_driver mtk_iommu_driver = { 8748c2ecf20Sopenharmony_ci .probe = mtk_iommu_probe, 8758c2ecf20Sopenharmony_ci .remove = mtk_iommu_remove, 8768c2ecf20Sopenharmony_ci .driver = { 8778c2ecf20Sopenharmony_ci .name = "mtk-iommu", 8788c2ecf20Sopenharmony_ci .of_match_table = mtk_iommu_of_ids, 8798c2ecf20Sopenharmony_ci .pm = &mtk_iommu_pm_ops, 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci}; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic int __init mtk_iommu_init(void) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci int ret; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ret = platform_driver_register(&mtk_iommu_driver); 8888c2ecf20Sopenharmony_ci if (ret != 0) 8898c2ecf20Sopenharmony_ci pr_err("Failed to register MTK IOMMU driver\n"); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cisubsys_initcall(mtk_iommu_init) 895