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