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