18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IOMMU API for ARM architected SMMU implementations. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 ARM Limited 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This driver currently supports: 108c2ecf20Sopenharmony_ci * - SMMUv1 and v2 implementations 118c2ecf20Sopenharmony_ci * - Stream-matching and stream-indexing 128c2ecf20Sopenharmony_ci * - v7/v8 long-descriptor format 138c2ecf20Sopenharmony_ci * - Non-secure access to the SMMU 148c2ecf20Sopenharmony_ci * - Context fault reporting 158c2ecf20Sopenharmony_ci * - Extended Stream ID (16 bit) 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "arm-smmu: " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/acpi.h> 218c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h> 228c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h> 258c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 268c2ecf20Sopenharmony_ci#include <linux/err.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/of.h> 328c2ecf20Sopenharmony_ci#include <linux/of_address.h> 338c2ecf20Sopenharmony_ci#include <linux/of_device.h> 348c2ecf20Sopenharmony_ci#include <linux/of_iommu.h> 358c2ecf20Sopenharmony_ci#include <linux/pci.h> 368c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 378c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 388c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 428c2ecf20Sopenharmony_ci#include <linux/fsl/mc.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include "arm-smmu.h" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU 488c2ecf20Sopenharmony_ci * global register space are still, in fact, using a hypervisor to mediate it 498c2ecf20Sopenharmony_ci * by trapping and emulating register accesses. Sadly, some deployed versions 508c2ecf20Sopenharmony_ci * of said trapping code have bugs wherein they go horribly wrong for stores 518c2ecf20Sopenharmony_ci * using r31 (i.e. XZR/WZR) as the source register. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#define QCOM_DUMMY_VAL -1 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MSI_IOVA_BASE 0x8000000 568c2ecf20Sopenharmony_ci#define MSI_IOVA_LENGTH 0x100000 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int force_stage; 598c2ecf20Sopenharmony_cimodule_param(force_stage, int, S_IRUGO); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_stage, 618c2ecf20Sopenharmony_ci "Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation."); 628c2ecf20Sopenharmony_cistatic bool disable_bypass = 638c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT); 648c2ecf20Sopenharmony_cimodule_param(disable_bypass, bool, S_IRUGO); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_bypass, 668c2ecf20Sopenharmony_ci "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU."); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define s2cr_init_val (struct arm_smmu_s2cr){ \ 698c2ecf20Sopenharmony_ci .type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS, \ 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic bool using_legacy_binding, using_generic_binding; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline int arm_smmu_rpm_get(struct arm_smmu_device *smmu) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 778c2ecf20Sopenharmony_ci return pm_runtime_resume_and_get(smmu->dev); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 858c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(smmu->dev); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return container_of(dom, struct arm_smmu_domain, domain); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver; 948c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS 978c2ecf20Sopenharmony_cistatic int arm_smmu_bus_init(struct iommu_ops *ops); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct device_node *dev_get_dev_node(struct device *dev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci if (dev_is_pci(dev)) { 1028c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_dev(dev)->bus; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci while (!pci_is_root_bus(bus)) 1058c2ecf20Sopenharmony_ci bus = bus->parent; 1068c2ecf20Sopenharmony_ci return of_node_get(bus->bridge->parent->of_node); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return of_node_get(dev->of_node); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci *((__be32 *)data) = cpu_to_be32(alias); 1158c2ecf20Sopenharmony_ci return 0; /* Continue walking */ 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int __find_legacy_master_phandle(struct device *dev, void *data) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct of_phandle_iterator *it = *(void **)data; 1218c2ecf20Sopenharmony_ci struct device_node *np = it->node; 1228c2ecf20Sopenharmony_ci int err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci of_for_each_phandle(it, err, dev->of_node, "mmu-masters", 1258c2ecf20Sopenharmony_ci "#stream-id-cells", -1) 1268c2ecf20Sopenharmony_ci if (it->node == np) { 1278c2ecf20Sopenharmony_ci *(void **)data = dev; 1288c2ecf20Sopenharmony_ci return 1; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci it->node = np; 1318c2ecf20Sopenharmony_ci return err == -ENOENT ? 0 : err; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int arm_smmu_register_legacy_master(struct device *dev, 1358c2ecf20Sopenharmony_ci struct arm_smmu_device **smmu) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct device *smmu_dev; 1388c2ecf20Sopenharmony_ci struct device_node *np; 1398c2ecf20Sopenharmony_ci struct of_phandle_iterator it; 1408c2ecf20Sopenharmony_ci void *data = ⁢ 1418c2ecf20Sopenharmony_ci u32 *sids; 1428c2ecf20Sopenharmony_ci __be32 pci_sid; 1438c2ecf20Sopenharmony_ci int err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci np = dev_get_dev_node(dev); 1468c2ecf20Sopenharmony_ci if (!np || !of_find_property(np, "#stream-id-cells", NULL)) { 1478c2ecf20Sopenharmony_ci of_node_put(np); 1488c2ecf20Sopenharmony_ci return -ENODEV; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci it.node = np; 1528c2ecf20Sopenharmony_ci err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data, 1538c2ecf20Sopenharmony_ci __find_legacy_master_phandle); 1548c2ecf20Sopenharmony_ci smmu_dev = data; 1558c2ecf20Sopenharmony_ci of_node_put(np); 1568c2ecf20Sopenharmony_ci if (err == 0) 1578c2ecf20Sopenharmony_ci return -ENODEV; 1588c2ecf20Sopenharmony_ci if (err < 0) 1598c2ecf20Sopenharmony_ci return err; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (dev_is_pci(dev)) { 1628c2ecf20Sopenharmony_ci /* "mmu-masters" assumes Stream ID == Requester ID */ 1638c2ecf20Sopenharmony_ci pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, 1648c2ecf20Sopenharmony_ci &pci_sid); 1658c2ecf20Sopenharmony_ci it.cur = &pci_sid; 1668c2ecf20Sopenharmony_ci it.cur_count = 1; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode, 1708c2ecf20Sopenharmony_ci &arm_smmu_ops); 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci return err; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL); 1758c2ecf20Sopenharmony_ci if (!sids) 1768c2ecf20Sopenharmony_ci return -ENOMEM; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci *smmu = dev_get_drvdata(smmu_dev); 1798c2ecf20Sopenharmony_ci of_phandle_iterator_args(&it, sids, it.cur_count); 1808c2ecf20Sopenharmony_ci err = iommu_fwspec_add_ids(dev, sids, it.cur_count); 1818c2ecf20Sopenharmony_ci kfree(sids); 1828c2ecf20Sopenharmony_ci return err; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * With the legacy DT binding in play, we have no guarantees about 1878c2ecf20Sopenharmony_ci * probe order, but then we're also not doing default domains, so we can 1888c2ecf20Sopenharmony_ci * delay setting bus ops until we're sure every possible SMMU is ready, 1898c2ecf20Sopenharmony_ci * and that way ensure that no probe_device() calls get missed. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistatic int arm_smmu_legacy_bus_init(void) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci if (using_legacy_binding) 1948c2ecf20Sopenharmony_ci return arm_smmu_bus_init(&arm_smmu_ops); 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_cidevice_initcall_sync(arm_smmu_legacy_bus_init); 1988c2ecf20Sopenharmony_ci#else 1998c2ecf20Sopenharmony_cistatic int arm_smmu_register_legacy_master(struct device *dev, 2008c2ecf20Sopenharmony_ci struct arm_smmu_device **smmu) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return -ENODEV; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci#endif /* CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void __arm_smmu_free_bitmap(unsigned long *map, int idx) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci clear_bit(idx, map); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* Wait for any pending TLB invalidations to complete */ 2128c2ecf20Sopenharmony_cistatic void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, 2138c2ecf20Sopenharmony_ci int sync, int status) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned int spin_cnt, delay; 2168c2ecf20Sopenharmony_ci u32 reg; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (smmu->impl && unlikely(smmu->impl->tlb_sync)) 2198c2ecf20Sopenharmony_ci return smmu->impl->tlb_sync(smmu, page, sync, status); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL); 2228c2ecf20Sopenharmony_ci for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { 2238c2ecf20Sopenharmony_ci for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { 2248c2ecf20Sopenharmony_ci reg = arm_smmu_readl(smmu, page, status); 2258c2ecf20Sopenharmony_ci if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE)) 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci cpu_relax(); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci udelay(delay); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 2328c2ecf20Sopenharmony_ci "TLB sync timed out -- SMMU may be deadlocked\n"); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci unsigned long flags; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu->global_sync_lock, flags); 2408c2ecf20Sopenharmony_ci __arm_smmu_tlb_sync(smmu, ARM_SMMU_GR0, ARM_SMMU_GR0_sTLBGSYNC, 2418c2ecf20Sopenharmony_ci ARM_SMMU_GR0_sTLBGSTATUS); 2428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu->global_sync_lock, flags); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_sync_context(struct arm_smmu_domain *smmu_domain) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 2488c2ecf20Sopenharmony_ci unsigned long flags; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->cb_lock, flags); 2518c2ecf20Sopenharmony_ci __arm_smmu_tlb_sync(smmu, ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx), 2528c2ecf20Sopenharmony_ci ARM_SMMU_CB_TLBSYNC, ARM_SMMU_CB_TLBSTATUS); 2538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_context_s1(void *cookie) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * The TLBI write may be relaxed, so ensure that PTEs cleared by the 2618c2ecf20Sopenharmony_ci * current CPU are visible beforehand. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci wmb(); 2648c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu_domain->smmu, smmu_domain->cfg.cbndx, 2658c2ecf20Sopenharmony_ci ARM_SMMU_CB_S1_TLBIASID, smmu_domain->cfg.asid); 2668c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(smmu_domain); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_context_s2(void *cookie) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 2728c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* See above */ 2758c2ecf20Sopenharmony_ci wmb(); 2768c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); 2778c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_range_s1(unsigned long iova, size_t size, 2818c2ecf20Sopenharmony_ci size_t granule, void *cookie, int reg) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 2848c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 2858c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 2868c2ecf20Sopenharmony_ci int idx = cfg->cbndx; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 2898c2ecf20Sopenharmony_ci wmb(); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (cfg->fmt != ARM_SMMU_CTX_FMT_AARCH64) { 2928c2ecf20Sopenharmony_ci iova = (iova >> 12) << 12; 2938c2ecf20Sopenharmony_ci iova |= cfg->asid; 2948c2ecf20Sopenharmony_ci do { 2958c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, reg, iova); 2968c2ecf20Sopenharmony_ci iova += granule; 2978c2ecf20Sopenharmony_ci } while (size -= granule); 2988c2ecf20Sopenharmony_ci } else { 2998c2ecf20Sopenharmony_ci iova >>= 12; 3008c2ecf20Sopenharmony_ci iova |= (u64)cfg->asid << 48; 3018c2ecf20Sopenharmony_ci do { 3028c2ecf20Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, reg, iova); 3038c2ecf20Sopenharmony_ci iova += granule >> 12; 3048c2ecf20Sopenharmony_ci } while (size -= granule); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_range_s2(unsigned long iova, size_t size, 3098c2ecf20Sopenharmony_ci size_t granule, void *cookie, int reg) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 3128c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 3138c2ecf20Sopenharmony_ci int idx = smmu_domain->cfg.cbndx; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 3168c2ecf20Sopenharmony_ci wmb(); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci iova >>= 12; 3198c2ecf20Sopenharmony_ci do { 3208c2ecf20Sopenharmony_ci if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64) 3218c2ecf20Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, reg, iova); 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, reg, iova); 3248c2ecf20Sopenharmony_ci iova += granule >> 12; 3258c2ecf20Sopenharmony_ci } while (size -= granule); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_walk_s1(unsigned long iova, size_t size, 3298c2ecf20Sopenharmony_ci size_t granule, void *cookie) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s1(iova, size, granule, cookie, 3328c2ecf20Sopenharmony_ci ARM_SMMU_CB_S1_TLBIVA); 3338c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_leaf_s1(unsigned long iova, size_t size, 3378c2ecf20Sopenharmony_ci size_t granule, void *cookie) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s1(iova, size, granule, cookie, 3408c2ecf20Sopenharmony_ci ARM_SMMU_CB_S1_TLBIVAL); 3418c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_add_page_s1(struct iommu_iotlb_gather *gather, 3458c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, 3468c2ecf20Sopenharmony_ci void *cookie) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s1(iova, granule, granule, cookie, 3498c2ecf20Sopenharmony_ci ARM_SMMU_CB_S1_TLBIVAL); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_walk_s2(unsigned long iova, size_t size, 3538c2ecf20Sopenharmony_ci size_t granule, void *cookie) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s2(iova, size, granule, cookie, 3568c2ecf20Sopenharmony_ci ARM_SMMU_CB_S2_TLBIIPAS2); 3578c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_leaf_s2(unsigned long iova, size_t size, 3618c2ecf20Sopenharmony_ci size_t granule, void *cookie) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s2(iova, size, granule, cookie, 3648c2ecf20Sopenharmony_ci ARM_SMMU_CB_S2_TLBIIPAS2L); 3658c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_add_page_s2(struct iommu_iotlb_gather *gather, 3698c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, 3708c2ecf20Sopenharmony_ci void *cookie) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range_s2(iova, granule, granule, cookie, 3738c2ecf20Sopenharmony_ci ARM_SMMU_CB_S2_TLBIIPAS2L); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_any_s2_v1(unsigned long iova, size_t size, 3778c2ecf20Sopenharmony_ci size_t granule, void *cookie) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_context_s2(cookie); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * On MMU-401 at least, the cost of firing off multiple TLBIVMIDs appears 3838c2ecf20Sopenharmony_ci * almost negligible, but the benefit of getting the first one in as far ahead 3848c2ecf20Sopenharmony_ci * of the sync as possible is significant, hence we don't just make this a 3858c2ecf20Sopenharmony_ci * no-op and call arm_smmu_tlb_inv_context_s2() from .iotlb_sync as you might 3868c2ecf20Sopenharmony_ci * think. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_add_page_s2_v1(struct iommu_iotlb_gather *gather, 3898c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, 3908c2ecf20Sopenharmony_ci void *cookie) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 3938c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 3968c2ecf20Sopenharmony_ci wmb(); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s1_tlb_ops = { 4028c2ecf20Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s1, 4038c2ecf20Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk_s1, 4048c2ecf20Sopenharmony_ci .tlb_flush_leaf = arm_smmu_tlb_inv_leaf_s1, 4058c2ecf20Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s1, 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s2_tlb_ops_v2 = { 4098c2ecf20Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s2, 4108c2ecf20Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk_s2, 4118c2ecf20Sopenharmony_ci .tlb_flush_leaf = arm_smmu_tlb_inv_leaf_s2, 4128c2ecf20Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s2, 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s2_tlb_ops_v1 = { 4168c2ecf20Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s2, 4178c2ecf20Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_any_s2_v1, 4188c2ecf20Sopenharmony_ci .tlb_flush_leaf = arm_smmu_tlb_inv_any_s2_v1, 4198c2ecf20Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s2_v1, 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_context_fault(int irq, void *dev) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci u32 fsr, fsynr, cbfrsynra; 4258c2ecf20Sopenharmony_ci unsigned long iova; 4268c2ecf20Sopenharmony_ci struct iommu_domain *domain = dev; 4278c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 4288c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 4298c2ecf20Sopenharmony_ci int idx = smmu_domain->cfg.cbndx; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR); 4328c2ecf20Sopenharmony_ci if (!(fsr & ARM_SMMU_FSR_FAULT)) 4338c2ecf20Sopenharmony_ci return IRQ_NONE; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci fsynr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSYNR0); 4368c2ecf20Sopenharmony_ci iova = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_FAR); 4378c2ecf20Sopenharmony_ci cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(idx)); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 4408c2ecf20Sopenharmony_ci "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n", 4418c2ecf20Sopenharmony_ci fsr, iova, fsynr, cbfrsynra, idx); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr); 4448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_global_fault(int irq, void *dev) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci u32 gfsr, gfsynr0, gfsynr1, gfsynr2; 4508c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 4518c2ecf20Sopenharmony_ci static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, 4528c2ecf20Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci gfsr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); 4558c2ecf20Sopenharmony_ci gfsynr0 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR0); 4568c2ecf20Sopenharmony_ci gfsynr1 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR1); 4578c2ecf20Sopenharmony_ci gfsynr2 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR2); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!gfsr) 4608c2ecf20Sopenharmony_ci return IRQ_NONE; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (__ratelimit(&rs)) { 4638c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT) && 4648c2ecf20Sopenharmony_ci (gfsr & ARM_SMMU_sGFSR_USF)) 4658c2ecf20Sopenharmony_ci dev_err(smmu->dev, 4668c2ecf20Sopenharmony_ci "Blocked unknown Stream ID 0x%hx; boot with \"arm-smmu.disable_bypass=0\" to allow, but this may have security implications\n", 4678c2ecf20Sopenharmony_ci (u16)gfsynr1); 4688c2ecf20Sopenharmony_ci else 4698c2ecf20Sopenharmony_ci dev_err(smmu->dev, 4708c2ecf20Sopenharmony_ci "Unexpected global fault, this could be serious\n"); 4718c2ecf20Sopenharmony_ci dev_err(smmu->dev, 4728c2ecf20Sopenharmony_ci "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n", 4738c2ecf20Sopenharmony_ci gfsr, gfsynr0, gfsynr1, gfsynr2); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, gfsr); 4778c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, 4818c2ecf20Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 4848c2ecf20Sopenharmony_ci struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx]; 4858c2ecf20Sopenharmony_ci bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci cb->cfg = cfg; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* TCR */ 4908c2ecf20Sopenharmony_ci if (stage1) { 4918c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 4928c2ecf20Sopenharmony_ci cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr; 4938c2ecf20Sopenharmony_ci } else { 4948c2ecf20Sopenharmony_ci cb->tcr[0] = arm_smmu_lpae_tcr(pgtbl_cfg); 4958c2ecf20Sopenharmony_ci cb->tcr[1] = arm_smmu_lpae_tcr2(pgtbl_cfg); 4968c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 4978c2ecf20Sopenharmony_ci cb->tcr[1] |= ARM_SMMU_TCR2_AS; 4988c2ecf20Sopenharmony_ci else 4998c2ecf20Sopenharmony_ci cb->tcr[0] |= ARM_SMMU_TCR_EAE; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } else { 5028c2ecf20Sopenharmony_ci cb->tcr[0] = arm_smmu_lpae_vtcr(pgtbl_cfg); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* TTBRs */ 5068c2ecf20Sopenharmony_ci if (stage1) { 5078c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 5088c2ecf20Sopenharmony_ci cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr; 5098c2ecf20Sopenharmony_ci cb->ttbr[1] = 0; 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci cb->ttbr[0] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, 5128c2ecf20Sopenharmony_ci cfg->asid); 5138c2ecf20Sopenharmony_ci cb->ttbr[1] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, 5148c2ecf20Sopenharmony_ci cfg->asid); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (pgtbl_cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) 5178c2ecf20Sopenharmony_ci cb->ttbr[1] |= pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 5188c2ecf20Sopenharmony_ci else 5198c2ecf20Sopenharmony_ci cb->ttbr[0] |= pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } else { 5228c2ecf20Sopenharmony_ci cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* MAIRs (stage-1 only) */ 5268c2ecf20Sopenharmony_ci if (stage1) { 5278c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 5288c2ecf20Sopenharmony_ci cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr; 5298c2ecf20Sopenharmony_ci cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr; 5308c2ecf20Sopenharmony_ci } else { 5318c2ecf20Sopenharmony_ci cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair; 5328c2ecf20Sopenharmony_ci cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair >> 32; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_civoid arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci u32 reg; 5408c2ecf20Sopenharmony_ci bool stage1; 5418c2ecf20Sopenharmony_ci struct arm_smmu_cb *cb = &smmu->cbs[idx]; 5428c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = cb->cfg; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Unassigned context banks only need disabling */ 5458c2ecf20Sopenharmony_ci if (!cfg) { 5468c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, 0); 5478c2ecf20Sopenharmony_ci return; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* CBA2R */ 5538c2ecf20Sopenharmony_ci if (smmu->version > ARM_SMMU_V1) { 5548c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 5558c2ecf20Sopenharmony_ci reg = ARM_SMMU_CBA2R_VA64; 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci reg = 0; 5588c2ecf20Sopenharmony_ci /* 16-bit VMIDs live in CBA2R */ 5598c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_VMID16) 5608c2ecf20Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBA2R_VMID16, cfg->vmid); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBA2R(idx), reg); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* CBAR */ 5668c2ecf20Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, cfg->cbar); 5678c2ecf20Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) 5688c2ecf20Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_IRPTNDX, cfg->irptndx); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * Use the weakest shareability/memory types, so they are 5728c2ecf20Sopenharmony_ci * overridden by the ttbcr/pte. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci if (stage1) { 5758c2ecf20Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_S1_BPSHCFG, 5768c2ecf20Sopenharmony_ci ARM_SMMU_CBAR_S1_BPSHCFG_NSH) | 5778c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_CBAR_S1_MEMATTR, 5788c2ecf20Sopenharmony_ci ARM_SMMU_CBAR_S1_MEMATTR_WB); 5798c2ecf20Sopenharmony_ci } else if (!(smmu->features & ARM_SMMU_FEAT_VMID16)) { 5808c2ecf20Sopenharmony_ci /* 8-bit VMIDs live in CBAR */ 5818c2ecf20Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_VMID, cfg->vmid); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(idx), reg); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * TCR 5878c2ecf20Sopenharmony_ci * We must write this before the TTBRs, since it determines the 5888c2ecf20Sopenharmony_ci * access behaviour of some fields (in particular, ASID[15:8]). 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ci if (stage1 && smmu->version > ARM_SMMU_V1) 5918c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR2, cb->tcr[1]); 5928c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR, cb->tcr[0]); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* TTBRs */ 5958c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 5968c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_CONTEXTIDR, cfg->asid); 5978c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); 5988c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR1, cb->ttbr[1]); 5998c2ecf20Sopenharmony_ci } else { 6008c2ecf20Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); 6018c2ecf20Sopenharmony_ci if (stage1) 6028c2ecf20Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR1, 6038c2ecf20Sopenharmony_ci cb->ttbr[1]); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* MAIRs (stage-1 only) */ 6078c2ecf20Sopenharmony_ci if (stage1) { 6088c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR0, cb->mair[0]); 6098c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR1, cb->mair[1]); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* SCTLR */ 6138c2ecf20Sopenharmony_ci reg = ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE | ARM_SMMU_SCTLR_AFE | 6148c2ecf20Sopenharmony_ci ARM_SMMU_SCTLR_TRE | ARM_SMMU_SCTLR_M; 6158c2ecf20Sopenharmony_ci if (stage1) 6168c2ecf20Sopenharmony_ci reg |= ARM_SMMU_SCTLR_S1_ASIDPNE; 6178c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 6188c2ecf20Sopenharmony_ci reg |= ARM_SMMU_SCTLR_E; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg); 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int arm_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain, 6248c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu, 6258c2ecf20Sopenharmony_ci struct device *dev, unsigned int start) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->alloc_context_bank) 6288c2ecf20Sopenharmony_ci return smmu->impl->alloc_context_bank(smmu_domain, smmu, dev, start); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return __arm_smmu_alloc_bitmap(smmu->context_map, start, smmu->num_context_banks); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int arm_smmu_init_domain_context(struct iommu_domain *domain, 6348c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu, 6358c2ecf20Sopenharmony_ci struct device *dev) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci int irq, start, ret = 0; 6388c2ecf20Sopenharmony_ci unsigned long ias, oas; 6398c2ecf20Sopenharmony_ci struct io_pgtable_ops *pgtbl_ops; 6408c2ecf20Sopenharmony_ci struct io_pgtable_cfg pgtbl_cfg; 6418c2ecf20Sopenharmony_ci enum io_pgtable_fmt fmt; 6428c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 6438c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 6448c2ecf20Sopenharmony_ci irqreturn_t (*context_fault)(int irq, void *dev); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 6478c2ecf20Sopenharmony_ci if (smmu_domain->smmu) 6488c2ecf20Sopenharmony_ci goto out_unlock; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) { 6518c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS; 6528c2ecf20Sopenharmony_ci smmu_domain->smmu = smmu; 6538c2ecf20Sopenharmony_ci goto out_unlock; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* 6578c2ecf20Sopenharmony_ci * Mapping the requested stage onto what we support is surprisingly 6588c2ecf20Sopenharmony_ci * complicated, mainly because the spec allows S1+S2 SMMUs without 6598c2ecf20Sopenharmony_ci * support for nested translation. That means we end up with the 6608c2ecf20Sopenharmony_ci * following table: 6618c2ecf20Sopenharmony_ci * 6628c2ecf20Sopenharmony_ci * Requested Supported Actual 6638c2ecf20Sopenharmony_ci * S1 N S1 6648c2ecf20Sopenharmony_ci * S1 S1+S2 S1 6658c2ecf20Sopenharmony_ci * S1 S2 S2 6668c2ecf20Sopenharmony_ci * S1 S1 S1 6678c2ecf20Sopenharmony_ci * N N N 6688c2ecf20Sopenharmony_ci * N S1+S2 S2 6698c2ecf20Sopenharmony_ci * N S2 S2 6708c2ecf20Sopenharmony_ci * N S1 S1 6718c2ecf20Sopenharmony_ci * 6728c2ecf20Sopenharmony_ci * Note that you can't actually request stage-2 mappings. 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) 6758c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S2; 6768c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) 6778c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* 6808c2ecf20Sopenharmony_ci * Choosing a suitable context format is even more fiddly. Until we 6818c2ecf20Sopenharmony_ci * grow some way for the caller to express a preference, and/or move 6828c2ecf20Sopenharmony_ci * the decision into the io-pgtable code where it arguably belongs, 6838c2ecf20Sopenharmony_ci * just aim for the closest thing to the rest of the system, and hope 6848c2ecf20Sopenharmony_ci * that the hardware isn't esoteric enough that we can't assume AArch64 6858c2ecf20Sopenharmony_ci * support to be a superset of AArch32 support... 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_L) 6888c2ecf20Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_L; 6898c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) && 6908c2ecf20Sopenharmony_ci !IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_ARM_LPAE) && 6918c2ecf20Sopenharmony_ci (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) && 6928c2ecf20Sopenharmony_ci (smmu_domain->stage == ARM_SMMU_DOMAIN_S1)) 6938c2ecf20Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_S; 6948c2ecf20Sopenharmony_ci if ((IS_ENABLED(CONFIG_64BIT) || cfg->fmt == ARM_SMMU_CTX_FMT_NONE) && 6958c2ecf20Sopenharmony_ci (smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K | 6968c2ecf20Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_16K | 6978c2ecf20Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_4K))) 6988c2ecf20Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH64; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) { 7018c2ecf20Sopenharmony_ci ret = -EINVAL; 7028c2ecf20Sopenharmony_ci goto out_unlock; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci switch (smmu_domain->stage) { 7068c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 7078c2ecf20Sopenharmony_ci cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; 7088c2ecf20Sopenharmony_ci start = smmu->num_s2_context_banks; 7098c2ecf20Sopenharmony_ci ias = smmu->va_size; 7108c2ecf20Sopenharmony_ci oas = smmu->ipa_size; 7118c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) { 7128c2ecf20Sopenharmony_ci fmt = ARM_64_LPAE_S1; 7138c2ecf20Sopenharmony_ci } else if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_L) { 7148c2ecf20Sopenharmony_ci fmt = ARM_32_LPAE_S1; 7158c2ecf20Sopenharmony_ci ias = min(ias, 32UL); 7168c2ecf20Sopenharmony_ci oas = min(oas, 40UL); 7178c2ecf20Sopenharmony_ci } else { 7188c2ecf20Sopenharmony_ci fmt = ARM_V7S; 7198c2ecf20Sopenharmony_ci ias = min(ias, 32UL); 7208c2ecf20Sopenharmony_ci oas = min(oas, 32UL); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s1_tlb_ops; 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * We will likely want to change this if/when KVM gets 7278c2ecf20Sopenharmony_ci * involved. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 7308c2ecf20Sopenharmony_ci cfg->cbar = CBAR_TYPE_S2_TRANS; 7318c2ecf20Sopenharmony_ci start = 0; 7328c2ecf20Sopenharmony_ci ias = smmu->ipa_size; 7338c2ecf20Sopenharmony_ci oas = smmu->pa_size; 7348c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) { 7358c2ecf20Sopenharmony_ci fmt = ARM_64_LPAE_S2; 7368c2ecf20Sopenharmony_ci } else { 7378c2ecf20Sopenharmony_ci fmt = ARM_32_LPAE_S2; 7388c2ecf20Sopenharmony_ci ias = min(ias, 40UL); 7398c2ecf20Sopenharmony_ci oas = min(oas, 40UL); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci if (smmu->version == ARM_SMMU_V2) 7428c2ecf20Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v2; 7438c2ecf20Sopenharmony_ci else 7448c2ecf20Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v1; 7458c2ecf20Sopenharmony_ci break; 7468c2ecf20Sopenharmony_ci default: 7478c2ecf20Sopenharmony_ci ret = -EINVAL; 7488c2ecf20Sopenharmony_ci goto out_unlock; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = arm_smmu_alloc_context_bank(smmu_domain, smmu, dev, start); 7528c2ecf20Sopenharmony_ci if (ret < 0) { 7538c2ecf20Sopenharmony_ci goto out_unlock; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci smmu_domain->smmu = smmu; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci cfg->cbndx = ret; 7598c2ecf20Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) { 7608c2ecf20Sopenharmony_ci cfg->irptndx = atomic_inc_return(&smmu->irptndx); 7618c2ecf20Sopenharmony_ci cfg->irptndx %= smmu->num_context_irqs; 7628c2ecf20Sopenharmony_ci } else { 7638c2ecf20Sopenharmony_ci cfg->irptndx = cfg->cbndx; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) 7678c2ecf20Sopenharmony_ci cfg->vmid = cfg->cbndx + 1; 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci cfg->asid = cfg->cbndx; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci pgtbl_cfg = (struct io_pgtable_cfg) { 7728c2ecf20Sopenharmony_ci .pgsize_bitmap = smmu->pgsize_bitmap, 7738c2ecf20Sopenharmony_ci .ias = ias, 7748c2ecf20Sopenharmony_ci .oas = oas, 7758c2ecf20Sopenharmony_ci .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK, 7768c2ecf20Sopenharmony_ci .tlb = smmu_domain->flush_ops, 7778c2ecf20Sopenharmony_ci .iommu_dev = smmu->dev, 7788c2ecf20Sopenharmony_ci }; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->init_context) { 7818c2ecf20Sopenharmony_ci ret = smmu->impl->init_context(smmu_domain, &pgtbl_cfg, dev); 7828c2ecf20Sopenharmony_ci if (ret) 7838c2ecf20Sopenharmony_ci goto out_clear_smmu; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (smmu_domain->non_strict) 7878c2ecf20Sopenharmony_ci pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); 7908c2ecf20Sopenharmony_ci if (!pgtbl_ops) { 7918c2ecf20Sopenharmony_ci ret = -ENOMEM; 7928c2ecf20Sopenharmony_ci goto out_clear_smmu; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* Update the domain's page sizes to reflect the page table format */ 7968c2ecf20Sopenharmony_ci domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (pgtbl_cfg.quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) { 7998c2ecf20Sopenharmony_ci domain->geometry.aperture_start = ~0UL << ias; 8008c2ecf20Sopenharmony_ci domain->geometry.aperture_end = ~0UL; 8018c2ecf20Sopenharmony_ci } else { 8028c2ecf20Sopenharmony_ci domain->geometry.aperture_end = (1UL << ias) - 1; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci domain->geometry.force_aperture = true; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* Initialise the context bank with our page table cfg */ 8088c2ecf20Sopenharmony_ci arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); 8098c2ecf20Sopenharmony_ci arm_smmu_write_context_bank(smmu, cfg->cbndx); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* 8128c2ecf20Sopenharmony_ci * Request context fault interrupt. Do this last to avoid the 8138c2ecf20Sopenharmony_ci * handler seeing a half-initialised domain state. 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_ci irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->context_fault) 8188c2ecf20Sopenharmony_ci context_fault = smmu->impl->context_fault; 8198c2ecf20Sopenharmony_ci else 8208c2ecf20Sopenharmony_ci context_fault = arm_smmu_context_fault; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci ret = devm_request_irq(smmu->dev, irq, context_fault, 8238c2ecf20Sopenharmony_ci IRQF_SHARED, "arm-smmu-context-fault", domain); 8248c2ecf20Sopenharmony_ci if (ret < 0) { 8258c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n", 8268c2ecf20Sopenharmony_ci cfg->irptndx, irq); 8278c2ecf20Sopenharmony_ci cfg->irptndx = ARM_SMMU_INVALID_IRPTNDX; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* Publish page table ops for map/unmap */ 8338c2ecf20Sopenharmony_ci smmu_domain->pgtbl_ops = pgtbl_ops; 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ciout_clear_smmu: 8378c2ecf20Sopenharmony_ci __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); 8388c2ecf20Sopenharmony_ci smmu_domain->smmu = NULL; 8398c2ecf20Sopenharmony_ciout_unlock: 8408c2ecf20Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 8418c2ecf20Sopenharmony_ci return ret; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void arm_smmu_destroy_domain_context(struct iommu_domain *domain) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 8478c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 8488c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 8498c2ecf20Sopenharmony_ci int ret, irq; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY) 8528c2ecf20Sopenharmony_ci return; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 8558c2ecf20Sopenharmony_ci if (ret < 0) 8568c2ecf20Sopenharmony_ci return; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* 8598c2ecf20Sopenharmony_ci * Disable the context bank and free the page tables before freeing 8608c2ecf20Sopenharmony_ci * it. 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_ci smmu->cbs[cfg->cbndx].cfg = NULL; 8638c2ecf20Sopenharmony_ci arm_smmu_write_context_bank(smmu, cfg->cbndx); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (cfg->irptndx != ARM_SMMU_INVALID_IRPTNDX) { 8668c2ecf20Sopenharmony_ci irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; 8678c2ecf20Sopenharmony_ci devm_free_irq(smmu->dev, irq, domain); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci free_io_pgtable_ops(smmu_domain->pgtbl_ops); 8718c2ecf20Sopenharmony_ci __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED && 8818c2ecf20Sopenharmony_ci type != IOMMU_DOMAIN_DMA && 8828c2ecf20Sopenharmony_ci type != IOMMU_DOMAIN_IDENTITY) 8838c2ecf20Sopenharmony_ci return NULL; 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * Allocate the domain and initialise some of its data structures. 8868c2ecf20Sopenharmony_ci * We can't really do anything meaningful until we've added a 8878c2ecf20Sopenharmony_ci * master. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); 8908c2ecf20Sopenharmony_ci if (!smmu_domain) 8918c2ecf20Sopenharmony_ci return NULL; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding || 8948c2ecf20Sopenharmony_ci iommu_get_dma_cookie(&smmu_domain->domain))) { 8958c2ecf20Sopenharmony_ci kfree(smmu_domain); 8968c2ecf20Sopenharmony_ci return NULL; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci mutex_init(&smmu_domain->init_mutex); 9008c2ecf20Sopenharmony_ci spin_lock_init(&smmu_domain->cb_lock); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return &smmu_domain->domain; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* 9108c2ecf20Sopenharmony_ci * Free the domain resources. We assume that all devices have 9118c2ecf20Sopenharmony_ci * already been detached. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci iommu_put_dma_cookie(domain); 9148c2ecf20Sopenharmony_ci arm_smmu_destroy_domain_context(domain); 9158c2ecf20Sopenharmony_ci kfree(smmu_domain); 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct arm_smmu_smr *smr = smmu->smrs + idx; 9218c2ecf20Sopenharmony_ci u32 reg = FIELD_PREP(ARM_SMMU_SMR_ID, smr->id) | 9228c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_SMR_MASK, smr->mask); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid) 9258c2ecf20Sopenharmony_ci reg |= ARM_SMMU_SMR_VALID; 9268c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), reg); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; 9328c2ecf20Sopenharmony_ci u32 reg; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->write_s2cr) { 9358c2ecf20Sopenharmony_ci smmu->impl->write_s2cr(smmu, idx); 9368c2ecf20Sopenharmony_ci return; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, s2cr->type) | 9408c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_CBNDX, s2cr->cbndx) | 9418c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs && 9448c2ecf20Sopenharmony_ci smmu->smrs[idx].valid) 9458c2ecf20Sopenharmony_ci reg |= ARM_SMMU_S2CR_EXIDVALID; 9468c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci arm_smmu_write_s2cr(smmu, idx); 9528c2ecf20Sopenharmony_ci if (smmu->smrs) 9538c2ecf20Sopenharmony_ci arm_smmu_write_smr(smmu, idx); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * The width of SMR's mask field depends on sCR0_EXIDENABLE, so this function 9588c2ecf20Sopenharmony_ci * should be called after sCR0 is written. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_cistatic void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci u32 smr; 9638c2ecf20Sopenharmony_ci int i; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (!smmu->smrs) 9668c2ecf20Sopenharmony_ci return; 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * If we've had to accommodate firmware memory regions, we may 9698c2ecf20Sopenharmony_ci * have live SMRs by now; tread carefully... 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * Somewhat perversely, not having a free SMR for this test implies we 9728c2ecf20Sopenharmony_ci * can get away without it anyway, as we'll only be able to 'allocate' 9738c2ecf20Sopenharmony_ci * these SMRs for the ID/mask values we're already trusting to be OK. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; i++) 9768c2ecf20Sopenharmony_ci if (!smmu->smrs[i].valid) 9778c2ecf20Sopenharmony_ci goto smr_ok; 9788c2ecf20Sopenharmony_ci return; 9798c2ecf20Sopenharmony_cismr_ok: 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * SMR.ID bits may not be preserved if the corresponding MASK 9828c2ecf20Sopenharmony_ci * bits are set, so check each one separately. We can reject 9838c2ecf20Sopenharmony_ci * masters later if they try to claim IDs outside these masks. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci smr = FIELD_PREP(ARM_SMMU_SMR_ID, smmu->streamid_mask); 9868c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(i), smr); 9878c2ecf20Sopenharmony_ci smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); 9888c2ecf20Sopenharmony_ci smmu->streamid_mask = FIELD_GET(ARM_SMMU_SMR_ID, smr); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci smr = FIELD_PREP(ARM_SMMU_SMR_MASK, smmu->streamid_mask); 9918c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(i), smr); 9928c2ecf20Sopenharmony_ci smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); 9938c2ecf20Sopenharmony_ci smmu->smr_mask_mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct arm_smmu_smr *smrs = smmu->smrs; 9998c2ecf20Sopenharmony_ci int i, free_idx = -ENOSPC; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Stream indexing is blissfully easy */ 10028c2ecf20Sopenharmony_ci if (!smrs) 10038c2ecf20Sopenharmony_ci return id; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* Validating SMRs is... less so */ 10068c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; ++i) { 10078c2ecf20Sopenharmony_ci if (!smrs[i].valid) { 10088c2ecf20Sopenharmony_ci /* 10098c2ecf20Sopenharmony_ci * Note the first free entry we come across, which 10108c2ecf20Sopenharmony_ci * we'll claim in the end if nothing else matches. 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_ci if (free_idx < 0) 10138c2ecf20Sopenharmony_ci free_idx = i; 10148c2ecf20Sopenharmony_ci continue; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * If the new entry is _entirely_ matched by an existing entry, 10188c2ecf20Sopenharmony_ci * then reuse that, with the guarantee that there also cannot 10198c2ecf20Sopenharmony_ci * be any subsequent conflicting entries. In normal use we'd 10208c2ecf20Sopenharmony_ci * expect simply identical entries for this case, but there's 10218c2ecf20Sopenharmony_ci * no harm in accommodating the generalisation. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci if ((mask & smrs[i].mask) == mask && 10248c2ecf20Sopenharmony_ci !((id ^ smrs[i].id) & ~smrs[i].mask)) 10258c2ecf20Sopenharmony_ci return i; 10268c2ecf20Sopenharmony_ci /* 10278c2ecf20Sopenharmony_ci * If the new entry has any other overlap with an existing one, 10288c2ecf20Sopenharmony_ci * though, then there always exists at least one stream ID 10298c2ecf20Sopenharmony_ci * which would cause a conflict, and we can't allow that risk. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask))) 10328c2ecf20Sopenharmony_ci return -EINVAL; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return free_idx; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci if (--smmu->s2crs[idx].count) 10418c2ecf20Sopenharmony_ci return false; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci smmu->s2crs[idx] = s2cr_init_val; 10448c2ecf20Sopenharmony_ci if (smmu->smrs) 10458c2ecf20Sopenharmony_ci smmu->smrs[idx].valid = false; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return true; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic int arm_smmu_master_alloc_smes(struct device *dev) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 10538c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 10548c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 10558c2ecf20Sopenharmony_ci struct arm_smmu_smr *smrs = smmu->smrs; 10568c2ecf20Sopenharmony_ci int i, idx, ret; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci mutex_lock(&smmu->stream_map_mutex); 10598c2ecf20Sopenharmony_ci /* Figure out a viable stream map entry allocation */ 10608c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 10618c2ecf20Sopenharmony_ci u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]); 10628c2ecf20Sopenharmony_ci u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (idx != INVALID_SMENDX) { 10658c2ecf20Sopenharmony_ci ret = -EEXIST; 10668c2ecf20Sopenharmony_ci goto out_err; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ret = arm_smmu_find_sme(smmu, sid, mask); 10708c2ecf20Sopenharmony_ci if (ret < 0) 10718c2ecf20Sopenharmony_ci goto out_err; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci idx = ret; 10748c2ecf20Sopenharmony_ci if (smrs && smmu->s2crs[idx].count == 0) { 10758c2ecf20Sopenharmony_ci smrs[idx].id = sid; 10768c2ecf20Sopenharmony_ci smrs[idx].mask = mask; 10778c2ecf20Sopenharmony_ci smrs[idx].valid = true; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci smmu->s2crs[idx].count++; 10808c2ecf20Sopenharmony_ci cfg->smendx[i] = (s16)idx; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* It worked! Now, poke the actual hardware */ 10848c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) 10858c2ecf20Sopenharmony_ci arm_smmu_write_sme(smmu, idx); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ciout_err: 10918c2ecf20Sopenharmony_ci while (i--) { 10928c2ecf20Sopenharmony_ci arm_smmu_free_sme(smmu, cfg->smendx[i]); 10938c2ecf20Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 10968c2ecf20Sopenharmony_ci return ret; 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg, 11008c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 11038c2ecf20Sopenharmony_ci int i, idx; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci mutex_lock(&smmu->stream_map_mutex); 11068c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 11078c2ecf20Sopenharmony_ci if (arm_smmu_free_sme(smmu, idx)) 11088c2ecf20Sopenharmony_ci arm_smmu_write_sme(smmu, idx); 11098c2ecf20Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, 11158c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg, 11168c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 11198c2ecf20Sopenharmony_ci struct arm_smmu_s2cr *s2cr = smmu->s2crs; 11208c2ecf20Sopenharmony_ci u8 cbndx = smmu_domain->cfg.cbndx; 11218c2ecf20Sopenharmony_ci enum arm_smmu_s2cr_type type; 11228c2ecf20Sopenharmony_ci int i, idx; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS) 11258c2ecf20Sopenharmony_ci type = S2CR_TYPE_BYPASS; 11268c2ecf20Sopenharmony_ci else 11278c2ecf20Sopenharmony_ci type = S2CR_TYPE_TRANS; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 11308c2ecf20Sopenharmony_ci if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx) 11318c2ecf20Sopenharmony_ci continue; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci s2cr[idx].type = type; 11348c2ecf20Sopenharmony_ci s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT; 11358c2ecf20Sopenharmony_ci s2cr[idx].cbndx = cbndx; 11368c2ecf20Sopenharmony_ci arm_smmu_write_s2cr(smmu, idx); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci return 0; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 11448c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 11458c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 11468c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 11478c2ecf20Sopenharmony_ci int ret; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) { 11508c2ecf20Sopenharmony_ci dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); 11518c2ecf20Sopenharmony_ci return -ENXIO; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* 11558c2ecf20Sopenharmony_ci * FIXME: The arch/arm DMA API code tries to attach devices to its own 11568c2ecf20Sopenharmony_ci * domains between of_xlate() and probe_device() - we have no way to cope 11578c2ecf20Sopenharmony_ci * with that, so until ARM gets converted to rely on groups and default 11588c2ecf20Sopenharmony_ci * domains, just say no (but more politely than by dereferencing NULL). 11598c2ecf20Sopenharmony_ci * This should be at least a WARN_ON once that's sorted. 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_ci cfg = dev_iommu_priv_get(dev); 11628c2ecf20Sopenharmony_ci if (!cfg) 11638c2ecf20Sopenharmony_ci return -ENODEV; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci smmu = cfg->smmu; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 11688c2ecf20Sopenharmony_ci if (ret < 0) 11698c2ecf20Sopenharmony_ci return ret; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* Ensure that the domain is finalised */ 11728c2ecf20Sopenharmony_ci ret = arm_smmu_init_domain_context(domain, smmu, dev); 11738c2ecf20Sopenharmony_ci if (ret < 0) 11748c2ecf20Sopenharmony_ci goto rpm_put; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* 11778c2ecf20Sopenharmony_ci * Sanity check the domain. We don't support domains across 11788c2ecf20Sopenharmony_ci * different SMMUs. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_ci if (smmu_domain->smmu != smmu) { 11818c2ecf20Sopenharmony_ci dev_err(dev, 11828c2ecf20Sopenharmony_ci "cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n", 11838c2ecf20Sopenharmony_ci dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev)); 11848c2ecf20Sopenharmony_ci ret = -EINVAL; 11858c2ecf20Sopenharmony_ci goto rpm_put; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci /* Looks ok, so add the device to the domain */ 11898c2ecf20Sopenharmony_ci ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * Setup an autosuspend delay to avoid bouncing runpm state. 11938c2ecf20Sopenharmony_ci * Otherwise, if a driver for a suspended consumer device 11948c2ecf20Sopenharmony_ci * unmaps buffers, it will runpm resume/suspend for each one. 11958c2ecf20Sopenharmony_ci * 11968c2ecf20Sopenharmony_ci * For example, when used by a GPU device, when an application 11978c2ecf20Sopenharmony_ci * or game exits, it can trigger unmapping 100s or 1000s of 11988c2ecf20Sopenharmony_ci * buffers. With a runpm cycle for each buffer, that adds up 11998c2ecf20Sopenharmony_ci * to 5-10sec worth of reprogramming the context bank, while 12008c2ecf20Sopenharmony_ci * the system appears to be locked up to the user. 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(smmu->dev, 20); 12038c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(smmu->dev); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cirpm_put: 12068c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 12078c2ecf20Sopenharmony_ci return ret; 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, 12118c2ecf20Sopenharmony_ci phys_addr_t paddr, size_t size, int prot, gfp_t gfp) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 12148c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; 12158c2ecf20Sopenharmony_ci int ret; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (!ops) 12188c2ecf20Sopenharmony_ci return -ENODEV; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci arm_smmu_rpm_get(smmu); 12218c2ecf20Sopenharmony_ci ret = ops->map(ops, iova, paddr, size, prot, gfp); 12228c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return ret; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, 12288c2ecf20Sopenharmony_ci size_t size, struct iommu_iotlb_gather *gather) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 12318c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; 12328c2ecf20Sopenharmony_ci size_t ret; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (!ops) 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci arm_smmu_rpm_get(smmu); 12388c2ecf20Sopenharmony_ci ret = ops->unmap(ops, iova, size, gather); 12398c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return ret; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 12478c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (smmu_domain->flush_ops) { 12508c2ecf20Sopenharmony_ci arm_smmu_rpm_get(smmu); 12518c2ecf20Sopenharmony_ci smmu_domain->flush_ops->tlb_flush_all(smmu_domain); 12528c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain, 12578c2ecf20Sopenharmony_ci struct iommu_iotlb_gather *gather) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 12608c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (!smmu) 12638c2ecf20Sopenharmony_ci return; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci arm_smmu_rpm_get(smmu); 12668c2ecf20Sopenharmony_ci if (smmu->version == ARM_SMMU_V2 || 12678c2ecf20Sopenharmony_ci smmu_domain->stage == ARM_SMMU_DOMAIN_S1) 12688c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_context(smmu_domain); 12698c2ecf20Sopenharmony_ci else 12708c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 12718c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_cistatic phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, 12758c2ecf20Sopenharmony_ci dma_addr_t iova) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 12788c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 12798c2ecf20Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 12808c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; 12818c2ecf20Sopenharmony_ci struct device *dev = smmu->dev; 12828c2ecf20Sopenharmony_ci void __iomem *reg; 12838c2ecf20Sopenharmony_ci u32 tmp; 12848c2ecf20Sopenharmony_ci u64 phys; 12858c2ecf20Sopenharmony_ci unsigned long va, flags; 12868c2ecf20Sopenharmony_ci int ret, idx = cfg->cbndx; 12878c2ecf20Sopenharmony_ci phys_addr_t addr = 0; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 12908c2ecf20Sopenharmony_ci if (ret < 0) 12918c2ecf20Sopenharmony_ci return 0; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->cb_lock, flags); 12948c2ecf20Sopenharmony_ci va = iova & ~0xfffUL; 12958c2ecf20Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 12968c2ecf20Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_ATS1PR, va); 12978c2ecf20Sopenharmony_ci else 12988c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ATS1PR, va); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci reg = arm_smmu_page(smmu, ARM_SMMU_CB(smmu, idx)) + ARM_SMMU_CB_ATSR; 13018c2ecf20Sopenharmony_ci if (readl_poll_timeout_atomic(reg, tmp, !(tmp & ARM_SMMU_ATSR_ACTIVE), 13028c2ecf20Sopenharmony_ci 5, 50)) { 13038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 13048c2ecf20Sopenharmony_ci dev_err(dev, 13058c2ecf20Sopenharmony_ci "iova to phys timed out on %pad. Falling back to software table walk.\n", 13068c2ecf20Sopenharmony_ci &iova); 13078c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 13088c2ecf20Sopenharmony_ci return ops->iova_to_phys(ops, iova); 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci phys = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_PAR); 13128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 13138c2ecf20Sopenharmony_ci if (phys & ARM_SMMU_CB_PAR_F) { 13148c2ecf20Sopenharmony_ci dev_err(dev, "translation fault!\n"); 13158c2ecf20Sopenharmony_ci dev_err(dev, "PAR = 0x%llx\n", phys); 13168c2ecf20Sopenharmony_ci goto out; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci addr = (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); 13208c2ecf20Sopenharmony_ciout: 13218c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return addr; 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, 13278c2ecf20Sopenharmony_ci dma_addr_t iova) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 13308c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) 13338c2ecf20Sopenharmony_ci return iova; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (!ops) 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && 13398c2ecf20Sopenharmony_ci smmu_domain->stage == ARM_SMMU_DOMAIN_S1) 13408c2ecf20Sopenharmony_ci return arm_smmu_iova_to_phys_hard(domain, iova); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci return ops->iova_to_phys(ops, iova); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic bool arm_smmu_capable(enum iommu_cap cap) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci switch (cap) { 13488c2ecf20Sopenharmony_ci case IOMMU_CAP_CACHE_COHERENCY: 13498c2ecf20Sopenharmony_ci /* 13508c2ecf20Sopenharmony_ci * Return true here as the SMMU can always send out coherent 13518c2ecf20Sopenharmony_ci * requests. 13528c2ecf20Sopenharmony_ci */ 13538c2ecf20Sopenharmony_ci return true; 13548c2ecf20Sopenharmony_ci case IOMMU_CAP_NOEXEC: 13558c2ecf20Sopenharmony_ci return true; 13568c2ecf20Sopenharmony_ci default: 13578c2ecf20Sopenharmony_ci return false; 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic 13628c2ecf20Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver, 13658c2ecf20Sopenharmony_ci fwnode); 13668c2ecf20Sopenharmony_ci put_device(dev); 13678c2ecf20Sopenharmony_ci return dev ? dev_get_drvdata(dev) : NULL; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = NULL; 13738c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 13748c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 13758c2ecf20Sopenharmony_ci int i, ret; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (using_legacy_binding) { 13788c2ecf20Sopenharmony_ci ret = arm_smmu_register_legacy_master(dev, &smmu); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* 13818c2ecf20Sopenharmony_ci * If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master() 13828c2ecf20Sopenharmony_ci * will allocate/initialise a new one. Thus we need to update fwspec for 13838c2ecf20Sopenharmony_ci * later use. 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_ci fwspec = dev_iommu_fwspec_get(dev); 13868c2ecf20Sopenharmony_ci if (ret) 13878c2ecf20Sopenharmony_ci goto out_free; 13888c2ecf20Sopenharmony_ci } else if (fwspec && fwspec->ops == &arm_smmu_ops) { 13898c2ecf20Sopenharmony_ci smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); 13908c2ecf20Sopenharmony_ci } else { 13918c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci ret = -EINVAL; 13958c2ecf20Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) { 13968c2ecf20Sopenharmony_ci u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]); 13978c2ecf20Sopenharmony_ci u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (sid & ~smmu->streamid_mask) { 14008c2ecf20Sopenharmony_ci dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n", 14018c2ecf20Sopenharmony_ci sid, smmu->streamid_mask); 14028c2ecf20Sopenharmony_ci goto out_free; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci if (mask & ~smmu->smr_mask_mask) { 14058c2ecf20Sopenharmony_ci dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n", 14068c2ecf20Sopenharmony_ci mask, smmu->smr_mask_mask); 14078c2ecf20Sopenharmony_ci goto out_free; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci ret = -ENOMEM; 14128c2ecf20Sopenharmony_ci cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]), 14138c2ecf20Sopenharmony_ci GFP_KERNEL); 14148c2ecf20Sopenharmony_ci if (!cfg) 14158c2ecf20Sopenharmony_ci goto out_free; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci cfg->smmu = smmu; 14188c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, cfg); 14198c2ecf20Sopenharmony_ci while (i--) 14208c2ecf20Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 14238c2ecf20Sopenharmony_ci if (ret < 0) 14248c2ecf20Sopenharmony_ci goto out_cfg_free; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci ret = arm_smmu_master_alloc_smes(dev); 14278c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (ret) 14308c2ecf20Sopenharmony_ci goto out_cfg_free; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci device_link_add(dev, smmu->dev, 14338c2ecf20Sopenharmony_ci DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return &smmu->iommu; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ciout_cfg_free: 14388c2ecf20Sopenharmony_ci kfree(cfg); 14398c2ecf20Sopenharmony_ciout_free: 14408c2ecf20Sopenharmony_ci iommu_fwspec_free(dev); 14418c2ecf20Sopenharmony_ci return ERR_PTR(ret); 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 14478c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 14488c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 14498c2ecf20Sopenharmony_ci int ret; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) 14528c2ecf20Sopenharmony_ci return; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci cfg = dev_iommu_priv_get(dev); 14558c2ecf20Sopenharmony_ci smmu = cfg->smmu; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 14588c2ecf20Sopenharmony_ci if (ret < 0) 14598c2ecf20Sopenharmony_ci return; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci arm_smmu_master_free_smes(cfg, fwspec); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, NULL); 14668c2ecf20Sopenharmony_ci kfree(cfg); 14678c2ecf20Sopenharmony_ci iommu_fwspec_free(dev); 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev) 14718c2ecf20Sopenharmony_ci{ 14728c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 14738c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 14748c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 14758c2ecf20Sopenharmony_ci struct iommu_group *group = NULL; 14768c2ecf20Sopenharmony_ci int i, idx; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 14798c2ecf20Sopenharmony_ci if (group && smmu->s2crs[idx].group && 14808c2ecf20Sopenharmony_ci group != smmu->s2crs[idx].group) 14818c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci group = smmu->s2crs[idx].group; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (group) 14878c2ecf20Sopenharmony_ci return iommu_group_ref_get(group); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (dev_is_pci(dev)) 14908c2ecf20Sopenharmony_ci group = pci_device_group(dev); 14918c2ecf20Sopenharmony_ci else if (dev_is_fsl_mc(dev)) 14928c2ecf20Sopenharmony_ci group = fsl_mc_device_group(dev); 14938c2ecf20Sopenharmony_ci else 14948c2ecf20Sopenharmony_ci group = generic_device_group(dev); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Remember group for faster lookups */ 14978c2ecf20Sopenharmony_ci if (!IS_ERR(group)) 14988c2ecf20Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) 14998c2ecf20Sopenharmony_ci smmu->s2crs[idx].group = group; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return group; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic int arm_smmu_domain_get_attr(struct iommu_domain *domain, 15058c2ecf20Sopenharmony_ci enum iommu_attr attr, void *data) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci switch(domain->type) { 15108c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_UNMANAGED: 15118c2ecf20Sopenharmony_ci switch (attr) { 15128c2ecf20Sopenharmony_ci case DOMAIN_ATTR_NESTING: 15138c2ecf20Sopenharmony_ci *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); 15148c2ecf20Sopenharmony_ci return 0; 15158c2ecf20Sopenharmony_ci default: 15168c2ecf20Sopenharmony_ci return -ENODEV; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci break; 15198c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_DMA: 15208c2ecf20Sopenharmony_ci switch (attr) { 15218c2ecf20Sopenharmony_ci case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: 15228c2ecf20Sopenharmony_ci *(int *)data = smmu_domain->non_strict; 15238c2ecf20Sopenharmony_ci return 0; 15248c2ecf20Sopenharmony_ci default: 15258c2ecf20Sopenharmony_ci return -ENODEV; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci break; 15288c2ecf20Sopenharmony_ci default: 15298c2ecf20Sopenharmony_ci return -EINVAL; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_cistatic int arm_smmu_domain_set_attr(struct iommu_domain *domain, 15348c2ecf20Sopenharmony_ci enum iommu_attr attr, void *data) 15358c2ecf20Sopenharmony_ci{ 15368c2ecf20Sopenharmony_ci int ret = 0; 15378c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci switch(domain->type) { 15428c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_UNMANAGED: 15438c2ecf20Sopenharmony_ci switch (attr) { 15448c2ecf20Sopenharmony_ci case DOMAIN_ATTR_NESTING: 15458c2ecf20Sopenharmony_ci if (smmu_domain->smmu) { 15468c2ecf20Sopenharmony_ci ret = -EPERM; 15478c2ecf20Sopenharmony_ci goto out_unlock; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (*(int *)data) 15518c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; 15528c2ecf20Sopenharmony_ci else 15538c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 15548c2ecf20Sopenharmony_ci break; 15558c2ecf20Sopenharmony_ci default: 15568c2ecf20Sopenharmony_ci ret = -ENODEV; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci break; 15598c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_DMA: 15608c2ecf20Sopenharmony_ci switch (attr) { 15618c2ecf20Sopenharmony_ci case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: 15628c2ecf20Sopenharmony_ci smmu_domain->non_strict = *(int *)data; 15638c2ecf20Sopenharmony_ci break; 15648c2ecf20Sopenharmony_ci default: 15658c2ecf20Sopenharmony_ci ret = -ENODEV; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci default: 15698c2ecf20Sopenharmony_ci ret = -EINVAL; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ciout_unlock: 15728c2ecf20Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 15738c2ecf20Sopenharmony_ci return ret; 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci u32 mask, fwid = 0; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (args->args_count > 0) 15818c2ecf20Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_ID, args->args[0]); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (args->args_count > 1) 15848c2ecf20Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, args->args[1]); 15858c2ecf20Sopenharmony_ci else if (!of_property_read_u32(args->np, "stream-match-mask", &mask)) 15868c2ecf20Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, mask); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return iommu_fwspec_add_ids(dev, &fwid, 1); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev, 15928c2ecf20Sopenharmony_ci struct list_head *head) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci struct iommu_resv_region *region; 15958c2ecf20Sopenharmony_ci int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, 15988c2ecf20Sopenharmony_ci prot, IOMMU_RESV_SW_MSI); 15998c2ecf20Sopenharmony_ci if (!region) 16008c2ecf20Sopenharmony_ci return; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci list_add_tail(®ion->list, head); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci iommu_dma_get_resv_regions(dev, head); 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic int arm_smmu_def_domain_type(struct device *dev) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 16108c2ecf20Sopenharmony_ci const struct arm_smmu_impl *impl = cfg->smmu->impl; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (impl && impl->def_domain_type) 16138c2ecf20Sopenharmony_ci return impl->def_domain_type(dev); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return 0; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = { 16198c2ecf20Sopenharmony_ci .capable = arm_smmu_capable, 16208c2ecf20Sopenharmony_ci .domain_alloc = arm_smmu_domain_alloc, 16218c2ecf20Sopenharmony_ci .domain_free = arm_smmu_domain_free, 16228c2ecf20Sopenharmony_ci .attach_dev = arm_smmu_attach_dev, 16238c2ecf20Sopenharmony_ci .map = arm_smmu_map, 16248c2ecf20Sopenharmony_ci .unmap = arm_smmu_unmap, 16258c2ecf20Sopenharmony_ci .flush_iotlb_all = arm_smmu_flush_iotlb_all, 16268c2ecf20Sopenharmony_ci .iotlb_sync = arm_smmu_iotlb_sync, 16278c2ecf20Sopenharmony_ci .iova_to_phys = arm_smmu_iova_to_phys, 16288c2ecf20Sopenharmony_ci .probe_device = arm_smmu_probe_device, 16298c2ecf20Sopenharmony_ci .release_device = arm_smmu_release_device, 16308c2ecf20Sopenharmony_ci .device_group = arm_smmu_device_group, 16318c2ecf20Sopenharmony_ci .domain_get_attr = arm_smmu_domain_get_attr, 16328c2ecf20Sopenharmony_ci .domain_set_attr = arm_smmu_domain_set_attr, 16338c2ecf20Sopenharmony_ci .of_xlate = arm_smmu_of_xlate, 16348c2ecf20Sopenharmony_ci .get_resv_regions = arm_smmu_get_resv_regions, 16358c2ecf20Sopenharmony_ci .put_resv_regions = generic_iommu_put_resv_regions, 16368c2ecf20Sopenharmony_ci .def_domain_type = arm_smmu_def_domain_type, 16378c2ecf20Sopenharmony_ci .pgsize_bitmap = -1UL, /* Restricted during device attach */ 16388c2ecf20Sopenharmony_ci}; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_cistatic void arm_smmu_device_reset(struct arm_smmu_device *smmu) 16418c2ecf20Sopenharmony_ci{ 16428c2ecf20Sopenharmony_ci int i; 16438c2ecf20Sopenharmony_ci u32 reg; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci /* clear global FSR */ 16468c2ecf20Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); 16478c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, reg); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* 16508c2ecf20Sopenharmony_ci * Reset stream mapping groups: Initial values mark all SMRn as 16518c2ecf20Sopenharmony_ci * invalid and all S2CRn as bypass unless overridden. 16528c2ecf20Sopenharmony_ci */ 16538c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; ++i) 16548c2ecf20Sopenharmony_ci arm_smmu_write_sme(smmu, i); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* Make sure all context banks are disabled and clear CB_FSR */ 16578c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_context_banks; ++i) { 16588c2ecf20Sopenharmony_ci arm_smmu_write_context_bank(smmu, i); 16598c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT); 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci /* Invalidate the TLB, just in case */ 16638c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLH, QCOM_DUMMY_VAL); 16648c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLNSNH, QCOM_DUMMY_VAL); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* Enable fault reporting */ 16698c2ecf20Sopenharmony_ci reg |= (ARM_SMMU_sCR0_GFRE | ARM_SMMU_sCR0_GFIE | 16708c2ecf20Sopenharmony_ci ARM_SMMU_sCR0_GCFGFRE | ARM_SMMU_sCR0_GCFGFIE); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci /* Disable TLB broadcasting. */ 16738c2ecf20Sopenharmony_ci reg |= (ARM_SMMU_sCR0_VMIDPNE | ARM_SMMU_sCR0_PTM); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* Enable client access, handling unmatched streams as appropriate */ 16768c2ecf20Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_CLIENTPD; 16778c2ecf20Sopenharmony_ci if (disable_bypass) 16788c2ecf20Sopenharmony_ci reg |= ARM_SMMU_sCR0_USFCFG; 16798c2ecf20Sopenharmony_ci else 16808c2ecf20Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_USFCFG; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* Disable forced broadcasting */ 16838c2ecf20Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_FB; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci /* Don't upgrade barriers */ 16868c2ecf20Sopenharmony_ci reg &= ~(ARM_SMMU_sCR0_BSU); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_VMID16) 16898c2ecf20Sopenharmony_ci reg |= ARM_SMMU_sCR0_VMID16EN; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_EXIDS) 16928c2ecf20Sopenharmony_ci reg |= ARM_SMMU_sCR0_EXIDENABLE; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->reset) 16958c2ecf20Sopenharmony_ci smmu->impl->reset(smmu); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci /* Push the button */ 16988c2ecf20Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 16998c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic int arm_smmu_id_size_to_bits(int size) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci switch (size) { 17058c2ecf20Sopenharmony_ci case 0: 17068c2ecf20Sopenharmony_ci return 32; 17078c2ecf20Sopenharmony_ci case 1: 17088c2ecf20Sopenharmony_ci return 36; 17098c2ecf20Sopenharmony_ci case 2: 17108c2ecf20Sopenharmony_ci return 40; 17118c2ecf20Sopenharmony_ci case 3: 17128c2ecf20Sopenharmony_ci return 42; 17138c2ecf20Sopenharmony_ci case 4: 17148c2ecf20Sopenharmony_ci return 44; 17158c2ecf20Sopenharmony_ci case 5: 17168c2ecf20Sopenharmony_ci default: 17178c2ecf20Sopenharmony_ci return 48; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci unsigned int size; 17248c2ecf20Sopenharmony_ci u32 id; 17258c2ecf20Sopenharmony_ci bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK; 17268c2ecf20Sopenharmony_ci int i, ret; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "probing hardware configuration...\n"); 17298c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "SMMUv%d with:\n", 17308c2ecf20Sopenharmony_ci smmu->version == ARM_SMMU_V2 ? 2 : 1); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* ID0 */ 17338c2ecf20Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID0); 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci /* Restrict available stages based on module parameter */ 17368c2ecf20Sopenharmony_ci if (force_stage == 1) 17378c2ecf20Sopenharmony_ci id &= ~(ARM_SMMU_ID0_S2TS | ARM_SMMU_ID0_NTS); 17388c2ecf20Sopenharmony_ci else if (force_stage == 2) 17398c2ecf20Sopenharmony_ci id &= ~(ARM_SMMU_ID0_S1TS | ARM_SMMU_ID0_NTS); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID0_S1TS) { 17428c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S1; 17438c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tstage 1 translation\n"); 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID0_S2TS) { 17478c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S2; 17488c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tstage 2 translation\n"); 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID0_NTS) { 17528c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_NESTED; 17538c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tnested translation\n"); 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (!(smmu->features & 17578c2ecf20Sopenharmony_ci (ARM_SMMU_FEAT_TRANS_S1 | ARM_SMMU_FEAT_TRANS_S2))) { 17588c2ecf20Sopenharmony_ci dev_err(smmu->dev, "\tno translation support!\n"); 17598c2ecf20Sopenharmony_ci return -ENODEV; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if ((id & ARM_SMMU_ID0_S1TS) && 17638c2ecf20Sopenharmony_ci ((smmu->version < ARM_SMMU_V2) || !(id & ARM_SMMU_ID0_ATOSNS))) { 17648c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; 17658c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\taddress translation ops\n"); 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* 17698c2ecf20Sopenharmony_ci * In order for DMA API calls to work properly, we must defer to what 17708c2ecf20Sopenharmony_ci * the FW says about coherency, regardless of what the hardware claims. 17718c2ecf20Sopenharmony_ci * Fortunately, this also opens up a workaround for systems where the 17728c2ecf20Sopenharmony_ci * ID register value has ended up configured incorrectly. 17738c2ecf20Sopenharmony_ci */ 17748c2ecf20Sopenharmony_ci cttw_reg = !!(id & ARM_SMMU_ID0_CTTW); 17758c2ecf20Sopenharmony_ci if (cttw_fw || cttw_reg) 17768c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\t%scoherent table walk\n", 17778c2ecf20Sopenharmony_ci cttw_fw ? "" : "non-"); 17788c2ecf20Sopenharmony_ci if (cttw_fw != cttw_reg) 17798c2ecf20Sopenharmony_ci dev_notice(smmu->dev, 17808c2ecf20Sopenharmony_ci "\t(IDR0.CTTW overridden by FW configuration)\n"); 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci /* Max. number of entries we have for stream matching/indexing */ 17838c2ecf20Sopenharmony_ci if (smmu->version == ARM_SMMU_V2 && id & ARM_SMMU_ID0_EXIDS) { 17848c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_EXIDS; 17858c2ecf20Sopenharmony_ci size = 1 << 16; 17868c2ecf20Sopenharmony_ci } else { 17878c2ecf20Sopenharmony_ci size = 1 << FIELD_GET(ARM_SMMU_ID0_NUMSIDB, id); 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci smmu->streamid_mask = size - 1; 17908c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID0_SMS) { 17918c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH; 17928c2ecf20Sopenharmony_ci size = FIELD_GET(ARM_SMMU_ID0_NUMSMRG, id); 17938c2ecf20Sopenharmony_ci if (size == 0) { 17948c2ecf20Sopenharmony_ci dev_err(smmu->dev, 17958c2ecf20Sopenharmony_ci "stream-matching supported, but no SMRs present!\n"); 17968c2ecf20Sopenharmony_ci return -ENODEV; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Zero-initialised to mark as invalid */ 18008c2ecf20Sopenharmony_ci smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs), 18018c2ecf20Sopenharmony_ci GFP_KERNEL); 18028c2ecf20Sopenharmony_ci if (!smmu->smrs) 18038c2ecf20Sopenharmony_ci return -ENOMEM; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci dev_notice(smmu->dev, 18068c2ecf20Sopenharmony_ci "\tstream matching with %u register groups", size); 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci /* s2cr->type == 0 means translation, so initialise explicitly */ 18098c2ecf20Sopenharmony_ci smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs), 18108c2ecf20Sopenharmony_ci GFP_KERNEL); 18118c2ecf20Sopenharmony_ci if (!smmu->s2crs) 18128c2ecf20Sopenharmony_ci return -ENOMEM; 18138c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 18148c2ecf20Sopenharmony_ci smmu->s2crs[i] = s2cr_init_val; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci smmu->num_mapping_groups = size; 18178c2ecf20Sopenharmony_ci mutex_init(&smmu->stream_map_mutex); 18188c2ecf20Sopenharmony_ci spin_lock_init(&smmu->global_sync_lock); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci if (smmu->version < ARM_SMMU_V2 || 18218c2ecf20Sopenharmony_ci !(id & ARM_SMMU_ID0_PTFS_NO_AARCH32)) { 18228c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; 18238c2ecf20Sopenharmony_ci if (!(id & ARM_SMMU_ID0_PTFS_NO_AARCH32S)) 18248c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_S; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* ID1 */ 18288c2ecf20Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID1); 18298c2ecf20Sopenharmony_ci smmu->pgshift = (id & ARM_SMMU_ID1_PAGESIZE) ? 16 : 12; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci /* Check for size mismatch of SMMU address space from mapped region */ 18328c2ecf20Sopenharmony_ci size = 1 << (FIELD_GET(ARM_SMMU_ID1_NUMPAGENDXB, id) + 1); 18338c2ecf20Sopenharmony_ci if (smmu->numpage != 2 * size << smmu->pgshift) 18348c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 18358c2ecf20Sopenharmony_ci "SMMU address space size (0x%x) differs from mapped region size (0x%x)!\n", 18368c2ecf20Sopenharmony_ci 2 * size << smmu->pgshift, smmu->numpage); 18378c2ecf20Sopenharmony_ci /* Now properly encode NUMPAGE to subsequently derive SMMU_CB_BASE */ 18388c2ecf20Sopenharmony_ci smmu->numpage = size; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci smmu->num_s2_context_banks = FIELD_GET(ARM_SMMU_ID1_NUMS2CB, id); 18418c2ecf20Sopenharmony_ci smmu->num_context_banks = FIELD_GET(ARM_SMMU_ID1_NUMCB, id); 18428c2ecf20Sopenharmony_ci if (smmu->num_s2_context_banks > smmu->num_context_banks) { 18438c2ecf20Sopenharmony_ci dev_err(smmu->dev, "impossible number of S2 context banks!\n"); 18448c2ecf20Sopenharmony_ci return -ENODEV; 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\t%u context banks (%u stage-2 only)\n", 18478c2ecf20Sopenharmony_ci smmu->num_context_banks, smmu->num_s2_context_banks); 18488c2ecf20Sopenharmony_ci smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks, 18498c2ecf20Sopenharmony_ci sizeof(*smmu->cbs), GFP_KERNEL); 18508c2ecf20Sopenharmony_ci if (!smmu->cbs) 18518c2ecf20Sopenharmony_ci return -ENOMEM; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci /* ID2 */ 18548c2ecf20Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID2); 18558c2ecf20Sopenharmony_ci size = arm_smmu_id_size_to_bits(FIELD_GET(ARM_SMMU_ID2_IAS, id)); 18568c2ecf20Sopenharmony_ci smmu->ipa_size = size; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci /* The output mask is also applied for bypass */ 18598c2ecf20Sopenharmony_ci size = arm_smmu_id_size_to_bits(FIELD_GET(ARM_SMMU_ID2_OAS, id)); 18608c2ecf20Sopenharmony_ci smmu->pa_size = size; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID2_VMID16) 18638c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_VMID16; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci /* 18668c2ecf20Sopenharmony_ci * What the page table walker can address actually depends on which 18678c2ecf20Sopenharmony_ci * descriptor format is in use, but since a) we don't know that yet, 18688c2ecf20Sopenharmony_ci * and b) it can vary per context bank, this will have to do... 18698c2ecf20Sopenharmony_ci */ 18708c2ecf20Sopenharmony_ci if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(size))) 18718c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 18728c2ecf20Sopenharmony_ci "failed to set DMA mask for table walker\n"); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) { 18758c2ecf20Sopenharmony_ci smmu->va_size = smmu->ipa_size; 18768c2ecf20Sopenharmony_ci if (smmu->version == ARM_SMMU_V1_64K) 18778c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K; 18788c2ecf20Sopenharmony_ci } else { 18798c2ecf20Sopenharmony_ci size = FIELD_GET(ARM_SMMU_ID2_UBS, id); 18808c2ecf20Sopenharmony_ci smmu->va_size = arm_smmu_id_size_to_bits(size); 18818c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_4K) 18828c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_4K; 18838c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_16K) 18848c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_16K; 18858c2ecf20Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_64K) 18868c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K; 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->cfg_probe) { 18908c2ecf20Sopenharmony_ci ret = smmu->impl->cfg_probe(smmu); 18918c2ecf20Sopenharmony_ci if (ret) 18928c2ecf20Sopenharmony_ci return ret; 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* Now we've corralled the various formats, what'll it do? */ 18968c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) 18978c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_64K | SZ_1M | SZ_16M; 18988c2ecf20Sopenharmony_ci if (smmu->features & 18998c2ecf20Sopenharmony_ci (ARM_SMMU_FEAT_FMT_AARCH32_L | ARM_SMMU_FEAT_FMT_AARCH64_4K)) 19008c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G; 19018c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_16K) 19028c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_16K | SZ_32M; 19038c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_64K) 19048c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_64K | SZ_512M; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if (arm_smmu_ops.pgsize_bitmap == -1UL) 19078c2ecf20Sopenharmony_ci arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap; 19088c2ecf20Sopenharmony_ci else 19098c2ecf20Sopenharmony_ci arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap; 19108c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tSupported page sizes: 0x%08lx\n", 19118c2ecf20Sopenharmony_ci smmu->pgsize_bitmap); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) 19158c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tStage-1: %lu-bit VA -> %lu-bit IPA\n", 19168c2ecf20Sopenharmony_ci smmu->va_size, smmu->ipa_size); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) 19198c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", 19208c2ecf20Sopenharmony_ci smmu->ipa_size, smmu->pa_size); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci return 0; 19238c2ecf20Sopenharmony_ci} 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_cistruct arm_smmu_match_data { 19268c2ecf20Sopenharmony_ci enum arm_smmu_arch_version version; 19278c2ecf20Sopenharmony_ci enum arm_smmu_implementation model; 19288c2ecf20Sopenharmony_ci}; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci#define ARM_SMMU_MATCH_DATA(name, ver, imp) \ 19318c2ecf20Sopenharmony_cistatic const struct arm_smmu_match_data name = { .version = ver, .model = imp } 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(smmu_generic_v1, ARM_SMMU_V1, GENERIC_SMMU); 19348c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(smmu_generic_v2, ARM_SMMU_V2, GENERIC_SMMU); 19358c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU); 19368c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500); 19378c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2); 19388c2ecf20Sopenharmony_ciARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = { 19418c2ecf20Sopenharmony_ci { .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 }, 19428c2ecf20Sopenharmony_ci { .compatible = "arm,smmu-v2", .data = &smmu_generic_v2 }, 19438c2ecf20Sopenharmony_ci { .compatible = "arm,mmu-400", .data = &smmu_generic_v1 }, 19448c2ecf20Sopenharmony_ci { .compatible = "arm,mmu-401", .data = &arm_mmu401 }, 19458c2ecf20Sopenharmony_ci { .compatible = "arm,mmu-500", .data = &arm_mmu500 }, 19468c2ecf20Sopenharmony_ci { .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 }, 19478c2ecf20Sopenharmony_ci { .compatible = "nvidia,smmu-500", .data = &arm_mmu500 }, 19488c2ecf20Sopenharmony_ci { .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 }, 19498c2ecf20Sopenharmony_ci { }, 19508c2ecf20Sopenharmony_ci}; 19518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 19548c2ecf20Sopenharmony_cistatic int acpi_smmu_get_data(u32 model, struct arm_smmu_device *smmu) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci int ret = 0; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci switch (model) { 19598c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_V1: 19608c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU400: 19618c2ecf20Sopenharmony_ci smmu->version = ARM_SMMU_V1; 19628c2ecf20Sopenharmony_ci smmu->model = GENERIC_SMMU; 19638c2ecf20Sopenharmony_ci break; 19648c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU401: 19658c2ecf20Sopenharmony_ci smmu->version = ARM_SMMU_V1_64K; 19668c2ecf20Sopenharmony_ci smmu->model = GENERIC_SMMU; 19678c2ecf20Sopenharmony_ci break; 19688c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_V2: 19698c2ecf20Sopenharmony_ci smmu->version = ARM_SMMU_V2; 19708c2ecf20Sopenharmony_ci smmu->model = GENERIC_SMMU; 19718c2ecf20Sopenharmony_ci break; 19728c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU500: 19738c2ecf20Sopenharmony_ci smmu->version = ARM_SMMU_V2; 19748c2ecf20Sopenharmony_ci smmu->model = ARM_MMU500; 19758c2ecf20Sopenharmony_ci break; 19768c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_CAVIUM_THUNDERX: 19778c2ecf20Sopenharmony_ci smmu->version = ARM_SMMU_V2; 19788c2ecf20Sopenharmony_ci smmu->model = CAVIUM_SMMUV2; 19798c2ecf20Sopenharmony_ci break; 19808c2ecf20Sopenharmony_ci default: 19818c2ecf20Sopenharmony_ci ret = -ENODEV; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci return ret; 19858c2ecf20Sopenharmony_ci} 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct platform_device *pdev, 19888c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 19898c2ecf20Sopenharmony_ci{ 19908c2ecf20Sopenharmony_ci struct device *dev = smmu->dev; 19918c2ecf20Sopenharmony_ci struct acpi_iort_node *node = 19928c2ecf20Sopenharmony_ci *(struct acpi_iort_node **)dev_get_platdata(dev); 19938c2ecf20Sopenharmony_ci struct acpi_iort_smmu *iort_smmu; 19948c2ecf20Sopenharmony_ci int ret; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci /* Retrieve SMMU1/2 specific data */ 19978c2ecf20Sopenharmony_ci iort_smmu = (struct acpi_iort_smmu *)node->node_data; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci ret = acpi_smmu_get_data(iort_smmu->model, smmu); 20008c2ecf20Sopenharmony_ci if (ret < 0) 20018c2ecf20Sopenharmony_ci return ret; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* Ignore the configuration access interrupt */ 20048c2ecf20Sopenharmony_ci smmu->num_global_irqs = 1; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci if (iort_smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) 20078c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci return 0; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci#else 20128c2ecf20Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct platform_device *pdev, 20138c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci return -ENODEV; 20168c2ecf20Sopenharmony_ci} 20178c2ecf20Sopenharmony_ci#endif 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct platform_device *pdev, 20208c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci const struct arm_smmu_match_data *data; 20238c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 20248c2ecf20Sopenharmony_ci bool legacy_binding; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "#global-interrupts", 20278c2ecf20Sopenharmony_ci &smmu->num_global_irqs)) { 20288c2ecf20Sopenharmony_ci dev_err(dev, "missing #global-interrupts property\n"); 20298c2ecf20Sopenharmony_ci return -ENODEV; 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci data = of_device_get_match_data(dev); 20338c2ecf20Sopenharmony_ci smmu->version = data->version; 20348c2ecf20Sopenharmony_ci smmu->model = data->model; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL); 20378c2ecf20Sopenharmony_ci if (legacy_binding && !using_generic_binding) { 20388c2ecf20Sopenharmony_ci if (!using_legacy_binding) { 20398c2ecf20Sopenharmony_ci pr_notice("deprecated \"mmu-masters\" DT property in use; %s support unavailable\n", 20408c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS) ? "DMA API" : "SMMU"); 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci using_legacy_binding = true; 20438c2ecf20Sopenharmony_ci } else if (!legacy_binding && !using_legacy_binding) { 20448c2ecf20Sopenharmony_ci using_generic_binding = true; 20458c2ecf20Sopenharmony_ci } else { 20468c2ecf20Sopenharmony_ci dev_err(dev, "not probing due to mismatched DT properties\n"); 20478c2ecf20Sopenharmony_ci return -ENODEV; 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci if (of_dma_is_coherent(dev->of_node)) 20518c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci return 0; 20548c2ecf20Sopenharmony_ci} 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_cistatic int arm_smmu_bus_init(struct iommu_ops *ops) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci int err; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci /* Oh, for a proper bus abstraction */ 20618c2ecf20Sopenharmony_ci if (!iommu_present(&platform_bus_type)) { 20628c2ecf20Sopenharmony_ci err = bus_set_iommu(&platform_bus_type, ops); 20638c2ecf20Sopenharmony_ci if (err) 20648c2ecf20Sopenharmony_ci return err; 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 20678c2ecf20Sopenharmony_ci if (!iommu_present(&amba_bustype)) { 20688c2ecf20Sopenharmony_ci err = bus_set_iommu(&amba_bustype, ops); 20698c2ecf20Sopenharmony_ci if (err) 20708c2ecf20Sopenharmony_ci goto err_reset_platform_ops; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci#endif 20738c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 20748c2ecf20Sopenharmony_ci if (!iommu_present(&pci_bus_type)) { 20758c2ecf20Sopenharmony_ci err = bus_set_iommu(&pci_bus_type, ops); 20768c2ecf20Sopenharmony_ci if (err) 20778c2ecf20Sopenharmony_ci goto err_reset_amba_ops; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci#endif 20808c2ecf20Sopenharmony_ci#ifdef CONFIG_FSL_MC_BUS 20818c2ecf20Sopenharmony_ci if (!iommu_present(&fsl_mc_bus_type)) { 20828c2ecf20Sopenharmony_ci err = bus_set_iommu(&fsl_mc_bus_type, ops); 20838c2ecf20Sopenharmony_ci if (err) 20848c2ecf20Sopenharmony_ci goto err_reset_pci_ops; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci#endif 20878c2ecf20Sopenharmony_ci return 0; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_cierr_reset_pci_ops: __maybe_unused; 20908c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 20918c2ecf20Sopenharmony_ci bus_set_iommu(&pci_bus_type, NULL); 20928c2ecf20Sopenharmony_ci#endif 20938c2ecf20Sopenharmony_cierr_reset_amba_ops: __maybe_unused; 20948c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 20958c2ecf20Sopenharmony_ci bus_set_iommu(&amba_bustype, NULL); 20968c2ecf20Sopenharmony_ci#endif 20978c2ecf20Sopenharmony_cierr_reset_platform_ops: __maybe_unused; 20988c2ecf20Sopenharmony_ci bus_set_iommu(&platform_bus_type, NULL); 20998c2ecf20Sopenharmony_ci return err; 21008c2ecf20Sopenharmony_ci} 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev) 21038c2ecf20Sopenharmony_ci{ 21048c2ecf20Sopenharmony_ci struct resource *res; 21058c2ecf20Sopenharmony_ci resource_size_t ioaddr; 21068c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 21078c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 21088c2ecf20Sopenharmony_ci int num_irqs, i, err; 21098c2ecf20Sopenharmony_ci irqreturn_t (*global_fault)(int irq, void *dev); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); 21128c2ecf20Sopenharmony_ci if (!smmu) { 21138c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate arm_smmu_device\n"); 21148c2ecf20Sopenharmony_ci return -ENOMEM; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci smmu->dev = dev; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci if (dev->of_node) 21198c2ecf20Sopenharmony_ci err = arm_smmu_device_dt_probe(pdev, smmu); 21208c2ecf20Sopenharmony_ci else 21218c2ecf20Sopenharmony_ci err = arm_smmu_device_acpi_probe(pdev, smmu); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci if (err) 21248c2ecf20Sopenharmony_ci return err; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci smmu->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 21278c2ecf20Sopenharmony_ci if (IS_ERR(smmu->base)) 21288c2ecf20Sopenharmony_ci return PTR_ERR(smmu->base); 21298c2ecf20Sopenharmony_ci ioaddr = res->start; 21308c2ecf20Sopenharmony_ci /* 21318c2ecf20Sopenharmony_ci * The resource size should effectively match the value of SMMU_TOP; 21328c2ecf20Sopenharmony_ci * stash that temporarily until we know PAGESIZE to validate it with. 21338c2ecf20Sopenharmony_ci */ 21348c2ecf20Sopenharmony_ci smmu->numpage = resource_size(res); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci smmu = arm_smmu_impl_init(smmu); 21378c2ecf20Sopenharmony_ci if (IS_ERR(smmu)) 21388c2ecf20Sopenharmony_ci return PTR_ERR(smmu); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci num_irqs = 0; 21418c2ecf20Sopenharmony_ci while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) { 21428c2ecf20Sopenharmony_ci num_irqs++; 21438c2ecf20Sopenharmony_ci if (num_irqs > smmu->num_global_irqs) 21448c2ecf20Sopenharmony_ci smmu->num_context_irqs++; 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci if (!smmu->num_context_irqs) { 21488c2ecf20Sopenharmony_ci dev_err(dev, "found %d interrupts but expected at least %d\n", 21498c2ecf20Sopenharmony_ci num_irqs, smmu->num_global_irqs + 1); 21508c2ecf20Sopenharmony_ci return -ENODEV; 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci smmu->irqs = devm_kcalloc(dev, num_irqs, sizeof(*smmu->irqs), 21548c2ecf20Sopenharmony_ci GFP_KERNEL); 21558c2ecf20Sopenharmony_ci if (!smmu->irqs) { 21568c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate %d irqs\n", num_irqs); 21578c2ecf20Sopenharmony_ci return -ENOMEM; 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci for (i = 0; i < num_irqs; ++i) { 21618c2ecf20Sopenharmony_ci int irq = platform_get_irq(pdev, i); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci if (irq < 0) 21648c2ecf20Sopenharmony_ci return -ENODEV; 21658c2ecf20Sopenharmony_ci smmu->irqs[i] = irq; 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci err = devm_clk_bulk_get_all(dev, &smmu->clks); 21698c2ecf20Sopenharmony_ci if (err < 0) { 21708c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clocks %d\n", err); 21718c2ecf20Sopenharmony_ci return err; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci smmu->num_clks = err; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci err = clk_bulk_prepare_enable(smmu->num_clks, smmu->clks); 21768c2ecf20Sopenharmony_ci if (err) 21778c2ecf20Sopenharmony_ci return err; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci err = arm_smmu_device_cfg_probe(smmu); 21808c2ecf20Sopenharmony_ci if (err) 21818c2ecf20Sopenharmony_ci return err; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci if (smmu->version == ARM_SMMU_V2) { 21848c2ecf20Sopenharmony_ci if (smmu->num_context_banks > smmu->num_context_irqs) { 21858c2ecf20Sopenharmony_ci dev_err(dev, 21868c2ecf20Sopenharmony_ci "found only %d context irq(s) but %d required\n", 21878c2ecf20Sopenharmony_ci smmu->num_context_irqs, smmu->num_context_banks); 21888c2ecf20Sopenharmony_ci return -ENODEV; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci /* Ignore superfluous interrupts */ 21928c2ecf20Sopenharmony_ci smmu->num_context_irqs = smmu->num_context_banks; 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci if (smmu->impl && smmu->impl->global_fault) 21968c2ecf20Sopenharmony_ci global_fault = smmu->impl->global_fault; 21978c2ecf20Sopenharmony_ci else 21988c2ecf20Sopenharmony_ci global_fault = arm_smmu_global_fault; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_global_irqs; ++i) { 22018c2ecf20Sopenharmony_ci err = devm_request_irq(smmu->dev, smmu->irqs[i], 22028c2ecf20Sopenharmony_ci global_fault, 22038c2ecf20Sopenharmony_ci IRQF_SHARED, 22048c2ecf20Sopenharmony_ci "arm-smmu global fault", 22058c2ecf20Sopenharmony_ci smmu); 22068c2ecf20Sopenharmony_ci if (err) { 22078c2ecf20Sopenharmony_ci dev_err(dev, "failed to request global IRQ %d (%u)\n", 22088c2ecf20Sopenharmony_ci i, smmu->irqs[i]); 22098c2ecf20Sopenharmony_ci return err; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci err = iommu_device_sysfs_add(&smmu->iommu, smmu->dev, NULL, 22148c2ecf20Sopenharmony_ci "smmu.%pa", &ioaddr); 22158c2ecf20Sopenharmony_ci if (err) { 22168c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register iommu in sysfs\n"); 22178c2ecf20Sopenharmony_ci return err; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops); 22218c2ecf20Sopenharmony_ci iommu_device_set_fwnode(&smmu->iommu, dev->fwnode); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci err = iommu_device_register(&smmu->iommu); 22248c2ecf20Sopenharmony_ci if (err) { 22258c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register iommu\n"); 22268c2ecf20Sopenharmony_ci return err; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, smmu); 22308c2ecf20Sopenharmony_ci arm_smmu_device_reset(smmu); 22318c2ecf20Sopenharmony_ci arm_smmu_test_smr_masks(smmu); 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci /* 22348c2ecf20Sopenharmony_ci * We want to avoid touching dev->power.lock in fastpaths unless 22358c2ecf20Sopenharmony_ci * it's really going to do something useful - pm_runtime_enabled() 22368c2ecf20Sopenharmony_ci * can serve as an ideal proxy for that decision. So, conditionally 22378c2ecf20Sopenharmony_ci * enable pm_runtime. 22388c2ecf20Sopenharmony_ci */ 22398c2ecf20Sopenharmony_ci if (dev->pm_domain) { 22408c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 22418c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* 22458c2ecf20Sopenharmony_ci * For ACPI and generic DT bindings, an SMMU will be probed before 22468c2ecf20Sopenharmony_ci * any device which might need it, so we want the bus ops in place 22478c2ecf20Sopenharmony_ci * ready to handle default domain setup as soon as any SMMU exists. 22488c2ecf20Sopenharmony_ci */ 22498c2ecf20Sopenharmony_ci if (!using_legacy_binding) 22508c2ecf20Sopenharmony_ci return arm_smmu_bus_init(&arm_smmu_ops); 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci return 0; 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cistatic int arm_smmu_device_remove(struct platform_device *pdev) 22568c2ecf20Sopenharmony_ci{ 22578c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci if (!smmu) 22608c2ecf20Sopenharmony_ci return -ENODEV; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS)) 22638c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "disabling translation\n"); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci arm_smmu_bus_init(NULL); 22668c2ecf20Sopenharmony_ci iommu_device_unregister(&smmu->iommu); 22678c2ecf20Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci arm_smmu_rpm_get(smmu); 22708c2ecf20Sopenharmony_ci /* Turn the thing off */ 22718c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, ARM_SMMU_sCR0_CLIENTPD); 22728c2ecf20Sopenharmony_ci arm_smmu_rpm_put(smmu); 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 22758c2ecf20Sopenharmony_ci pm_runtime_force_suspend(smmu->dev); 22768c2ecf20Sopenharmony_ci else 22778c2ecf20Sopenharmony_ci clk_bulk_disable(smmu->num_clks, smmu->clks); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci clk_bulk_unprepare(smmu->num_clks, smmu->clks); 22808c2ecf20Sopenharmony_ci return 0; 22818c2ecf20Sopenharmony_ci} 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev) 22848c2ecf20Sopenharmony_ci{ 22858c2ecf20Sopenharmony_ci arm_smmu_device_remove(pdev); 22868c2ecf20Sopenharmony_ci} 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_cistatic int __maybe_unused arm_smmu_runtime_resume(struct device *dev) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 22918c2ecf20Sopenharmony_ci int ret; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci ret = clk_bulk_enable(smmu->num_clks, smmu->clks); 22948c2ecf20Sopenharmony_ci if (ret) 22958c2ecf20Sopenharmony_ci return ret; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci arm_smmu_device_reset(smmu); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci return 0; 23008c2ecf20Sopenharmony_ci} 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_cistatic int __maybe_unused arm_smmu_runtime_suspend(struct device *dev) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci clk_bulk_disable(smmu->num_clks, smmu->clks); 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci return 0; 23098c2ecf20Sopenharmony_ci} 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_cistatic int __maybe_unused arm_smmu_pm_resume(struct device *dev) 23128c2ecf20Sopenharmony_ci{ 23138c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev)) 23148c2ecf20Sopenharmony_ci return 0; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci return arm_smmu_runtime_resume(dev); 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_cistatic int __maybe_unused arm_smmu_pm_suspend(struct device *dev) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev)) 23228c2ecf20Sopenharmony_ci return 0; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci return arm_smmu_runtime_suspend(dev); 23258c2ecf20Sopenharmony_ci} 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_cistatic const struct dev_pm_ops arm_smmu_pm_ops = { 23288c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(arm_smmu_pm_suspend, arm_smmu_pm_resume) 23298c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(arm_smmu_runtime_suspend, 23308c2ecf20Sopenharmony_ci arm_smmu_runtime_resume, NULL) 23318c2ecf20Sopenharmony_ci}; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver = { 23348c2ecf20Sopenharmony_ci .driver = { 23358c2ecf20Sopenharmony_ci .name = "arm-smmu", 23368c2ecf20Sopenharmony_ci .of_match_table = arm_smmu_of_match, 23378c2ecf20Sopenharmony_ci .pm = &arm_smmu_pm_ops, 23388c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 23398c2ecf20Sopenharmony_ci }, 23408c2ecf20Sopenharmony_ci .probe = arm_smmu_device_probe, 23418c2ecf20Sopenharmony_ci .remove = arm_smmu_device_remove, 23428c2ecf20Sopenharmony_ci .shutdown = arm_smmu_device_shutdown, 23438c2ecf20Sopenharmony_ci}; 23448c2ecf20Sopenharmony_cimodule_platform_driver(arm_smmu_driver); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); 23478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>"); 23488c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu"); 23498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2350