18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// Copyright (C) 2019-2020 NVIDIA CORPORATION. All rights reserved. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 58c2ecf20Sopenharmony_ci#include <linux/delay.h> 68c2ecf20Sopenharmony_ci#include <linux/of.h> 78c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "arm-smmu.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * Tegra194 has three ARM MMU-500 Instances. 148c2ecf20Sopenharmony_ci * Two of them are used together and must be programmed identically for 158c2ecf20Sopenharmony_ci * interleaved IOVA accesses across them and translates accesses from 168c2ecf20Sopenharmony_ci * non-isochronous HW devices. 178c2ecf20Sopenharmony_ci * Third one is used for translating accesses from isochronous HW devices. 188c2ecf20Sopenharmony_ci * This implementation supports programming of the two instances that must 198c2ecf20Sopenharmony_ci * be programmed identically. 208c2ecf20Sopenharmony_ci * The third instance usage is through standard arm-smmu driver itself and 218c2ecf20Sopenharmony_ci * is out of scope of this implementation. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci#define NUM_SMMU_INSTANCES 2 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct nvidia_smmu { 268c2ecf20Sopenharmony_ci struct arm_smmu_device smmu; 278c2ecf20Sopenharmony_ci void __iomem *bases[NUM_SMMU_INSTANCES]; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline void __iomem *nvidia_smmu_page(struct arm_smmu_device *smmu, 318c2ecf20Sopenharmony_ci unsigned int inst, int page) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct nvidia_smmu *nvidia_smmu; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci nvidia_smmu = container_of(smmu, struct nvidia_smmu, smmu); 368c2ecf20Sopenharmony_ci return nvidia_smmu->bases[inst] + (page << smmu->pgshift); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic u32 nvidia_smmu_read_reg(struct arm_smmu_device *smmu, 408c2ecf20Sopenharmony_ci int page, int offset) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci void __iomem *reg = nvidia_smmu_page(smmu, 0, page) + offset; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return readl_relaxed(reg); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void nvidia_smmu_write_reg(struct arm_smmu_device *smmu, 488c2ecf20Sopenharmony_ci int page, int offset, u32 val) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci unsigned int i; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci for (i = 0; i < NUM_SMMU_INSTANCES; i++) { 538c2ecf20Sopenharmony_ci void __iomem *reg = nvidia_smmu_page(smmu, i, page) + offset; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci writel_relaxed(val, reg); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic u64 nvidia_smmu_read_reg64(struct arm_smmu_device *smmu, 608c2ecf20Sopenharmony_ci int page, int offset) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci void __iomem *reg = nvidia_smmu_page(smmu, 0, page) + offset; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return readq_relaxed(reg); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void nvidia_smmu_write_reg64(struct arm_smmu_device *smmu, 688c2ecf20Sopenharmony_ci int page, int offset, u64 val) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < NUM_SMMU_INSTANCES; i++) { 738c2ecf20Sopenharmony_ci void __iomem *reg = nvidia_smmu_page(smmu, i, page) + offset; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci writeq_relaxed(val, reg); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void nvidia_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, 808c2ecf20Sopenharmony_ci int sync, int status) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci unsigned int delay; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci arm_smmu_writel(smmu, page, sync, 0); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { 878c2ecf20Sopenharmony_ci unsigned int spin_cnt; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { 908c2ecf20Sopenharmony_ci u32 val = 0; 918c2ecf20Sopenharmony_ci unsigned int i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < NUM_SMMU_INSTANCES; i++) { 948c2ecf20Sopenharmony_ci void __iomem *reg; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci reg = nvidia_smmu_page(smmu, i, page) + status; 978c2ecf20Sopenharmony_ci val |= readl_relaxed(reg); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!(val & ARM_SMMU_sTLBGSTATUS_GSACTIVE)) 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci cpu_relax(); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci udelay(delay); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 1108c2ecf20Sopenharmony_ci "TLB sync timed out -- SMMU may be deadlocked\n"); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int nvidia_smmu_reset(struct arm_smmu_device *smmu) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci unsigned int i; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (i = 0; i < NUM_SMMU_INSTANCES; i++) { 1188c2ecf20Sopenharmony_ci u32 val; 1198c2ecf20Sopenharmony_ci void __iomem *reg = nvidia_smmu_page(smmu, i, ARM_SMMU_GR0) + 1208c2ecf20Sopenharmony_ci ARM_SMMU_GR0_sGFSR; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* clear global FSR */ 1238c2ecf20Sopenharmony_ci val = readl_relaxed(reg); 1248c2ecf20Sopenharmony_ci writel_relaxed(val, reg); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic irqreturn_t nvidia_smmu_global_fault_inst(int irq, 1318c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu, 1328c2ecf20Sopenharmony_ci int inst) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u32 gfsr, gfsynr0, gfsynr1, gfsynr2; 1358c2ecf20Sopenharmony_ci void __iomem *gr0_base = nvidia_smmu_page(smmu, inst, 0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR); 1388c2ecf20Sopenharmony_ci if (!gfsr) 1398c2ecf20Sopenharmony_ci return IRQ_NONE; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0); 1428c2ecf20Sopenharmony_ci gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1); 1438c2ecf20Sopenharmony_ci gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 1468c2ecf20Sopenharmony_ci "Unexpected global fault, this could be serious\n"); 1478c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 1488c2ecf20Sopenharmony_ci "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n", 1498c2ecf20Sopenharmony_ci gfsr, gfsynr0, gfsynr1, gfsynr2); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci writel_relaxed(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR); 1528c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic irqreturn_t nvidia_smmu_global_fault(int irq, void *dev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci unsigned int inst; 1588c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 1598c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) { 1628c2ecf20Sopenharmony_ci irqreturn_t irq_ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci irq_ret = nvidia_smmu_global_fault_inst(irq, smmu, inst); 1658c2ecf20Sopenharmony_ci if (irq_ret == IRQ_HANDLED) 1668c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic irqreturn_t nvidia_smmu_context_fault_bank(int irq, 1738c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu, 1748c2ecf20Sopenharmony_ci int idx, int inst) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci u32 fsr, fsynr, cbfrsynra; 1778c2ecf20Sopenharmony_ci unsigned long iova; 1788c2ecf20Sopenharmony_ci void __iomem *gr1_base = nvidia_smmu_page(smmu, inst, 1); 1798c2ecf20Sopenharmony_ci void __iomem *cb_base = nvidia_smmu_page(smmu, inst, smmu->numpage + idx); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); 1828c2ecf20Sopenharmony_ci if (!(fsr & ARM_SMMU_FSR_FAULT)) 1838c2ecf20Sopenharmony_ci return IRQ_NONE; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0); 1868c2ecf20Sopenharmony_ci iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR); 1878c2ecf20Sopenharmony_ci cbfrsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(idx)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 1908c2ecf20Sopenharmony_ci "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n", 1918c2ecf20Sopenharmony_ci fsr, iova, fsynr, cbfrsynra, idx); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR); 1948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic irqreturn_t nvidia_smmu_context_fault(int irq, void *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int idx; 2008c2ecf20Sopenharmony_ci unsigned int inst; 2018c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 2028c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 2038c2ecf20Sopenharmony_ci struct iommu_domain *domain = dev; 2048c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci smmu_domain = container_of(domain, struct arm_smmu_domain, domain); 2078c2ecf20Sopenharmony_ci smmu = smmu_domain->smmu; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) { 2108c2ecf20Sopenharmony_ci irqreturn_t irq_ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Interrupt line is shared between all contexts. 2148c2ecf20Sopenharmony_ci * Check for faults across all contexts. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci for (idx = 0; idx < smmu->num_context_banks; idx++) { 2178c2ecf20Sopenharmony_ci irq_ret = nvidia_smmu_context_fault_bank(irq, smmu, 2188c2ecf20Sopenharmony_ci idx, inst); 2198c2ecf20Sopenharmony_ci if (irq_ret == IRQ_HANDLED) 2208c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl nvidia_smmu_impl = { 2288c2ecf20Sopenharmony_ci .read_reg = nvidia_smmu_read_reg, 2298c2ecf20Sopenharmony_ci .write_reg = nvidia_smmu_write_reg, 2308c2ecf20Sopenharmony_ci .read_reg64 = nvidia_smmu_read_reg64, 2318c2ecf20Sopenharmony_ci .write_reg64 = nvidia_smmu_write_reg64, 2328c2ecf20Sopenharmony_ci .reset = nvidia_smmu_reset, 2338c2ecf20Sopenharmony_ci .tlb_sync = nvidia_smmu_tlb_sync, 2348c2ecf20Sopenharmony_ci .global_fault = nvidia_smmu_global_fault, 2358c2ecf20Sopenharmony_ci .context_fault = nvidia_smmu_context_fault, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistruct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct resource *res; 2418c2ecf20Sopenharmony_ci struct device *dev = smmu->dev; 2428c2ecf20Sopenharmony_ci struct nvidia_smmu *nvidia_smmu; 2438c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci nvidia_smmu = devm_kzalloc(dev, sizeof(*nvidia_smmu), GFP_KERNEL); 2468c2ecf20Sopenharmony_ci if (!nvidia_smmu) 2478c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * Copy the data from struct arm_smmu_device *smmu allocated in 2518c2ecf20Sopenharmony_ci * arm-smmu.c. The smmu from struct nvidia_smmu replaces the smmu 2528c2ecf20Sopenharmony_ci * pointer used in arm-smmu.c once this function returns. 2538c2ecf20Sopenharmony_ci * This is necessary to derive nvidia_smmu from smmu pointer passed 2548c2ecf20Sopenharmony_ci * through arm_smmu_impl function calls subsequently. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci nvidia_smmu->smmu = *smmu; 2578c2ecf20Sopenharmony_ci /* Instance 0 is ioremapped by arm-smmu.c. */ 2588c2ecf20Sopenharmony_ci nvidia_smmu->bases[0] = smmu->base; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 2618c2ecf20Sopenharmony_ci if (!res) 2628c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci nvidia_smmu->bases[1] = devm_ioremap_resource(dev, res); 2658c2ecf20Sopenharmony_ci if (IS_ERR(nvidia_smmu->bases[1])) 2668c2ecf20Sopenharmony_ci return ERR_CAST(nvidia_smmu->bases[1]); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci nvidia_smmu->smmu.impl = &nvidia_smmu_impl; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * Free the struct arm_smmu_device *smmu allocated in arm-smmu.c. 2728c2ecf20Sopenharmony_ci * Once this function returns, arm-smmu.c would use arm_smmu_device 2738c2ecf20Sopenharmony_ci * allocated as part of struct nvidia_smmu. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci devm_kfree(dev, smmu); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return &nvidia_smmu->smmu; 2788c2ecf20Sopenharmony_ci} 279