162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IOMMU API for ARM architected SMMU implementations. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 ARM Limited 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This driver currently supports: 1062306a36Sopenharmony_ci * - SMMUv1 and v2 implementations 1162306a36Sopenharmony_ci * - Stream-matching and stream-indexing 1262306a36Sopenharmony_ci * - v7/v8 long-descriptor format 1362306a36Sopenharmony_ci * - Non-secure access to the SMMU 1462306a36Sopenharmony_ci * - Context fault reporting 1562306a36Sopenharmony_ci * - Extended Stream ID (16 bit) 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define pr_fmt(fmt) "arm-smmu: " fmt 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/acpi.h> 2162306a36Sopenharmony_ci#include <linux/acpi_iort.h> 2262306a36Sopenharmony_ci#include <linux/bitfield.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2562306a36Sopenharmony_ci#include <linux/err.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <linux/iopoll.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/of.h> 3162306a36Sopenharmony_ci#include <linux/of_address.h> 3262306a36Sopenharmony_ci#include <linux/pci.h> 3362306a36Sopenharmony_ci#include <linux/platform_device.h> 3462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3562306a36Sopenharmony_ci#include <linux/ratelimit.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/fsl/mc.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "arm-smmu.h" 4162306a36Sopenharmony_ci#include "../../dma-iommu.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU 4562306a36Sopenharmony_ci * global register space are still, in fact, using a hypervisor to mediate it 4662306a36Sopenharmony_ci * by trapping and emulating register accesses. Sadly, some deployed versions 4762306a36Sopenharmony_ci * of said trapping code have bugs wherein they go horribly wrong for stores 4862306a36Sopenharmony_ci * using r31 (i.e. XZR/WZR) as the source register. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define QCOM_DUMMY_VAL -1 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define MSI_IOVA_BASE 0x8000000 5362306a36Sopenharmony_ci#define MSI_IOVA_LENGTH 0x100000 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int force_stage; 5662306a36Sopenharmony_cimodule_param(force_stage, int, S_IRUGO); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(force_stage, 5862306a36Sopenharmony_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."); 5962306a36Sopenharmony_cistatic bool disable_bypass = 6062306a36Sopenharmony_ci IS_ENABLED(CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT); 6162306a36Sopenharmony_cimodule_param(disable_bypass, bool, S_IRUGO); 6262306a36Sopenharmony_ciMODULE_PARM_DESC(disable_bypass, 6362306a36Sopenharmony_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."); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define s2cr_init_val (struct arm_smmu_s2cr){ \ 6662306a36Sopenharmony_ci .type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS, \ 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic bool using_legacy_binding, using_generic_binding; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline int arm_smmu_rpm_get(struct arm_smmu_device *smmu) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 7462306a36Sopenharmony_ci return pm_runtime_resume_and_get(smmu->dev); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 8262306a36Sopenharmony_ci pm_runtime_put_autosuspend(smmu->dev); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return container_of(dom, struct arm_smmu_domain, domain); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver; 9162306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#ifdef CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS 9462306a36Sopenharmony_cistatic struct device_node *dev_get_dev_node(struct device *dev) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (dev_is_pci(dev)) { 9762306a36Sopenharmony_ci struct pci_bus *bus = to_pci_dev(dev)->bus; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci while (!pci_is_root_bus(bus)) 10062306a36Sopenharmony_ci bus = bus->parent; 10162306a36Sopenharmony_ci return of_node_get(bus->bridge->parent->of_node); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return of_node_get(dev->of_node); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci *((__be32 *)data) = cpu_to_be32(alias); 11062306a36Sopenharmony_ci return 0; /* Continue walking */ 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int __find_legacy_master_phandle(struct device *dev, void *data) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct of_phandle_iterator *it = *(void **)data; 11662306a36Sopenharmony_ci struct device_node *np = it->node; 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci of_for_each_phandle(it, err, dev->of_node, "mmu-masters", 12062306a36Sopenharmony_ci "#stream-id-cells", -1) 12162306a36Sopenharmony_ci if (it->node == np) { 12262306a36Sopenharmony_ci *(void **)data = dev; 12362306a36Sopenharmony_ci return 1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci it->node = np; 12662306a36Sopenharmony_ci return err == -ENOENT ? 0 : err; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int arm_smmu_register_legacy_master(struct device *dev, 13062306a36Sopenharmony_ci struct arm_smmu_device **smmu) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct device *smmu_dev; 13362306a36Sopenharmony_ci struct device_node *np; 13462306a36Sopenharmony_ci struct of_phandle_iterator it; 13562306a36Sopenharmony_ci void *data = ⁢ 13662306a36Sopenharmony_ci u32 *sids; 13762306a36Sopenharmony_ci __be32 pci_sid; 13862306a36Sopenharmony_ci int err; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci np = dev_get_dev_node(dev); 14162306a36Sopenharmony_ci if (!np || !of_property_present(np, "#stream-id-cells")) { 14262306a36Sopenharmony_ci of_node_put(np); 14362306a36Sopenharmony_ci return -ENODEV; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci it.node = np; 14762306a36Sopenharmony_ci err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data, 14862306a36Sopenharmony_ci __find_legacy_master_phandle); 14962306a36Sopenharmony_ci smmu_dev = data; 15062306a36Sopenharmony_ci of_node_put(np); 15162306a36Sopenharmony_ci if (err == 0) 15262306a36Sopenharmony_ci return -ENODEV; 15362306a36Sopenharmony_ci if (err < 0) 15462306a36Sopenharmony_ci return err; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (dev_is_pci(dev)) { 15762306a36Sopenharmony_ci /* "mmu-masters" assumes Stream ID == Requester ID */ 15862306a36Sopenharmony_ci pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, 15962306a36Sopenharmony_ci &pci_sid); 16062306a36Sopenharmony_ci it.cur = &pci_sid; 16162306a36Sopenharmony_ci it.cur_count = 1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode, 16562306a36Sopenharmony_ci &arm_smmu_ops); 16662306a36Sopenharmony_ci if (err) 16762306a36Sopenharmony_ci return err; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL); 17062306a36Sopenharmony_ci if (!sids) 17162306a36Sopenharmony_ci return -ENOMEM; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci *smmu = dev_get_drvdata(smmu_dev); 17462306a36Sopenharmony_ci of_phandle_iterator_args(&it, sids, it.cur_count); 17562306a36Sopenharmony_ci err = iommu_fwspec_add_ids(dev, sids, it.cur_count); 17662306a36Sopenharmony_ci kfree(sids); 17762306a36Sopenharmony_ci return err; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci#else 18062306a36Sopenharmony_cistatic int arm_smmu_register_legacy_master(struct device *dev, 18162306a36Sopenharmony_ci struct arm_smmu_device **smmu) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci return -ENODEV; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci#endif /* CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void __arm_smmu_free_bitmap(unsigned long *map, int idx) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci clear_bit(idx, map); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* Wait for any pending TLB invalidations to complete */ 19362306a36Sopenharmony_cistatic void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, 19462306a36Sopenharmony_ci int sync, int status) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci unsigned int spin_cnt, delay; 19762306a36Sopenharmony_ci u32 reg; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (smmu->impl && unlikely(smmu->impl->tlb_sync)) 20062306a36Sopenharmony_ci return smmu->impl->tlb_sync(smmu, page, sync, status); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL); 20362306a36Sopenharmony_ci for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { 20462306a36Sopenharmony_ci for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { 20562306a36Sopenharmony_ci reg = arm_smmu_readl(smmu, page, status); 20662306a36Sopenharmony_ci if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE)) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci cpu_relax(); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci udelay(delay); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci dev_err_ratelimited(smmu->dev, 21362306a36Sopenharmony_ci "TLB sync timed out -- SMMU may be deadlocked\n"); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci unsigned long flags; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spin_lock_irqsave(&smmu->global_sync_lock, flags); 22162306a36Sopenharmony_ci __arm_smmu_tlb_sync(smmu, ARM_SMMU_GR0, ARM_SMMU_GR0_sTLBGSYNC, 22262306a36Sopenharmony_ci ARM_SMMU_GR0_sTLBGSTATUS); 22362306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu->global_sync_lock, flags); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void arm_smmu_tlb_sync_context(struct arm_smmu_domain *smmu_domain) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 22962306a36Sopenharmony_ci unsigned long flags; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->cb_lock, flags); 23262306a36Sopenharmony_ci __arm_smmu_tlb_sync(smmu, ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx), 23362306a36Sopenharmony_ci ARM_SMMU_CB_TLBSYNC, ARM_SMMU_CB_TLBSTATUS); 23462306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_context_s1(void *cookie) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * The TLBI write may be relaxed, so ensure that PTEs cleared by the 24262306a36Sopenharmony_ci * current CPU are visible beforehand. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci wmb(); 24562306a36Sopenharmony_ci arm_smmu_cb_write(smmu_domain->smmu, smmu_domain->cfg.cbndx, 24662306a36Sopenharmony_ci ARM_SMMU_CB_S1_TLBIASID, smmu_domain->cfg.asid); 24762306a36Sopenharmony_ci arm_smmu_tlb_sync_context(smmu_domain); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_context_s2(void *cookie) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 25362306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* See above */ 25662306a36Sopenharmony_ci wmb(); 25762306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); 25862306a36Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_range_s1(unsigned long iova, size_t size, 26262306a36Sopenharmony_ci size_t granule, void *cookie, int reg) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 26562306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 26662306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 26762306a36Sopenharmony_ci int idx = cfg->cbndx; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 27062306a36Sopenharmony_ci wmb(); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (cfg->fmt != ARM_SMMU_CTX_FMT_AARCH64) { 27362306a36Sopenharmony_ci iova = (iova >> 12) << 12; 27462306a36Sopenharmony_ci iova |= cfg->asid; 27562306a36Sopenharmony_ci do { 27662306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, reg, iova); 27762306a36Sopenharmony_ci iova += granule; 27862306a36Sopenharmony_ci } while (size -= granule); 27962306a36Sopenharmony_ci } else { 28062306a36Sopenharmony_ci iova >>= 12; 28162306a36Sopenharmony_ci iova |= (u64)cfg->asid << 48; 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, reg, iova); 28462306a36Sopenharmony_ci iova += granule >> 12; 28562306a36Sopenharmony_ci } while (size -= granule); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_range_s2(unsigned long iova, size_t size, 29062306a36Sopenharmony_ci size_t granule, void *cookie, int reg) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 29362306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 29462306a36Sopenharmony_ci int idx = smmu_domain->cfg.cbndx; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 29762306a36Sopenharmony_ci wmb(); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci iova >>= 12; 30062306a36Sopenharmony_ci do { 30162306a36Sopenharmony_ci if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64) 30262306a36Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, reg, iova); 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, reg, iova); 30562306a36Sopenharmony_ci iova += granule >> 12; 30662306a36Sopenharmony_ci } while (size -= granule); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_walk_s1(unsigned long iova, size_t size, 31062306a36Sopenharmony_ci size_t granule, void *cookie) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 31362306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (cfg->flush_walk_prefer_tlbiasid) { 31662306a36Sopenharmony_ci arm_smmu_tlb_inv_context_s1(cookie); 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci arm_smmu_tlb_inv_range_s1(iova, size, granule, cookie, 31962306a36Sopenharmony_ci ARM_SMMU_CB_S1_TLBIVA); 32062306a36Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void arm_smmu_tlb_add_page_s1(struct iommu_iotlb_gather *gather, 32562306a36Sopenharmony_ci unsigned long iova, size_t granule, 32662306a36Sopenharmony_ci void *cookie) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci arm_smmu_tlb_inv_range_s1(iova, granule, granule, cookie, 32962306a36Sopenharmony_ci ARM_SMMU_CB_S1_TLBIVAL); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_walk_s2(unsigned long iova, size_t size, 33362306a36Sopenharmony_ci size_t granule, void *cookie) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci arm_smmu_tlb_inv_range_s2(iova, size, granule, cookie, 33662306a36Sopenharmony_ci ARM_SMMU_CB_S2_TLBIIPAS2); 33762306a36Sopenharmony_ci arm_smmu_tlb_sync_context(cookie); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void arm_smmu_tlb_add_page_s2(struct iommu_iotlb_gather *gather, 34162306a36Sopenharmony_ci unsigned long iova, size_t granule, 34262306a36Sopenharmony_ci void *cookie) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci arm_smmu_tlb_inv_range_s2(iova, granule, granule, cookie, 34562306a36Sopenharmony_ci ARM_SMMU_CB_S2_TLBIIPAS2L); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_walk_s2_v1(unsigned long iova, size_t size, 34962306a36Sopenharmony_ci size_t granule, void *cookie) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci arm_smmu_tlb_inv_context_s2(cookie); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * On MMU-401 at least, the cost of firing off multiple TLBIVMIDs appears 35562306a36Sopenharmony_ci * almost negligible, but the benefit of getting the first one in as far ahead 35662306a36Sopenharmony_ci * of the sync as possible is significant, hence we don't just make this a 35762306a36Sopenharmony_ci * no-op and call arm_smmu_tlb_inv_context_s2() from .iotlb_sync as you might 35862306a36Sopenharmony_ci * think. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_cistatic void arm_smmu_tlb_add_page_s2_v1(struct iommu_iotlb_gather *gather, 36162306a36Sopenharmony_ci unsigned long iova, size_t granule, 36262306a36Sopenharmony_ci void *cookie) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 36562306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) 36862306a36Sopenharmony_ci wmb(); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s1_tlb_ops = { 37462306a36Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s1, 37562306a36Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk_s1, 37662306a36Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s1, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s2_tlb_ops_v2 = { 38062306a36Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s2, 38162306a36Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk_s2, 38262306a36Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s2, 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_s2_tlb_ops_v1 = { 38662306a36Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context_s2, 38762306a36Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk_s2_v1, 38862306a36Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_add_page_s2_v1, 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic irqreturn_t arm_smmu_context_fault(int irq, void *dev) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci u32 fsr, fsynr, cbfrsynra; 39462306a36Sopenharmony_ci unsigned long iova; 39562306a36Sopenharmony_ci struct iommu_domain *domain = dev; 39662306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 39762306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 39862306a36Sopenharmony_ci int idx = smmu_domain->cfg.cbndx; 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR); 40262306a36Sopenharmony_ci if (!(fsr & ARM_SMMU_FSR_FAULT)) 40362306a36Sopenharmony_ci return IRQ_NONE; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci fsynr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSYNR0); 40662306a36Sopenharmony_ci iova = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_FAR); 40762306a36Sopenharmony_ci cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(idx)); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ret = report_iommu_fault(domain, NULL, iova, 41062306a36Sopenharmony_ci fsynr & ARM_SMMU_FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (ret == -ENOSYS) 41362306a36Sopenharmony_ci dev_err_ratelimited(smmu->dev, 41462306a36Sopenharmony_ci "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n", 41562306a36Sopenharmony_ci fsr, iova, fsynr, cbfrsynra, idx); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr); 41862306a36Sopenharmony_ci return IRQ_HANDLED; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic irqreturn_t arm_smmu_global_fault(int irq, void *dev) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u32 gfsr, gfsynr0, gfsynr1, gfsynr2; 42462306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev; 42562306a36Sopenharmony_ci static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, 42662306a36Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci gfsr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); 42962306a36Sopenharmony_ci gfsynr0 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR0); 43062306a36Sopenharmony_ci gfsynr1 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR1); 43162306a36Sopenharmony_ci gfsynr2 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR2); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!gfsr) 43462306a36Sopenharmony_ci return IRQ_NONE; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (__ratelimit(&rs)) { 43762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT) && 43862306a36Sopenharmony_ci (gfsr & ARM_SMMU_sGFSR_USF)) 43962306a36Sopenharmony_ci dev_err(smmu->dev, 44062306a36Sopenharmony_ci "Blocked unknown Stream ID 0x%hx; boot with \"arm-smmu.disable_bypass=0\" to allow, but this may have security implications\n", 44162306a36Sopenharmony_ci (u16)gfsynr1); 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci dev_err(smmu->dev, 44462306a36Sopenharmony_ci "Unexpected global fault, this could be serious\n"); 44562306a36Sopenharmony_ci dev_err(smmu->dev, 44662306a36Sopenharmony_ci "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n", 44762306a36Sopenharmony_ci gfsr, gfsynr0, gfsynr1, gfsynr2); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, gfsr); 45162306a36Sopenharmony_ci return IRQ_HANDLED; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, 45562306a36Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 45862306a36Sopenharmony_ci struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx]; 45962306a36Sopenharmony_ci bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci cb->cfg = cfg; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* TCR */ 46462306a36Sopenharmony_ci if (stage1) { 46562306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 46662306a36Sopenharmony_ci cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr; 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci cb->tcr[0] = arm_smmu_lpae_tcr(pgtbl_cfg); 46962306a36Sopenharmony_ci cb->tcr[1] = arm_smmu_lpae_tcr2(pgtbl_cfg); 47062306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 47162306a36Sopenharmony_ci cb->tcr[1] |= ARM_SMMU_TCR2_AS; 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci cb->tcr[0] |= ARM_SMMU_TCR_EAE; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci } else { 47662306a36Sopenharmony_ci cb->tcr[0] = arm_smmu_lpae_vtcr(pgtbl_cfg); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* TTBRs */ 48062306a36Sopenharmony_ci if (stage1) { 48162306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 48262306a36Sopenharmony_ci cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr; 48362306a36Sopenharmony_ci cb->ttbr[1] = 0; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci cb->ttbr[0] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, 48662306a36Sopenharmony_ci cfg->asid); 48762306a36Sopenharmony_ci cb->ttbr[1] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, 48862306a36Sopenharmony_ci cfg->asid); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (pgtbl_cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) 49162306a36Sopenharmony_ci cb->ttbr[1] |= pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 49262306a36Sopenharmony_ci else 49362306a36Sopenharmony_ci cb->ttbr[0] |= pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* MAIRs (stage-1 only) */ 50062306a36Sopenharmony_ci if (stage1) { 50162306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 50262306a36Sopenharmony_ci cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr; 50362306a36Sopenharmony_ci cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr; 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair; 50662306a36Sopenharmony_ci cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair >> 32; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_civoid arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci u32 reg; 51462306a36Sopenharmony_ci bool stage1; 51562306a36Sopenharmony_ci struct arm_smmu_cb *cb = &smmu->cbs[idx]; 51662306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = cb->cfg; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Unassigned context banks only need disabling */ 51962306a36Sopenharmony_ci if (!cfg) { 52062306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, 0); 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* CBA2R */ 52762306a36Sopenharmony_ci if (smmu->version > ARM_SMMU_V1) { 52862306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 52962306a36Sopenharmony_ci reg = ARM_SMMU_CBA2R_VA64; 53062306a36Sopenharmony_ci else 53162306a36Sopenharmony_ci reg = 0; 53262306a36Sopenharmony_ci /* 16-bit VMIDs live in CBA2R */ 53362306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_VMID16) 53462306a36Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBA2R_VMID16, cfg->vmid); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBA2R(idx), reg); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* CBAR */ 54062306a36Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, cfg->cbar); 54162306a36Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) 54262306a36Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_IRPTNDX, cfg->irptndx); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * Use the weakest shareability/memory types, so they are 54662306a36Sopenharmony_ci * overridden by the ttbcr/pte. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (stage1) { 54962306a36Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_S1_BPSHCFG, 55062306a36Sopenharmony_ci ARM_SMMU_CBAR_S1_BPSHCFG_NSH) | 55162306a36Sopenharmony_ci FIELD_PREP(ARM_SMMU_CBAR_S1_MEMATTR, 55262306a36Sopenharmony_ci ARM_SMMU_CBAR_S1_MEMATTR_WB); 55362306a36Sopenharmony_ci } else if (!(smmu->features & ARM_SMMU_FEAT_VMID16)) { 55462306a36Sopenharmony_ci /* 8-bit VMIDs live in CBAR */ 55562306a36Sopenharmony_ci reg |= FIELD_PREP(ARM_SMMU_CBAR_VMID, cfg->vmid); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(idx), reg); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * TCR 56162306a36Sopenharmony_ci * We must write this before the TTBRs, since it determines the 56262306a36Sopenharmony_ci * access behaviour of some fields (in particular, ASID[15:8]). 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (stage1 && smmu->version > ARM_SMMU_V1) 56562306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR2, cb->tcr[1]); 56662306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR, cb->tcr[0]); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* TTBRs */ 56962306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { 57062306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_CONTEXTIDR, cfg->asid); 57162306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); 57262306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR1, cb->ttbr[1]); 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); 57562306a36Sopenharmony_ci if (stage1) 57662306a36Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR1, 57762306a36Sopenharmony_ci cb->ttbr[1]); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* MAIRs (stage-1 only) */ 58162306a36Sopenharmony_ci if (stage1) { 58262306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR0, cb->mair[0]); 58362306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR1, cb->mair[1]); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* SCTLR */ 58762306a36Sopenharmony_ci reg = ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE | ARM_SMMU_SCTLR_AFE | 58862306a36Sopenharmony_ci ARM_SMMU_SCTLR_TRE | ARM_SMMU_SCTLR_M; 58962306a36Sopenharmony_ci if (stage1) 59062306a36Sopenharmony_ci reg |= ARM_SMMU_SCTLR_S1_ASIDPNE; 59162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 59262306a36Sopenharmony_ci reg |= ARM_SMMU_SCTLR_E; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (smmu->impl && smmu->impl->write_sctlr) 59562306a36Sopenharmony_ci smmu->impl->write_sctlr(smmu, idx, reg); 59662306a36Sopenharmony_ci else 59762306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int arm_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain, 60162306a36Sopenharmony_ci struct arm_smmu_device *smmu, 60262306a36Sopenharmony_ci struct device *dev, unsigned int start) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (smmu->impl && smmu->impl->alloc_context_bank) 60562306a36Sopenharmony_ci return smmu->impl->alloc_context_bank(smmu_domain, smmu, dev, start); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return __arm_smmu_alloc_bitmap(smmu->context_map, start, smmu->num_context_banks); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int arm_smmu_init_domain_context(struct iommu_domain *domain, 61162306a36Sopenharmony_ci struct arm_smmu_device *smmu, 61262306a36Sopenharmony_ci struct device *dev) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci int irq, start, ret = 0; 61562306a36Sopenharmony_ci unsigned long ias, oas; 61662306a36Sopenharmony_ci struct io_pgtable_ops *pgtbl_ops; 61762306a36Sopenharmony_ci struct io_pgtable_cfg pgtbl_cfg; 61862306a36Sopenharmony_ci enum io_pgtable_fmt fmt; 61962306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 62062306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 62162306a36Sopenharmony_ci irqreturn_t (*context_fault)(int irq, void *dev); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 62462306a36Sopenharmony_ci if (smmu_domain->smmu) 62562306a36Sopenharmony_ci goto out_unlock; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) { 62862306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS; 62962306a36Sopenharmony_ci smmu_domain->smmu = smmu; 63062306a36Sopenharmony_ci goto out_unlock; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* 63462306a36Sopenharmony_ci * Mapping the requested stage onto what we support is surprisingly 63562306a36Sopenharmony_ci * complicated, mainly because the spec allows S1+S2 SMMUs without 63662306a36Sopenharmony_ci * support for nested translation. That means we end up with the 63762306a36Sopenharmony_ci * following table: 63862306a36Sopenharmony_ci * 63962306a36Sopenharmony_ci * Requested Supported Actual 64062306a36Sopenharmony_ci * S1 N S1 64162306a36Sopenharmony_ci * S1 S1+S2 S1 64262306a36Sopenharmony_ci * S1 S2 S2 64362306a36Sopenharmony_ci * S1 S1 S1 64462306a36Sopenharmony_ci * N N N 64562306a36Sopenharmony_ci * N S1+S2 S2 64662306a36Sopenharmony_ci * N S2 S2 64762306a36Sopenharmony_ci * N S1 S1 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * Note that you can't actually request stage-2 mappings. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) 65262306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S2; 65362306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) 65462306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* 65762306a36Sopenharmony_ci * Choosing a suitable context format is even more fiddly. Until we 65862306a36Sopenharmony_ci * grow some way for the caller to express a preference, and/or move 65962306a36Sopenharmony_ci * the decision into the io-pgtable code where it arguably belongs, 66062306a36Sopenharmony_ci * just aim for the closest thing to the rest of the system, and hope 66162306a36Sopenharmony_ci * that the hardware isn't esoteric enough that we can't assume AArch64 66262306a36Sopenharmony_ci * support to be a superset of AArch32 support... 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_L) 66562306a36Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_L; 66662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) && 66762306a36Sopenharmony_ci !IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_ARM_LPAE) && 66862306a36Sopenharmony_ci (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) && 66962306a36Sopenharmony_ci (smmu_domain->stage == ARM_SMMU_DOMAIN_S1)) 67062306a36Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_S; 67162306a36Sopenharmony_ci if ((IS_ENABLED(CONFIG_64BIT) || cfg->fmt == ARM_SMMU_CTX_FMT_NONE) && 67262306a36Sopenharmony_ci (smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K | 67362306a36Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_16K | 67462306a36Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_4K))) 67562306a36Sopenharmony_ci cfg->fmt = ARM_SMMU_CTX_FMT_AARCH64; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) { 67862306a36Sopenharmony_ci ret = -EINVAL; 67962306a36Sopenharmony_ci goto out_unlock; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci switch (smmu_domain->stage) { 68362306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 68462306a36Sopenharmony_ci cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; 68562306a36Sopenharmony_ci start = smmu->num_s2_context_banks; 68662306a36Sopenharmony_ci ias = smmu->va_size; 68762306a36Sopenharmony_ci oas = smmu->ipa_size; 68862306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) { 68962306a36Sopenharmony_ci fmt = ARM_64_LPAE_S1; 69062306a36Sopenharmony_ci } else if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_L) { 69162306a36Sopenharmony_ci fmt = ARM_32_LPAE_S1; 69262306a36Sopenharmony_ci ias = min(ias, 32UL); 69362306a36Sopenharmony_ci oas = min(oas, 40UL); 69462306a36Sopenharmony_ci } else { 69562306a36Sopenharmony_ci fmt = ARM_V7S; 69662306a36Sopenharmony_ci ias = min(ias, 32UL); 69762306a36Sopenharmony_ci oas = min(oas, 32UL); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s1_tlb_ops; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 70262306a36Sopenharmony_ci /* 70362306a36Sopenharmony_ci * We will likely want to change this if/when KVM gets 70462306a36Sopenharmony_ci * involved. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 70762306a36Sopenharmony_ci cfg->cbar = CBAR_TYPE_S2_TRANS; 70862306a36Sopenharmony_ci start = 0; 70962306a36Sopenharmony_ci ias = smmu->ipa_size; 71062306a36Sopenharmony_ci oas = smmu->pa_size; 71162306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) { 71262306a36Sopenharmony_ci fmt = ARM_64_LPAE_S2; 71362306a36Sopenharmony_ci } else { 71462306a36Sopenharmony_ci fmt = ARM_32_LPAE_S2; 71562306a36Sopenharmony_ci ias = min(ias, 40UL); 71662306a36Sopenharmony_ci oas = min(oas, 40UL); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci if (smmu->version == ARM_SMMU_V2) 71962306a36Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v2; 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v1; 72262306a36Sopenharmony_ci break; 72362306a36Sopenharmony_ci default: 72462306a36Sopenharmony_ci ret = -EINVAL; 72562306a36Sopenharmony_ci goto out_unlock; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci ret = arm_smmu_alloc_context_bank(smmu_domain, smmu, dev, start); 72962306a36Sopenharmony_ci if (ret < 0) { 73062306a36Sopenharmony_ci goto out_unlock; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci smmu_domain->smmu = smmu; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci cfg->cbndx = ret; 73662306a36Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) { 73762306a36Sopenharmony_ci cfg->irptndx = atomic_inc_return(&smmu->irptndx); 73862306a36Sopenharmony_ci cfg->irptndx %= smmu->num_context_irqs; 73962306a36Sopenharmony_ci } else { 74062306a36Sopenharmony_ci cfg->irptndx = cfg->cbndx; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) 74462306a36Sopenharmony_ci cfg->vmid = cfg->cbndx + 1; 74562306a36Sopenharmony_ci else 74662306a36Sopenharmony_ci cfg->asid = cfg->cbndx; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci pgtbl_cfg = (struct io_pgtable_cfg) { 74962306a36Sopenharmony_ci .pgsize_bitmap = smmu->pgsize_bitmap, 75062306a36Sopenharmony_ci .ias = ias, 75162306a36Sopenharmony_ci .oas = oas, 75262306a36Sopenharmony_ci .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK, 75362306a36Sopenharmony_ci .tlb = smmu_domain->flush_ops, 75462306a36Sopenharmony_ci .iommu_dev = smmu->dev, 75562306a36Sopenharmony_ci }; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (smmu->impl && smmu->impl->init_context) { 75862306a36Sopenharmony_ci ret = smmu->impl->init_context(smmu_domain, &pgtbl_cfg, dev); 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci goto out_clear_smmu; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (smmu_domain->pgtbl_quirks) 76462306a36Sopenharmony_ci pgtbl_cfg.quirks |= smmu_domain->pgtbl_quirks; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); 76762306a36Sopenharmony_ci if (!pgtbl_ops) { 76862306a36Sopenharmony_ci ret = -ENOMEM; 76962306a36Sopenharmony_ci goto out_clear_smmu; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Update the domain's page sizes to reflect the page table format */ 77362306a36Sopenharmony_ci domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (pgtbl_cfg.quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) { 77662306a36Sopenharmony_ci domain->geometry.aperture_start = ~0UL << ias; 77762306a36Sopenharmony_ci domain->geometry.aperture_end = ~0UL; 77862306a36Sopenharmony_ci } else { 77962306a36Sopenharmony_ci domain->geometry.aperture_end = (1UL << ias) - 1; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci domain->geometry.force_aperture = true; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Initialise the context bank with our page table cfg */ 78562306a36Sopenharmony_ci arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); 78662306a36Sopenharmony_ci arm_smmu_write_context_bank(smmu, cfg->cbndx); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Request context fault interrupt. Do this last to avoid the 79062306a36Sopenharmony_ci * handler seeing a half-initialised domain state. 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_ci irq = smmu->irqs[cfg->irptndx]; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (smmu->impl && smmu->impl->context_fault) 79562306a36Sopenharmony_ci context_fault = smmu->impl->context_fault; 79662306a36Sopenharmony_ci else 79762306a36Sopenharmony_ci context_fault = arm_smmu_context_fault; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = devm_request_irq(smmu->dev, irq, context_fault, 80062306a36Sopenharmony_ci IRQF_SHARED, "arm-smmu-context-fault", domain); 80162306a36Sopenharmony_ci if (ret < 0) { 80262306a36Sopenharmony_ci dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n", 80362306a36Sopenharmony_ci cfg->irptndx, irq); 80462306a36Sopenharmony_ci cfg->irptndx = ARM_SMMU_INVALID_IRPTNDX; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Publish page table ops for map/unmap */ 81062306a36Sopenharmony_ci smmu_domain->pgtbl_ops = pgtbl_ops; 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ciout_clear_smmu: 81462306a36Sopenharmony_ci __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); 81562306a36Sopenharmony_ci smmu_domain->smmu = NULL; 81662306a36Sopenharmony_ciout_unlock: 81762306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 81862306a36Sopenharmony_ci return ret; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic void arm_smmu_destroy_domain_context(struct iommu_domain *domain) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 82462306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 82562306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 82662306a36Sopenharmony_ci int ret, irq; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY) 82962306a36Sopenharmony_ci return; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 83262306a36Sopenharmony_ci if (ret < 0) 83362306a36Sopenharmony_ci return; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * Disable the context bank and free the page tables before freeing 83762306a36Sopenharmony_ci * it. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci smmu->cbs[cfg->cbndx].cfg = NULL; 84062306a36Sopenharmony_ci arm_smmu_write_context_bank(smmu, cfg->cbndx); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (cfg->irptndx != ARM_SMMU_INVALID_IRPTNDX) { 84362306a36Sopenharmony_ci irq = smmu->irqs[cfg->irptndx]; 84462306a36Sopenharmony_ci devm_free_irq(smmu->dev, irq, domain); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci free_io_pgtable_ops(smmu_domain->pgtbl_ops); 84862306a36Sopenharmony_ci __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_IDENTITY) { 85862306a36Sopenharmony_ci if (using_legacy_binding || type != IOMMU_DOMAIN_DMA) 85962306a36Sopenharmony_ci return NULL; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * Allocate the domain and initialise some of its data structures. 86362306a36Sopenharmony_ci * We can't really do anything meaningful until we've added a 86462306a36Sopenharmony_ci * master. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); 86762306a36Sopenharmony_ci if (!smmu_domain) 86862306a36Sopenharmony_ci return NULL; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci mutex_init(&smmu_domain->init_mutex); 87162306a36Sopenharmony_ci spin_lock_init(&smmu_domain->cb_lock); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return &smmu_domain->domain; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Free the domain resources. We assume that all devices have 88262306a36Sopenharmony_ci * already been detached. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci arm_smmu_destroy_domain_context(domain); 88562306a36Sopenharmony_ci kfree(smmu_domain); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct arm_smmu_smr *smr = smmu->smrs + idx; 89162306a36Sopenharmony_ci u32 reg = FIELD_PREP(ARM_SMMU_SMR_ID, smr->id) | 89262306a36Sopenharmony_ci FIELD_PREP(ARM_SMMU_SMR_MASK, smr->mask); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid) 89562306a36Sopenharmony_ci reg |= ARM_SMMU_SMR_VALID; 89662306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), reg); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; 90262306a36Sopenharmony_ci u32 reg; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (smmu->impl && smmu->impl->write_s2cr) { 90562306a36Sopenharmony_ci smmu->impl->write_s2cr(smmu, idx); 90662306a36Sopenharmony_ci return; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, s2cr->type) | 91062306a36Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_CBNDX, s2cr->cbndx) | 91162306a36Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs && 91462306a36Sopenharmony_ci smmu->smrs[idx].valid) 91562306a36Sopenharmony_ci reg |= ARM_SMMU_S2CR_EXIDVALID; 91662306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci arm_smmu_write_s2cr(smmu, idx); 92262306a36Sopenharmony_ci if (smmu->smrs) 92362306a36Sopenharmony_ci arm_smmu_write_smr(smmu, idx); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * The width of SMR's mask field depends on sCR0_EXIDENABLE, so this function 92862306a36Sopenharmony_ci * should be called after sCR0 is written. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_cistatic void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci u32 smr; 93362306a36Sopenharmony_ci int i; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!smmu->smrs) 93662306a36Sopenharmony_ci return; 93762306a36Sopenharmony_ci /* 93862306a36Sopenharmony_ci * If we've had to accommodate firmware memory regions, we may 93962306a36Sopenharmony_ci * have live SMRs by now; tread carefully... 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * Somewhat perversely, not having a free SMR for this test implies we 94262306a36Sopenharmony_ci * can get away without it anyway, as we'll only be able to 'allocate' 94362306a36Sopenharmony_ci * these SMRs for the ID/mask values we're already trusting to be OK. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; i++) 94662306a36Sopenharmony_ci if (!smmu->smrs[i].valid) 94762306a36Sopenharmony_ci goto smr_ok; 94862306a36Sopenharmony_ci return; 94962306a36Sopenharmony_cismr_ok: 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * SMR.ID bits may not be preserved if the corresponding MASK 95262306a36Sopenharmony_ci * bits are set, so check each one separately. We can reject 95362306a36Sopenharmony_ci * masters later if they try to claim IDs outside these masks. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci smr = FIELD_PREP(ARM_SMMU_SMR_ID, smmu->streamid_mask); 95662306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(i), smr); 95762306a36Sopenharmony_ci smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); 95862306a36Sopenharmony_ci smmu->streamid_mask = FIELD_GET(ARM_SMMU_SMR_ID, smr); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci smr = FIELD_PREP(ARM_SMMU_SMR_MASK, smmu->streamid_mask); 96162306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(i), smr); 96262306a36Sopenharmony_ci smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); 96362306a36Sopenharmony_ci smmu->smr_mask_mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr); 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct arm_smmu_smr *smrs = smmu->smrs; 96962306a36Sopenharmony_ci int i, free_idx = -ENOSPC; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Stream indexing is blissfully easy */ 97262306a36Sopenharmony_ci if (!smrs) 97362306a36Sopenharmony_ci return id; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* Validating SMRs is... less so */ 97662306a36Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; ++i) { 97762306a36Sopenharmony_ci if (!smrs[i].valid) { 97862306a36Sopenharmony_ci /* 97962306a36Sopenharmony_ci * Note the first free entry we come across, which 98062306a36Sopenharmony_ci * we'll claim in the end if nothing else matches. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci if (free_idx < 0) 98362306a36Sopenharmony_ci free_idx = i; 98462306a36Sopenharmony_ci continue; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci /* 98762306a36Sopenharmony_ci * If the new entry is _entirely_ matched by an existing entry, 98862306a36Sopenharmony_ci * then reuse that, with the guarantee that there also cannot 98962306a36Sopenharmony_ci * be any subsequent conflicting entries. In normal use we'd 99062306a36Sopenharmony_ci * expect simply identical entries for this case, but there's 99162306a36Sopenharmony_ci * no harm in accommodating the generalisation. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci if ((mask & smrs[i].mask) == mask && 99462306a36Sopenharmony_ci !((id ^ smrs[i].id) & ~smrs[i].mask)) 99562306a36Sopenharmony_ci return i; 99662306a36Sopenharmony_ci /* 99762306a36Sopenharmony_ci * If the new entry has any other overlap with an existing one, 99862306a36Sopenharmony_ci * though, then there always exists at least one stream ID 99962306a36Sopenharmony_ci * which would cause a conflict, and we can't allow that risk. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask))) 100262306a36Sopenharmony_ci return -EINVAL; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return free_idx; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci if (--smmu->s2crs[idx].count) 101162306a36Sopenharmony_ci return false; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci smmu->s2crs[idx] = s2cr_init_val; 101462306a36Sopenharmony_ci if (smmu->smrs) 101562306a36Sopenharmony_ci smmu->smrs[idx].valid = false; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return true; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int arm_smmu_master_alloc_smes(struct device *dev) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 102362306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 102462306a36Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 102562306a36Sopenharmony_ci struct arm_smmu_smr *smrs = smmu->smrs; 102662306a36Sopenharmony_ci int i, idx, ret; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mutex_lock(&smmu->stream_map_mutex); 102962306a36Sopenharmony_ci /* Figure out a viable stream map entry allocation */ 103062306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 103162306a36Sopenharmony_ci u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]); 103262306a36Sopenharmony_ci u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (idx != INVALID_SMENDX) { 103562306a36Sopenharmony_ci ret = -EEXIST; 103662306a36Sopenharmony_ci goto out_err; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci ret = arm_smmu_find_sme(smmu, sid, mask); 104062306a36Sopenharmony_ci if (ret < 0) 104162306a36Sopenharmony_ci goto out_err; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci idx = ret; 104462306a36Sopenharmony_ci if (smrs && smmu->s2crs[idx].count == 0) { 104562306a36Sopenharmony_ci smrs[idx].id = sid; 104662306a36Sopenharmony_ci smrs[idx].mask = mask; 104762306a36Sopenharmony_ci smrs[idx].valid = true; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci smmu->s2crs[idx].count++; 105062306a36Sopenharmony_ci cfg->smendx[i] = (s16)idx; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* It worked! Now, poke the actual hardware */ 105462306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) 105562306a36Sopenharmony_ci arm_smmu_write_sme(smmu, idx); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 105862306a36Sopenharmony_ci return 0; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ciout_err: 106162306a36Sopenharmony_ci while (i--) { 106262306a36Sopenharmony_ci arm_smmu_free_sme(smmu, cfg->smendx[i]); 106362306a36Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 106662306a36Sopenharmony_ci return ret; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg, 107062306a36Sopenharmony_ci struct iommu_fwspec *fwspec) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 107362306a36Sopenharmony_ci int i, idx; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci mutex_lock(&smmu->stream_map_mutex); 107662306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 107762306a36Sopenharmony_ci if (arm_smmu_free_sme(smmu, idx)) 107862306a36Sopenharmony_ci arm_smmu_write_sme(smmu, idx); 107962306a36Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, 108562306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg, 108662306a36Sopenharmony_ci struct iommu_fwspec *fwspec) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 108962306a36Sopenharmony_ci struct arm_smmu_s2cr *s2cr = smmu->s2crs; 109062306a36Sopenharmony_ci u8 cbndx = smmu_domain->cfg.cbndx; 109162306a36Sopenharmony_ci enum arm_smmu_s2cr_type type; 109262306a36Sopenharmony_ci int i, idx; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS) 109562306a36Sopenharmony_ci type = S2CR_TYPE_BYPASS; 109662306a36Sopenharmony_ci else 109762306a36Sopenharmony_ci type = S2CR_TYPE_TRANS; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 110062306a36Sopenharmony_ci if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx) 110162306a36Sopenharmony_ci continue; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci s2cr[idx].type = type; 110462306a36Sopenharmony_ci s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT; 110562306a36Sopenharmony_ci s2cr[idx].cbndx = cbndx; 110662306a36Sopenharmony_ci arm_smmu_write_s2cr(smmu, idx); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 111462306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 111562306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 111662306a36Sopenharmony_ci struct arm_smmu_device *smmu; 111762306a36Sopenharmony_ci int ret; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) { 112062306a36Sopenharmony_ci dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); 112162306a36Sopenharmony_ci return -ENXIO; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* 112562306a36Sopenharmony_ci * FIXME: The arch/arm DMA API code tries to attach devices to its own 112662306a36Sopenharmony_ci * domains between of_xlate() and probe_device() - we have no way to cope 112762306a36Sopenharmony_ci * with that, so until ARM gets converted to rely on groups and default 112862306a36Sopenharmony_ci * domains, just say no (but more politely than by dereferencing NULL). 112962306a36Sopenharmony_ci * This should be at least a WARN_ON once that's sorted. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci cfg = dev_iommu_priv_get(dev); 113262306a36Sopenharmony_ci if (!cfg) 113362306a36Sopenharmony_ci return -ENODEV; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci smmu = cfg->smmu; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 113862306a36Sopenharmony_ci if (ret < 0) 113962306a36Sopenharmony_ci return ret; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Ensure that the domain is finalised */ 114262306a36Sopenharmony_ci ret = arm_smmu_init_domain_context(domain, smmu, dev); 114362306a36Sopenharmony_ci if (ret < 0) 114462306a36Sopenharmony_ci goto rpm_put; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* 114762306a36Sopenharmony_ci * Sanity check the domain. We don't support domains across 114862306a36Sopenharmony_ci * different SMMUs. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci if (smmu_domain->smmu != smmu) { 115162306a36Sopenharmony_ci ret = -EINVAL; 115262306a36Sopenharmony_ci goto rpm_put; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* Looks ok, so add the device to the domain */ 115662306a36Sopenharmony_ci ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* 115962306a36Sopenharmony_ci * Setup an autosuspend delay to avoid bouncing runpm state. 116062306a36Sopenharmony_ci * Otherwise, if a driver for a suspended consumer device 116162306a36Sopenharmony_ci * unmaps buffers, it will runpm resume/suspend for each one. 116262306a36Sopenharmony_ci * 116362306a36Sopenharmony_ci * For example, when used by a GPU device, when an application 116462306a36Sopenharmony_ci * or game exits, it can trigger unmapping 100s or 1000s of 116562306a36Sopenharmony_ci * buffers. With a runpm cycle for each buffer, that adds up 116662306a36Sopenharmony_ci * to 5-10sec worth of reprogramming the context bank, while 116762306a36Sopenharmony_ci * the system appears to be locked up to the user. 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(smmu->dev, 20); 117062306a36Sopenharmony_ci pm_runtime_use_autosuspend(smmu->dev); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cirpm_put: 117362306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 117462306a36Sopenharmony_ci return ret; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic int arm_smmu_map_pages(struct iommu_domain *domain, unsigned long iova, 117862306a36Sopenharmony_ci phys_addr_t paddr, size_t pgsize, size_t pgcount, 117962306a36Sopenharmony_ci int prot, gfp_t gfp, size_t *mapped) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 118262306a36Sopenharmony_ci struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; 118362306a36Sopenharmony_ci int ret; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (!ops) 118662306a36Sopenharmony_ci return -ENODEV; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci arm_smmu_rpm_get(smmu); 118962306a36Sopenharmony_ci ret = ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot, gfp, mapped); 119062306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return ret; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic size_t arm_smmu_unmap_pages(struct iommu_domain *domain, unsigned long iova, 119662306a36Sopenharmony_ci size_t pgsize, size_t pgcount, 119762306a36Sopenharmony_ci struct iommu_iotlb_gather *iotlb_gather) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 120062306a36Sopenharmony_ci struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; 120162306a36Sopenharmony_ci size_t ret; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (!ops) 120462306a36Sopenharmony_ci return 0; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci arm_smmu_rpm_get(smmu); 120762306a36Sopenharmony_ci ret = ops->unmap_pages(ops, iova, pgsize, pgcount, iotlb_gather); 120862306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return ret; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 121662306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (smmu_domain->flush_ops) { 121962306a36Sopenharmony_ci arm_smmu_rpm_get(smmu); 122062306a36Sopenharmony_ci smmu_domain->flush_ops->tlb_flush_all(smmu_domain); 122162306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain, 122662306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 122962306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (!smmu) 123262306a36Sopenharmony_ci return; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci arm_smmu_rpm_get(smmu); 123562306a36Sopenharmony_ci if (smmu->version == ARM_SMMU_V2 || 123662306a36Sopenharmony_ci smmu_domain->stage == ARM_SMMU_DOMAIN_S1) 123762306a36Sopenharmony_ci arm_smmu_tlb_sync_context(smmu_domain); 123862306a36Sopenharmony_ci else 123962306a36Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 124062306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, 124462306a36Sopenharmony_ci dma_addr_t iova) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 124762306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 124862306a36Sopenharmony_ci struct arm_smmu_cfg *cfg = &smmu_domain->cfg; 124962306a36Sopenharmony_ci struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; 125062306a36Sopenharmony_ci struct device *dev = smmu->dev; 125162306a36Sopenharmony_ci void __iomem *reg; 125262306a36Sopenharmony_ci u32 tmp; 125362306a36Sopenharmony_ci u64 phys; 125462306a36Sopenharmony_ci unsigned long va, flags; 125562306a36Sopenharmony_ci int ret, idx = cfg->cbndx; 125662306a36Sopenharmony_ci phys_addr_t addr = 0; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 125962306a36Sopenharmony_ci if (ret < 0) 126062306a36Sopenharmony_ci return 0; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->cb_lock, flags); 126362306a36Sopenharmony_ci va = iova & ~0xfffUL; 126462306a36Sopenharmony_ci if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) 126562306a36Sopenharmony_ci arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_ATS1PR, va); 126662306a36Sopenharmony_ci else 126762306a36Sopenharmony_ci arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ATS1PR, va); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci reg = arm_smmu_page(smmu, ARM_SMMU_CB(smmu, idx)) + ARM_SMMU_CB_ATSR; 127062306a36Sopenharmony_ci if (readl_poll_timeout_atomic(reg, tmp, !(tmp & ARM_SMMU_ATSR_ACTIVE), 127162306a36Sopenharmony_ci 5, 50)) { 127262306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 127362306a36Sopenharmony_ci dev_err(dev, 127462306a36Sopenharmony_ci "iova to phys timed out on %pad. Falling back to software table walk.\n", 127562306a36Sopenharmony_ci &iova); 127662306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 127762306a36Sopenharmony_ci return ops->iova_to_phys(ops, iova); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci phys = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_PAR); 128162306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); 128262306a36Sopenharmony_ci if (phys & ARM_SMMU_CB_PAR_F) { 128362306a36Sopenharmony_ci dev_err(dev, "translation fault!\n"); 128462306a36Sopenharmony_ci dev_err(dev, "PAR = 0x%llx\n", phys); 128562306a36Sopenharmony_ci goto out; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci addr = (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); 128962306a36Sopenharmony_ciout: 129062306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return addr; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, 129662306a36Sopenharmony_ci dma_addr_t iova) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 129962306a36Sopenharmony_ci struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (!ops) 130262306a36Sopenharmony_ci return 0; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && 130562306a36Sopenharmony_ci smmu_domain->stage == ARM_SMMU_DOMAIN_S1) 130662306a36Sopenharmony_ci return arm_smmu_iova_to_phys_hard(domain, iova); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return ops->iova_to_phys(ops, iova); 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic bool arm_smmu_capable(struct device *dev, enum iommu_cap cap) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci switch (cap) { 131662306a36Sopenharmony_ci case IOMMU_CAP_CACHE_COHERENCY: 131762306a36Sopenharmony_ci /* 131862306a36Sopenharmony_ci * It's overwhelmingly the case in practice that when the pagetable 131962306a36Sopenharmony_ci * walk interface is connected to a coherent interconnect, all the 132062306a36Sopenharmony_ci * translation interfaces are too. Furthermore if the device is 132162306a36Sopenharmony_ci * natively coherent, then its translation interface must also be. 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_ci return cfg->smmu->features & ARM_SMMU_FEAT_COHERENT_WALK || 132462306a36Sopenharmony_ci device_get_dma_attr(dev) == DEV_DMA_COHERENT; 132562306a36Sopenharmony_ci case IOMMU_CAP_NOEXEC: 132662306a36Sopenharmony_ci case IOMMU_CAP_DEFERRED_FLUSH: 132762306a36Sopenharmony_ci return true; 132862306a36Sopenharmony_ci default: 132962306a36Sopenharmony_ci return false; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic 133462306a36Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver, 133762306a36Sopenharmony_ci fwnode); 133862306a36Sopenharmony_ci put_device(dev); 133962306a36Sopenharmony_ci return dev ? dev_get_drvdata(dev) : NULL; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci struct arm_smmu_device *smmu = NULL; 134562306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 134662306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 134762306a36Sopenharmony_ci int i, ret; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (using_legacy_binding) { 135062306a36Sopenharmony_ci ret = arm_smmu_register_legacy_master(dev, &smmu); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci /* 135362306a36Sopenharmony_ci * If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master() 135462306a36Sopenharmony_ci * will allocate/initialise a new one. Thus we need to update fwspec for 135562306a36Sopenharmony_ci * later use. 135662306a36Sopenharmony_ci */ 135762306a36Sopenharmony_ci fwspec = dev_iommu_fwspec_get(dev); 135862306a36Sopenharmony_ci if (ret) 135962306a36Sopenharmony_ci goto out_free; 136062306a36Sopenharmony_ci } else if (fwspec && fwspec->ops == &arm_smmu_ops) { 136162306a36Sopenharmony_ci smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); 136262306a36Sopenharmony_ci } else { 136362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci ret = -EINVAL; 136762306a36Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) { 136862306a36Sopenharmony_ci u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]); 136962306a36Sopenharmony_ci u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (sid & ~smmu->streamid_mask) { 137262306a36Sopenharmony_ci dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n", 137362306a36Sopenharmony_ci sid, smmu->streamid_mask); 137462306a36Sopenharmony_ci goto out_free; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci if (mask & ~smmu->smr_mask_mask) { 137762306a36Sopenharmony_ci dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n", 137862306a36Sopenharmony_ci mask, smmu->smr_mask_mask); 137962306a36Sopenharmony_ci goto out_free; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci ret = -ENOMEM; 138462306a36Sopenharmony_ci cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]), 138562306a36Sopenharmony_ci GFP_KERNEL); 138662306a36Sopenharmony_ci if (!cfg) 138762306a36Sopenharmony_ci goto out_free; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci cfg->smmu = smmu; 139062306a36Sopenharmony_ci dev_iommu_priv_set(dev, cfg); 139162306a36Sopenharmony_ci while (i--) 139262306a36Sopenharmony_ci cfg->smendx[i] = INVALID_SMENDX; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci ret = arm_smmu_rpm_get(smmu); 139562306a36Sopenharmony_ci if (ret < 0) 139662306a36Sopenharmony_ci goto out_cfg_free; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci ret = arm_smmu_master_alloc_smes(dev); 139962306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (ret) 140262306a36Sopenharmony_ci goto out_cfg_free; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci device_link_add(dev, smmu->dev, 140562306a36Sopenharmony_ci DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return &smmu->iommu; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ciout_cfg_free: 141062306a36Sopenharmony_ci kfree(cfg); 141162306a36Sopenharmony_ciout_free: 141262306a36Sopenharmony_ci iommu_fwspec_free(dev); 141362306a36Sopenharmony_ci return ERR_PTR(ret); 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 141962306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 142062306a36Sopenharmony_ci int ret; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci ret = arm_smmu_rpm_get(cfg->smmu); 142362306a36Sopenharmony_ci if (ret < 0) 142462306a36Sopenharmony_ci return; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci arm_smmu_master_free_smes(cfg, fwspec); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci arm_smmu_rpm_put(cfg->smmu); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci dev_iommu_priv_set(dev, NULL); 143162306a36Sopenharmony_ci kfree(cfg); 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic void arm_smmu_probe_finalize(struct device *dev) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg; 143762306a36Sopenharmony_ci struct arm_smmu_device *smmu; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci cfg = dev_iommu_priv_get(dev); 144062306a36Sopenharmony_ci smmu = cfg->smmu; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci if (smmu->impl && smmu->impl->probe_finalize) 144362306a36Sopenharmony_ci smmu->impl->probe_finalize(smmu, dev); 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 144962306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 145062306a36Sopenharmony_ci struct arm_smmu_device *smmu = cfg->smmu; 145162306a36Sopenharmony_ci struct iommu_group *group = NULL; 145262306a36Sopenharmony_ci int i, idx; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci mutex_lock(&smmu->stream_map_mutex); 145562306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) { 145662306a36Sopenharmony_ci if (group && smmu->s2crs[idx].group && 145762306a36Sopenharmony_ci group != smmu->s2crs[idx].group) { 145862306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 145962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci group = smmu->s2crs[idx].group; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (group) { 146662306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 146762306a36Sopenharmony_ci return iommu_group_ref_get(group); 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci if (dev_is_pci(dev)) 147162306a36Sopenharmony_ci group = pci_device_group(dev); 147262306a36Sopenharmony_ci else if (dev_is_fsl_mc(dev)) 147362306a36Sopenharmony_ci group = fsl_mc_device_group(dev); 147462306a36Sopenharmony_ci else 147562306a36Sopenharmony_ci group = generic_device_group(dev); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci /* Remember group for faster lookups */ 147862306a36Sopenharmony_ci if (!IS_ERR(group)) 147962306a36Sopenharmony_ci for_each_cfg_sme(cfg, fwspec, i, idx) 148062306a36Sopenharmony_ci smmu->s2crs[idx].group = group; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci mutex_unlock(&smmu->stream_map_mutex); 148362306a36Sopenharmony_ci return group; 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int arm_smmu_enable_nesting(struct iommu_domain *domain) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 148962306a36Sopenharmony_ci int ret = 0; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 149262306a36Sopenharmony_ci if (smmu_domain->smmu) 149362306a36Sopenharmony_ci ret = -EPERM; 149462306a36Sopenharmony_ci else 149562306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; 149662306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci return ret; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain, 150262306a36Sopenharmony_ci unsigned long quirks) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 150562306a36Sopenharmony_ci int ret = 0; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 150862306a36Sopenharmony_ci if (smmu_domain->smmu) 150962306a36Sopenharmony_ci ret = -EPERM; 151062306a36Sopenharmony_ci else 151162306a36Sopenharmony_ci smmu_domain->pgtbl_quirks = quirks; 151262306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci return ret; 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci u32 mask, fwid = 0; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (args->args_count > 0) 152262306a36Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_ID, args->args[0]); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (args->args_count > 1) 152562306a36Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, args->args[1]); 152662306a36Sopenharmony_ci else if (!of_property_read_u32(args->np, "stream-match-mask", &mask)) 152762306a36Sopenharmony_ci fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, mask); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return iommu_fwspec_add_ids(dev, &fwid, 1); 153062306a36Sopenharmony_ci} 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev, 153362306a36Sopenharmony_ci struct list_head *head) 153462306a36Sopenharmony_ci{ 153562306a36Sopenharmony_ci struct iommu_resv_region *region; 153662306a36Sopenharmony_ci int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, 153962306a36Sopenharmony_ci prot, IOMMU_RESV_SW_MSI, GFP_KERNEL); 154062306a36Sopenharmony_ci if (!region) 154162306a36Sopenharmony_ci return; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci list_add_tail(®ion->list, head); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci iommu_dma_get_resv_regions(dev, head); 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic int arm_smmu_def_domain_type(struct device *dev) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); 155162306a36Sopenharmony_ci const struct arm_smmu_impl *impl = cfg->smmu->impl; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (using_legacy_binding) 155462306a36Sopenharmony_ci return IOMMU_DOMAIN_IDENTITY; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (impl && impl->def_domain_type) 155762306a36Sopenharmony_ci return impl->def_domain_type(dev); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci return 0; 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = { 156362306a36Sopenharmony_ci .capable = arm_smmu_capable, 156462306a36Sopenharmony_ci .domain_alloc = arm_smmu_domain_alloc, 156562306a36Sopenharmony_ci .probe_device = arm_smmu_probe_device, 156662306a36Sopenharmony_ci .release_device = arm_smmu_release_device, 156762306a36Sopenharmony_ci .probe_finalize = arm_smmu_probe_finalize, 156862306a36Sopenharmony_ci .device_group = arm_smmu_device_group, 156962306a36Sopenharmony_ci .of_xlate = arm_smmu_of_xlate, 157062306a36Sopenharmony_ci .get_resv_regions = arm_smmu_get_resv_regions, 157162306a36Sopenharmony_ci .def_domain_type = arm_smmu_def_domain_type, 157262306a36Sopenharmony_ci .pgsize_bitmap = -1UL, /* Restricted during device attach */ 157362306a36Sopenharmony_ci .owner = THIS_MODULE, 157462306a36Sopenharmony_ci .default_domain_ops = &(const struct iommu_domain_ops) { 157562306a36Sopenharmony_ci .attach_dev = arm_smmu_attach_dev, 157662306a36Sopenharmony_ci .map_pages = arm_smmu_map_pages, 157762306a36Sopenharmony_ci .unmap_pages = arm_smmu_unmap_pages, 157862306a36Sopenharmony_ci .flush_iotlb_all = arm_smmu_flush_iotlb_all, 157962306a36Sopenharmony_ci .iotlb_sync = arm_smmu_iotlb_sync, 158062306a36Sopenharmony_ci .iova_to_phys = arm_smmu_iova_to_phys, 158162306a36Sopenharmony_ci .enable_nesting = arm_smmu_enable_nesting, 158262306a36Sopenharmony_ci .set_pgtable_quirks = arm_smmu_set_pgtable_quirks, 158362306a36Sopenharmony_ci .free = arm_smmu_domain_free, 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci}; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic void arm_smmu_device_reset(struct arm_smmu_device *smmu) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci int i; 159062306a36Sopenharmony_ci u32 reg; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* clear global FSR */ 159362306a36Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); 159462306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, reg); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* 159762306a36Sopenharmony_ci * Reset stream mapping groups: Initial values mark all SMRn as 159862306a36Sopenharmony_ci * invalid and all S2CRn as bypass unless overridden. 159962306a36Sopenharmony_ci */ 160062306a36Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; ++i) 160162306a36Sopenharmony_ci arm_smmu_write_sme(smmu, i); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* Make sure all context banks are disabled and clear CB_FSR */ 160462306a36Sopenharmony_ci for (i = 0; i < smmu->num_context_banks; ++i) { 160562306a36Sopenharmony_ci arm_smmu_write_context_bank(smmu, i); 160662306a36Sopenharmony_ci arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT); 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci /* Invalidate the TLB, just in case */ 161062306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLH, QCOM_DUMMY_VAL); 161162306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLNSNH, QCOM_DUMMY_VAL); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* Enable fault reporting */ 161662306a36Sopenharmony_ci reg |= (ARM_SMMU_sCR0_GFRE | ARM_SMMU_sCR0_GFIE | 161762306a36Sopenharmony_ci ARM_SMMU_sCR0_GCFGFRE | ARM_SMMU_sCR0_GCFGFIE); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci /* Disable TLB broadcasting. */ 162062306a36Sopenharmony_ci reg |= (ARM_SMMU_sCR0_VMIDPNE | ARM_SMMU_sCR0_PTM); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* Enable client access, handling unmatched streams as appropriate */ 162362306a36Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_CLIENTPD; 162462306a36Sopenharmony_ci if (disable_bypass) 162562306a36Sopenharmony_ci reg |= ARM_SMMU_sCR0_USFCFG; 162662306a36Sopenharmony_ci else 162762306a36Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_USFCFG; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci /* Disable forced broadcasting */ 163062306a36Sopenharmony_ci reg &= ~ARM_SMMU_sCR0_FB; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* Don't upgrade barriers */ 163362306a36Sopenharmony_ci reg &= ~(ARM_SMMU_sCR0_BSU); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_VMID16) 163662306a36Sopenharmony_ci reg |= ARM_SMMU_sCR0_VMID16EN; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_EXIDS) 163962306a36Sopenharmony_ci reg |= ARM_SMMU_sCR0_EXIDENABLE; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci if (smmu->impl && smmu->impl->reset) 164262306a36Sopenharmony_ci smmu->impl->reset(smmu); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci /* Push the button */ 164562306a36Sopenharmony_ci arm_smmu_tlb_sync_global(smmu); 164662306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_cistatic int arm_smmu_id_size_to_bits(int size) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci switch (size) { 165262306a36Sopenharmony_ci case 0: 165362306a36Sopenharmony_ci return 32; 165462306a36Sopenharmony_ci case 1: 165562306a36Sopenharmony_ci return 36; 165662306a36Sopenharmony_ci case 2: 165762306a36Sopenharmony_ci return 40; 165862306a36Sopenharmony_ci case 3: 165962306a36Sopenharmony_ci return 42; 166062306a36Sopenharmony_ci case 4: 166162306a36Sopenharmony_ci return 44; 166262306a36Sopenharmony_ci case 5: 166362306a36Sopenharmony_ci default: 166462306a36Sopenharmony_ci return 48; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci} 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_cistatic int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci unsigned int size; 167162306a36Sopenharmony_ci u32 id; 167262306a36Sopenharmony_ci bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK; 167362306a36Sopenharmony_ci int i, ret; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci dev_notice(smmu->dev, "probing hardware configuration...\n"); 167662306a36Sopenharmony_ci dev_notice(smmu->dev, "SMMUv%d with:\n", 167762306a36Sopenharmony_ci smmu->version == ARM_SMMU_V2 ? 2 : 1); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci /* ID0 */ 168062306a36Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID0); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci /* Restrict available stages based on module parameter */ 168362306a36Sopenharmony_ci if (force_stage == 1) 168462306a36Sopenharmony_ci id &= ~(ARM_SMMU_ID0_S2TS | ARM_SMMU_ID0_NTS); 168562306a36Sopenharmony_ci else if (force_stage == 2) 168662306a36Sopenharmony_ci id &= ~(ARM_SMMU_ID0_S1TS | ARM_SMMU_ID0_NTS); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci if (id & ARM_SMMU_ID0_S1TS) { 168962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S1; 169062306a36Sopenharmony_ci dev_notice(smmu->dev, "\tstage 1 translation\n"); 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (id & ARM_SMMU_ID0_S2TS) { 169462306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S2; 169562306a36Sopenharmony_ci dev_notice(smmu->dev, "\tstage 2 translation\n"); 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (id & ARM_SMMU_ID0_NTS) { 169962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_NESTED; 170062306a36Sopenharmony_ci dev_notice(smmu->dev, "\tnested translation\n"); 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (!(smmu->features & 170462306a36Sopenharmony_ci (ARM_SMMU_FEAT_TRANS_S1 | ARM_SMMU_FEAT_TRANS_S2))) { 170562306a36Sopenharmony_ci dev_err(smmu->dev, "\tno translation support!\n"); 170662306a36Sopenharmony_ci return -ENODEV; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if ((id & ARM_SMMU_ID0_S1TS) && 171062306a36Sopenharmony_ci ((smmu->version < ARM_SMMU_V2) || !(id & ARM_SMMU_ID0_ATOSNS))) { 171162306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; 171262306a36Sopenharmony_ci dev_notice(smmu->dev, "\taddress translation ops\n"); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci /* 171662306a36Sopenharmony_ci * In order for DMA API calls to work properly, we must defer to what 171762306a36Sopenharmony_ci * the FW says about coherency, regardless of what the hardware claims. 171862306a36Sopenharmony_ci * Fortunately, this also opens up a workaround for systems where the 171962306a36Sopenharmony_ci * ID register value has ended up configured incorrectly. 172062306a36Sopenharmony_ci */ 172162306a36Sopenharmony_ci cttw_reg = !!(id & ARM_SMMU_ID0_CTTW); 172262306a36Sopenharmony_ci if (cttw_fw || cttw_reg) 172362306a36Sopenharmony_ci dev_notice(smmu->dev, "\t%scoherent table walk\n", 172462306a36Sopenharmony_ci cttw_fw ? "" : "non-"); 172562306a36Sopenharmony_ci if (cttw_fw != cttw_reg) 172662306a36Sopenharmony_ci dev_notice(smmu->dev, 172762306a36Sopenharmony_ci "\t(IDR0.CTTW overridden by FW configuration)\n"); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* Max. number of entries we have for stream matching/indexing */ 173062306a36Sopenharmony_ci if (smmu->version == ARM_SMMU_V2 && id & ARM_SMMU_ID0_EXIDS) { 173162306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_EXIDS; 173262306a36Sopenharmony_ci size = 1 << 16; 173362306a36Sopenharmony_ci } else { 173462306a36Sopenharmony_ci size = 1 << FIELD_GET(ARM_SMMU_ID0_NUMSIDB, id); 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci smmu->streamid_mask = size - 1; 173762306a36Sopenharmony_ci if (id & ARM_SMMU_ID0_SMS) { 173862306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH; 173962306a36Sopenharmony_ci size = FIELD_GET(ARM_SMMU_ID0_NUMSMRG, id); 174062306a36Sopenharmony_ci if (size == 0) { 174162306a36Sopenharmony_ci dev_err(smmu->dev, 174262306a36Sopenharmony_ci "stream-matching supported, but no SMRs present!\n"); 174362306a36Sopenharmony_ci return -ENODEV; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci /* Zero-initialised to mark as invalid */ 174762306a36Sopenharmony_ci smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs), 174862306a36Sopenharmony_ci GFP_KERNEL); 174962306a36Sopenharmony_ci if (!smmu->smrs) 175062306a36Sopenharmony_ci return -ENOMEM; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci dev_notice(smmu->dev, 175362306a36Sopenharmony_ci "\tstream matching with %u register groups", size); 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci /* s2cr->type == 0 means translation, so initialise explicitly */ 175662306a36Sopenharmony_ci smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs), 175762306a36Sopenharmony_ci GFP_KERNEL); 175862306a36Sopenharmony_ci if (!smmu->s2crs) 175962306a36Sopenharmony_ci return -ENOMEM; 176062306a36Sopenharmony_ci for (i = 0; i < size; i++) 176162306a36Sopenharmony_ci smmu->s2crs[i] = s2cr_init_val; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci smmu->num_mapping_groups = size; 176462306a36Sopenharmony_ci mutex_init(&smmu->stream_map_mutex); 176562306a36Sopenharmony_ci spin_lock_init(&smmu->global_sync_lock); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (smmu->version < ARM_SMMU_V2 || 176862306a36Sopenharmony_ci !(id & ARM_SMMU_ID0_PTFS_NO_AARCH32)) { 176962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; 177062306a36Sopenharmony_ci if (!(id & ARM_SMMU_ID0_PTFS_NO_AARCH32S)) 177162306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_S; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci /* ID1 */ 177562306a36Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID1); 177662306a36Sopenharmony_ci smmu->pgshift = (id & ARM_SMMU_ID1_PAGESIZE) ? 16 : 12; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci /* Check for size mismatch of SMMU address space from mapped region */ 177962306a36Sopenharmony_ci size = 1 << (FIELD_GET(ARM_SMMU_ID1_NUMPAGENDXB, id) + 1); 178062306a36Sopenharmony_ci if (smmu->numpage != 2 * size << smmu->pgshift) 178162306a36Sopenharmony_ci dev_warn(smmu->dev, 178262306a36Sopenharmony_ci "SMMU address space size (0x%x) differs from mapped region size (0x%x)!\n", 178362306a36Sopenharmony_ci 2 * size << smmu->pgshift, smmu->numpage); 178462306a36Sopenharmony_ci /* Now properly encode NUMPAGE to subsequently derive SMMU_CB_BASE */ 178562306a36Sopenharmony_ci smmu->numpage = size; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci smmu->num_s2_context_banks = FIELD_GET(ARM_SMMU_ID1_NUMS2CB, id); 178862306a36Sopenharmony_ci smmu->num_context_banks = FIELD_GET(ARM_SMMU_ID1_NUMCB, id); 178962306a36Sopenharmony_ci if (smmu->num_s2_context_banks > smmu->num_context_banks) { 179062306a36Sopenharmony_ci dev_err(smmu->dev, "impossible number of S2 context banks!\n"); 179162306a36Sopenharmony_ci return -ENODEV; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci dev_notice(smmu->dev, "\t%u context banks (%u stage-2 only)\n", 179462306a36Sopenharmony_ci smmu->num_context_banks, smmu->num_s2_context_banks); 179562306a36Sopenharmony_ci smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks, 179662306a36Sopenharmony_ci sizeof(*smmu->cbs), GFP_KERNEL); 179762306a36Sopenharmony_ci if (!smmu->cbs) 179862306a36Sopenharmony_ci return -ENOMEM; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* ID2 */ 180162306a36Sopenharmony_ci id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID2); 180262306a36Sopenharmony_ci size = arm_smmu_id_size_to_bits(FIELD_GET(ARM_SMMU_ID2_IAS, id)); 180362306a36Sopenharmony_ci smmu->ipa_size = size; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* The output mask is also applied for bypass */ 180662306a36Sopenharmony_ci size = arm_smmu_id_size_to_bits(FIELD_GET(ARM_SMMU_ID2_OAS, id)); 180762306a36Sopenharmony_ci smmu->pa_size = size; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (id & ARM_SMMU_ID2_VMID16) 181062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_VMID16; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* 181362306a36Sopenharmony_ci * What the page table walker can address actually depends on which 181462306a36Sopenharmony_ci * descriptor format is in use, but since a) we don't know that yet, 181562306a36Sopenharmony_ci * and b) it can vary per context bank, this will have to do... 181662306a36Sopenharmony_ci */ 181762306a36Sopenharmony_ci if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(size))) 181862306a36Sopenharmony_ci dev_warn(smmu->dev, 181962306a36Sopenharmony_ci "failed to set DMA mask for table walker\n"); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci if (smmu->version < ARM_SMMU_V2) { 182262306a36Sopenharmony_ci smmu->va_size = smmu->ipa_size; 182362306a36Sopenharmony_ci if (smmu->version == ARM_SMMU_V1_64K) 182462306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K; 182562306a36Sopenharmony_ci } else { 182662306a36Sopenharmony_ci size = FIELD_GET(ARM_SMMU_ID2_UBS, id); 182762306a36Sopenharmony_ci smmu->va_size = arm_smmu_id_size_to_bits(size); 182862306a36Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_4K) 182962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_4K; 183062306a36Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_16K) 183162306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_16K; 183262306a36Sopenharmony_ci if (id & ARM_SMMU_ID2_PTFS_64K) 183362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (smmu->impl && smmu->impl->cfg_probe) { 183762306a36Sopenharmony_ci ret = smmu->impl->cfg_probe(smmu); 183862306a36Sopenharmony_ci if (ret) 183962306a36Sopenharmony_ci return ret; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci /* Now we've corralled the various formats, what'll it do? */ 184362306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) 184462306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_64K | SZ_1M | SZ_16M; 184562306a36Sopenharmony_ci if (smmu->features & 184662306a36Sopenharmony_ci (ARM_SMMU_FEAT_FMT_AARCH32_L | ARM_SMMU_FEAT_FMT_AARCH64_4K)) 184762306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G; 184862306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_16K) 184962306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_16K | SZ_32M; 185062306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH64_64K) 185162306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_64K | SZ_512M; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (arm_smmu_ops.pgsize_bitmap == -1UL) 185462306a36Sopenharmony_ci arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap; 185562306a36Sopenharmony_ci else 185662306a36Sopenharmony_ci arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap; 185762306a36Sopenharmony_ci dev_notice(smmu->dev, "\tSupported page sizes: 0x%08lx\n", 185862306a36Sopenharmony_ci smmu->pgsize_bitmap); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) 186262306a36Sopenharmony_ci dev_notice(smmu->dev, "\tStage-1: %lu-bit VA -> %lu-bit IPA\n", 186362306a36Sopenharmony_ci smmu->va_size, smmu->ipa_size); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) 186662306a36Sopenharmony_ci dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", 186762306a36Sopenharmony_ci smmu->ipa_size, smmu->pa_size); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci return 0; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistruct arm_smmu_match_data { 187362306a36Sopenharmony_ci enum arm_smmu_arch_version version; 187462306a36Sopenharmony_ci enum arm_smmu_implementation model; 187562306a36Sopenharmony_ci}; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci#define ARM_SMMU_MATCH_DATA(name, ver, imp) \ 187862306a36Sopenharmony_cistatic const struct arm_smmu_match_data name = { .version = ver, .model = imp } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(smmu_generic_v1, ARM_SMMU_V1, GENERIC_SMMU); 188162306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(smmu_generic_v2, ARM_SMMU_V2, GENERIC_SMMU); 188262306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU); 188362306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500); 188462306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2); 188562306a36Sopenharmony_ciARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = { 188862306a36Sopenharmony_ci { .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 }, 188962306a36Sopenharmony_ci { .compatible = "arm,smmu-v2", .data = &smmu_generic_v2 }, 189062306a36Sopenharmony_ci { .compatible = "arm,mmu-400", .data = &smmu_generic_v1 }, 189162306a36Sopenharmony_ci { .compatible = "arm,mmu-401", .data = &arm_mmu401 }, 189262306a36Sopenharmony_ci { .compatible = "arm,mmu-500", .data = &arm_mmu500 }, 189362306a36Sopenharmony_ci { .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 }, 189462306a36Sopenharmony_ci { .compatible = "nvidia,smmu-500", .data = &arm_mmu500 }, 189562306a36Sopenharmony_ci { .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 }, 189662306a36Sopenharmony_ci { }, 189762306a36Sopenharmony_ci}; 189862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci#ifdef CONFIG_ACPI 190162306a36Sopenharmony_cistatic int acpi_smmu_get_data(u32 model, struct arm_smmu_device *smmu) 190262306a36Sopenharmony_ci{ 190362306a36Sopenharmony_ci int ret = 0; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci switch (model) { 190662306a36Sopenharmony_ci case ACPI_IORT_SMMU_V1: 190762306a36Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU400: 190862306a36Sopenharmony_ci smmu->version = ARM_SMMU_V1; 190962306a36Sopenharmony_ci smmu->model = GENERIC_SMMU; 191062306a36Sopenharmony_ci break; 191162306a36Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU401: 191262306a36Sopenharmony_ci smmu->version = ARM_SMMU_V1_64K; 191362306a36Sopenharmony_ci smmu->model = GENERIC_SMMU; 191462306a36Sopenharmony_ci break; 191562306a36Sopenharmony_ci case ACPI_IORT_SMMU_V2: 191662306a36Sopenharmony_ci smmu->version = ARM_SMMU_V2; 191762306a36Sopenharmony_ci smmu->model = GENERIC_SMMU; 191862306a36Sopenharmony_ci break; 191962306a36Sopenharmony_ci case ACPI_IORT_SMMU_CORELINK_MMU500: 192062306a36Sopenharmony_ci smmu->version = ARM_SMMU_V2; 192162306a36Sopenharmony_ci smmu->model = ARM_MMU500; 192262306a36Sopenharmony_ci break; 192362306a36Sopenharmony_ci case ACPI_IORT_SMMU_CAVIUM_THUNDERX: 192462306a36Sopenharmony_ci smmu->version = ARM_SMMU_V2; 192562306a36Sopenharmony_ci smmu->model = CAVIUM_SMMUV2; 192662306a36Sopenharmony_ci break; 192762306a36Sopenharmony_ci default: 192862306a36Sopenharmony_ci ret = -ENODEV; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci return ret; 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct arm_smmu_device *smmu, 193562306a36Sopenharmony_ci u32 *global_irqs, u32 *pmu_irqs) 193662306a36Sopenharmony_ci{ 193762306a36Sopenharmony_ci struct device *dev = smmu->dev; 193862306a36Sopenharmony_ci struct acpi_iort_node *node = 193962306a36Sopenharmony_ci *(struct acpi_iort_node **)dev_get_platdata(dev); 194062306a36Sopenharmony_ci struct acpi_iort_smmu *iort_smmu; 194162306a36Sopenharmony_ci int ret; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci /* Retrieve SMMU1/2 specific data */ 194462306a36Sopenharmony_ci iort_smmu = (struct acpi_iort_smmu *)node->node_data; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci ret = acpi_smmu_get_data(iort_smmu->model, smmu); 194762306a36Sopenharmony_ci if (ret < 0) 194862306a36Sopenharmony_ci return ret; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci /* Ignore the configuration access interrupt */ 195162306a36Sopenharmony_ci *global_irqs = 1; 195262306a36Sopenharmony_ci *pmu_irqs = 0; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci if (iort_smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) 195562306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci return 0; 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci#else 196062306a36Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct arm_smmu_device *smmu, 196162306a36Sopenharmony_ci u32 *global_irqs, u32 *pmu_irqs) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci return -ENODEV; 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci#endif 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct arm_smmu_device *smmu, 196862306a36Sopenharmony_ci u32 *global_irqs, u32 *pmu_irqs) 196962306a36Sopenharmony_ci{ 197062306a36Sopenharmony_ci const struct arm_smmu_match_data *data; 197162306a36Sopenharmony_ci struct device *dev = smmu->dev; 197262306a36Sopenharmony_ci bool legacy_binding; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "#global-interrupts", global_irqs)) 197562306a36Sopenharmony_ci return dev_err_probe(dev, -ENODEV, 197662306a36Sopenharmony_ci "missing #global-interrupts property\n"); 197762306a36Sopenharmony_ci *pmu_irqs = 0; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci data = of_device_get_match_data(dev); 198062306a36Sopenharmony_ci smmu->version = data->version; 198162306a36Sopenharmony_ci smmu->model = data->model; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL); 198462306a36Sopenharmony_ci if (legacy_binding && !using_generic_binding) { 198562306a36Sopenharmony_ci if (!using_legacy_binding) { 198662306a36Sopenharmony_ci pr_notice("deprecated \"mmu-masters\" DT property in use; %s support unavailable\n", 198762306a36Sopenharmony_ci IS_ENABLED(CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS) ? "DMA API" : "SMMU"); 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci using_legacy_binding = true; 199062306a36Sopenharmony_ci } else if (!legacy_binding && !using_legacy_binding) { 199162306a36Sopenharmony_ci using_generic_binding = true; 199262306a36Sopenharmony_ci } else { 199362306a36Sopenharmony_ci dev_err(dev, "not probing due to mismatched DT properties\n"); 199462306a36Sopenharmony_ci return -ENODEV; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci if (of_dma_is_coherent(dev->of_node)) 199862306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci return 0; 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci struct list_head rmr_list; 200662306a36Sopenharmony_ci struct iommu_resv_region *e; 200762306a36Sopenharmony_ci int idx, cnt = 0; 200862306a36Sopenharmony_ci u32 reg; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci INIT_LIST_HEAD(&rmr_list); 201162306a36Sopenharmony_ci iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci /* 201462306a36Sopenharmony_ci * Rather than trying to look at existing mappings that 201562306a36Sopenharmony_ci * are setup by the firmware and then invalidate the ones 201662306a36Sopenharmony_ci * that do no have matching RMR entries, just disable the 201762306a36Sopenharmony_ci * SMMU until it gets enabled again in the reset routine. 201862306a36Sopenharmony_ci */ 201962306a36Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0); 202062306a36Sopenharmony_ci reg |= ARM_SMMU_sCR0_CLIENTPD; 202162306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci list_for_each_entry(e, &rmr_list, list) { 202462306a36Sopenharmony_ci struct iommu_iort_rmr_data *rmr; 202562306a36Sopenharmony_ci int i; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci rmr = container_of(e, struct iommu_iort_rmr_data, rr); 202862306a36Sopenharmony_ci for (i = 0; i < rmr->num_sids; i++) { 202962306a36Sopenharmony_ci idx = arm_smmu_find_sme(smmu, rmr->sids[i], ~0); 203062306a36Sopenharmony_ci if (idx < 0) 203162306a36Sopenharmony_ci continue; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci if (smmu->s2crs[idx].count == 0) { 203462306a36Sopenharmony_ci smmu->smrs[idx].id = rmr->sids[i]; 203562306a36Sopenharmony_ci smmu->smrs[idx].mask = 0; 203662306a36Sopenharmony_ci smmu->smrs[idx].valid = true; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci smmu->s2crs[idx].count++; 203962306a36Sopenharmony_ci smmu->s2crs[idx].type = S2CR_TYPE_BYPASS; 204062306a36Sopenharmony_ci smmu->s2crs[idx].privcfg = S2CR_PRIVCFG_DEFAULT; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci cnt++; 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt, 204762306a36Sopenharmony_ci cnt == 1 ? "" : "s"); 204862306a36Sopenharmony_ci iort_put_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); 204962306a36Sopenharmony_ci} 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci struct resource *res; 205462306a36Sopenharmony_ci struct arm_smmu_device *smmu; 205562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 205662306a36Sopenharmony_ci int num_irqs, i, err; 205762306a36Sopenharmony_ci u32 global_irqs, pmu_irqs; 205862306a36Sopenharmony_ci irqreturn_t (*global_fault)(int irq, void *dev); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); 206162306a36Sopenharmony_ci if (!smmu) { 206262306a36Sopenharmony_ci dev_err(dev, "failed to allocate arm_smmu_device\n"); 206362306a36Sopenharmony_ci return -ENOMEM; 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci smmu->dev = dev; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (dev->of_node) 206862306a36Sopenharmony_ci err = arm_smmu_device_dt_probe(smmu, &global_irqs, &pmu_irqs); 206962306a36Sopenharmony_ci else 207062306a36Sopenharmony_ci err = arm_smmu_device_acpi_probe(smmu, &global_irqs, &pmu_irqs); 207162306a36Sopenharmony_ci if (err) 207262306a36Sopenharmony_ci return err; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci smmu->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 207562306a36Sopenharmony_ci if (IS_ERR(smmu->base)) 207662306a36Sopenharmony_ci return PTR_ERR(smmu->base); 207762306a36Sopenharmony_ci smmu->ioaddr = res->start; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci /* 208062306a36Sopenharmony_ci * The resource size should effectively match the value of SMMU_TOP; 208162306a36Sopenharmony_ci * stash that temporarily until we know PAGESIZE to validate it with. 208262306a36Sopenharmony_ci */ 208362306a36Sopenharmony_ci smmu->numpage = resource_size(res); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci smmu = arm_smmu_impl_init(smmu); 208662306a36Sopenharmony_ci if (IS_ERR(smmu)) 208762306a36Sopenharmony_ci return PTR_ERR(smmu); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci num_irqs = platform_irq_count(pdev); 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci smmu->num_context_irqs = num_irqs - global_irqs - pmu_irqs; 209262306a36Sopenharmony_ci if (smmu->num_context_irqs <= 0) 209362306a36Sopenharmony_ci return dev_err_probe(dev, -ENODEV, 209462306a36Sopenharmony_ci "found %d interrupts but expected at least %d\n", 209562306a36Sopenharmony_ci num_irqs, global_irqs + pmu_irqs + 1); 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci smmu->irqs = devm_kcalloc(dev, smmu->num_context_irqs, 209862306a36Sopenharmony_ci sizeof(*smmu->irqs), GFP_KERNEL); 209962306a36Sopenharmony_ci if (!smmu->irqs) 210062306a36Sopenharmony_ci return dev_err_probe(dev, -ENOMEM, "failed to allocate %d irqs\n", 210162306a36Sopenharmony_ci smmu->num_context_irqs); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci for (i = 0; i < smmu->num_context_irqs; i++) { 210462306a36Sopenharmony_ci int irq = platform_get_irq(pdev, global_irqs + pmu_irqs + i); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci if (irq < 0) 210762306a36Sopenharmony_ci return irq; 210862306a36Sopenharmony_ci smmu->irqs[i] = irq; 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci err = devm_clk_bulk_get_all(dev, &smmu->clks); 211262306a36Sopenharmony_ci if (err < 0) { 211362306a36Sopenharmony_ci dev_err(dev, "failed to get clocks %d\n", err); 211462306a36Sopenharmony_ci return err; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci smmu->num_clks = err; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci err = clk_bulk_prepare_enable(smmu->num_clks, smmu->clks); 211962306a36Sopenharmony_ci if (err) 212062306a36Sopenharmony_ci return err; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci err = arm_smmu_device_cfg_probe(smmu); 212362306a36Sopenharmony_ci if (err) 212462306a36Sopenharmony_ci return err; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (smmu->version == ARM_SMMU_V2) { 212762306a36Sopenharmony_ci if (smmu->num_context_banks > smmu->num_context_irqs) { 212862306a36Sopenharmony_ci dev_err(dev, 212962306a36Sopenharmony_ci "found only %d context irq(s) but %d required\n", 213062306a36Sopenharmony_ci smmu->num_context_irqs, smmu->num_context_banks); 213162306a36Sopenharmony_ci return -ENODEV; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci /* Ignore superfluous interrupts */ 213562306a36Sopenharmony_ci smmu->num_context_irqs = smmu->num_context_banks; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (smmu->impl && smmu->impl->global_fault) 213962306a36Sopenharmony_ci global_fault = smmu->impl->global_fault; 214062306a36Sopenharmony_ci else 214162306a36Sopenharmony_ci global_fault = arm_smmu_global_fault; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci for (i = 0; i < global_irqs; i++) { 214462306a36Sopenharmony_ci int irq = platform_get_irq(pdev, i); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (irq < 0) 214762306a36Sopenharmony_ci return irq; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci err = devm_request_irq(dev, irq, global_fault, IRQF_SHARED, 215062306a36Sopenharmony_ci "arm-smmu global fault", smmu); 215162306a36Sopenharmony_ci if (err) 215262306a36Sopenharmony_ci return dev_err_probe(dev, err, 215362306a36Sopenharmony_ci "failed to request global IRQ %d (%u)\n", 215462306a36Sopenharmony_ci i, irq); 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci err = iommu_device_sysfs_add(&smmu->iommu, smmu->dev, NULL, 215862306a36Sopenharmony_ci "smmu.%pa", &smmu->ioaddr); 215962306a36Sopenharmony_ci if (err) { 216062306a36Sopenharmony_ci dev_err(dev, "Failed to register iommu in sysfs\n"); 216162306a36Sopenharmony_ci return err; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci err = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev); 216562306a36Sopenharmony_ci if (err) { 216662306a36Sopenharmony_ci dev_err(dev, "Failed to register iommu\n"); 216762306a36Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 216862306a36Sopenharmony_ci return err; 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci platform_set_drvdata(pdev, smmu); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* Check for RMRs and install bypass SMRs if any */ 217462306a36Sopenharmony_ci arm_smmu_rmr_install_bypass_smr(smmu); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci arm_smmu_device_reset(smmu); 217762306a36Sopenharmony_ci arm_smmu_test_smr_masks(smmu); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci /* 218062306a36Sopenharmony_ci * We want to avoid touching dev->power.lock in fastpaths unless 218162306a36Sopenharmony_ci * it's really going to do something useful - pm_runtime_enabled() 218262306a36Sopenharmony_ci * can serve as an ideal proxy for that decision. So, conditionally 218362306a36Sopenharmony_ci * enable pm_runtime. 218462306a36Sopenharmony_ci */ 218562306a36Sopenharmony_ci if (dev->pm_domain) { 218662306a36Sopenharmony_ci pm_runtime_set_active(dev); 218762306a36Sopenharmony_ci pm_runtime_enable(dev); 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci return 0; 219162306a36Sopenharmony_ci} 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev) 219462306a36Sopenharmony_ci{ 219562306a36Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS)) 219862306a36Sopenharmony_ci dev_notice(&pdev->dev, "disabling translation\n"); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci arm_smmu_rpm_get(smmu); 220162306a36Sopenharmony_ci /* Turn the thing off */ 220262306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, ARM_SMMU_sCR0_CLIENTPD); 220362306a36Sopenharmony_ci arm_smmu_rpm_put(smmu); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci if (pm_runtime_enabled(smmu->dev)) 220662306a36Sopenharmony_ci pm_runtime_force_suspend(smmu->dev); 220762306a36Sopenharmony_ci else 220862306a36Sopenharmony_ci clk_bulk_disable(smmu->num_clks, smmu->clks); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci clk_bulk_unprepare(smmu->num_clks, smmu->clks); 221162306a36Sopenharmony_ci} 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_cistatic void arm_smmu_device_remove(struct platform_device *pdev) 221462306a36Sopenharmony_ci{ 221562306a36Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci iommu_device_unregister(&smmu->iommu); 221862306a36Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci arm_smmu_device_shutdown(pdev); 222162306a36Sopenharmony_ci} 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_cistatic int __maybe_unused arm_smmu_runtime_resume(struct device *dev) 222462306a36Sopenharmony_ci{ 222562306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 222662306a36Sopenharmony_ci int ret; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci ret = clk_bulk_enable(smmu->num_clks, smmu->clks); 222962306a36Sopenharmony_ci if (ret) 223062306a36Sopenharmony_ci return ret; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci arm_smmu_device_reset(smmu); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci return 0; 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_cistatic int __maybe_unused arm_smmu_runtime_suspend(struct device *dev) 223862306a36Sopenharmony_ci{ 223962306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci clk_bulk_disable(smmu->num_clks, smmu->clks); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci return 0; 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_cistatic int __maybe_unused arm_smmu_pm_resume(struct device *dev) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci int ret; 224962306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci ret = clk_bulk_prepare(smmu->num_clks, smmu->clks); 225262306a36Sopenharmony_ci if (ret) 225362306a36Sopenharmony_ci return ret; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (pm_runtime_suspended(dev)) 225662306a36Sopenharmony_ci return 0; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci ret = arm_smmu_runtime_resume(dev); 225962306a36Sopenharmony_ci if (ret) 226062306a36Sopenharmony_ci clk_bulk_unprepare(smmu->num_clks, smmu->clks); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci return ret; 226362306a36Sopenharmony_ci} 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic int __maybe_unused arm_smmu_pm_suspend(struct device *dev) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci int ret = 0; 226862306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci if (pm_runtime_suspended(dev)) 227162306a36Sopenharmony_ci goto clk_unprepare; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci ret = arm_smmu_runtime_suspend(dev); 227462306a36Sopenharmony_ci if (ret) 227562306a36Sopenharmony_ci return ret; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ciclk_unprepare: 227862306a36Sopenharmony_ci clk_bulk_unprepare(smmu->num_clks, smmu->clks); 227962306a36Sopenharmony_ci return ret; 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_cistatic const struct dev_pm_ops arm_smmu_pm_ops = { 228362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(arm_smmu_pm_suspend, arm_smmu_pm_resume) 228462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(arm_smmu_runtime_suspend, 228562306a36Sopenharmony_ci arm_smmu_runtime_resume, NULL) 228662306a36Sopenharmony_ci}; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver = { 228962306a36Sopenharmony_ci .driver = { 229062306a36Sopenharmony_ci .name = "arm-smmu", 229162306a36Sopenharmony_ci .of_match_table = arm_smmu_of_match, 229262306a36Sopenharmony_ci .pm = &arm_smmu_pm_ops, 229362306a36Sopenharmony_ci .suppress_bind_attrs = true, 229462306a36Sopenharmony_ci }, 229562306a36Sopenharmony_ci .probe = arm_smmu_device_probe, 229662306a36Sopenharmony_ci .remove_new = arm_smmu_device_remove, 229762306a36Sopenharmony_ci .shutdown = arm_smmu_device_shutdown, 229862306a36Sopenharmony_ci}; 229962306a36Sopenharmony_cimodule_platform_driver(arm_smmu_driver); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); 230262306a36Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>"); 230362306a36Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu"); 230462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2305