162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IOMMU API for ARM architected SMMUv3 implementations.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 ARM Limited
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This driver is powered by bad coffee and bombay mix.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/acpi.h>
1362306a36Sopenharmony_ci#include <linux/acpi_iort.h>
1462306a36Sopenharmony_ci#include <linux/bitops.h>
1562306a36Sopenharmony_ci#include <linux/crash_dump.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/io-pgtable.h>
2062306a36Sopenharmony_ci#include <linux/iopoll.h>
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/msi.h>
2362306a36Sopenharmony_ci#include <linux/of.h>
2462306a36Sopenharmony_ci#include <linux/of_address.h>
2562306a36Sopenharmony_ci#include <linux/of_platform.h>
2662306a36Sopenharmony_ci#include <linux/pci.h>
2762306a36Sopenharmony_ci#include <linux/pci-ats.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "arm-smmu-v3.h"
3162306a36Sopenharmony_ci#include "../../dma-iommu.h"
3262306a36Sopenharmony_ci#include "../../iommu-sva.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic bool disable_bypass = true;
3562306a36Sopenharmony_cimodule_param(disable_bypass, bool, 0444);
3662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_bypass,
3762306a36Sopenharmony_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.");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic bool disable_msipolling;
4062306a36Sopenharmony_cimodule_param(disable_msipolling, bool, 0444);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(disable_msipolling,
4262306a36Sopenharmony_ci	"Disable MSI-based polling for CMD_SYNC completion.");
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cienum arm_smmu_msi_index {
4562306a36Sopenharmony_ci	EVTQ_MSI_INDEX,
4662306a36Sopenharmony_ci	GERROR_MSI_INDEX,
4762306a36Sopenharmony_ci	PRIQ_MSI_INDEX,
4862306a36Sopenharmony_ci	ARM_SMMU_MAX_MSIS,
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
5262306a36Sopenharmony_ci	[EVTQ_MSI_INDEX] = {
5362306a36Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG0,
5462306a36Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG1,
5562306a36Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG2,
5662306a36Sopenharmony_ci	},
5762306a36Sopenharmony_ci	[GERROR_MSI_INDEX] = {
5862306a36Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG0,
5962306a36Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG1,
6062306a36Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG2,
6162306a36Sopenharmony_ci	},
6262306a36Sopenharmony_ci	[PRIQ_MSI_INDEX] = {
6362306a36Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG0,
6462306a36Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG1,
6562306a36Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG2,
6662306a36Sopenharmony_ci	},
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct arm_smmu_option_prop {
7062306a36Sopenharmony_ci	u32 opt;
7162306a36Sopenharmony_ci	const char *prop;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciDEFINE_XARRAY_ALLOC1(arm_smmu_asid_xa);
7562306a36Sopenharmony_ciDEFINE_MUTEX(arm_smmu_asid_lock);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * Special value used by SVA when a process dies, to quiesce a CD without
7962306a36Sopenharmony_ci * disabling it.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistruct arm_smmu_ctx_desc quiet_cd = { 0 };
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct arm_smmu_option_prop arm_smmu_options[] = {
8462306a36Sopenharmony_ci	{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
8562306a36Sopenharmony_ci	{ ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"},
8662306a36Sopenharmony_ci	{ 0, NULL},
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void parse_driver_options(struct arm_smmu_device *smmu)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int i = 0;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	do {
9462306a36Sopenharmony_ci		if (of_property_read_bool(smmu->dev->of_node,
9562306a36Sopenharmony_ci						arm_smmu_options[i].prop)) {
9662306a36Sopenharmony_ci			smmu->options |= arm_smmu_options[i].opt;
9762306a36Sopenharmony_ci			dev_notice(smmu->dev, "option %s\n",
9862306a36Sopenharmony_ci				arm_smmu_options[i].prop);
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci	} while (arm_smmu_options[++i].opt);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Low-level queue manipulation functions */
10462306a36Sopenharmony_cistatic bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	u32 space, prod, cons;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	prod = Q_IDX(q, q->prod);
10962306a36Sopenharmony_ci	cons = Q_IDX(q, q->cons);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons))
11262306a36Sopenharmony_ci		space = (1 << q->max_n_shift) - (prod - cons);
11362306a36Sopenharmony_ci	else
11462306a36Sopenharmony_ci		space = cons - prod;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return space >= n;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic bool queue_full(struct arm_smmu_ll_queue *q)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
12262306a36Sopenharmony_ci	       Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic bool queue_empty(struct arm_smmu_ll_queue *q)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
12862306a36Sopenharmony_ci	       Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) &&
13462306a36Sopenharmony_ci		(Q_IDX(q, q->cons) > Q_IDX(q, prod))) ||
13562306a36Sopenharmony_ci	       ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) &&
13662306a36Sopenharmony_ci		(Q_IDX(q, q->cons) <= Q_IDX(q, prod)));
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void queue_sync_cons_out(struct arm_smmu_queue *q)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	/*
14262306a36Sopenharmony_ci	 * Ensure that all CPU accesses (reads and writes) to the queue
14362306a36Sopenharmony_ci	 * are complete before we update the cons pointer.
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	__iomb();
14662306a36Sopenharmony_ci	writel_relaxed(q->llq.cons, q->cons_reg);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void queue_inc_cons(struct arm_smmu_ll_queue *q)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1;
15262306a36Sopenharmony_ci	q->cons = Q_OVF(q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void queue_sync_cons_ovf(struct arm_smmu_queue *q)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (likely(Q_OVF(llq->prod) == Q_OVF(llq->cons)))
16062306a36Sopenharmony_ci		return;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) |
16362306a36Sopenharmony_ci		      Q_IDX(llq, llq->cons);
16462306a36Sopenharmony_ci	queue_sync_cons_out(q);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int queue_sync_prod_in(struct arm_smmu_queue *q)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	u32 prod;
17062306a36Sopenharmony_ci	int ret = 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * We can't use the _relaxed() variant here, as we must prevent
17462306a36Sopenharmony_ci	 * speculative reads of the queue before we have determined that
17562306a36Sopenharmony_ci	 * prod has indeed moved.
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	prod = readl(q->prod_reg);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (Q_OVF(prod) != Q_OVF(q->llq.prod))
18062306a36Sopenharmony_ci		ret = -EOVERFLOW;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	q->llq.prod = prod;
18362306a36Sopenharmony_ci	return ret;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic u32 queue_inc_prod_n(struct arm_smmu_ll_queue *q, int n)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + n;
18962306a36Sopenharmony_ci	return Q_OVF(q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void queue_poll_init(struct arm_smmu_device *smmu,
19362306a36Sopenharmony_ci			    struct arm_smmu_queue_poll *qp)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	qp->delay = 1;
19662306a36Sopenharmony_ci	qp->spin_cnt = 0;
19762306a36Sopenharmony_ci	qp->wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
19862306a36Sopenharmony_ci	qp->timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int queue_poll(struct arm_smmu_queue_poll *qp)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	if (ktime_compare(ktime_get(), qp->timeout) > 0)
20462306a36Sopenharmony_ci		return -ETIMEDOUT;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (qp->wfe) {
20762306a36Sopenharmony_ci		wfe();
20862306a36Sopenharmony_ci	} else if (++qp->spin_cnt < ARM_SMMU_POLL_SPIN_COUNT) {
20962306a36Sopenharmony_ci		cpu_relax();
21062306a36Sopenharmony_ci	} else {
21162306a36Sopenharmony_ci		udelay(qp->delay);
21262306a36Sopenharmony_ci		qp->delay *= 2;
21362306a36Sopenharmony_ci		qp->spin_cnt = 0;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void queue_write(__le64 *dst, u64 *src, size_t n_dwords)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	int i;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	for (i = 0; i < n_dwords; ++i)
22462306a36Sopenharmony_ci		*dst++ = cpu_to_le64(*src++);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void queue_read(u64 *dst, __le64 *src, size_t n_dwords)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	int i;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	for (i = 0; i < n_dwords; ++i)
23262306a36Sopenharmony_ci		*dst++ = le64_to_cpu(*src++);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	if (queue_empty(&q->llq))
23862306a36Sopenharmony_ci		return -EAGAIN;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	queue_read(ent, Q_ENT(q, q->llq.cons), q->ent_dwords);
24162306a36Sopenharmony_ci	queue_inc_cons(&q->llq);
24262306a36Sopenharmony_ci	queue_sync_cons_out(q);
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/* High-level queue accessors */
24762306a36Sopenharmony_cistatic int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	memset(cmd, 0, 1 << CMDQ_ENT_SZ_SHIFT);
25062306a36Sopenharmony_ci	cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	switch (ent->opcode) {
25362306a36Sopenharmony_ci	case CMDQ_OP_TLBI_EL2_ALL:
25462306a36Sopenharmony_ci	case CMDQ_OP_TLBI_NSNH_ALL:
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci	case CMDQ_OP_PREFETCH_CFG:
25762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid);
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case CMDQ_OP_CFGI_CD:
26062306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid);
26162306a36Sopenharmony_ci		fallthrough;
26262306a36Sopenharmony_ci	case CMDQ_OP_CFGI_STE:
26362306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
26462306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf);
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	case CMDQ_OP_CFGI_CD_ALL:
26762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case CMDQ_OP_CFGI_ALL:
27062306a36Sopenharmony_ci		/* Cover the entire SID range */
27162306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31);
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	case CMDQ_OP_TLBI_NH_VA:
27462306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
27562306a36Sopenharmony_ci		fallthrough;
27662306a36Sopenharmony_ci	case CMDQ_OP_TLBI_EL2_VA:
27762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
27862306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
27962306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
28062306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
28162306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
28262306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
28362306a36Sopenharmony_ci		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	case CMDQ_OP_TLBI_S2_IPA:
28662306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
28762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
28862306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
28962306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
29062306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
29162306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
29262306a36Sopenharmony_ci		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	case CMDQ_OP_TLBI_NH_ASID:
29562306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
29662306a36Sopenharmony_ci		fallthrough;
29762306a36Sopenharmony_ci	case CMDQ_OP_TLBI_S12_VMALL:
29862306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	case CMDQ_OP_TLBI_EL2_ASID:
30162306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	case CMDQ_OP_ATC_INV:
30462306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
30562306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_GLOBAL, ent->atc.global);
30662306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SSID, ent->atc.ssid);
30762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SID, ent->atc.sid);
30862306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_ATC_1_SIZE, ent->atc.size);
30962306a36Sopenharmony_ci		cmd[1] |= ent->atc.addr & CMDQ_ATC_1_ADDR_MASK;
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	case CMDQ_OP_PRI_RESP:
31262306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
31362306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SSID, ent->pri.ssid);
31462306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SID, ent->pri.sid);
31562306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_PRI_1_GRPID, ent->pri.grpid);
31662306a36Sopenharmony_ci		switch (ent->pri.resp) {
31762306a36Sopenharmony_ci		case PRI_RESP_DENY:
31862306a36Sopenharmony_ci		case PRI_RESP_FAIL:
31962306a36Sopenharmony_ci		case PRI_RESP_SUCC:
32062306a36Sopenharmony_ci			break;
32162306a36Sopenharmony_ci		default:
32262306a36Sopenharmony_ci			return -EINVAL;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp);
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case CMDQ_OP_RESUME:
32762306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_RESUME_0_SID, ent->resume.sid);
32862306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_RESUME_0_RESP, ent->resume.resp);
32962306a36Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_RESUME_1_STAG, ent->resume.stag);
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case CMDQ_OP_CMD_SYNC:
33262306a36Sopenharmony_ci		if (ent->sync.msiaddr) {
33362306a36Sopenharmony_ci			cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ);
33462306a36Sopenharmony_ci			cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK;
33562306a36Sopenharmony_ci		} else {
33662306a36Sopenharmony_ci			cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV);
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH);
33962306a36Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB);
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		return -ENOENT;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	return &smmu->cmdq;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,
35462306a36Sopenharmony_ci					 struct arm_smmu_queue *q, u32 prod)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent ent = {
35762306a36Sopenharmony_ci		.opcode = CMDQ_OP_CMD_SYNC,
35862306a36Sopenharmony_ci	};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/*
36162306a36Sopenharmony_ci	 * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI
36262306a36Sopenharmony_ci	 * payload, so the write will zero the entire command on that platform.
36362306a36Sopenharmony_ci	 */
36462306a36Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_MSIPOLL) {
36562306a36Sopenharmony_ci		ent.sync.msiaddr = q->base_dma + Q_IDX(&q->llq, prod) *
36662306a36Sopenharmony_ci				   q->ent_dwords * 8;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	arm_smmu_cmdq_build_cmd(cmd, &ent);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
37362306a36Sopenharmony_ci				     struct arm_smmu_queue *q)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	static const char * const cerror_str[] = {
37662306a36Sopenharmony_ci		[CMDQ_ERR_CERROR_NONE_IDX]	= "No error",
37762306a36Sopenharmony_ci		[CMDQ_ERR_CERROR_ILL_IDX]	= "Illegal command",
37862306a36Sopenharmony_ci		[CMDQ_ERR_CERROR_ABT_IDX]	= "Abort on command fetch",
37962306a36Sopenharmony_ci		[CMDQ_ERR_CERROR_ATC_INV_IDX]	= "ATC invalidate timeout",
38062306a36Sopenharmony_ci	};
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	int i;
38362306a36Sopenharmony_ci	u64 cmd[CMDQ_ENT_DWORDS];
38462306a36Sopenharmony_ci	u32 cons = readl_relaxed(q->cons_reg);
38562306a36Sopenharmony_ci	u32 idx = FIELD_GET(CMDQ_CONS_ERR, cons);
38662306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd_sync = {
38762306a36Sopenharmony_ci		.opcode = CMDQ_OP_CMD_SYNC,
38862306a36Sopenharmony_ci	};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons,
39162306a36Sopenharmony_ci		idx < ARRAY_SIZE(cerror_str) ?  cerror_str[idx] : "Unknown");
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	switch (idx) {
39462306a36Sopenharmony_ci	case CMDQ_ERR_CERROR_ABT_IDX:
39562306a36Sopenharmony_ci		dev_err(smmu->dev, "retrying command fetch\n");
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci	case CMDQ_ERR_CERROR_NONE_IDX:
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci	case CMDQ_ERR_CERROR_ATC_INV_IDX:
40062306a36Sopenharmony_ci		/*
40162306a36Sopenharmony_ci		 * ATC Invalidation Completion timeout. CONS is still pointing
40262306a36Sopenharmony_ci		 * at the CMD_SYNC. Attempt to complete other pending commands
40362306a36Sopenharmony_ci		 * by repeating the CMD_SYNC, though we might well end up back
40462306a36Sopenharmony_ci		 * here since the ATC invalidation may still be pending.
40562306a36Sopenharmony_ci		 */
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci	case CMDQ_ERR_CERROR_ILL_IDX:
40862306a36Sopenharmony_ci	default:
40962306a36Sopenharmony_ci		break;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/*
41362306a36Sopenharmony_ci	 * We may have concurrent producers, so we need to be careful
41462306a36Sopenharmony_ci	 * not to touch any of the shadow cmdq state.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	queue_read(cmd, Q_ENT(q, cons), q->ent_dwords);
41762306a36Sopenharmony_ci	dev_err(smmu->dev, "skipping command in error state:\n");
41862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cmd); ++i)
41962306a36Sopenharmony_ci		dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Convert the erroneous command into a CMD_SYNC */
42262306a36Sopenharmony_ci	arm_smmu_cmdq_build_cmd(cmd, &cmd_sync);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	__arm_smmu_cmdq_skip_err(smmu, &smmu->cmdq.q);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/*
43362306a36Sopenharmony_ci * Command queue locking.
43462306a36Sopenharmony_ci * This is a form of bastardised rwlock with the following major changes:
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * - The only LOCK routines are exclusive_trylock() and shared_lock().
43762306a36Sopenharmony_ci *   Neither have barrier semantics, and instead provide only a control
43862306a36Sopenharmony_ci *   dependency.
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * - The UNLOCK routines are supplemented with shared_tryunlock(), which
44162306a36Sopenharmony_ci *   fails if the caller appears to be the last lock holder (yes, this is
44262306a36Sopenharmony_ci *   racy). All successful UNLOCK routines have RELEASE semantics.
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic void arm_smmu_cmdq_shared_lock(struct arm_smmu_cmdq *cmdq)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int val;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * We can try to avoid the cmpxchg() loop by simply incrementing the
45062306a36Sopenharmony_ci	 * lock counter. When held in exclusive state, the lock counter is set
45162306a36Sopenharmony_ci	 * to INT_MIN so these increments won't hurt as the value will remain
45262306a36Sopenharmony_ci	 * negative.
45362306a36Sopenharmony_ci	 */
45462306a36Sopenharmony_ci	if (atomic_fetch_inc_relaxed(&cmdq->lock) >= 0)
45562306a36Sopenharmony_ci		return;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	do {
45862306a36Sopenharmony_ci		val = atomic_cond_read_relaxed(&cmdq->lock, VAL >= 0);
45962306a36Sopenharmony_ci	} while (atomic_cmpxchg_relaxed(&cmdq->lock, val, val + 1) != val);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void arm_smmu_cmdq_shared_unlock(struct arm_smmu_cmdq *cmdq)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	(void)atomic_dec_return_release(&cmdq->lock);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic bool arm_smmu_cmdq_shared_tryunlock(struct arm_smmu_cmdq *cmdq)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	if (atomic_read(&cmdq->lock) == 1)
47062306a36Sopenharmony_ci		return false;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	arm_smmu_cmdq_shared_unlock(cmdq);
47362306a36Sopenharmony_ci	return true;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci#define arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)		\
47762306a36Sopenharmony_ci({									\
47862306a36Sopenharmony_ci	bool __ret;							\
47962306a36Sopenharmony_ci	local_irq_save(flags);						\
48062306a36Sopenharmony_ci	__ret = !atomic_cmpxchg_relaxed(&cmdq->lock, 0, INT_MIN);	\
48162306a36Sopenharmony_ci	if (!__ret)							\
48262306a36Sopenharmony_ci		local_irq_restore(flags);				\
48362306a36Sopenharmony_ci	__ret;								\
48462306a36Sopenharmony_ci})
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci#define arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags)		\
48762306a36Sopenharmony_ci({									\
48862306a36Sopenharmony_ci	atomic_set_release(&cmdq->lock, 0);				\
48962306a36Sopenharmony_ci	local_irq_restore(flags);					\
49062306a36Sopenharmony_ci})
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/*
49462306a36Sopenharmony_ci * Command queue insertion.
49562306a36Sopenharmony_ci * This is made fiddly by our attempts to achieve some sort of scalability
49662306a36Sopenharmony_ci * since there is one queue shared amongst all of the CPUs in the system.  If
49762306a36Sopenharmony_ci * you like mixed-size concurrency, dependency ordering and relaxed atomics,
49862306a36Sopenharmony_ci * then you'll *love* this monstrosity.
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci * The basic idea is to split the queue up into ranges of commands that are
50162306a36Sopenharmony_ci * owned by a given CPU; the owner may not have written all of the commands
50262306a36Sopenharmony_ci * itself, but is responsible for advancing the hardware prod pointer when
50362306a36Sopenharmony_ci * the time comes. The algorithm is roughly:
50462306a36Sopenharmony_ci *
50562306a36Sopenharmony_ci * 	1. Allocate some space in the queue. At this point we also discover
50662306a36Sopenharmony_ci *	   whether the head of the queue is currently owned by another CPU,
50762306a36Sopenharmony_ci *	   or whether we are the owner.
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci *	2. Write our commands into our allocated slots in the queue.
51062306a36Sopenharmony_ci *
51162306a36Sopenharmony_ci *	3. Mark our slots as valid in arm_smmu_cmdq.valid_map.
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci *	4. If we are an owner:
51462306a36Sopenharmony_ci *		a. Wait for the previous owner to finish.
51562306a36Sopenharmony_ci *		b. Mark the queue head as unowned, which tells us the range
51662306a36Sopenharmony_ci *		   that we are responsible for publishing.
51762306a36Sopenharmony_ci *		c. Wait for all commands in our owned range to become valid.
51862306a36Sopenharmony_ci *		d. Advance the hardware prod pointer.
51962306a36Sopenharmony_ci *		e. Tell the next owner we've finished.
52062306a36Sopenharmony_ci *
52162306a36Sopenharmony_ci *	5. If we are inserting a CMD_SYNC (we may or may not have been an
52262306a36Sopenharmony_ci *	   owner), then we need to stick around until it has completed:
52362306a36Sopenharmony_ci *		a. If we have MSIs, the SMMU can write back into the CMD_SYNC
52462306a36Sopenharmony_ci *		   to clear the first 4 bytes.
52562306a36Sopenharmony_ci *		b. Otherwise, we spin waiting for the hardware cons pointer to
52662306a36Sopenharmony_ci *		   advance past our command.
52762306a36Sopenharmony_ci *
52862306a36Sopenharmony_ci * The devil is in the details, particularly the use of locking for handling
52962306a36Sopenharmony_ci * SYNC completion and freeing up space in the queue before we think that it is
53062306a36Sopenharmony_ci * full.
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_cistatic void __arm_smmu_cmdq_poll_set_valid_map(struct arm_smmu_cmdq *cmdq,
53362306a36Sopenharmony_ci					       u32 sprod, u32 eprod, bool set)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	u32 swidx, sbidx, ewidx, ebidx;
53662306a36Sopenharmony_ci	struct arm_smmu_ll_queue llq = {
53762306a36Sopenharmony_ci		.max_n_shift	= cmdq->q.llq.max_n_shift,
53862306a36Sopenharmony_ci		.prod		= sprod,
53962306a36Sopenharmony_ci	};
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ewidx = BIT_WORD(Q_IDX(&llq, eprod));
54262306a36Sopenharmony_ci	ebidx = Q_IDX(&llq, eprod) % BITS_PER_LONG;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	while (llq.prod != eprod) {
54562306a36Sopenharmony_ci		unsigned long mask;
54662306a36Sopenharmony_ci		atomic_long_t *ptr;
54762306a36Sopenharmony_ci		u32 limit = BITS_PER_LONG;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		swidx = BIT_WORD(Q_IDX(&llq, llq.prod));
55062306a36Sopenharmony_ci		sbidx = Q_IDX(&llq, llq.prod) % BITS_PER_LONG;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		ptr = &cmdq->valid_map[swidx];
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		if ((swidx == ewidx) && (sbidx < ebidx))
55562306a36Sopenharmony_ci			limit = ebidx;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		mask = GENMASK(limit - 1, sbidx);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		/*
56062306a36Sopenharmony_ci		 * The valid bit is the inverse of the wrap bit. This means
56162306a36Sopenharmony_ci		 * that a zero-initialised queue is invalid and, after marking
56262306a36Sopenharmony_ci		 * all entries as valid, they become invalid again when we
56362306a36Sopenharmony_ci		 * wrap.
56462306a36Sopenharmony_ci		 */
56562306a36Sopenharmony_ci		if (set) {
56662306a36Sopenharmony_ci			atomic_long_xor(mask, ptr);
56762306a36Sopenharmony_ci		} else { /* Poll */
56862306a36Sopenharmony_ci			unsigned long valid;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci			valid = (ULONG_MAX + !!Q_WRP(&llq, llq.prod)) & mask;
57162306a36Sopenharmony_ci			atomic_long_cond_read_relaxed(ptr, (VAL & mask) == valid);
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		llq.prod = queue_inc_prod_n(&llq, limit - sbidx);
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/* Mark all entries in the range [sprod, eprod) as valid */
57962306a36Sopenharmony_cistatic void arm_smmu_cmdq_set_valid_map(struct arm_smmu_cmdq *cmdq,
58062306a36Sopenharmony_ci					u32 sprod, u32 eprod)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	__arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, true);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/* Wait for all entries in the range [sprod, eprod) to become valid */
58662306a36Sopenharmony_cistatic void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq,
58762306a36Sopenharmony_ci					 u32 sprod, u32 eprod)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	__arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, false);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci/* Wait for the command queue to become non-full */
59362306a36Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu,
59462306a36Sopenharmony_ci					     struct arm_smmu_ll_queue *llq)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	unsigned long flags;
59762306a36Sopenharmony_ci	struct arm_smmu_queue_poll qp;
59862306a36Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
59962306a36Sopenharmony_ci	int ret = 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/*
60262306a36Sopenharmony_ci	 * Try to update our copy of cons by grabbing exclusive cmdq access. If
60362306a36Sopenharmony_ci	 * that fails, spin until somebody else updates it for us.
60462306a36Sopenharmony_ci	 */
60562306a36Sopenharmony_ci	if (arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)) {
60662306a36Sopenharmony_ci		WRITE_ONCE(cmdq->q.llq.cons, readl_relaxed(cmdq->q.cons_reg));
60762306a36Sopenharmony_ci		arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags);
60862306a36Sopenharmony_ci		llq->val = READ_ONCE(cmdq->q.llq.val);
60962306a36Sopenharmony_ci		return 0;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	queue_poll_init(smmu, &qp);
61362306a36Sopenharmony_ci	do {
61462306a36Sopenharmony_ci		llq->val = READ_ONCE(cmdq->q.llq.val);
61562306a36Sopenharmony_ci		if (!queue_full(llq))
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		ret = queue_poll(&qp);
61962306a36Sopenharmony_ci	} while (!ret);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return ret;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/*
62562306a36Sopenharmony_ci * Wait until the SMMU signals a CMD_SYNC completion MSI.
62662306a36Sopenharmony_ci * Must be called with the cmdq lock held in some capacity.
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu,
62962306a36Sopenharmony_ci					  struct arm_smmu_ll_queue *llq)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	int ret = 0;
63262306a36Sopenharmony_ci	struct arm_smmu_queue_poll qp;
63362306a36Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
63462306a36Sopenharmony_ci	u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod));
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	queue_poll_init(smmu, &qp);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/*
63962306a36Sopenharmony_ci	 * The MSI won't generate an event, since it's being written back
64062306a36Sopenharmony_ci	 * into the command queue.
64162306a36Sopenharmony_ci	 */
64262306a36Sopenharmony_ci	qp.wfe = false;
64362306a36Sopenharmony_ci	smp_cond_load_relaxed(cmd, !VAL || (ret = queue_poll(&qp)));
64462306a36Sopenharmony_ci	llq->cons = ret ? llq->prod : queue_inc_prod_n(llq, 1);
64562306a36Sopenharmony_ci	return ret;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci/*
64962306a36Sopenharmony_ci * Wait until the SMMU cons index passes llq->prod.
65062306a36Sopenharmony_ci * Must be called with the cmdq lock held in some capacity.
65162306a36Sopenharmony_ci */
65262306a36Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu,
65362306a36Sopenharmony_ci					       struct arm_smmu_ll_queue *llq)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct arm_smmu_queue_poll qp;
65662306a36Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
65762306a36Sopenharmony_ci	u32 prod = llq->prod;
65862306a36Sopenharmony_ci	int ret = 0;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	queue_poll_init(smmu, &qp);
66162306a36Sopenharmony_ci	llq->val = READ_ONCE(cmdq->q.llq.val);
66262306a36Sopenharmony_ci	do {
66362306a36Sopenharmony_ci		if (queue_consumed(llq, prod))
66462306a36Sopenharmony_ci			break;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		ret = queue_poll(&qp);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		/*
66962306a36Sopenharmony_ci		 * This needs to be a readl() so that our subsequent call
67062306a36Sopenharmony_ci		 * to arm_smmu_cmdq_shared_tryunlock() can fail accurately.
67162306a36Sopenharmony_ci		 *
67262306a36Sopenharmony_ci		 * Specifically, we need to ensure that we observe all
67362306a36Sopenharmony_ci		 * shared_lock()s by other CMD_SYNCs that share our owner,
67462306a36Sopenharmony_ci		 * so that a failing call to tryunlock() means that we're
67562306a36Sopenharmony_ci		 * the last one out and therefore we can safely advance
67662306a36Sopenharmony_ci		 * cmdq->q.llq.cons. Roughly speaking:
67762306a36Sopenharmony_ci		 *
67862306a36Sopenharmony_ci		 * CPU 0		CPU1			CPU2 (us)
67962306a36Sopenharmony_ci		 *
68062306a36Sopenharmony_ci		 * if (sync)
68162306a36Sopenharmony_ci		 * 	shared_lock();
68262306a36Sopenharmony_ci		 *
68362306a36Sopenharmony_ci		 * dma_wmb();
68462306a36Sopenharmony_ci		 * set_valid_map();
68562306a36Sopenharmony_ci		 *
68662306a36Sopenharmony_ci		 * 			if (owner) {
68762306a36Sopenharmony_ci		 *				poll_valid_map();
68862306a36Sopenharmony_ci		 *				<control dependency>
68962306a36Sopenharmony_ci		 *				writel(prod_reg);
69062306a36Sopenharmony_ci		 *
69162306a36Sopenharmony_ci		 *						readl(cons_reg);
69262306a36Sopenharmony_ci		 *						tryunlock();
69362306a36Sopenharmony_ci		 *
69462306a36Sopenharmony_ci		 * Requires us to see CPU 0's shared_lock() acquisition.
69562306a36Sopenharmony_ci		 */
69662306a36Sopenharmony_ci		llq->cons = readl(cmdq->q.cons_reg);
69762306a36Sopenharmony_ci	} while (!ret);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return ret;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu,
70362306a36Sopenharmony_ci					 struct arm_smmu_ll_queue *llq)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_MSIPOLL)
70662306a36Sopenharmony_ci		return __arm_smmu_cmdq_poll_until_msi(smmu, llq);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return __arm_smmu_cmdq_poll_until_consumed(smmu, llq);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds,
71262306a36Sopenharmony_ci					u32 prod, int n)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	int i;
71562306a36Sopenharmony_ci	struct arm_smmu_ll_queue llq = {
71662306a36Sopenharmony_ci		.max_n_shift	= cmdq->q.llq.max_n_shift,
71762306a36Sopenharmony_ci		.prod		= prod,
71862306a36Sopenharmony_ci	};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	for (i = 0; i < n; ++i) {
72162306a36Sopenharmony_ci		u64 *cmd = &cmds[i * CMDQ_ENT_DWORDS];
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		prod = queue_inc_prod_n(&llq, i);
72462306a36Sopenharmony_ci		queue_write(Q_ENT(&cmdq->q, prod), cmd, CMDQ_ENT_DWORDS);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/*
72962306a36Sopenharmony_ci * This is the actual insertion function, and provides the following
73062306a36Sopenharmony_ci * ordering guarantees to callers:
73162306a36Sopenharmony_ci *
73262306a36Sopenharmony_ci * - There is a dma_wmb() before publishing any commands to the queue.
73362306a36Sopenharmony_ci *   This can be relied upon to order prior writes to data structures
73462306a36Sopenharmony_ci *   in memory (such as a CD or an STE) before the command.
73562306a36Sopenharmony_ci *
73662306a36Sopenharmony_ci * - On completion of a CMD_SYNC, there is a control dependency.
73762306a36Sopenharmony_ci *   This can be relied upon to order subsequent writes to memory (e.g.
73862306a36Sopenharmony_ci *   freeing an IOVA) after completion of the CMD_SYNC.
73962306a36Sopenharmony_ci *
74062306a36Sopenharmony_ci * - Command insertion is totally ordered, so if two CPUs each race to
74162306a36Sopenharmony_ci *   insert their own list of commands then all of the commands from one
74262306a36Sopenharmony_ci *   CPU will appear before any of the commands from the other CPU.
74362306a36Sopenharmony_ci */
74462306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
74562306a36Sopenharmony_ci				       u64 *cmds, int n, bool sync)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	u64 cmd_sync[CMDQ_ENT_DWORDS];
74862306a36Sopenharmony_ci	u32 prod;
74962306a36Sopenharmony_ci	unsigned long flags;
75062306a36Sopenharmony_ci	bool owner;
75162306a36Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu);
75262306a36Sopenharmony_ci	struct arm_smmu_ll_queue llq, head;
75362306a36Sopenharmony_ci	int ret = 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	llq.max_n_shift = cmdq->q.llq.max_n_shift;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* 1. Allocate some space in the queue */
75862306a36Sopenharmony_ci	local_irq_save(flags);
75962306a36Sopenharmony_ci	llq.val = READ_ONCE(cmdq->q.llq.val);
76062306a36Sopenharmony_ci	do {
76162306a36Sopenharmony_ci		u64 old;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		while (!queue_has_space(&llq, n + sync)) {
76462306a36Sopenharmony_ci			local_irq_restore(flags);
76562306a36Sopenharmony_ci			if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq))
76662306a36Sopenharmony_ci				dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
76762306a36Sopenharmony_ci			local_irq_save(flags);
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		head.cons = llq.cons;
77162306a36Sopenharmony_ci		head.prod = queue_inc_prod_n(&llq, n + sync) |
77262306a36Sopenharmony_ci					     CMDQ_PROD_OWNED_FLAG;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		old = cmpxchg_relaxed(&cmdq->q.llq.val, llq.val, head.val);
77562306a36Sopenharmony_ci		if (old == llq.val)
77662306a36Sopenharmony_ci			break;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		llq.val = old;
77962306a36Sopenharmony_ci	} while (1);
78062306a36Sopenharmony_ci	owner = !(llq.prod & CMDQ_PROD_OWNED_FLAG);
78162306a36Sopenharmony_ci	head.prod &= ~CMDQ_PROD_OWNED_FLAG;
78262306a36Sopenharmony_ci	llq.prod &= ~CMDQ_PROD_OWNED_FLAG;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/*
78562306a36Sopenharmony_ci	 * 2. Write our commands into the queue
78662306a36Sopenharmony_ci	 * Dependency ordering from the cmpxchg() loop above.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n);
78962306a36Sopenharmony_ci	if (sync) {
79062306a36Sopenharmony_ci		prod = queue_inc_prod_n(&llq, n);
79162306a36Sopenharmony_ci		arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, &cmdq->q, prod);
79262306a36Sopenharmony_ci		queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		/*
79562306a36Sopenharmony_ci		 * In order to determine completion of our CMD_SYNC, we must
79662306a36Sopenharmony_ci		 * ensure that the queue can't wrap twice without us noticing.
79762306a36Sopenharmony_ci		 * We achieve that by taking the cmdq lock as shared before
79862306a36Sopenharmony_ci		 * marking our slot as valid.
79962306a36Sopenharmony_ci		 */
80062306a36Sopenharmony_ci		arm_smmu_cmdq_shared_lock(cmdq);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* 3. Mark our slots as valid, ensuring commands are visible first */
80462306a36Sopenharmony_ci	dma_wmb();
80562306a36Sopenharmony_ci	arm_smmu_cmdq_set_valid_map(cmdq, llq.prod, head.prod);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* 4. If we are the owner, take control of the SMMU hardware */
80862306a36Sopenharmony_ci	if (owner) {
80962306a36Sopenharmony_ci		/* a. Wait for previous owner to finish */
81062306a36Sopenharmony_ci		atomic_cond_read_relaxed(&cmdq->owner_prod, VAL == llq.prod);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		/* b. Stop gathering work by clearing the owned flag */
81362306a36Sopenharmony_ci		prod = atomic_fetch_andnot_relaxed(CMDQ_PROD_OWNED_FLAG,
81462306a36Sopenharmony_ci						   &cmdq->q.llq.atomic.prod);
81562306a36Sopenharmony_ci		prod &= ~CMDQ_PROD_OWNED_FLAG;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		/*
81862306a36Sopenharmony_ci		 * c. Wait for any gathered work to be written to the queue.
81962306a36Sopenharmony_ci		 * Note that we read our own entries so that we have the control
82062306a36Sopenharmony_ci		 * dependency required by (d).
82162306a36Sopenharmony_ci		 */
82262306a36Sopenharmony_ci		arm_smmu_cmdq_poll_valid_map(cmdq, llq.prod, prod);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		/*
82562306a36Sopenharmony_ci		 * d. Advance the hardware prod pointer
82662306a36Sopenharmony_ci		 * Control dependency ordering from the entries becoming valid.
82762306a36Sopenharmony_ci		 */
82862306a36Sopenharmony_ci		writel_relaxed(prod, cmdq->q.prod_reg);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		/*
83162306a36Sopenharmony_ci		 * e. Tell the next owner we're done
83262306a36Sopenharmony_ci		 * Make sure we've updated the hardware first, so that we don't
83362306a36Sopenharmony_ci		 * race to update prod and potentially move it backwards.
83462306a36Sopenharmony_ci		 */
83562306a36Sopenharmony_ci		atomic_set_release(&cmdq->owner_prod, prod);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */
83962306a36Sopenharmony_ci	if (sync) {
84062306a36Sopenharmony_ci		llq.prod = queue_inc_prod_n(&llq, n);
84162306a36Sopenharmony_ci		ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq);
84262306a36Sopenharmony_ci		if (ret) {
84362306a36Sopenharmony_ci			dev_err_ratelimited(smmu->dev,
84462306a36Sopenharmony_ci					    "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n",
84562306a36Sopenharmony_ci					    llq.prod,
84662306a36Sopenharmony_ci					    readl_relaxed(cmdq->q.prod_reg),
84762306a36Sopenharmony_ci					    readl_relaxed(cmdq->q.cons_reg));
84862306a36Sopenharmony_ci		}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		/*
85162306a36Sopenharmony_ci		 * Try to unlock the cmdq lock. This will fail if we're the last
85262306a36Sopenharmony_ci		 * reader, in which case we can safely update cmdq->q.llq.cons
85362306a36Sopenharmony_ci		 */
85462306a36Sopenharmony_ci		if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) {
85562306a36Sopenharmony_ci			WRITE_ONCE(cmdq->q.llq.cons, llq.cons);
85662306a36Sopenharmony_ci			arm_smmu_cmdq_shared_unlock(cmdq);
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	local_irq_restore(flags);
86162306a36Sopenharmony_ci	return ret;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
86562306a36Sopenharmony_ci				     struct arm_smmu_cmdq_ent *ent,
86662306a36Sopenharmony_ci				     bool sync)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	u64 cmd[CMDQ_ENT_DWORDS];
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (unlikely(arm_smmu_cmdq_build_cmd(cmd, ent))) {
87162306a36Sopenharmony_ci		dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
87262306a36Sopenharmony_ci			 ent->opcode);
87362306a36Sopenharmony_ci		return -EINVAL;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, sync);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
88062306a36Sopenharmony_ci				   struct arm_smmu_cmdq_ent *ent)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	return __arm_smmu_cmdq_issue_cmd(smmu, ent, false);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu,
88662306a36Sopenharmony_ci					     struct arm_smmu_cmdq_ent *ent)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	return __arm_smmu_cmdq_issue_cmd(smmu, ent, true);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
89262306a36Sopenharmony_ci				    struct arm_smmu_cmdq_batch *cmds,
89362306a36Sopenharmony_ci				    struct arm_smmu_cmdq_ent *cmd)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	int index;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (cmds->num == CMDQ_BATCH_ENTRIES - 1 &&
89862306a36Sopenharmony_ci	    (smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC)) {
89962306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true);
90062306a36Sopenharmony_ci		cmds->num = 0;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (cmds->num == CMDQ_BATCH_ENTRIES) {
90462306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false);
90562306a36Sopenharmony_ci		cmds->num = 0;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	index = cmds->num * CMDQ_ENT_DWORDS;
90962306a36Sopenharmony_ci	if (unlikely(arm_smmu_cmdq_build_cmd(&cmds->cmds[index], cmd))) {
91062306a36Sopenharmony_ci		dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
91162306a36Sopenharmony_ci			 cmd->opcode);
91262306a36Sopenharmony_ci		return;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	cmds->num++;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
91962306a36Sopenharmony_ci				      struct arm_smmu_cmdq_batch *cmds)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic int arm_smmu_page_response(struct device *dev,
92562306a36Sopenharmony_ci				  struct iommu_fault_event *unused,
92662306a36Sopenharmony_ci				  struct iommu_page_response *resp)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {0};
92962306a36Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
93062306a36Sopenharmony_ci	int sid = master->streams[0].id;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (master->stall_enabled) {
93362306a36Sopenharmony_ci		cmd.opcode		= CMDQ_OP_RESUME;
93462306a36Sopenharmony_ci		cmd.resume.sid		= sid;
93562306a36Sopenharmony_ci		cmd.resume.stag		= resp->grpid;
93662306a36Sopenharmony_ci		switch (resp->code) {
93762306a36Sopenharmony_ci		case IOMMU_PAGE_RESP_INVALID:
93862306a36Sopenharmony_ci		case IOMMU_PAGE_RESP_FAILURE:
93962306a36Sopenharmony_ci			cmd.resume.resp = CMDQ_RESUME_0_RESP_ABORT;
94062306a36Sopenharmony_ci			break;
94162306a36Sopenharmony_ci		case IOMMU_PAGE_RESP_SUCCESS:
94262306a36Sopenharmony_ci			cmd.resume.resp = CMDQ_RESUME_0_RESP_RETRY;
94362306a36Sopenharmony_ci			break;
94462306a36Sopenharmony_ci		default:
94562306a36Sopenharmony_ci			return -EINVAL;
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci	} else {
94862306a36Sopenharmony_ci		return -ENODEV;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	arm_smmu_cmdq_issue_cmd(master->smmu, &cmd);
95262306a36Sopenharmony_ci	/*
95362306a36Sopenharmony_ci	 * Don't send a SYNC, it doesn't do anything for RESUME or PRI_RESP.
95462306a36Sopenharmony_ci	 * RESUME consumption guarantees that the stalled transaction will be
95562306a36Sopenharmony_ci	 * terminated... at some point in the future. PRI_RESP is fire and
95662306a36Sopenharmony_ci	 * forget.
95762306a36Sopenharmony_ci	 */
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/* Context descriptor manipulation functions */
96362306a36Sopenharmony_civoid arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
96662306a36Sopenharmony_ci		.opcode	= smmu->features & ARM_SMMU_FEAT_E2H ?
96762306a36Sopenharmony_ci			CMDQ_OP_TLBI_EL2_ASID : CMDQ_OP_TLBI_NH_ASID,
96862306a36Sopenharmony_ci		.tlbi.asid = asid,
96962306a36Sopenharmony_ci	};
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain,
97562306a36Sopenharmony_ci			     int ssid, bool leaf)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	size_t i;
97862306a36Sopenharmony_ci	unsigned long flags;
97962306a36Sopenharmony_ci	struct arm_smmu_master *master;
98062306a36Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds;
98162306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
98262306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
98362306a36Sopenharmony_ci		.opcode	= CMDQ_OP_CFGI_CD,
98462306a36Sopenharmony_ci		.cfgi	= {
98562306a36Sopenharmony_ci			.ssid	= ssid,
98662306a36Sopenharmony_ci			.leaf	= leaf,
98762306a36Sopenharmony_ci		},
98862306a36Sopenharmony_ci	};
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	cmds.num = 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
99362306a36Sopenharmony_ci	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
99462306a36Sopenharmony_ci		for (i = 0; i < master->num_streams; i++) {
99562306a36Sopenharmony_ci			cmd.cfgi.sid = master->streams[i].id;
99662306a36Sopenharmony_ci			arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
99762306a36Sopenharmony_ci		}
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	arm_smmu_cmdq_batch_submit(smmu, &cmds);
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic int arm_smmu_alloc_cd_leaf_table(struct arm_smmu_device *smmu,
100562306a36Sopenharmony_ci					struct arm_smmu_l1_ctx_desc *l1_desc)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	size_t size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	l1_desc->l2ptr = dmam_alloc_coherent(smmu->dev, size,
101062306a36Sopenharmony_ci					     &l1_desc->l2ptr_dma, GFP_KERNEL);
101162306a36Sopenharmony_ci	if (!l1_desc->l2ptr) {
101262306a36Sopenharmony_ci		dev_warn(smmu->dev,
101362306a36Sopenharmony_ci			 "failed to allocate context descriptor table\n");
101462306a36Sopenharmony_ci		return -ENOMEM;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci	return 0;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void arm_smmu_write_cd_l1_desc(__le64 *dst,
102062306a36Sopenharmony_ci				      struct arm_smmu_l1_ctx_desc *l1_desc)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	u64 val = (l1_desc->l2ptr_dma & CTXDESC_L1_DESC_L2PTR_MASK) |
102362306a36Sopenharmony_ci		  CTXDESC_L1_DESC_V;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
102662306a36Sopenharmony_ci	WRITE_ONCE(*dst, cpu_to_le64(val));
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_domain *smmu_domain,
103062306a36Sopenharmony_ci				   u32 ssid)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	__le64 *l1ptr;
103362306a36Sopenharmony_ci	unsigned int idx;
103462306a36Sopenharmony_ci	struct arm_smmu_l1_ctx_desc *l1_desc;
103562306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
103662306a36Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (smmu_domain->s1_cfg.s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
103962306a36Sopenharmony_ci		return cdcfg->cdtab + ssid * CTXDESC_CD_DWORDS;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	idx = ssid >> CTXDESC_SPLIT;
104262306a36Sopenharmony_ci	l1_desc = &cdcfg->l1_desc[idx];
104362306a36Sopenharmony_ci	if (!l1_desc->l2ptr) {
104462306a36Sopenharmony_ci		if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
104562306a36Sopenharmony_ci			return NULL;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci		l1ptr = cdcfg->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
104862306a36Sopenharmony_ci		arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
104962306a36Sopenharmony_ci		/* An invalid L1CD can be cached */
105062306a36Sopenharmony_ci		arm_smmu_sync_cd(smmu_domain, ssid, false);
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci	idx = ssid & (CTXDESC_L2_ENTRIES - 1);
105362306a36Sopenharmony_ci	return l1_desc->l2ptr + idx * CTXDESC_CD_DWORDS;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ciint arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid,
105762306a36Sopenharmony_ci			    struct arm_smmu_ctx_desc *cd)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	/*
106062306a36Sopenharmony_ci	 * This function handles the following cases:
106162306a36Sopenharmony_ci	 *
106262306a36Sopenharmony_ci	 * (1) Install primary CD, for normal DMA traffic (SSID = IOMMU_NO_PASID = 0).
106362306a36Sopenharmony_ci	 * (2) Install a secondary CD, for SID+SSID traffic.
106462306a36Sopenharmony_ci	 * (3) Update ASID of a CD. Atomically write the first 64 bits of the
106562306a36Sopenharmony_ci	 *     CD, then invalidate the old entry and mappings.
106662306a36Sopenharmony_ci	 * (4) Quiesce the context without clearing the valid bit. Disable
106762306a36Sopenharmony_ci	 *     translation, and ignore any translation fault.
106862306a36Sopenharmony_ci	 * (5) Remove a secondary CD.
106962306a36Sopenharmony_ci	 */
107062306a36Sopenharmony_ci	u64 val;
107162306a36Sopenharmony_ci	bool cd_live;
107262306a36Sopenharmony_ci	__le64 *cdptr;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg.s1cdmax)))
107562306a36Sopenharmony_ci		return -E2BIG;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	cdptr = arm_smmu_get_cd_ptr(smmu_domain, ssid);
107862306a36Sopenharmony_ci	if (!cdptr)
107962306a36Sopenharmony_ci		return -ENOMEM;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	val = le64_to_cpu(cdptr[0]);
108262306a36Sopenharmony_ci	cd_live = !!(val & CTXDESC_CD_0_V);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (!cd) { /* (5) */
108562306a36Sopenharmony_ci		val = 0;
108662306a36Sopenharmony_ci	} else if (cd == &quiet_cd) { /* (4) */
108762306a36Sopenharmony_ci		val |= CTXDESC_CD_0_TCR_EPD0;
108862306a36Sopenharmony_ci	} else if (cd_live) { /* (3) */
108962306a36Sopenharmony_ci		val &= ~CTXDESC_CD_0_ASID;
109062306a36Sopenharmony_ci		val |= FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid);
109162306a36Sopenharmony_ci		/*
109262306a36Sopenharmony_ci		 * Until CD+TLB invalidation, both ASIDs may be used for tagging
109362306a36Sopenharmony_ci		 * this substream's traffic
109462306a36Sopenharmony_ci		 */
109562306a36Sopenharmony_ci	} else { /* (1) and (2) */
109662306a36Sopenharmony_ci		cdptr[1] = cpu_to_le64(cd->ttbr & CTXDESC_CD_1_TTB0_MASK);
109762306a36Sopenharmony_ci		cdptr[2] = 0;
109862306a36Sopenharmony_ci		cdptr[3] = cpu_to_le64(cd->mair);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		/*
110162306a36Sopenharmony_ci		 * STE is live, and the SMMU might read dwords of this CD in any
110262306a36Sopenharmony_ci		 * order. Ensure that it observes valid values before reading
110362306a36Sopenharmony_ci		 * V=1.
110462306a36Sopenharmony_ci		 */
110562306a36Sopenharmony_ci		arm_smmu_sync_cd(smmu_domain, ssid, true);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci		val = cd->tcr |
110862306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
110962306a36Sopenharmony_ci			CTXDESC_CD_0_ENDI |
111062306a36Sopenharmony_ci#endif
111162306a36Sopenharmony_ci			CTXDESC_CD_0_R | CTXDESC_CD_0_A |
111262306a36Sopenharmony_ci			(cd->mm ? 0 : CTXDESC_CD_0_ASET) |
111362306a36Sopenharmony_ci			CTXDESC_CD_0_AA64 |
111462306a36Sopenharmony_ci			FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) |
111562306a36Sopenharmony_ci			CTXDESC_CD_0_V;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		if (smmu_domain->stall_enabled)
111862306a36Sopenharmony_ci			val |= CTXDESC_CD_0_S;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	/*
112262306a36Sopenharmony_ci	 * The SMMU accesses 64-bit values atomically. See IHI0070Ca 3.21.3
112362306a36Sopenharmony_ci	 * "Configuration structures and configuration invalidation completion"
112462306a36Sopenharmony_ci	 *
112562306a36Sopenharmony_ci	 *   The size of single-copy atomic reads made by the SMMU is
112662306a36Sopenharmony_ci	 *   IMPLEMENTATION DEFINED but must be at least 64 bits. Any single
112762306a36Sopenharmony_ci	 *   field within an aligned 64-bit span of a structure can be altered
112862306a36Sopenharmony_ci	 *   without first making the structure invalid.
112962306a36Sopenharmony_ci	 */
113062306a36Sopenharmony_ci	WRITE_ONCE(cdptr[0], cpu_to_le64(val));
113162306a36Sopenharmony_ci	arm_smmu_sync_cd(smmu_domain, ssid, true);
113262306a36Sopenharmony_ci	return 0;
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int arm_smmu_alloc_cd_tables(struct arm_smmu_domain *smmu_domain)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	int ret;
113862306a36Sopenharmony_ci	size_t l1size;
113962306a36Sopenharmony_ci	size_t max_contexts;
114062306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
114162306a36Sopenharmony_ci	struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
114262306a36Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &cfg->cdcfg;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	max_contexts = 1 << cfg->s1cdmax;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) ||
114762306a36Sopenharmony_ci	    max_contexts <= CTXDESC_L2_ENTRIES) {
114862306a36Sopenharmony_ci		cfg->s1fmt = STRTAB_STE_0_S1FMT_LINEAR;
114962306a36Sopenharmony_ci		cdcfg->num_l1_ents = max_contexts;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		l1size = max_contexts * (CTXDESC_CD_DWORDS << 3);
115262306a36Sopenharmony_ci	} else {
115362306a36Sopenharmony_ci		cfg->s1fmt = STRTAB_STE_0_S1FMT_64K_L2;
115462306a36Sopenharmony_ci		cdcfg->num_l1_ents = DIV_ROUND_UP(max_contexts,
115562306a36Sopenharmony_ci						  CTXDESC_L2_ENTRIES);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		cdcfg->l1_desc = devm_kcalloc(smmu->dev, cdcfg->num_l1_ents,
115862306a36Sopenharmony_ci					      sizeof(*cdcfg->l1_desc),
115962306a36Sopenharmony_ci					      GFP_KERNEL);
116062306a36Sopenharmony_ci		if (!cdcfg->l1_desc)
116162306a36Sopenharmony_ci			return -ENOMEM;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3);
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	cdcfg->cdtab = dmam_alloc_coherent(smmu->dev, l1size, &cdcfg->cdtab_dma,
116762306a36Sopenharmony_ci					   GFP_KERNEL);
116862306a36Sopenharmony_ci	if (!cdcfg->cdtab) {
116962306a36Sopenharmony_ci		dev_warn(smmu->dev, "failed to allocate context descriptor\n");
117062306a36Sopenharmony_ci		ret = -ENOMEM;
117162306a36Sopenharmony_ci		goto err_free_l1;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	return 0;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cierr_free_l1:
117762306a36Sopenharmony_ci	if (cdcfg->l1_desc) {
117862306a36Sopenharmony_ci		devm_kfree(smmu->dev, cdcfg->l1_desc);
117962306a36Sopenharmony_ci		cdcfg->l1_desc = NULL;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	return ret;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	int i;
118762306a36Sopenharmony_ci	size_t size, l1size;
118862306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
118962306a36Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (cdcfg->l1_desc) {
119262306a36Sopenharmony_ci		size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		for (i = 0; i < cdcfg->num_l1_ents; i++) {
119562306a36Sopenharmony_ci			if (!cdcfg->l1_desc[i].l2ptr)
119662306a36Sopenharmony_ci				continue;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci			dmam_free_coherent(smmu->dev, size,
119962306a36Sopenharmony_ci					   cdcfg->l1_desc[i].l2ptr,
120062306a36Sopenharmony_ci					   cdcfg->l1_desc[i].l2ptr_dma);
120162306a36Sopenharmony_ci		}
120262306a36Sopenharmony_ci		devm_kfree(smmu->dev, cdcfg->l1_desc);
120362306a36Sopenharmony_ci		cdcfg->l1_desc = NULL;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3);
120662306a36Sopenharmony_ci	} else {
120762306a36Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_CD_DWORDS << 3);
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	dmam_free_coherent(smmu->dev, l1size, cdcfg->cdtab, cdcfg->cdtab_dma);
121162306a36Sopenharmony_ci	cdcfg->cdtab_dma = 0;
121262306a36Sopenharmony_ci	cdcfg->cdtab = NULL;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cibool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	bool free;
121862306a36Sopenharmony_ci	struct arm_smmu_ctx_desc *old_cd;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (!cd->asid)
122162306a36Sopenharmony_ci		return false;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	free = refcount_dec_and_test(&cd->refs);
122462306a36Sopenharmony_ci	if (free) {
122562306a36Sopenharmony_ci		old_cd = xa_erase(&arm_smmu_asid_xa, cd->asid);
122662306a36Sopenharmony_ci		WARN_ON(old_cd != cd);
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci	return free;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/* Stream table manipulation functions */
123262306a36Sopenharmony_cistatic void
123362306a36Sopenharmony_ciarm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	u64 val = 0;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span);
123862306a36Sopenharmony_ci	val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
124162306a36Sopenharmony_ci	WRITE_ONCE(*dst, cpu_to_le64(val));
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
124762306a36Sopenharmony_ci		.opcode	= CMDQ_OP_CFGI_STE,
124862306a36Sopenharmony_ci		.cfgi	= {
124962306a36Sopenharmony_ci			.sid	= sid,
125062306a36Sopenharmony_ci			.leaf	= true,
125162306a36Sopenharmony_ci		},
125262306a36Sopenharmony_ci	};
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
125862306a36Sopenharmony_ci				      __le64 *dst)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	/*
126162306a36Sopenharmony_ci	 * This is hideously complicated, but we only really care about
126262306a36Sopenharmony_ci	 * three cases at the moment:
126362306a36Sopenharmony_ci	 *
126462306a36Sopenharmony_ci	 * 1. Invalid (all zero) -> bypass/fault (init)
126562306a36Sopenharmony_ci	 * 2. Bypass/fault -> translation/bypass (attach)
126662306a36Sopenharmony_ci	 * 3. Translation/bypass -> bypass/fault (detach)
126762306a36Sopenharmony_ci	 *
126862306a36Sopenharmony_ci	 * Given that we can't update the STE atomically and the SMMU
126962306a36Sopenharmony_ci	 * doesn't read the thing in a defined order, that leaves us
127062306a36Sopenharmony_ci	 * with the following maintenance requirements:
127162306a36Sopenharmony_ci	 *
127262306a36Sopenharmony_ci	 * 1. Update Config, return (init time STEs aren't live)
127362306a36Sopenharmony_ci	 * 2. Write everything apart from dword 0, sync, write dword 0, sync
127462306a36Sopenharmony_ci	 * 3. Update Config, sync
127562306a36Sopenharmony_ci	 */
127662306a36Sopenharmony_ci	u64 val = le64_to_cpu(dst[0]);
127762306a36Sopenharmony_ci	bool ste_live = false;
127862306a36Sopenharmony_ci	struct arm_smmu_device *smmu = NULL;
127962306a36Sopenharmony_ci	struct arm_smmu_s1_cfg *s1_cfg = NULL;
128062306a36Sopenharmony_ci	struct arm_smmu_s2_cfg *s2_cfg = NULL;
128162306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = NULL;
128262306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent prefetch_cmd = {
128362306a36Sopenharmony_ci		.opcode		= CMDQ_OP_PREFETCH_CFG,
128462306a36Sopenharmony_ci		.prefetch	= {
128562306a36Sopenharmony_ci			.sid	= sid,
128662306a36Sopenharmony_ci		},
128762306a36Sopenharmony_ci	};
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (master) {
129062306a36Sopenharmony_ci		smmu_domain = master->domain;
129162306a36Sopenharmony_ci		smmu = master->smmu;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	if (smmu_domain) {
129562306a36Sopenharmony_ci		switch (smmu_domain->stage) {
129662306a36Sopenharmony_ci		case ARM_SMMU_DOMAIN_S1:
129762306a36Sopenharmony_ci			s1_cfg = &smmu_domain->s1_cfg;
129862306a36Sopenharmony_ci			break;
129962306a36Sopenharmony_ci		case ARM_SMMU_DOMAIN_S2:
130062306a36Sopenharmony_ci		case ARM_SMMU_DOMAIN_NESTED:
130162306a36Sopenharmony_ci			s2_cfg = &smmu_domain->s2_cfg;
130262306a36Sopenharmony_ci			break;
130362306a36Sopenharmony_ci		default:
130462306a36Sopenharmony_ci			break;
130562306a36Sopenharmony_ci		}
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (val & STRTAB_STE_0_V) {
130962306a36Sopenharmony_ci		switch (FIELD_GET(STRTAB_STE_0_CFG, val)) {
131062306a36Sopenharmony_ci		case STRTAB_STE_0_CFG_BYPASS:
131162306a36Sopenharmony_ci			break;
131262306a36Sopenharmony_ci		case STRTAB_STE_0_CFG_S1_TRANS:
131362306a36Sopenharmony_ci		case STRTAB_STE_0_CFG_S2_TRANS:
131462306a36Sopenharmony_ci			ste_live = true;
131562306a36Sopenharmony_ci			break;
131662306a36Sopenharmony_ci		case STRTAB_STE_0_CFG_ABORT:
131762306a36Sopenharmony_ci			BUG_ON(!disable_bypass);
131862306a36Sopenharmony_ci			break;
131962306a36Sopenharmony_ci		default:
132062306a36Sopenharmony_ci			BUG(); /* STE corruption */
132162306a36Sopenharmony_ci		}
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* Nuke the existing STE_0 value, as we're going to rewrite it */
132562306a36Sopenharmony_ci	val = STRTAB_STE_0_V;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	/* Bypass/fault */
132862306a36Sopenharmony_ci	if (!smmu_domain || !(s1_cfg || s2_cfg)) {
132962306a36Sopenharmony_ci		if (!smmu_domain && disable_bypass)
133062306a36Sopenharmony_ci			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
133162306a36Sopenharmony_ci		else
133262306a36Sopenharmony_ci			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		dst[0] = cpu_to_le64(val);
133562306a36Sopenharmony_ci		dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
133662306a36Sopenharmony_ci						STRTAB_STE_1_SHCFG_INCOMING));
133762306a36Sopenharmony_ci		dst[2] = 0; /* Nuke the VMID */
133862306a36Sopenharmony_ci		/*
133962306a36Sopenharmony_ci		 * The SMMU can perform negative caching, so we must sync
134062306a36Sopenharmony_ci		 * the STE regardless of whether the old value was live.
134162306a36Sopenharmony_ci		 */
134262306a36Sopenharmony_ci		if (smmu)
134362306a36Sopenharmony_ci			arm_smmu_sync_ste_for_sid(smmu, sid);
134462306a36Sopenharmony_ci		return;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (s1_cfg) {
134862306a36Sopenharmony_ci		u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ?
134962306a36Sopenharmony_ci			STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci		BUG_ON(ste_live);
135262306a36Sopenharmony_ci		dst[1] = cpu_to_le64(
135362306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
135462306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
135562306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
135662306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
135762306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_STRW, strw));
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_STALLS &&
136062306a36Sopenharmony_ci		    !master->stall_enabled)
136162306a36Sopenharmony_ci			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci		val |= (s1_cfg->cdcfg.cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) |
136462306a36Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) |
136562306a36Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_S1CDMAX, s1_cfg->s1cdmax) |
136662306a36Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_S1FMT, s1_cfg->s1fmt);
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	if (s2_cfg) {
137062306a36Sopenharmony_ci		BUG_ON(ste_live);
137162306a36Sopenharmony_ci		dst[2] = cpu_to_le64(
137262306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
137362306a36Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) |
137462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
137562306a36Sopenharmony_ci			 STRTAB_STE_2_S2ENDI |
137662306a36Sopenharmony_ci#endif
137762306a36Sopenharmony_ci			 STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
137862306a36Sopenharmony_ci			 STRTAB_STE_2_S2R);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	if (master->ats_enabled)
138662306a36Sopenharmony_ci		dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS,
138762306a36Sopenharmony_ci						 STRTAB_STE_1_EATS_TRANS));
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	arm_smmu_sync_ste_for_sid(smmu, sid);
139062306a36Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
139162306a36Sopenharmony_ci	WRITE_ONCE(dst[0], cpu_to_le64(val));
139262306a36Sopenharmony_ci	arm_smmu_sync_ste_for_sid(smmu, sid);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* It's likely that we'll want to use the new STE soon */
139562306a36Sopenharmony_ci	if (!(smmu->options & ARM_SMMU_OPT_SKIP_PREFETCH))
139662306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cistatic void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool force)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	unsigned int i;
140262306a36Sopenharmony_ci	u64 val = STRTAB_STE_0_V;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (disable_bypass && !force)
140562306a36Sopenharmony_ci		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
140662306a36Sopenharmony_ci	else
140762306a36Sopenharmony_ci		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	for (i = 0; i < nent; ++i) {
141062306a36Sopenharmony_ci		strtab[0] = cpu_to_le64(val);
141162306a36Sopenharmony_ci		strtab[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
141262306a36Sopenharmony_ci						   STRTAB_STE_1_SHCFG_INCOMING));
141362306a36Sopenharmony_ci		strtab[2] = 0;
141462306a36Sopenharmony_ci		strtab += STRTAB_STE_DWORDS;
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	size_t size;
142162306a36Sopenharmony_ci	void *strtab;
142262306a36Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
142362306a36Sopenharmony_ci	struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (desc->l2ptr)
142662306a36Sopenharmony_ci		return 0;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
142962306a36Sopenharmony_ci	strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	desc->span = STRTAB_SPLIT + 1;
143262306a36Sopenharmony_ci	desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
143362306a36Sopenharmony_ci					  GFP_KERNEL);
143462306a36Sopenharmony_ci	if (!desc->l2ptr) {
143562306a36Sopenharmony_ci		dev_err(smmu->dev,
143662306a36Sopenharmony_ci			"failed to allocate l2 stream table for SID %u\n",
143762306a36Sopenharmony_ci			sid);
143862306a36Sopenharmony_ci		return -ENOMEM;
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT, false);
144262306a36Sopenharmony_ci	arm_smmu_write_strtab_l1_desc(strtab, desc);
144362306a36Sopenharmony_ci	return 0;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic struct arm_smmu_master *
144762306a36Sopenharmony_ciarm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	struct rb_node *node;
145062306a36Sopenharmony_ci	struct arm_smmu_stream *stream;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	lockdep_assert_held(&smmu->streams_mutex);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	node = smmu->streams.rb_node;
145562306a36Sopenharmony_ci	while (node) {
145662306a36Sopenharmony_ci		stream = rb_entry(node, struct arm_smmu_stream, node);
145762306a36Sopenharmony_ci		if (stream->id < sid)
145862306a36Sopenharmony_ci			node = node->rb_right;
145962306a36Sopenharmony_ci		else if (stream->id > sid)
146062306a36Sopenharmony_ci			node = node->rb_left;
146162306a36Sopenharmony_ci		else
146262306a36Sopenharmony_ci			return stream->master;
146362306a36Sopenharmony_ci	}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	return NULL;
146662306a36Sopenharmony_ci}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci/* IRQ and event handlers */
146962306a36Sopenharmony_cistatic int arm_smmu_handle_evt(struct arm_smmu_device *smmu, u64 *evt)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	int ret;
147262306a36Sopenharmony_ci	u32 reason;
147362306a36Sopenharmony_ci	u32 perm = 0;
147462306a36Sopenharmony_ci	struct arm_smmu_master *master;
147562306a36Sopenharmony_ci	bool ssid_valid = evt[0] & EVTQ_0_SSV;
147662306a36Sopenharmony_ci	u32 sid = FIELD_GET(EVTQ_0_SID, evt[0]);
147762306a36Sopenharmony_ci	struct iommu_fault_event fault_evt = { };
147862306a36Sopenharmony_ci	struct iommu_fault *flt = &fault_evt.fault;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	switch (FIELD_GET(EVTQ_0_ID, evt[0])) {
148162306a36Sopenharmony_ci	case EVT_ID_TRANSLATION_FAULT:
148262306a36Sopenharmony_ci		reason = IOMMU_FAULT_REASON_PTE_FETCH;
148362306a36Sopenharmony_ci		break;
148462306a36Sopenharmony_ci	case EVT_ID_ADDR_SIZE_FAULT:
148562306a36Sopenharmony_ci		reason = IOMMU_FAULT_REASON_OOR_ADDRESS;
148662306a36Sopenharmony_ci		break;
148762306a36Sopenharmony_ci	case EVT_ID_ACCESS_FAULT:
148862306a36Sopenharmony_ci		reason = IOMMU_FAULT_REASON_ACCESS;
148962306a36Sopenharmony_ci		break;
149062306a36Sopenharmony_ci	case EVT_ID_PERMISSION_FAULT:
149162306a36Sopenharmony_ci		reason = IOMMU_FAULT_REASON_PERMISSION;
149262306a36Sopenharmony_ci		break;
149362306a36Sopenharmony_ci	default:
149462306a36Sopenharmony_ci		return -EOPNOTSUPP;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	/* Stage-2 is always pinned at the moment */
149862306a36Sopenharmony_ci	if (evt[1] & EVTQ_1_S2)
149962306a36Sopenharmony_ci		return -EFAULT;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (evt[1] & EVTQ_1_RnW)
150262306a36Sopenharmony_ci		perm |= IOMMU_FAULT_PERM_READ;
150362306a36Sopenharmony_ci	else
150462306a36Sopenharmony_ci		perm |= IOMMU_FAULT_PERM_WRITE;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (evt[1] & EVTQ_1_InD)
150762306a36Sopenharmony_ci		perm |= IOMMU_FAULT_PERM_EXEC;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	if (evt[1] & EVTQ_1_PnU)
151062306a36Sopenharmony_ci		perm |= IOMMU_FAULT_PERM_PRIV;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (evt[1] & EVTQ_1_STALL) {
151362306a36Sopenharmony_ci		flt->type = IOMMU_FAULT_PAGE_REQ;
151462306a36Sopenharmony_ci		flt->prm = (struct iommu_fault_page_request) {
151562306a36Sopenharmony_ci			.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE,
151662306a36Sopenharmony_ci			.grpid = FIELD_GET(EVTQ_1_STAG, evt[1]),
151762306a36Sopenharmony_ci			.perm = perm,
151862306a36Sopenharmony_ci			.addr = FIELD_GET(EVTQ_2_ADDR, evt[2]),
151962306a36Sopenharmony_ci		};
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		if (ssid_valid) {
152262306a36Sopenharmony_ci			flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
152362306a36Sopenharmony_ci			flt->prm.pasid = FIELD_GET(EVTQ_0_SSID, evt[0]);
152462306a36Sopenharmony_ci		}
152562306a36Sopenharmony_ci	} else {
152662306a36Sopenharmony_ci		flt->type = IOMMU_FAULT_DMA_UNRECOV;
152762306a36Sopenharmony_ci		flt->event = (struct iommu_fault_unrecoverable) {
152862306a36Sopenharmony_ci			.reason = reason,
152962306a36Sopenharmony_ci			.flags = IOMMU_FAULT_UNRECOV_ADDR_VALID,
153062306a36Sopenharmony_ci			.perm = perm,
153162306a36Sopenharmony_ci			.addr = FIELD_GET(EVTQ_2_ADDR, evt[2]),
153262306a36Sopenharmony_ci		};
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		if (ssid_valid) {
153562306a36Sopenharmony_ci			flt->event.flags |= IOMMU_FAULT_UNRECOV_PASID_VALID;
153662306a36Sopenharmony_ci			flt->event.pasid = FIELD_GET(EVTQ_0_SSID, evt[0]);
153762306a36Sopenharmony_ci		}
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	mutex_lock(&smmu->streams_mutex);
154162306a36Sopenharmony_ci	master = arm_smmu_find_master(smmu, sid);
154262306a36Sopenharmony_ci	if (!master) {
154362306a36Sopenharmony_ci		ret = -EINVAL;
154462306a36Sopenharmony_ci		goto out_unlock;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ret = iommu_report_device_fault(master->dev, &fault_evt);
154862306a36Sopenharmony_ci	if (ret && flt->type == IOMMU_FAULT_PAGE_REQ) {
154962306a36Sopenharmony_ci		/* Nobody cared, abort the access */
155062306a36Sopenharmony_ci		struct iommu_page_response resp = {
155162306a36Sopenharmony_ci			.pasid		= flt->prm.pasid,
155262306a36Sopenharmony_ci			.grpid		= flt->prm.grpid,
155362306a36Sopenharmony_ci			.code		= IOMMU_PAGE_RESP_FAILURE,
155462306a36Sopenharmony_ci		};
155562306a36Sopenharmony_ci		arm_smmu_page_response(master->dev, &fault_evt, &resp);
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ciout_unlock:
155962306a36Sopenharmony_ci	mutex_unlock(&smmu->streams_mutex);
156062306a36Sopenharmony_ci	return ret;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	int i, ret;
156662306a36Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
156762306a36Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->evtq.q;
156862306a36Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
156962306a36Sopenharmony_ci	static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,
157062306a36Sopenharmony_ci				      DEFAULT_RATELIMIT_BURST);
157162306a36Sopenharmony_ci	u64 evt[EVTQ_ENT_DWORDS];
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	do {
157462306a36Sopenharmony_ci		while (!queue_remove_raw(q, evt)) {
157562306a36Sopenharmony_ci			u8 id = FIELD_GET(EVTQ_0_ID, evt[0]);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci			ret = arm_smmu_handle_evt(smmu, evt);
157862306a36Sopenharmony_ci			if (!ret || !__ratelimit(&rs))
157962306a36Sopenharmony_ci				continue;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci			dev_info(smmu->dev, "event 0x%02x received:\n", id);
158262306a36Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(evt); ++i)
158362306a36Sopenharmony_ci				dev_info(smmu->dev, "\t0x%016llx\n",
158462306a36Sopenharmony_ci					 (unsigned long long)evt[i]);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci			cond_resched();
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		/*
159062306a36Sopenharmony_ci		 * Not much we can do on overflow, so scream and pretend we're
159162306a36Sopenharmony_ci		 * trying harder.
159262306a36Sopenharmony_ci		 */
159362306a36Sopenharmony_ci		if (queue_sync_prod_in(q) == -EOVERFLOW)
159462306a36Sopenharmony_ci			dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n");
159562306a36Sopenharmony_ci	} while (!queue_empty(llq));
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	/* Sync our overflow flag, as we believe we're up to speed */
159862306a36Sopenharmony_ci	queue_sync_cons_ovf(q);
159962306a36Sopenharmony_ci	return IRQ_HANDLED;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cistatic void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
160362306a36Sopenharmony_ci{
160462306a36Sopenharmony_ci	u32 sid, ssid;
160562306a36Sopenharmony_ci	u16 grpid;
160662306a36Sopenharmony_ci	bool ssv, last;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	sid = FIELD_GET(PRIQ_0_SID, evt[0]);
160962306a36Sopenharmony_ci	ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]);
161062306a36Sopenharmony_ci	ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : IOMMU_NO_PASID;
161162306a36Sopenharmony_ci	last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]);
161262306a36Sopenharmony_ci	grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	dev_info(smmu->dev, "unexpected PRI request received:\n");
161562306a36Sopenharmony_ci	dev_info(smmu->dev,
161662306a36Sopenharmony_ci		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
161762306a36Sopenharmony_ci		 sid, ssid, grpid, last ? "L" : "",
161862306a36Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
161962306a36Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_READ ? "R" : "",
162062306a36Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
162162306a36Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
162262306a36Sopenharmony_ci		 evt[1] & PRIQ_1_ADDR_MASK);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	if (last) {
162562306a36Sopenharmony_ci		struct arm_smmu_cmdq_ent cmd = {
162662306a36Sopenharmony_ci			.opcode			= CMDQ_OP_PRI_RESP,
162762306a36Sopenharmony_ci			.substream_valid	= ssv,
162862306a36Sopenharmony_ci			.pri			= {
162962306a36Sopenharmony_ci				.sid	= sid,
163062306a36Sopenharmony_ci				.ssid	= ssid,
163162306a36Sopenharmony_ci				.grpid	= grpid,
163262306a36Sopenharmony_ci				.resp	= PRI_RESP_DENY,
163362306a36Sopenharmony_ci			},
163462306a36Sopenharmony_ci		};
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_cistatic irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
164362306a36Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->priq.q;
164462306a36Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
164562306a36Sopenharmony_ci	u64 evt[PRIQ_ENT_DWORDS];
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	do {
164862306a36Sopenharmony_ci		while (!queue_remove_raw(q, evt))
164962306a36Sopenharmony_ci			arm_smmu_handle_ppr(smmu, evt);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci		if (queue_sync_prod_in(q) == -EOVERFLOW)
165262306a36Sopenharmony_ci			dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n");
165362306a36Sopenharmony_ci	} while (!queue_empty(llq));
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	/* Sync our overflow flag, as we believe we're up to speed */
165662306a36Sopenharmony_ci	queue_sync_cons_ovf(q);
165762306a36Sopenharmony_ci	return IRQ_HANDLED;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu);
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
166362306a36Sopenharmony_ci{
166462306a36Sopenharmony_ci	u32 gerror, gerrorn, active;
166562306a36Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
166862306a36Sopenharmony_ci	gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	active = gerror ^ gerrorn;
167162306a36Sopenharmony_ci	if (!(active & GERROR_ERR_MASK))
167262306a36Sopenharmony_ci		return IRQ_NONE; /* No errors pending */
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	dev_warn(smmu->dev,
167562306a36Sopenharmony_ci		 "unexpected global error reported (0x%08x), this could be serious\n",
167662306a36Sopenharmony_ci		 active);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (active & GERROR_SFM_ERR) {
167962306a36Sopenharmony_ci		dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
168062306a36Sopenharmony_ci		arm_smmu_device_disable(smmu);
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (active & GERROR_MSI_GERROR_ABT_ERR)
168462306a36Sopenharmony_ci		dev_warn(smmu->dev, "GERROR MSI write aborted\n");
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (active & GERROR_MSI_PRIQ_ABT_ERR)
168762306a36Sopenharmony_ci		dev_warn(smmu->dev, "PRIQ MSI write aborted\n");
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	if (active & GERROR_MSI_EVTQ_ABT_ERR)
169062306a36Sopenharmony_ci		dev_warn(smmu->dev, "EVTQ MSI write aborted\n");
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	if (active & GERROR_MSI_CMDQ_ABT_ERR)
169362306a36Sopenharmony_ci		dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (active & GERROR_PRIQ_ABT_ERR)
169662306a36Sopenharmony_ci		dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n");
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	if (active & GERROR_EVTQ_ABT_ERR)
169962306a36Sopenharmony_ci		dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n");
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (active & GERROR_CMDQ_ERR)
170262306a36Sopenharmony_ci		arm_smmu_cmdq_skip_err(smmu);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	writel(gerror, smmu->base + ARM_SMMU_GERRORN);
170562306a36Sopenharmony_ci	return IRQ_HANDLED;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	arm_smmu_evtq_thread(irq, dev);
171362306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
171462306a36Sopenharmony_ci		arm_smmu_priq_thread(irq, dev);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	return IRQ_HANDLED;
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev)
172062306a36Sopenharmony_ci{
172162306a36Sopenharmony_ci	arm_smmu_gerror_handler(irq, dev);
172262306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic void
172662306a36Sopenharmony_ciarm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size,
172762306a36Sopenharmony_ci			struct arm_smmu_cmdq_ent *cmd)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	size_t log2_span;
173062306a36Sopenharmony_ci	size_t span_mask;
173162306a36Sopenharmony_ci	/* ATC invalidates are always on 4096-bytes pages */
173262306a36Sopenharmony_ci	size_t inval_grain_shift = 12;
173362306a36Sopenharmony_ci	unsigned long page_start, page_end;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	/*
173662306a36Sopenharmony_ci	 * ATS and PASID:
173762306a36Sopenharmony_ci	 *
173862306a36Sopenharmony_ci	 * If substream_valid is clear, the PCIe TLP is sent without a PASID
173962306a36Sopenharmony_ci	 * prefix. In that case all ATC entries within the address range are
174062306a36Sopenharmony_ci	 * invalidated, including those that were requested with a PASID! There
174162306a36Sopenharmony_ci	 * is no way to invalidate only entries without PASID.
174262306a36Sopenharmony_ci	 *
174362306a36Sopenharmony_ci	 * When using STRTAB_STE_1_S1DSS_SSID0 (reserving CD 0 for non-PASID
174462306a36Sopenharmony_ci	 * traffic), translation requests without PASID create ATC entries
174562306a36Sopenharmony_ci	 * without PASID, which must be invalidated with substream_valid clear.
174662306a36Sopenharmony_ci	 * This has the unpleasant side-effect of invalidating all PASID-tagged
174762306a36Sopenharmony_ci	 * ATC entries within the address range.
174862306a36Sopenharmony_ci	 */
174962306a36Sopenharmony_ci	*cmd = (struct arm_smmu_cmdq_ent) {
175062306a36Sopenharmony_ci		.opcode			= CMDQ_OP_ATC_INV,
175162306a36Sopenharmony_ci		.substream_valid	= (ssid != IOMMU_NO_PASID),
175262306a36Sopenharmony_ci		.atc.ssid		= ssid,
175362306a36Sopenharmony_ci	};
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (!size) {
175662306a36Sopenharmony_ci		cmd->atc.size = ATC_INV_SIZE_ALL;
175762306a36Sopenharmony_ci		return;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	page_start	= iova >> inval_grain_shift;
176162306a36Sopenharmony_ci	page_end	= (iova + size - 1) >> inval_grain_shift;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	/*
176462306a36Sopenharmony_ci	 * In an ATS Invalidate Request, the address must be aligned on the
176562306a36Sopenharmony_ci	 * range size, which must be a power of two number of page sizes. We
176662306a36Sopenharmony_ci	 * thus have to choose between grossly over-invalidating the region, or
176762306a36Sopenharmony_ci	 * splitting the invalidation into multiple commands. For simplicity
176862306a36Sopenharmony_ci	 * we'll go with the first solution, but should refine it in the future
176962306a36Sopenharmony_ci	 * if multiple commands are shown to be more efficient.
177062306a36Sopenharmony_ci	 *
177162306a36Sopenharmony_ci	 * Find the smallest power of two that covers the range. The most
177262306a36Sopenharmony_ci	 * significant differing bit between the start and end addresses,
177362306a36Sopenharmony_ci	 * fls(start ^ end), indicates the required span. For example:
177462306a36Sopenharmony_ci	 *
177562306a36Sopenharmony_ci	 * We want to invalidate pages [8; 11]. This is already the ideal range:
177662306a36Sopenharmony_ci	 *		x = 0b1000 ^ 0b1011 = 0b11
177762306a36Sopenharmony_ci	 *		span = 1 << fls(x) = 4
177862306a36Sopenharmony_ci	 *
177962306a36Sopenharmony_ci	 * To invalidate pages [7; 10], we need to invalidate [0; 15]:
178062306a36Sopenharmony_ci	 *		x = 0b0111 ^ 0b1010 = 0b1101
178162306a36Sopenharmony_ci	 *		span = 1 << fls(x) = 16
178262306a36Sopenharmony_ci	 */
178362306a36Sopenharmony_ci	log2_span	= fls_long(page_start ^ page_end);
178462306a36Sopenharmony_ci	span_mask	= (1ULL << log2_span) - 1;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	page_start	&= ~span_mask;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	cmd->atc.addr	= page_start << inval_grain_shift;
178962306a36Sopenharmony_ci	cmd->atc.size	= log2_span;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic int arm_smmu_atc_inv_master(struct arm_smmu_master *master)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	int i;
179562306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
179662306a36Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	arm_smmu_atc_inv_to_cmd(IOMMU_NO_PASID, 0, 0, &cmd);
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	cmds.num = 0;
180162306a36Sopenharmony_ci	for (i = 0; i < master->num_streams; i++) {
180262306a36Sopenharmony_ci		cmd.atc.sid = master->streams[i].id;
180362306a36Sopenharmony_ci		arm_smmu_cmdq_batch_add(master->smmu, &cmds, &cmd);
180462306a36Sopenharmony_ci	}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	return arm_smmu_cmdq_batch_submit(master->smmu, &cmds);
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ciint arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,
181062306a36Sopenharmony_ci			    unsigned long iova, size_t size)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	int i;
181362306a36Sopenharmony_ci	unsigned long flags;
181462306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
181562306a36Sopenharmony_ci	struct arm_smmu_master *master;
181662306a36Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
181962306a36Sopenharmony_ci		return 0;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	/*
182262306a36Sopenharmony_ci	 * Ensure that we've completed prior invalidation of the main TLBs
182362306a36Sopenharmony_ci	 * before we read 'nr_ats_masters' in case of a concurrent call to
182462306a36Sopenharmony_ci	 * arm_smmu_enable_ats():
182562306a36Sopenharmony_ci	 *
182662306a36Sopenharmony_ci	 *	// unmap()			// arm_smmu_enable_ats()
182762306a36Sopenharmony_ci	 *	TLBI+SYNC			atomic_inc(&nr_ats_masters);
182862306a36Sopenharmony_ci	 *	smp_mb();			[...]
182962306a36Sopenharmony_ci	 *	atomic_read(&nr_ats_masters);	pci_enable_ats() // writel()
183062306a36Sopenharmony_ci	 *
183162306a36Sopenharmony_ci	 * Ensures that we always see the incremented 'nr_ats_masters' count if
183262306a36Sopenharmony_ci	 * ATS was enabled at the PCI device before completion of the TLBI.
183362306a36Sopenharmony_ci	 */
183462306a36Sopenharmony_ci	smp_mb();
183562306a36Sopenharmony_ci	if (!atomic_read(&smmu_domain->nr_ats_masters))
183662306a36Sopenharmony_ci		return 0;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	cmds.num = 0;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
184362306a36Sopenharmony_ci	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
184462306a36Sopenharmony_ci		if (!master->ats_enabled)
184562306a36Sopenharmony_ci			continue;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		for (i = 0; i < master->num_streams; i++) {
184862306a36Sopenharmony_ci			cmd.atc.sid = master->streams[i].id;
184962306a36Sopenharmony_ci			arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd);
185062306a36Sopenharmony_ci		}
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	return arm_smmu_cmdq_batch_submit(smmu_domain->smmu, &cmds);
185562306a36Sopenharmony_ci}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci/* IO_PGTABLE API */
185862306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_context(void *cookie)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = cookie;
186162306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
186262306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	/*
186562306a36Sopenharmony_ci	 * NOTE: when io-pgtable is in non-strict mode, we may get here with
186662306a36Sopenharmony_ci	 * PTEs previously cleared by unmaps on the current CPU not yet visible
186762306a36Sopenharmony_ci	 * to the SMMU. We are relying on the dma_wmb() implicit during cmd
186862306a36Sopenharmony_ci	 * insertion to guarantee those are observed before the TLBI. Do be
186962306a36Sopenharmony_ci	 * careful, 007.
187062306a36Sopenharmony_ci	 */
187162306a36Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
187262306a36Sopenharmony_ci		arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid);
187362306a36Sopenharmony_ci	} else {
187462306a36Sopenharmony_ci		cmd.opcode	= CMDQ_OP_TLBI_S12_VMALL;
187562306a36Sopenharmony_ci		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
187662306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0);
187962306a36Sopenharmony_ci}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_cistatic void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd,
188262306a36Sopenharmony_ci				     unsigned long iova, size_t size,
188362306a36Sopenharmony_ci				     size_t granule,
188462306a36Sopenharmony_ci				     struct arm_smmu_domain *smmu_domain)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
188762306a36Sopenharmony_ci	unsigned long end = iova + size, num_pages = 0, tg = 0;
188862306a36Sopenharmony_ci	size_t inv_range = granule;
188962306a36Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	if (!size)
189262306a36Sopenharmony_ci		return;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
189562306a36Sopenharmony_ci		/* Get the leaf page size */
189662306a36Sopenharmony_ci		tg = __ffs(smmu_domain->domain.pgsize_bitmap);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		num_pages = size >> tg;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci		/* Convert page size of 12,14,16 (log2) to 1,2,3 */
190162306a36Sopenharmony_ci		cmd->tlbi.tg = (tg - 10) / 2;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci		/*
190462306a36Sopenharmony_ci		 * Determine what level the granule is at. For non-leaf, both
190562306a36Sopenharmony_ci		 * io-pgtable and SVA pass a nominal last-level granule because
190662306a36Sopenharmony_ci		 * they don't know what level(s) actually apply, so ignore that
190762306a36Sopenharmony_ci		 * and leave TTL=0. However for various errata reasons we still
190862306a36Sopenharmony_ci		 * want to use a range command, so avoid the SVA corner case
190962306a36Sopenharmony_ci		 * where both scale and num could be 0 as well.
191062306a36Sopenharmony_ci		 */
191162306a36Sopenharmony_ci		if (cmd->tlbi.leaf)
191262306a36Sopenharmony_ci			cmd->tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3));
191362306a36Sopenharmony_ci		else if ((num_pages & CMDQ_TLBI_RANGE_NUM_MAX) == 1)
191462306a36Sopenharmony_ci			num_pages++;
191562306a36Sopenharmony_ci	}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	cmds.num = 0;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	while (iova < end) {
192062306a36Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
192162306a36Sopenharmony_ci			/*
192262306a36Sopenharmony_ci			 * On each iteration of the loop, the range is 5 bits
192362306a36Sopenharmony_ci			 * worth of the aligned size remaining.
192462306a36Sopenharmony_ci			 * The range in pages is:
192562306a36Sopenharmony_ci			 *
192662306a36Sopenharmony_ci			 * range = (num_pages & (0x1f << __ffs(num_pages)))
192762306a36Sopenharmony_ci			 */
192862306a36Sopenharmony_ci			unsigned long scale, num;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci			/* Determine the power of 2 multiple number of pages */
193162306a36Sopenharmony_ci			scale = __ffs(num_pages);
193262306a36Sopenharmony_ci			cmd->tlbi.scale = scale;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci			/* Determine how many chunks of 2^scale size we have */
193562306a36Sopenharmony_ci			num = (num_pages >> scale) & CMDQ_TLBI_RANGE_NUM_MAX;
193662306a36Sopenharmony_ci			cmd->tlbi.num = num - 1;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci			/* range is num * 2^scale * pgsize */
193962306a36Sopenharmony_ci			inv_range = num << (scale + tg);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci			/* Clear out the lower order bits for the next iteration */
194262306a36Sopenharmony_ci			num_pages -= num << scale;
194362306a36Sopenharmony_ci		}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci		cmd->tlbi.addr = iova;
194662306a36Sopenharmony_ci		arm_smmu_cmdq_batch_add(smmu, &cmds, cmd);
194762306a36Sopenharmony_ci		iova += inv_range;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci	arm_smmu_cmdq_batch_submit(smmu, &cmds);
195062306a36Sopenharmony_ci}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_range_domain(unsigned long iova, size_t size,
195362306a36Sopenharmony_ci					  size_t granule, bool leaf,
195462306a36Sopenharmony_ci					  struct arm_smmu_domain *smmu_domain)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
195762306a36Sopenharmony_ci		.tlbi = {
195862306a36Sopenharmony_ci			.leaf	= leaf,
195962306a36Sopenharmony_ci		},
196062306a36Sopenharmony_ci	};
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
196362306a36Sopenharmony_ci		cmd.opcode	= smmu_domain->smmu->features & ARM_SMMU_FEAT_E2H ?
196462306a36Sopenharmony_ci				  CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA;
196562306a36Sopenharmony_ci		cmd.tlbi.asid	= smmu_domain->s1_cfg.cd.asid;
196662306a36Sopenharmony_ci	} else {
196762306a36Sopenharmony_ci		cmd.opcode	= CMDQ_OP_TLBI_S2_IPA;
196862306a36Sopenharmony_ci		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci	__arm_smmu_tlb_inv_range(&cmd, iova, size, granule, smmu_domain);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	/*
197362306a36Sopenharmony_ci	 * Unfortunately, this can't be leaf-only since we may have
197462306a36Sopenharmony_ci	 * zapped an entire table.
197562306a36Sopenharmony_ci	 */
197662306a36Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, iova, size);
197762306a36Sopenharmony_ci}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_civoid arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid,
198062306a36Sopenharmony_ci				 size_t granule, bool leaf,
198162306a36Sopenharmony_ci				 struct arm_smmu_domain *smmu_domain)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
198462306a36Sopenharmony_ci		.opcode	= smmu_domain->smmu->features & ARM_SMMU_FEAT_E2H ?
198562306a36Sopenharmony_ci			  CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA,
198662306a36Sopenharmony_ci		.tlbi = {
198762306a36Sopenharmony_ci			.asid	= asid,
198862306a36Sopenharmony_ci			.leaf	= leaf,
198962306a36Sopenharmony_ci		},
199062306a36Sopenharmony_ci	};
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	__arm_smmu_tlb_inv_range(&cmd, iova, size, granule, smmu_domain);
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather,
199662306a36Sopenharmony_ci					 unsigned long iova, size_t granule,
199762306a36Sopenharmony_ci					 void *cookie)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = cookie;
200062306a36Sopenharmony_ci	struct iommu_domain *domain = &smmu_domain->domain;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	iommu_iotlb_gather_add_page(domain, gather, iova, granule);
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size,
200662306a36Sopenharmony_ci				  size_t granule, void *cookie)
200762306a36Sopenharmony_ci{
200862306a36Sopenharmony_ci	arm_smmu_tlb_inv_range_domain(iova, size, granule, false, cookie);
200962306a36Sopenharmony_ci}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_flush_ops = {
201262306a36Sopenharmony_ci	.tlb_flush_all	= arm_smmu_tlb_inv_context,
201362306a36Sopenharmony_ci	.tlb_flush_walk = arm_smmu_tlb_inv_walk,
201462306a36Sopenharmony_ci	.tlb_add_page	= arm_smmu_tlb_inv_page_nosync,
201562306a36Sopenharmony_ci};
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci/* IOMMU API */
201862306a36Sopenharmony_cistatic bool arm_smmu_capable(struct device *dev, enum iommu_cap cap)
201962306a36Sopenharmony_ci{
202062306a36Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	switch (cap) {
202362306a36Sopenharmony_ci	case IOMMU_CAP_CACHE_COHERENCY:
202462306a36Sopenharmony_ci		/* Assume that a coherent TCU implies coherent TBUs */
202562306a36Sopenharmony_ci		return master->smmu->features & ARM_SMMU_FEAT_COHERENCY;
202662306a36Sopenharmony_ci	case IOMMU_CAP_NOEXEC:
202762306a36Sopenharmony_ci	case IOMMU_CAP_DEFERRED_FLUSH:
202862306a36Sopenharmony_ci		return true;
202962306a36Sopenharmony_ci	default:
203062306a36Sopenharmony_ci		return false;
203162306a36Sopenharmony_ci	}
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
203562306a36Sopenharmony_ci{
203662306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	if (type == IOMMU_DOMAIN_SVA)
203962306a36Sopenharmony_ci		return arm_smmu_sva_domain_alloc();
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	if (type != IOMMU_DOMAIN_UNMANAGED &&
204262306a36Sopenharmony_ci	    type != IOMMU_DOMAIN_DMA &&
204362306a36Sopenharmony_ci	    type != IOMMU_DOMAIN_IDENTITY)
204462306a36Sopenharmony_ci		return NULL;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	/*
204762306a36Sopenharmony_ci	 * Allocate the domain and initialise some of its data structures.
204862306a36Sopenharmony_ci	 * We can't really do anything meaningful until we've added a
204962306a36Sopenharmony_ci	 * master.
205062306a36Sopenharmony_ci	 */
205162306a36Sopenharmony_ci	smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
205262306a36Sopenharmony_ci	if (!smmu_domain)
205362306a36Sopenharmony_ci		return NULL;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	mutex_init(&smmu_domain->init_mutex);
205662306a36Sopenharmony_ci	INIT_LIST_HEAD(&smmu_domain->devices);
205762306a36Sopenharmony_ci	spin_lock_init(&smmu_domain->devices_lock);
205862306a36Sopenharmony_ci	INIT_LIST_HEAD(&smmu_domain->mmu_notifiers);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	return &smmu_domain->domain;
206162306a36Sopenharmony_ci}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain)
206462306a36Sopenharmony_ci{
206562306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
206662306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	free_io_pgtable_ops(smmu_domain->pgtbl_ops);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	/* Free the CD and ASID, if we allocated them */
207162306a36Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
207262306a36Sopenharmony_ci		struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci		/* Prevent SVA from touching the CD while we're freeing it */
207562306a36Sopenharmony_ci		mutex_lock(&arm_smmu_asid_lock);
207662306a36Sopenharmony_ci		if (cfg->cdcfg.cdtab)
207762306a36Sopenharmony_ci			arm_smmu_free_cd_tables(smmu_domain);
207862306a36Sopenharmony_ci		arm_smmu_free_asid(&cfg->cd);
207962306a36Sopenharmony_ci		mutex_unlock(&arm_smmu_asid_lock);
208062306a36Sopenharmony_ci	} else {
208162306a36Sopenharmony_ci		struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
208262306a36Sopenharmony_ci		if (cfg->vmid)
208362306a36Sopenharmony_ci			ida_free(&smmu->vmid_map, cfg->vmid);
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	kfree(smmu_domain);
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
209062306a36Sopenharmony_ci				       struct arm_smmu_master *master,
209162306a36Sopenharmony_ci				       struct io_pgtable_cfg *pgtbl_cfg)
209262306a36Sopenharmony_ci{
209362306a36Sopenharmony_ci	int ret;
209462306a36Sopenharmony_ci	u32 asid;
209562306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
209662306a36Sopenharmony_ci	struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
209762306a36Sopenharmony_ci	typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	refcount_set(&cfg->cd.refs, 1);
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	/* Prevent SVA from modifying the ASID until it is written to the CD */
210262306a36Sopenharmony_ci	mutex_lock(&arm_smmu_asid_lock);
210362306a36Sopenharmony_ci	ret = xa_alloc(&arm_smmu_asid_xa, &asid, &cfg->cd,
210462306a36Sopenharmony_ci		       XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL);
210562306a36Sopenharmony_ci	if (ret)
210662306a36Sopenharmony_ci		goto out_unlock;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	cfg->s1cdmax = master->ssid_bits;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	smmu_domain->stall_enabled = master->stall_enabled;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	ret = arm_smmu_alloc_cd_tables(smmu_domain);
211362306a36Sopenharmony_ci	if (ret)
211462306a36Sopenharmony_ci		goto out_free_asid;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	cfg->cd.asid	= (u16)asid;
211762306a36Sopenharmony_ci	cfg->cd.ttbr	= pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
211862306a36Sopenharmony_ci	cfg->cd.tcr	= FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, tcr->tsz) |
211962306a36Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_TG0, tcr->tg) |
212062306a36Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, tcr->irgn) |
212162306a36Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, tcr->orgn) |
212262306a36Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_SH0, tcr->sh) |
212362306a36Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_IPS, tcr->ips) |
212462306a36Sopenharmony_ci			  CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64;
212562306a36Sopenharmony_ci	cfg->cd.mair	= pgtbl_cfg->arm_lpae_s1_cfg.mair;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	/*
212862306a36Sopenharmony_ci	 * Note that this will end up calling arm_smmu_sync_cd() before
212962306a36Sopenharmony_ci	 * the master has been added to the devices list for this domain.
213062306a36Sopenharmony_ci	 * This isn't an issue because the STE hasn't been installed yet.
213162306a36Sopenharmony_ci	 */
213262306a36Sopenharmony_ci	ret = arm_smmu_write_ctx_desc(smmu_domain, IOMMU_NO_PASID, &cfg->cd);
213362306a36Sopenharmony_ci	if (ret)
213462306a36Sopenharmony_ci		goto out_free_cd_tables;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	mutex_unlock(&arm_smmu_asid_lock);
213762306a36Sopenharmony_ci	return 0;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ciout_free_cd_tables:
214062306a36Sopenharmony_ci	arm_smmu_free_cd_tables(smmu_domain);
214162306a36Sopenharmony_ciout_free_asid:
214262306a36Sopenharmony_ci	arm_smmu_free_asid(&cfg->cd);
214362306a36Sopenharmony_ciout_unlock:
214462306a36Sopenharmony_ci	mutex_unlock(&arm_smmu_asid_lock);
214562306a36Sopenharmony_ci	return ret;
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
214962306a36Sopenharmony_ci				       struct arm_smmu_master *master,
215062306a36Sopenharmony_ci				       struct io_pgtable_cfg *pgtbl_cfg)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	int vmid;
215362306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
215462306a36Sopenharmony_ci	struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
215562306a36Sopenharmony_ci	typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	/* Reserve VMID 0 for stage-2 bypass STEs */
215862306a36Sopenharmony_ci	vmid = ida_alloc_range(&smmu->vmid_map, 1, (1 << smmu->vmid_bits) - 1,
215962306a36Sopenharmony_ci			       GFP_KERNEL);
216062306a36Sopenharmony_ci	if (vmid < 0)
216162306a36Sopenharmony_ci		return vmid;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	vtcr = &pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
216462306a36Sopenharmony_ci	cfg->vmid	= (u16)vmid;
216562306a36Sopenharmony_ci	cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
216662306a36Sopenharmony_ci	cfg->vtcr	= FIELD_PREP(STRTAB_STE_2_VTCR_S2T0SZ, vtcr->tsz) |
216762306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2SL0, vtcr->sl) |
216862306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2IR0, vtcr->irgn) |
216962306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2OR0, vtcr->orgn) |
217062306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2SH0, vtcr->sh) |
217162306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) |
217262306a36Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps);
217362306a36Sopenharmony_ci	return 0;
217462306a36Sopenharmony_ci}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_cistatic int arm_smmu_domain_finalise(struct iommu_domain *domain,
217762306a36Sopenharmony_ci				    struct arm_smmu_master *master)
217862306a36Sopenharmony_ci{
217962306a36Sopenharmony_ci	int ret;
218062306a36Sopenharmony_ci	unsigned long ias, oas;
218162306a36Sopenharmony_ci	enum io_pgtable_fmt fmt;
218262306a36Sopenharmony_ci	struct io_pgtable_cfg pgtbl_cfg;
218362306a36Sopenharmony_ci	struct io_pgtable_ops *pgtbl_ops;
218462306a36Sopenharmony_ci	int (*finalise_stage_fn)(struct arm_smmu_domain *,
218562306a36Sopenharmony_ci				 struct arm_smmu_master *,
218662306a36Sopenharmony_ci				 struct io_pgtable_cfg *);
218762306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
218862306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
219162306a36Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
219262306a36Sopenharmony_ci		return 0;
219362306a36Sopenharmony_ci	}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	/* Restrict the stage to what we can actually support */
219662306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
219762306a36Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
219862306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
219962306a36Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	switch (smmu_domain->stage) {
220262306a36Sopenharmony_ci	case ARM_SMMU_DOMAIN_S1:
220362306a36Sopenharmony_ci		ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
220462306a36Sopenharmony_ci		ias = min_t(unsigned long, ias, VA_BITS);
220562306a36Sopenharmony_ci		oas = smmu->ias;
220662306a36Sopenharmony_ci		fmt = ARM_64_LPAE_S1;
220762306a36Sopenharmony_ci		finalise_stage_fn = arm_smmu_domain_finalise_s1;
220862306a36Sopenharmony_ci		break;
220962306a36Sopenharmony_ci	case ARM_SMMU_DOMAIN_NESTED:
221062306a36Sopenharmony_ci	case ARM_SMMU_DOMAIN_S2:
221162306a36Sopenharmony_ci		ias = smmu->ias;
221262306a36Sopenharmony_ci		oas = smmu->oas;
221362306a36Sopenharmony_ci		fmt = ARM_64_LPAE_S2;
221462306a36Sopenharmony_ci		finalise_stage_fn = arm_smmu_domain_finalise_s2;
221562306a36Sopenharmony_ci		break;
221662306a36Sopenharmony_ci	default:
221762306a36Sopenharmony_ci		return -EINVAL;
221862306a36Sopenharmony_ci	}
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	pgtbl_cfg = (struct io_pgtable_cfg) {
222162306a36Sopenharmony_ci		.pgsize_bitmap	= smmu->pgsize_bitmap,
222262306a36Sopenharmony_ci		.ias		= ias,
222362306a36Sopenharmony_ci		.oas		= oas,
222462306a36Sopenharmony_ci		.coherent_walk	= smmu->features & ARM_SMMU_FEAT_COHERENCY,
222562306a36Sopenharmony_ci		.tlb		= &arm_smmu_flush_ops,
222662306a36Sopenharmony_ci		.iommu_dev	= smmu->dev,
222762306a36Sopenharmony_ci	};
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
223062306a36Sopenharmony_ci	if (!pgtbl_ops)
223162306a36Sopenharmony_ci		return -ENOMEM;
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
223462306a36Sopenharmony_ci	domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
223562306a36Sopenharmony_ci	domain->geometry.force_aperture = true;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg);
223862306a36Sopenharmony_ci	if (ret < 0) {
223962306a36Sopenharmony_ci		free_io_pgtable_ops(pgtbl_ops);
224062306a36Sopenharmony_ci		return ret;
224162306a36Sopenharmony_ci	}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	smmu_domain->pgtbl_ops = pgtbl_ops;
224462306a36Sopenharmony_ci	return 0;
224562306a36Sopenharmony_ci}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_cistatic __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
224862306a36Sopenharmony_ci{
224962306a36Sopenharmony_ci	__le64 *step;
225062306a36Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
225362306a36Sopenharmony_ci		struct arm_smmu_strtab_l1_desc *l1_desc;
225462306a36Sopenharmony_ci		int idx;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci		/* Two-level walk */
225762306a36Sopenharmony_ci		idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
225862306a36Sopenharmony_ci		l1_desc = &cfg->l1_desc[idx];
225962306a36Sopenharmony_ci		idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
226062306a36Sopenharmony_ci		step = &l1_desc->l2ptr[idx];
226162306a36Sopenharmony_ci	} else {
226262306a36Sopenharmony_ci		/* Simple linear lookup */
226362306a36Sopenharmony_ci		step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	return step;
226762306a36Sopenharmony_ci}
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_cistatic void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
227062306a36Sopenharmony_ci{
227162306a36Sopenharmony_ci	int i, j;
227262306a36Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	for (i = 0; i < master->num_streams; ++i) {
227562306a36Sopenharmony_ci		u32 sid = master->streams[i].id;
227662306a36Sopenharmony_ci		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		/* Bridged PCI devices may end up with duplicated IDs */
227962306a36Sopenharmony_ci		for (j = 0; j < i; j++)
228062306a36Sopenharmony_ci			if (master->streams[j].id == sid)
228162306a36Sopenharmony_ci				break;
228262306a36Sopenharmony_ci		if (j < i)
228362306a36Sopenharmony_ci			continue;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci		arm_smmu_write_strtab_ent(master, sid, step);
228662306a36Sopenharmony_ci	}
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_cistatic bool arm_smmu_ats_supported(struct arm_smmu_master *master)
229062306a36Sopenharmony_ci{
229162306a36Sopenharmony_ci	struct device *dev = master->dev;
229262306a36Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
229362306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_ATS))
229662306a36Sopenharmony_ci		return false;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS))
229962306a36Sopenharmony_ci		return false;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev));
230262306a36Sopenharmony_ci}
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_cistatic void arm_smmu_enable_ats(struct arm_smmu_master *master)
230562306a36Sopenharmony_ci{
230662306a36Sopenharmony_ci	size_t stu;
230762306a36Sopenharmony_ci	struct pci_dev *pdev;
230862306a36Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
230962306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	/* Don't enable ATS at the endpoint if it's not enabled in the STE */
231262306a36Sopenharmony_ci	if (!master->ats_enabled)
231362306a36Sopenharmony_ci		return;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	/* Smallest Translation Unit: log2 of the smallest supported granule */
231662306a36Sopenharmony_ci	stu = __ffs(smmu->pgsize_bitmap);
231762306a36Sopenharmony_ci	pdev = to_pci_dev(master->dev);
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	atomic_inc(&smmu_domain->nr_ats_masters);
232062306a36Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0);
232162306a36Sopenharmony_ci	if (pci_enable_ats(pdev, stu))
232262306a36Sopenharmony_ci		dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu);
232362306a36Sopenharmony_ci}
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_cistatic void arm_smmu_disable_ats(struct arm_smmu_master *master)
232662306a36Sopenharmony_ci{
232762306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	if (!master->ats_enabled)
233062306a36Sopenharmony_ci		return;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	pci_disable_ats(to_pci_dev(master->dev));
233362306a36Sopenharmony_ci	/*
233462306a36Sopenharmony_ci	 * Ensure ATS is disabled at the endpoint before we issue the
233562306a36Sopenharmony_ci	 * ATC invalidation via the SMMU.
233662306a36Sopenharmony_ci	 */
233762306a36Sopenharmony_ci	wmb();
233862306a36Sopenharmony_ci	arm_smmu_atc_inv_master(master);
233962306a36Sopenharmony_ci	atomic_dec(&smmu_domain->nr_ats_masters);
234062306a36Sopenharmony_ci}
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_cistatic int arm_smmu_enable_pasid(struct arm_smmu_master *master)
234362306a36Sopenharmony_ci{
234462306a36Sopenharmony_ci	int ret;
234562306a36Sopenharmony_ci	int features;
234662306a36Sopenharmony_ci	int num_pasids;
234762306a36Sopenharmony_ci	struct pci_dev *pdev;
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	if (!dev_is_pci(master->dev))
235062306a36Sopenharmony_ci		return -ENODEV;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	pdev = to_pci_dev(master->dev);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	features = pci_pasid_features(pdev);
235562306a36Sopenharmony_ci	if (features < 0)
235662306a36Sopenharmony_ci		return features;
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	num_pasids = pci_max_pasids(pdev);
235962306a36Sopenharmony_ci	if (num_pasids <= 0)
236062306a36Sopenharmony_ci		return num_pasids;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	ret = pci_enable_pasid(pdev, features);
236362306a36Sopenharmony_ci	if (ret) {
236462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable PASID\n");
236562306a36Sopenharmony_ci		return ret;
236662306a36Sopenharmony_ci	}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	master->ssid_bits = min_t(u8, ilog2(num_pasids),
236962306a36Sopenharmony_ci				  master->smmu->ssid_bits);
237062306a36Sopenharmony_ci	return 0;
237162306a36Sopenharmony_ci}
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_cistatic void arm_smmu_disable_pasid(struct arm_smmu_master *master)
237462306a36Sopenharmony_ci{
237562306a36Sopenharmony_ci	struct pci_dev *pdev;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	if (!dev_is_pci(master->dev))
237862306a36Sopenharmony_ci		return;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	pdev = to_pci_dev(master->dev);
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	if (!pdev->pasid_enabled)
238362306a36Sopenharmony_ci		return;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	master->ssid_bits = 0;
238662306a36Sopenharmony_ci	pci_disable_pasid(pdev);
238762306a36Sopenharmony_ci}
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_cistatic void arm_smmu_detach_dev(struct arm_smmu_master *master)
239062306a36Sopenharmony_ci{
239162306a36Sopenharmony_ci	unsigned long flags;
239262306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	if (!smmu_domain)
239562306a36Sopenharmony_ci		return;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	arm_smmu_disable_ats(master);
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
240062306a36Sopenharmony_ci	list_del(&master->domain_head);
240162306a36Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	master->domain = NULL;
240462306a36Sopenharmony_ci	master->ats_enabled = false;
240562306a36Sopenharmony_ci	arm_smmu_install_ste_for_dev(master);
240662306a36Sopenharmony_ci}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
240962306a36Sopenharmony_ci{
241062306a36Sopenharmony_ci	int ret = 0;
241162306a36Sopenharmony_ci	unsigned long flags;
241262306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
241362306a36Sopenharmony_ci	struct arm_smmu_device *smmu;
241462306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
241562306a36Sopenharmony_ci	struct arm_smmu_master *master;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	if (!fwspec)
241862306a36Sopenharmony_ci		return -ENOENT;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	master = dev_iommu_priv_get(dev);
242162306a36Sopenharmony_ci	smmu = master->smmu;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	/*
242462306a36Sopenharmony_ci	 * Checking that SVA is disabled ensures that this device isn't bound to
242562306a36Sopenharmony_ci	 * any mm, and can be safely detached from its old domain. Bonds cannot
242662306a36Sopenharmony_ci	 * be removed concurrently since we're holding the group mutex.
242762306a36Sopenharmony_ci	 */
242862306a36Sopenharmony_ci	if (arm_smmu_master_sva_enabled(master)) {
242962306a36Sopenharmony_ci		dev_err(dev, "cannot attach - SVA enabled\n");
243062306a36Sopenharmony_ci		return -EBUSY;
243162306a36Sopenharmony_ci	}
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	arm_smmu_detach_dev(master);
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	mutex_lock(&smmu_domain->init_mutex);
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	if (!smmu_domain->smmu) {
243862306a36Sopenharmony_ci		smmu_domain->smmu = smmu;
243962306a36Sopenharmony_ci		ret = arm_smmu_domain_finalise(domain, master);
244062306a36Sopenharmony_ci		if (ret) {
244162306a36Sopenharmony_ci			smmu_domain->smmu = NULL;
244262306a36Sopenharmony_ci			goto out_unlock;
244362306a36Sopenharmony_ci		}
244462306a36Sopenharmony_ci	} else if (smmu_domain->smmu != smmu) {
244562306a36Sopenharmony_ci		ret = -EINVAL;
244662306a36Sopenharmony_ci		goto out_unlock;
244762306a36Sopenharmony_ci	} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 &&
244862306a36Sopenharmony_ci		   master->ssid_bits != smmu_domain->s1_cfg.s1cdmax) {
244962306a36Sopenharmony_ci		ret = -EINVAL;
245062306a36Sopenharmony_ci		goto out_unlock;
245162306a36Sopenharmony_ci	} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 &&
245262306a36Sopenharmony_ci		   smmu_domain->stall_enabled != master->stall_enabled) {
245362306a36Sopenharmony_ci		ret = -EINVAL;
245462306a36Sopenharmony_ci		goto out_unlock;
245562306a36Sopenharmony_ci	}
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	master->domain = smmu_domain;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	/*
246062306a36Sopenharmony_ci	 * The SMMU does not support enabling ATS with bypass. When the STE is
246162306a36Sopenharmony_ci	 * in bypass (STE.Config[2:0] == 0b100), ATS Translation Requests and
246262306a36Sopenharmony_ci	 * Translated transactions are denied as though ATS is disabled for the
246362306a36Sopenharmony_ci	 * stream (STE.EATS == 0b00), causing F_BAD_ATS_TREQ and
246462306a36Sopenharmony_ci	 * F_TRANSL_FORBIDDEN events (IHI0070Ea 5.2 Stream Table Entry).
246562306a36Sopenharmony_ci	 */
246662306a36Sopenharmony_ci	if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS)
246762306a36Sopenharmony_ci		master->ats_enabled = arm_smmu_ats_supported(master);
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	arm_smmu_install_ste_for_dev(master);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
247262306a36Sopenharmony_ci	list_add(&master->domain_head, &smmu_domain->devices);
247362306a36Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	arm_smmu_enable_ats(master);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ciout_unlock:
247862306a36Sopenharmony_ci	mutex_unlock(&smmu_domain->init_mutex);
247962306a36Sopenharmony_ci	return ret;
248062306a36Sopenharmony_ci}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_cistatic int arm_smmu_map_pages(struct iommu_domain *domain, unsigned long iova,
248362306a36Sopenharmony_ci			      phys_addr_t paddr, size_t pgsize, size_t pgcount,
248462306a36Sopenharmony_ci			      int prot, gfp_t gfp, size_t *mapped)
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	if (!ops)
248962306a36Sopenharmony_ci		return -ENODEV;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	return ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot, gfp, mapped);
249262306a36Sopenharmony_ci}
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_cistatic size_t arm_smmu_unmap_pages(struct iommu_domain *domain, unsigned long iova,
249562306a36Sopenharmony_ci				   size_t pgsize, size_t pgcount,
249662306a36Sopenharmony_ci				   struct iommu_iotlb_gather *gather)
249762306a36Sopenharmony_ci{
249862306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
249962306a36Sopenharmony_ci	struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	if (!ops)
250262306a36Sopenharmony_ci		return 0;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	return ops->unmap_pages(ops, iova, pgsize, pgcount, gather);
250562306a36Sopenharmony_ci}
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain)
250862306a36Sopenharmony_ci{
250962306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	if (smmu_domain->smmu)
251262306a36Sopenharmony_ci		arm_smmu_tlb_inv_context(smmu_domain);
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain,
251662306a36Sopenharmony_ci				struct iommu_iotlb_gather *gather)
251762306a36Sopenharmony_ci{
251862306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	if (!gather->pgsize)
252162306a36Sopenharmony_ci		return;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	arm_smmu_tlb_inv_range_domain(gather->start,
252462306a36Sopenharmony_ci				      gather->end - gather->start + 1,
252562306a36Sopenharmony_ci				      gather->pgsize, true, smmu_domain);
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_cistatic phys_addr_t
252962306a36Sopenharmony_ciarm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	if (!ops)
253462306a36Sopenharmony_ci		return 0;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	return ops->iova_to_phys(ops, iova);
253762306a36Sopenharmony_ci}
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_cistatic
254262306a36Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
254362306a36Sopenharmony_ci{
254462306a36Sopenharmony_ci	struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver,
254562306a36Sopenharmony_ci							  fwnode);
254662306a36Sopenharmony_ci	put_device(dev);
254762306a36Sopenharmony_ci	return dev ? dev_get_drvdata(dev) : NULL;
254862306a36Sopenharmony_ci}
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_cistatic bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
255162306a36Sopenharmony_ci{
255262306a36Sopenharmony_ci	unsigned long limit = smmu->strtab_cfg.num_l1_ents;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
255562306a36Sopenharmony_ci		limit *= 1UL << STRTAB_SPLIT;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	return sid < limit;
255862306a36Sopenharmony_ci}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_cistatic int arm_smmu_init_sid_strtab(struct arm_smmu_device *smmu, u32 sid)
256162306a36Sopenharmony_ci{
256262306a36Sopenharmony_ci	/* Check the SIDs are in range of the SMMU and our stream table */
256362306a36Sopenharmony_ci	if (!arm_smmu_sid_in_range(smmu, sid))
256462306a36Sopenharmony_ci		return -ERANGE;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	/* Ensure l2 strtab is initialised */
256762306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
256862306a36Sopenharmony_ci		return arm_smmu_init_l2_strtab(smmu, sid);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	return 0;
257162306a36Sopenharmony_ci}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_cistatic int arm_smmu_insert_master(struct arm_smmu_device *smmu,
257462306a36Sopenharmony_ci				  struct arm_smmu_master *master)
257562306a36Sopenharmony_ci{
257662306a36Sopenharmony_ci	int i;
257762306a36Sopenharmony_ci	int ret = 0;
257862306a36Sopenharmony_ci	struct arm_smmu_stream *new_stream, *cur_stream;
257962306a36Sopenharmony_ci	struct rb_node **new_node, *parent_node = NULL;
258062306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams),
258362306a36Sopenharmony_ci				  GFP_KERNEL);
258462306a36Sopenharmony_ci	if (!master->streams)
258562306a36Sopenharmony_ci		return -ENOMEM;
258662306a36Sopenharmony_ci	master->num_streams = fwspec->num_ids;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	mutex_lock(&smmu->streams_mutex);
258962306a36Sopenharmony_ci	for (i = 0; i < fwspec->num_ids; i++) {
259062306a36Sopenharmony_ci		u32 sid = fwspec->ids[i];
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci		new_stream = &master->streams[i];
259362306a36Sopenharmony_ci		new_stream->id = sid;
259462306a36Sopenharmony_ci		new_stream->master = master;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci		ret = arm_smmu_init_sid_strtab(smmu, sid);
259762306a36Sopenharmony_ci		if (ret)
259862306a36Sopenharmony_ci			break;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci		/* Insert into SID tree */
260162306a36Sopenharmony_ci		new_node = &(smmu->streams.rb_node);
260262306a36Sopenharmony_ci		while (*new_node) {
260362306a36Sopenharmony_ci			cur_stream = rb_entry(*new_node, struct arm_smmu_stream,
260462306a36Sopenharmony_ci					      node);
260562306a36Sopenharmony_ci			parent_node = *new_node;
260662306a36Sopenharmony_ci			if (cur_stream->id > new_stream->id) {
260762306a36Sopenharmony_ci				new_node = &((*new_node)->rb_left);
260862306a36Sopenharmony_ci			} else if (cur_stream->id < new_stream->id) {
260962306a36Sopenharmony_ci				new_node = &((*new_node)->rb_right);
261062306a36Sopenharmony_ci			} else {
261162306a36Sopenharmony_ci				dev_warn(master->dev,
261262306a36Sopenharmony_ci					 "stream %u already in tree\n",
261362306a36Sopenharmony_ci					 cur_stream->id);
261462306a36Sopenharmony_ci				ret = -EINVAL;
261562306a36Sopenharmony_ci				break;
261662306a36Sopenharmony_ci			}
261762306a36Sopenharmony_ci		}
261862306a36Sopenharmony_ci		if (ret)
261962306a36Sopenharmony_ci			break;
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci		rb_link_node(&new_stream->node, parent_node, new_node);
262262306a36Sopenharmony_ci		rb_insert_color(&new_stream->node, &smmu->streams);
262362306a36Sopenharmony_ci	}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	if (ret) {
262662306a36Sopenharmony_ci		for (i--; i >= 0; i--)
262762306a36Sopenharmony_ci			rb_erase(&master->streams[i].node, &smmu->streams);
262862306a36Sopenharmony_ci		kfree(master->streams);
262962306a36Sopenharmony_ci	}
263062306a36Sopenharmony_ci	mutex_unlock(&smmu->streams_mutex);
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	return ret;
263362306a36Sopenharmony_ci}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_cistatic void arm_smmu_remove_master(struct arm_smmu_master *master)
263662306a36Sopenharmony_ci{
263762306a36Sopenharmony_ci	int i;
263862306a36Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
263962306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	if (!smmu || !master->streams)
264262306a36Sopenharmony_ci		return;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	mutex_lock(&smmu->streams_mutex);
264562306a36Sopenharmony_ci	for (i = 0; i < fwspec->num_ids; i++)
264662306a36Sopenharmony_ci		rb_erase(&master->streams[i].node, &smmu->streams);
264762306a36Sopenharmony_ci	mutex_unlock(&smmu->streams_mutex);
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	kfree(master->streams);
265062306a36Sopenharmony_ci}
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev)
265562306a36Sopenharmony_ci{
265662306a36Sopenharmony_ci	int ret;
265762306a36Sopenharmony_ci	struct arm_smmu_device *smmu;
265862306a36Sopenharmony_ci	struct arm_smmu_master *master;
265962306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	if (!fwspec || fwspec->ops != &arm_smmu_ops)
266262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	if (WARN_ON_ONCE(dev_iommu_priv_get(dev)))
266562306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
266862306a36Sopenharmony_ci	if (!smmu)
266962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	master = kzalloc(sizeof(*master), GFP_KERNEL);
267262306a36Sopenharmony_ci	if (!master)
267362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	master->dev = dev;
267662306a36Sopenharmony_ci	master->smmu = smmu;
267762306a36Sopenharmony_ci	INIT_LIST_HEAD(&master->bonds);
267862306a36Sopenharmony_ci	dev_iommu_priv_set(dev, master);
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	ret = arm_smmu_insert_master(smmu, master);
268162306a36Sopenharmony_ci	if (ret)
268262306a36Sopenharmony_ci		goto err_free_master;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
268562306a36Sopenharmony_ci	master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	/*
268862306a36Sopenharmony_ci	 * Note that PASID must be enabled before, and disabled after ATS:
268962306a36Sopenharmony_ci	 * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register
269062306a36Sopenharmony_ci	 *
269162306a36Sopenharmony_ci	 *   Behavior is undefined if this bit is Set and the value of the PASID
269262306a36Sopenharmony_ci	 *   Enable, Execute Requested Enable, or Privileged Mode Requested bits
269362306a36Sopenharmony_ci	 *   are changed.
269462306a36Sopenharmony_ci	 */
269562306a36Sopenharmony_ci	arm_smmu_enable_pasid(master);
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
269862306a36Sopenharmony_ci		master->ssid_bits = min_t(u8, master->ssid_bits,
269962306a36Sopenharmony_ci					  CTXDESC_LINEAR_CDMAX);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	if ((smmu->features & ARM_SMMU_FEAT_STALLS &&
270262306a36Sopenharmony_ci	     device_property_read_bool(dev, "dma-can-stall")) ||
270362306a36Sopenharmony_ci	    smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
270462306a36Sopenharmony_ci		master->stall_enabled = true;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	return &smmu->iommu;
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_cierr_free_master:
270962306a36Sopenharmony_ci	kfree(master);
271062306a36Sopenharmony_ci	dev_iommu_priv_set(dev, NULL);
271162306a36Sopenharmony_ci	return ERR_PTR(ret);
271262306a36Sopenharmony_ci}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev)
271562306a36Sopenharmony_ci{
271662306a36Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	if (WARN_ON(arm_smmu_master_sva_enabled(master)))
271962306a36Sopenharmony_ci		iopf_queue_remove_device(master->smmu->evtq.iopf, dev);
272062306a36Sopenharmony_ci	arm_smmu_detach_dev(master);
272162306a36Sopenharmony_ci	arm_smmu_disable_pasid(master);
272262306a36Sopenharmony_ci	arm_smmu_remove_master(master);
272362306a36Sopenharmony_ci	kfree(master);
272462306a36Sopenharmony_ci}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev)
272762306a36Sopenharmony_ci{
272862306a36Sopenharmony_ci	struct iommu_group *group;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	/*
273162306a36Sopenharmony_ci	 * We don't support devices sharing stream IDs other than PCI RID
273262306a36Sopenharmony_ci	 * aliases, since the necessary ID-to-device lookup becomes rather
273362306a36Sopenharmony_ci	 * impractical given a potential sparse 32-bit stream ID space.
273462306a36Sopenharmony_ci	 */
273562306a36Sopenharmony_ci	if (dev_is_pci(dev))
273662306a36Sopenharmony_ci		group = pci_device_group(dev);
273762306a36Sopenharmony_ci	else
273862306a36Sopenharmony_ci		group = generic_device_group(dev);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	return group;
274162306a36Sopenharmony_ci}
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_cistatic int arm_smmu_enable_nesting(struct iommu_domain *domain)
274462306a36Sopenharmony_ci{
274562306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
274662306a36Sopenharmony_ci	int ret = 0;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	mutex_lock(&smmu_domain->init_mutex);
274962306a36Sopenharmony_ci	if (smmu_domain->smmu)
275062306a36Sopenharmony_ci		ret = -EPERM;
275162306a36Sopenharmony_ci	else
275262306a36Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
275362306a36Sopenharmony_ci	mutex_unlock(&smmu_domain->init_mutex);
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	return ret;
275662306a36Sopenharmony_ci}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
275962306a36Sopenharmony_ci{
276062306a36Sopenharmony_ci	return iommu_fwspec_add_ids(dev, args->args, 1);
276162306a36Sopenharmony_ci}
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev,
276462306a36Sopenharmony_ci				      struct list_head *head)
276562306a36Sopenharmony_ci{
276662306a36Sopenharmony_ci	struct iommu_resv_region *region;
276762306a36Sopenharmony_ci	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
277062306a36Sopenharmony_ci					 prot, IOMMU_RESV_SW_MSI, GFP_KERNEL);
277162306a36Sopenharmony_ci	if (!region)
277262306a36Sopenharmony_ci		return;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	list_add_tail(&region->list, head);
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	iommu_dma_get_resv_regions(dev, head);
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_cistatic int arm_smmu_dev_enable_feature(struct device *dev,
278062306a36Sopenharmony_ci				       enum iommu_dev_features feat)
278162306a36Sopenharmony_ci{
278262306a36Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	if (!master)
278562306a36Sopenharmony_ci		return -ENODEV;
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	switch (feat) {
278862306a36Sopenharmony_ci	case IOMMU_DEV_FEAT_IOPF:
278962306a36Sopenharmony_ci		if (!arm_smmu_master_iopf_supported(master))
279062306a36Sopenharmony_ci			return -EINVAL;
279162306a36Sopenharmony_ci		if (master->iopf_enabled)
279262306a36Sopenharmony_ci			return -EBUSY;
279362306a36Sopenharmony_ci		master->iopf_enabled = true;
279462306a36Sopenharmony_ci		return 0;
279562306a36Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
279662306a36Sopenharmony_ci		if (!arm_smmu_master_sva_supported(master))
279762306a36Sopenharmony_ci			return -EINVAL;
279862306a36Sopenharmony_ci		if (arm_smmu_master_sva_enabled(master))
279962306a36Sopenharmony_ci			return -EBUSY;
280062306a36Sopenharmony_ci		return arm_smmu_master_enable_sva(master);
280162306a36Sopenharmony_ci	default:
280262306a36Sopenharmony_ci		return -EINVAL;
280362306a36Sopenharmony_ci	}
280462306a36Sopenharmony_ci}
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_cistatic int arm_smmu_dev_disable_feature(struct device *dev,
280762306a36Sopenharmony_ci					enum iommu_dev_features feat)
280862306a36Sopenharmony_ci{
280962306a36Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	if (!master)
281262306a36Sopenharmony_ci		return -EINVAL;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	switch (feat) {
281562306a36Sopenharmony_ci	case IOMMU_DEV_FEAT_IOPF:
281662306a36Sopenharmony_ci		if (!master->iopf_enabled)
281762306a36Sopenharmony_ci			return -EINVAL;
281862306a36Sopenharmony_ci		if (master->sva_enabled)
281962306a36Sopenharmony_ci			return -EBUSY;
282062306a36Sopenharmony_ci		master->iopf_enabled = false;
282162306a36Sopenharmony_ci		return 0;
282262306a36Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
282362306a36Sopenharmony_ci		if (!arm_smmu_master_sva_enabled(master))
282462306a36Sopenharmony_ci			return -EINVAL;
282562306a36Sopenharmony_ci		return arm_smmu_master_disable_sva(master);
282662306a36Sopenharmony_ci	default:
282762306a36Sopenharmony_ci		return -EINVAL;
282862306a36Sopenharmony_ci	}
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci/*
283262306a36Sopenharmony_ci * HiSilicon PCIe tune and trace device can be used to trace TLP headers on the
283362306a36Sopenharmony_ci * PCIe link and save the data to memory by DMA. The hardware is restricted to
283462306a36Sopenharmony_ci * use identity mapping only.
283562306a36Sopenharmony_ci */
283662306a36Sopenharmony_ci#define IS_HISI_PTT_DEVICE(pdev)	((pdev)->vendor == PCI_VENDOR_ID_HUAWEI && \
283762306a36Sopenharmony_ci					 (pdev)->device == 0xa12e)
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_cistatic int arm_smmu_def_domain_type(struct device *dev)
284062306a36Sopenharmony_ci{
284162306a36Sopenharmony_ci	if (dev_is_pci(dev)) {
284262306a36Sopenharmony_ci		struct pci_dev *pdev = to_pci_dev(dev);
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci		if (IS_HISI_PTT_DEVICE(pdev))
284562306a36Sopenharmony_ci			return IOMMU_DOMAIN_IDENTITY;
284662306a36Sopenharmony_ci	}
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	return 0;
284962306a36Sopenharmony_ci}
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_cistatic void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid)
285262306a36Sopenharmony_ci{
285362306a36Sopenharmony_ci	struct iommu_domain *domain;
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci	domain = iommu_get_domain_for_dev_pasid(dev, pasid, IOMMU_DOMAIN_SVA);
285662306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(domain)) || !domain)
285762306a36Sopenharmony_ci		return;
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	arm_smmu_sva_remove_dev_pasid(domain, dev, pasid);
286062306a36Sopenharmony_ci}
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = {
286362306a36Sopenharmony_ci	.capable		= arm_smmu_capable,
286462306a36Sopenharmony_ci	.domain_alloc		= arm_smmu_domain_alloc,
286562306a36Sopenharmony_ci	.probe_device		= arm_smmu_probe_device,
286662306a36Sopenharmony_ci	.release_device		= arm_smmu_release_device,
286762306a36Sopenharmony_ci	.device_group		= arm_smmu_device_group,
286862306a36Sopenharmony_ci	.of_xlate		= arm_smmu_of_xlate,
286962306a36Sopenharmony_ci	.get_resv_regions	= arm_smmu_get_resv_regions,
287062306a36Sopenharmony_ci	.remove_dev_pasid	= arm_smmu_remove_dev_pasid,
287162306a36Sopenharmony_ci	.dev_enable_feat	= arm_smmu_dev_enable_feature,
287262306a36Sopenharmony_ci	.dev_disable_feat	= arm_smmu_dev_disable_feature,
287362306a36Sopenharmony_ci	.page_response		= arm_smmu_page_response,
287462306a36Sopenharmony_ci	.def_domain_type	= arm_smmu_def_domain_type,
287562306a36Sopenharmony_ci	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
287662306a36Sopenharmony_ci	.owner			= THIS_MODULE,
287762306a36Sopenharmony_ci	.default_domain_ops = &(const struct iommu_domain_ops) {
287862306a36Sopenharmony_ci		.attach_dev		= arm_smmu_attach_dev,
287962306a36Sopenharmony_ci		.map_pages		= arm_smmu_map_pages,
288062306a36Sopenharmony_ci		.unmap_pages		= arm_smmu_unmap_pages,
288162306a36Sopenharmony_ci		.flush_iotlb_all	= arm_smmu_flush_iotlb_all,
288262306a36Sopenharmony_ci		.iotlb_sync		= arm_smmu_iotlb_sync,
288362306a36Sopenharmony_ci		.iova_to_phys		= arm_smmu_iova_to_phys,
288462306a36Sopenharmony_ci		.enable_nesting		= arm_smmu_enable_nesting,
288562306a36Sopenharmony_ci		.free			= arm_smmu_domain_free,
288662306a36Sopenharmony_ci	}
288762306a36Sopenharmony_ci};
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci/* Probing and initialisation functions */
289062306a36Sopenharmony_cistatic int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
289162306a36Sopenharmony_ci				   struct arm_smmu_queue *q,
289262306a36Sopenharmony_ci				   void __iomem *page,
289362306a36Sopenharmony_ci				   unsigned long prod_off,
289462306a36Sopenharmony_ci				   unsigned long cons_off,
289562306a36Sopenharmony_ci				   size_t dwords, const char *name)
289662306a36Sopenharmony_ci{
289762306a36Sopenharmony_ci	size_t qsz;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	do {
290062306a36Sopenharmony_ci		qsz = ((1 << q->llq.max_n_shift) * dwords) << 3;
290162306a36Sopenharmony_ci		q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma,
290262306a36Sopenharmony_ci					      GFP_KERNEL);
290362306a36Sopenharmony_ci		if (q->base || qsz < PAGE_SIZE)
290462306a36Sopenharmony_ci			break;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci		q->llq.max_n_shift--;
290762306a36Sopenharmony_ci	} while (1);
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	if (!q->base) {
291062306a36Sopenharmony_ci		dev_err(smmu->dev,
291162306a36Sopenharmony_ci			"failed to allocate queue (0x%zx bytes) for %s\n",
291262306a36Sopenharmony_ci			qsz, name);
291362306a36Sopenharmony_ci		return -ENOMEM;
291462306a36Sopenharmony_ci	}
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	if (!WARN_ON(q->base_dma & (qsz - 1))) {
291762306a36Sopenharmony_ci		dev_info(smmu->dev, "allocated %u entries for %s\n",
291862306a36Sopenharmony_ci			 1 << q->llq.max_n_shift, name);
291962306a36Sopenharmony_ci	}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	q->prod_reg	= page + prod_off;
292262306a36Sopenharmony_ci	q->cons_reg	= page + cons_off;
292362306a36Sopenharmony_ci	q->ent_dwords	= dwords;
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	q->q_base  = Q_BASE_RWA;
292662306a36Sopenharmony_ci	q->q_base |= q->base_dma & Q_BASE_ADDR_MASK;
292762306a36Sopenharmony_ci	q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->llq.max_n_shift);
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	q->llq.prod = q->llq.cons = 0;
293062306a36Sopenharmony_ci	return 0;
293162306a36Sopenharmony_ci}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_cistatic int arm_smmu_cmdq_init(struct arm_smmu_device *smmu)
293462306a36Sopenharmony_ci{
293562306a36Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
293662306a36Sopenharmony_ci	unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	atomic_set(&cmdq->owner_prod, 0);
293962306a36Sopenharmony_ci	atomic_set(&cmdq->lock, 0);
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	cmdq->valid_map = (atomic_long_t *)devm_bitmap_zalloc(smmu->dev, nents,
294262306a36Sopenharmony_ci							      GFP_KERNEL);
294362306a36Sopenharmony_ci	if (!cmdq->valid_map)
294462306a36Sopenharmony_ci		return -ENOMEM;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	return 0;
294762306a36Sopenharmony_ci}
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_cistatic int arm_smmu_init_queues(struct arm_smmu_device *smmu)
295062306a36Sopenharmony_ci{
295162306a36Sopenharmony_ci	int ret;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* cmdq */
295462306a36Sopenharmony_ci	ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, smmu->base,
295562306a36Sopenharmony_ci				      ARM_SMMU_CMDQ_PROD, ARM_SMMU_CMDQ_CONS,
295662306a36Sopenharmony_ci				      CMDQ_ENT_DWORDS, "cmdq");
295762306a36Sopenharmony_ci	if (ret)
295862306a36Sopenharmony_ci		return ret;
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	ret = arm_smmu_cmdq_init(smmu);
296162306a36Sopenharmony_ci	if (ret)
296262306a36Sopenharmony_ci		return ret;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	/* evtq */
296562306a36Sopenharmony_ci	ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, smmu->page1,
296662306a36Sopenharmony_ci				      ARM_SMMU_EVTQ_PROD, ARM_SMMU_EVTQ_CONS,
296762306a36Sopenharmony_ci				      EVTQ_ENT_DWORDS, "evtq");
296862306a36Sopenharmony_ci	if (ret)
296962306a36Sopenharmony_ci		return ret;
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	if ((smmu->features & ARM_SMMU_FEAT_SVA) &&
297262306a36Sopenharmony_ci	    (smmu->features & ARM_SMMU_FEAT_STALLS)) {
297362306a36Sopenharmony_ci		smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev));
297462306a36Sopenharmony_ci		if (!smmu->evtq.iopf)
297562306a36Sopenharmony_ci			return -ENOMEM;
297662306a36Sopenharmony_ci	}
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	/* priq */
297962306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_PRI))
298062306a36Sopenharmony_ci		return 0;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	return arm_smmu_init_one_queue(smmu, &smmu->priq.q, smmu->page1,
298362306a36Sopenharmony_ci				       ARM_SMMU_PRIQ_PROD, ARM_SMMU_PRIQ_CONS,
298462306a36Sopenharmony_ci				       PRIQ_ENT_DWORDS, "priq");
298562306a36Sopenharmony_ci}
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_cistatic int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
298862306a36Sopenharmony_ci{
298962306a36Sopenharmony_ci	unsigned int i;
299062306a36Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
299162306a36Sopenharmony_ci	void *strtab = smmu->strtab_cfg.strtab;
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	cfg->l1_desc = devm_kcalloc(smmu->dev, cfg->num_l1_ents,
299462306a36Sopenharmony_ci				    sizeof(*cfg->l1_desc), GFP_KERNEL);
299562306a36Sopenharmony_ci	if (!cfg->l1_desc)
299662306a36Sopenharmony_ci		return -ENOMEM;
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci	for (i = 0; i < cfg->num_l1_ents; ++i) {
299962306a36Sopenharmony_ci		arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
300062306a36Sopenharmony_ci		strtab += STRTAB_L1_DESC_DWORDS << 3;
300162306a36Sopenharmony_ci	}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	return 0;
300462306a36Sopenharmony_ci}
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_cistatic int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
300762306a36Sopenharmony_ci{
300862306a36Sopenharmony_ci	void *strtab;
300962306a36Sopenharmony_ci	u64 reg;
301062306a36Sopenharmony_ci	u32 size, l1size;
301162306a36Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	/* Calculate the L1 size, capped to the SIDSIZE. */
301462306a36Sopenharmony_ci	size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
301562306a36Sopenharmony_ci	size = min(size, smmu->sid_bits - STRTAB_SPLIT);
301662306a36Sopenharmony_ci	cfg->num_l1_ents = 1 << size;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	size += STRTAB_SPLIT;
301962306a36Sopenharmony_ci	if (size < smmu->sid_bits)
302062306a36Sopenharmony_ci		dev_warn(smmu->dev,
302162306a36Sopenharmony_ci			 "2-level strtab only covers %u/%u bits of SID\n",
302262306a36Sopenharmony_ci			 size, smmu->sid_bits);
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
302562306a36Sopenharmony_ci	strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma,
302662306a36Sopenharmony_ci				     GFP_KERNEL);
302762306a36Sopenharmony_ci	if (!strtab) {
302862306a36Sopenharmony_ci		dev_err(smmu->dev,
302962306a36Sopenharmony_ci			"failed to allocate l1 stream table (%u bytes)\n",
303062306a36Sopenharmony_ci			l1size);
303162306a36Sopenharmony_ci		return -ENOMEM;
303262306a36Sopenharmony_ci	}
303362306a36Sopenharmony_ci	cfg->strtab = strtab;
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	/* Configure strtab_base_cfg for 2 levels */
303662306a36Sopenharmony_ci	reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL);
303762306a36Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size);
303862306a36Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT);
303962306a36Sopenharmony_ci	cfg->strtab_base_cfg = reg;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	return arm_smmu_init_l1_strtab(smmu);
304262306a36Sopenharmony_ci}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_cistatic int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
304562306a36Sopenharmony_ci{
304662306a36Sopenharmony_ci	void *strtab;
304762306a36Sopenharmony_ci	u64 reg;
304862306a36Sopenharmony_ci	u32 size;
304962306a36Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
305062306a36Sopenharmony_ci
305162306a36Sopenharmony_ci	size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
305262306a36Sopenharmony_ci	strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma,
305362306a36Sopenharmony_ci				     GFP_KERNEL);
305462306a36Sopenharmony_ci	if (!strtab) {
305562306a36Sopenharmony_ci		dev_err(smmu->dev,
305662306a36Sopenharmony_ci			"failed to allocate linear stream table (%u bytes)\n",
305762306a36Sopenharmony_ci			size);
305862306a36Sopenharmony_ci		return -ENOMEM;
305962306a36Sopenharmony_ci	}
306062306a36Sopenharmony_ci	cfg->strtab = strtab;
306162306a36Sopenharmony_ci	cfg->num_l1_ents = 1 << smmu->sid_bits;
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci	/* Configure strtab_base_cfg for a linear table covering all SIDs */
306462306a36Sopenharmony_ci	reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR);
306562306a36Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits);
306662306a36Sopenharmony_ci	cfg->strtab_base_cfg = reg;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents, false);
306962306a36Sopenharmony_ci	return 0;
307062306a36Sopenharmony_ci}
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_cistatic int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
307362306a36Sopenharmony_ci{
307462306a36Sopenharmony_ci	u64 reg;
307562306a36Sopenharmony_ci	int ret;
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
307862306a36Sopenharmony_ci		ret = arm_smmu_init_strtab_2lvl(smmu);
307962306a36Sopenharmony_ci	else
308062306a36Sopenharmony_ci		ret = arm_smmu_init_strtab_linear(smmu);
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	if (ret)
308362306a36Sopenharmony_ci		return ret;
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	/* Set the strtab base address */
308662306a36Sopenharmony_ci	reg  = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK;
308762306a36Sopenharmony_ci	reg |= STRTAB_BASE_RA;
308862306a36Sopenharmony_ci	smmu->strtab_cfg.strtab_base = reg;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	ida_init(&smmu->vmid_map);
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	return 0;
309362306a36Sopenharmony_ci}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_cistatic int arm_smmu_init_structures(struct arm_smmu_device *smmu)
309662306a36Sopenharmony_ci{
309762306a36Sopenharmony_ci	int ret;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	mutex_init(&smmu->streams_mutex);
310062306a36Sopenharmony_ci	smmu->streams = RB_ROOT;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	ret = arm_smmu_init_queues(smmu);
310362306a36Sopenharmony_ci	if (ret)
310462306a36Sopenharmony_ci		return ret;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	return arm_smmu_init_strtab(smmu);
310762306a36Sopenharmony_ci}
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_cistatic int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
311062306a36Sopenharmony_ci				   unsigned int reg_off, unsigned int ack_off)
311162306a36Sopenharmony_ci{
311262306a36Sopenharmony_ci	u32 reg;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	writel_relaxed(val, smmu->base + reg_off);
311562306a36Sopenharmony_ci	return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
311662306a36Sopenharmony_ci					  1, ARM_SMMU_POLL_TIMEOUT_US);
311762306a36Sopenharmony_ci}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci/* GBPA is "special" */
312062306a36Sopenharmony_cistatic int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
312162306a36Sopenharmony_ci{
312262306a36Sopenharmony_ci	int ret;
312362306a36Sopenharmony_ci	u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
312662306a36Sopenharmony_ci					 1, ARM_SMMU_POLL_TIMEOUT_US);
312762306a36Sopenharmony_ci	if (ret)
312862306a36Sopenharmony_ci		return ret;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	reg &= ~clr;
313162306a36Sopenharmony_ci	reg |= set;
313262306a36Sopenharmony_ci	writel_relaxed(reg | GBPA_UPDATE, gbpa);
313362306a36Sopenharmony_ci	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
313462306a36Sopenharmony_ci					 1, ARM_SMMU_POLL_TIMEOUT_US);
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	if (ret)
313762306a36Sopenharmony_ci		dev_err(smmu->dev, "GBPA not responding to update\n");
313862306a36Sopenharmony_ci	return ret;
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_cistatic void arm_smmu_free_msis(void *data)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	struct device *dev = data;
314462306a36Sopenharmony_ci	platform_msi_domain_free_irqs(dev);
314562306a36Sopenharmony_ci}
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_cistatic void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
314862306a36Sopenharmony_ci{
314962306a36Sopenharmony_ci	phys_addr_t doorbell;
315062306a36Sopenharmony_ci	struct device *dev = msi_desc_to_dev(desc);
315162306a36Sopenharmony_ci	struct arm_smmu_device *smmu = dev_get_drvdata(dev);
315262306a36Sopenharmony_ci	phys_addr_t *cfg = arm_smmu_msi_cfg[desc->msi_index];
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
315562306a36Sopenharmony_ci	doorbell &= MSI_CFG0_ADDR_MASK;
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	writeq_relaxed(doorbell, smmu->base + cfg[0]);
315862306a36Sopenharmony_ci	writel_relaxed(msg->data, smmu->base + cfg[1]);
315962306a36Sopenharmony_ci	writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
316062306a36Sopenharmony_ci}
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_cistatic void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
316362306a36Sopenharmony_ci{
316462306a36Sopenharmony_ci	int ret, nvec = ARM_SMMU_MAX_MSIS;
316562306a36Sopenharmony_ci	struct device *dev = smmu->dev;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	/* Clear the MSI address regs */
316862306a36Sopenharmony_ci	writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
316962306a36Sopenharmony_ci	writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
317262306a36Sopenharmony_ci		writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
317362306a36Sopenharmony_ci	else
317462306a36Sopenharmony_ci		nvec--;
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_MSI))
317762306a36Sopenharmony_ci		return;
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	if (!dev->msi.domain) {
318062306a36Sopenharmony_ci		dev_info(smmu->dev, "msi_domain absent - falling back to wired irqs\n");
318162306a36Sopenharmony_ci		return;
318262306a36Sopenharmony_ci	}
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
318562306a36Sopenharmony_ci	ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
318662306a36Sopenharmony_ci	if (ret) {
318762306a36Sopenharmony_ci		dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n");
318862306a36Sopenharmony_ci		return;
318962306a36Sopenharmony_ci	}
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	smmu->evtq.q.irq = msi_get_virq(dev, EVTQ_MSI_INDEX);
319262306a36Sopenharmony_ci	smmu->gerr_irq = msi_get_virq(dev, GERROR_MSI_INDEX);
319362306a36Sopenharmony_ci	smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX);
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	/* Add callback to free MSIs on teardown */
319662306a36Sopenharmony_ci	devm_add_action(dev, arm_smmu_free_msis, dev);
319762306a36Sopenharmony_ci}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_cistatic void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
320062306a36Sopenharmony_ci{
320162306a36Sopenharmony_ci	int irq, ret;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	arm_smmu_setup_msis(smmu);
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	/* Request interrupt lines */
320662306a36Sopenharmony_ci	irq = smmu->evtq.q.irq;
320762306a36Sopenharmony_ci	if (irq) {
320862306a36Sopenharmony_ci		ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
320962306a36Sopenharmony_ci						arm_smmu_evtq_thread,
321062306a36Sopenharmony_ci						IRQF_ONESHOT,
321162306a36Sopenharmony_ci						"arm-smmu-v3-evtq", smmu);
321262306a36Sopenharmony_ci		if (ret < 0)
321362306a36Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable evtq irq\n");
321462306a36Sopenharmony_ci	} else {
321562306a36Sopenharmony_ci		dev_warn(smmu->dev, "no evtq irq - events will not be reported!\n");
321662306a36Sopenharmony_ci	}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	irq = smmu->gerr_irq;
321962306a36Sopenharmony_ci	if (irq) {
322062306a36Sopenharmony_ci		ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
322162306a36Sopenharmony_ci				       0, "arm-smmu-v3-gerror", smmu);
322262306a36Sopenharmony_ci		if (ret < 0)
322362306a36Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable gerror irq\n");
322462306a36Sopenharmony_ci	} else {
322562306a36Sopenharmony_ci		dev_warn(smmu->dev, "no gerr irq - errors will not be reported!\n");
322662306a36Sopenharmony_ci	}
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI) {
322962306a36Sopenharmony_ci		irq = smmu->priq.q.irq;
323062306a36Sopenharmony_ci		if (irq) {
323162306a36Sopenharmony_ci			ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
323262306a36Sopenharmony_ci							arm_smmu_priq_thread,
323362306a36Sopenharmony_ci							IRQF_ONESHOT,
323462306a36Sopenharmony_ci							"arm-smmu-v3-priq",
323562306a36Sopenharmony_ci							smmu);
323662306a36Sopenharmony_ci			if (ret < 0)
323762306a36Sopenharmony_ci				dev_warn(smmu->dev,
323862306a36Sopenharmony_ci					 "failed to enable priq irq\n");
323962306a36Sopenharmony_ci		} else {
324062306a36Sopenharmony_ci			dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
324162306a36Sopenharmony_ci		}
324262306a36Sopenharmony_ci	}
324362306a36Sopenharmony_ci}
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_cistatic int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
324662306a36Sopenharmony_ci{
324762306a36Sopenharmony_ci	int ret, irq;
324862306a36Sopenharmony_ci	u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	/* Disable IRQs first */
325162306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
325262306a36Sopenharmony_ci				      ARM_SMMU_IRQ_CTRLACK);
325362306a36Sopenharmony_ci	if (ret) {
325462306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to disable irqs\n");
325562306a36Sopenharmony_ci		return ret;
325662306a36Sopenharmony_ci	}
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	irq = smmu->combined_irq;
325962306a36Sopenharmony_ci	if (irq) {
326062306a36Sopenharmony_ci		/*
326162306a36Sopenharmony_ci		 * Cavium ThunderX2 implementation doesn't support unique irq
326262306a36Sopenharmony_ci		 * lines. Use a single irq line for all the SMMUv3 interrupts.
326362306a36Sopenharmony_ci		 */
326462306a36Sopenharmony_ci		ret = devm_request_threaded_irq(smmu->dev, irq,
326562306a36Sopenharmony_ci					arm_smmu_combined_irq_handler,
326662306a36Sopenharmony_ci					arm_smmu_combined_irq_thread,
326762306a36Sopenharmony_ci					IRQF_ONESHOT,
326862306a36Sopenharmony_ci					"arm-smmu-v3-combined-irq", smmu);
326962306a36Sopenharmony_ci		if (ret < 0)
327062306a36Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable combined irq\n");
327162306a36Sopenharmony_ci	} else
327262306a36Sopenharmony_ci		arm_smmu_setup_unique_irqs(smmu);
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
327562306a36Sopenharmony_ci		irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	/* Enable interrupt generation on the SMMU */
327862306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
327962306a36Sopenharmony_ci				      ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
328062306a36Sopenharmony_ci	if (ret)
328162306a36Sopenharmony_ci		dev_warn(smmu->dev, "failed to enable irqs\n");
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	return 0;
328462306a36Sopenharmony_ci}
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu)
328762306a36Sopenharmony_ci{
328862306a36Sopenharmony_ci	int ret;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
329162306a36Sopenharmony_ci	if (ret)
329262306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to clear cr0\n");
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	return ret;
329562306a36Sopenharmony_ci}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_cistatic int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
329862306a36Sopenharmony_ci{
329962306a36Sopenharmony_ci	int ret;
330062306a36Sopenharmony_ci	u32 reg, enables;
330162306a36Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	/* Clear CR0 and sync (disables SMMU and queue processing) */
330462306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
330562306a36Sopenharmony_ci	if (reg & CR0_SMMUEN) {
330662306a36Sopenharmony_ci		dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
330762306a36Sopenharmony_ci		WARN_ON(is_kdump_kernel() && !disable_bypass);
330862306a36Sopenharmony_ci		arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
330962306a36Sopenharmony_ci	}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	ret = arm_smmu_device_disable(smmu);
331262306a36Sopenharmony_ci	if (ret)
331362306a36Sopenharmony_ci		return ret;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	/* CR1 (table and queue memory attributes) */
331662306a36Sopenharmony_ci	reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) |
331762306a36Sopenharmony_ci	      FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) |
331862306a36Sopenharmony_ci	      FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) |
331962306a36Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) |
332062306a36Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) |
332162306a36Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB);
332262306a36Sopenharmony_ci	writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ci	/* CR2 (random crap) */
332562306a36Sopenharmony_ci	reg = CR2_PTM | CR2_RECINVSID;
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_E2H)
332862306a36Sopenharmony_ci		reg |= CR2_E2H;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	/* Stream table */
333362306a36Sopenharmony_ci	writeq_relaxed(smmu->strtab_cfg.strtab_base,
333462306a36Sopenharmony_ci		       smmu->base + ARM_SMMU_STRTAB_BASE);
333562306a36Sopenharmony_ci	writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
333662306a36Sopenharmony_ci		       smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	/* Command queue */
333962306a36Sopenharmony_ci	writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
334062306a36Sopenharmony_ci	writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
334162306a36Sopenharmony_ci	writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	enables = CR0_CMDQEN;
334462306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
334562306a36Sopenharmony_ci				      ARM_SMMU_CR0ACK);
334662306a36Sopenharmony_ci	if (ret) {
334762306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to enable command queue\n");
334862306a36Sopenharmony_ci		return ret;
334962306a36Sopenharmony_ci	}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	/* Invalidate any cached configuration */
335262306a36Sopenharmony_ci	cmd.opcode = CMDQ_OP_CFGI_ALL;
335362306a36Sopenharmony_ci	arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci	/* Invalidate any stale TLB entries */
335662306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_HYP) {
335762306a36Sopenharmony_ci		cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
335862306a36Sopenharmony_ci		arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
335962306a36Sopenharmony_ci	}
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
336262306a36Sopenharmony_ci	arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	/* Event queue */
336562306a36Sopenharmony_ci	writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
336662306a36Sopenharmony_ci	writel_relaxed(smmu->evtq.q.llq.prod, smmu->page1 + ARM_SMMU_EVTQ_PROD);
336762306a36Sopenharmony_ci	writel_relaxed(smmu->evtq.q.llq.cons, smmu->page1 + ARM_SMMU_EVTQ_CONS);
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_ci	enables |= CR0_EVTQEN;
337062306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
337162306a36Sopenharmony_ci				      ARM_SMMU_CR0ACK);
337262306a36Sopenharmony_ci	if (ret) {
337362306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to enable event queue\n");
337462306a36Sopenharmony_ci		return ret;
337562306a36Sopenharmony_ci	}
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	/* PRI queue */
337862306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI) {
337962306a36Sopenharmony_ci		writeq_relaxed(smmu->priq.q.q_base,
338062306a36Sopenharmony_ci			       smmu->base + ARM_SMMU_PRIQ_BASE);
338162306a36Sopenharmony_ci		writel_relaxed(smmu->priq.q.llq.prod,
338262306a36Sopenharmony_ci			       smmu->page1 + ARM_SMMU_PRIQ_PROD);
338362306a36Sopenharmony_ci		writel_relaxed(smmu->priq.q.llq.cons,
338462306a36Sopenharmony_ci			       smmu->page1 + ARM_SMMU_PRIQ_CONS);
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci		enables |= CR0_PRIQEN;
338762306a36Sopenharmony_ci		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
338862306a36Sopenharmony_ci					      ARM_SMMU_CR0ACK);
338962306a36Sopenharmony_ci		if (ret) {
339062306a36Sopenharmony_ci			dev_err(smmu->dev, "failed to enable PRI queue\n");
339162306a36Sopenharmony_ci			return ret;
339262306a36Sopenharmony_ci		}
339362306a36Sopenharmony_ci	}
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_ATS) {
339662306a36Sopenharmony_ci		enables |= CR0_ATSCHK;
339762306a36Sopenharmony_ci		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
339862306a36Sopenharmony_ci					      ARM_SMMU_CR0ACK);
339962306a36Sopenharmony_ci		if (ret) {
340062306a36Sopenharmony_ci			dev_err(smmu->dev, "failed to enable ATS check\n");
340162306a36Sopenharmony_ci			return ret;
340262306a36Sopenharmony_ci		}
340362306a36Sopenharmony_ci	}
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	ret = arm_smmu_setup_irqs(smmu);
340662306a36Sopenharmony_ci	if (ret) {
340762306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to setup irqs\n");
340862306a36Sopenharmony_ci		return ret;
340962306a36Sopenharmony_ci	}
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	if (is_kdump_kernel())
341262306a36Sopenharmony_ci		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	/* Enable the SMMU interface, or ensure bypass */
341562306a36Sopenharmony_ci	if (!bypass || disable_bypass) {
341662306a36Sopenharmony_ci		enables |= CR0_SMMUEN;
341762306a36Sopenharmony_ci	} else {
341862306a36Sopenharmony_ci		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
341962306a36Sopenharmony_ci		if (ret)
342062306a36Sopenharmony_ci			return ret;
342162306a36Sopenharmony_ci	}
342262306a36Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
342362306a36Sopenharmony_ci				      ARM_SMMU_CR0ACK);
342462306a36Sopenharmony_ci	if (ret) {
342562306a36Sopenharmony_ci		dev_err(smmu->dev, "failed to enable SMMU interface\n");
342662306a36Sopenharmony_ci		return ret;
342762306a36Sopenharmony_ci	}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	return 0;
343062306a36Sopenharmony_ci}
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_ci#define IIDR_IMPLEMENTER_ARM		0x43b
343362306a36Sopenharmony_ci#define IIDR_PRODUCTID_ARM_MMU_600	0x483
343462306a36Sopenharmony_ci#define IIDR_PRODUCTID_ARM_MMU_700	0x487
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_cistatic void arm_smmu_device_iidr_probe(struct arm_smmu_device *smmu)
343762306a36Sopenharmony_ci{
343862306a36Sopenharmony_ci	u32 reg;
343962306a36Sopenharmony_ci	unsigned int implementer, productid, variant, revision;
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IIDR);
344262306a36Sopenharmony_ci	implementer = FIELD_GET(IIDR_IMPLEMENTER, reg);
344362306a36Sopenharmony_ci	productid = FIELD_GET(IIDR_PRODUCTID, reg);
344462306a36Sopenharmony_ci	variant = FIELD_GET(IIDR_VARIANT, reg);
344562306a36Sopenharmony_ci	revision = FIELD_GET(IIDR_REVISION, reg);
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	switch (implementer) {
344862306a36Sopenharmony_ci	case IIDR_IMPLEMENTER_ARM:
344962306a36Sopenharmony_ci		switch (productid) {
345062306a36Sopenharmony_ci		case IIDR_PRODUCTID_ARM_MMU_600:
345162306a36Sopenharmony_ci			/* Arm erratum 1076982 */
345262306a36Sopenharmony_ci			if (variant == 0 && revision <= 2)
345362306a36Sopenharmony_ci				smmu->features &= ~ARM_SMMU_FEAT_SEV;
345462306a36Sopenharmony_ci			/* Arm erratum 1209401 */
345562306a36Sopenharmony_ci			if (variant < 2)
345662306a36Sopenharmony_ci				smmu->features &= ~ARM_SMMU_FEAT_NESTING;
345762306a36Sopenharmony_ci			break;
345862306a36Sopenharmony_ci		case IIDR_PRODUCTID_ARM_MMU_700:
345962306a36Sopenharmony_ci			/* Arm erratum 2812531 */
346062306a36Sopenharmony_ci			smmu->features &= ~ARM_SMMU_FEAT_BTM;
346162306a36Sopenharmony_ci			smmu->options |= ARM_SMMU_OPT_CMDQ_FORCE_SYNC;
346262306a36Sopenharmony_ci			/* Arm errata 2268618, 2812531 */
346362306a36Sopenharmony_ci			smmu->features &= ~ARM_SMMU_FEAT_NESTING;
346462306a36Sopenharmony_ci			break;
346562306a36Sopenharmony_ci		}
346662306a36Sopenharmony_ci		break;
346762306a36Sopenharmony_ci	}
346862306a36Sopenharmony_ci}
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_cistatic int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
347162306a36Sopenharmony_ci{
347262306a36Sopenharmony_ci	u32 reg;
347362306a36Sopenharmony_ci	bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	/* IDR0 */
347662306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	/* 2-level structures */
347962306a36Sopenharmony_ci	if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL)
348062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	if (reg & IDR0_CD2L)
348362306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB;
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	/*
348662306a36Sopenharmony_ci	 * Translation table endianness.
348762306a36Sopenharmony_ci	 * We currently require the same endianness as the CPU, but this
348862306a36Sopenharmony_ci	 * could be changed later by adding a new IO_PGTABLE_QUIRK.
348962306a36Sopenharmony_ci	 */
349062306a36Sopenharmony_ci	switch (FIELD_GET(IDR0_TTENDIAN, reg)) {
349162306a36Sopenharmony_ci	case IDR0_TTENDIAN_MIXED:
349262306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
349362306a36Sopenharmony_ci		break;
349462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
349562306a36Sopenharmony_ci	case IDR0_TTENDIAN_BE:
349662306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_BE;
349762306a36Sopenharmony_ci		break;
349862306a36Sopenharmony_ci#else
349962306a36Sopenharmony_ci	case IDR0_TTENDIAN_LE:
350062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_LE;
350162306a36Sopenharmony_ci		break;
350262306a36Sopenharmony_ci#endif
350362306a36Sopenharmony_ci	default:
350462306a36Sopenharmony_ci		dev_err(smmu->dev, "unknown/unsupported TT endianness!\n");
350562306a36Sopenharmony_ci		return -ENXIO;
350662306a36Sopenharmony_ci	}
350762306a36Sopenharmony_ci
350862306a36Sopenharmony_ci	/* Boolean feature flags */
350962306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI)
351062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_PRI;
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS)
351362306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_ATS;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	if (reg & IDR0_SEV)
351662306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_SEV;
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	if (reg & IDR0_MSI) {
351962306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_MSI;
352062306a36Sopenharmony_ci		if (coherent && !disable_msipolling)
352162306a36Sopenharmony_ci			smmu->options |= ARM_SMMU_OPT_MSIPOLL;
352262306a36Sopenharmony_ci	}
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci	if (reg & IDR0_HYP) {
352562306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_HYP;
352662306a36Sopenharmony_ci		if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))
352762306a36Sopenharmony_ci			smmu->features |= ARM_SMMU_FEAT_E2H;
352862306a36Sopenharmony_ci	}
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci	/*
353162306a36Sopenharmony_ci	 * The coherency feature as set by FW is used in preference to the ID
353262306a36Sopenharmony_ci	 * register, but warn on mismatch.
353362306a36Sopenharmony_ci	 */
353462306a36Sopenharmony_ci	if (!!(reg & IDR0_COHACC) != coherent)
353562306a36Sopenharmony_ci		dev_warn(smmu->dev, "IDR0.COHACC overridden by FW configuration (%s)\n",
353662306a36Sopenharmony_ci			 coherent ? "true" : "false");
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci	switch (FIELD_GET(IDR0_STALL_MODEL, reg)) {
353962306a36Sopenharmony_ci	case IDR0_STALL_MODEL_FORCE:
354062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_STALL_FORCE;
354162306a36Sopenharmony_ci		fallthrough;
354262306a36Sopenharmony_ci	case IDR0_STALL_MODEL_STALL:
354362306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_STALLS;
354462306a36Sopenharmony_ci	}
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	if (reg & IDR0_S1P)
354762306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci	if (reg & IDR0_S2P)
355062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	if (!(reg & (IDR0_S1P | IDR0_S2P))) {
355362306a36Sopenharmony_ci		dev_err(smmu->dev, "no translation support!\n");
355462306a36Sopenharmony_ci		return -ENXIO;
355562306a36Sopenharmony_ci	}
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	/* We only support the AArch64 table format at present */
355862306a36Sopenharmony_ci	switch (FIELD_GET(IDR0_TTF, reg)) {
355962306a36Sopenharmony_ci	case IDR0_TTF_AARCH32_64:
356062306a36Sopenharmony_ci		smmu->ias = 40;
356162306a36Sopenharmony_ci		fallthrough;
356262306a36Sopenharmony_ci	case IDR0_TTF_AARCH64:
356362306a36Sopenharmony_ci		break;
356462306a36Sopenharmony_ci	default:
356562306a36Sopenharmony_ci		dev_err(smmu->dev, "AArch64 table format not supported!\n");
356662306a36Sopenharmony_ci		return -ENXIO;
356762306a36Sopenharmony_ci	}
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci	/* ASID/VMID sizes */
357062306a36Sopenharmony_ci	smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
357162306a36Sopenharmony_ci	smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci	/* IDR1 */
357462306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
357562306a36Sopenharmony_ci	if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
357662306a36Sopenharmony_ci		dev_err(smmu->dev, "embedded implementation not supported\n");
357762306a36Sopenharmony_ci		return -ENXIO;
357862306a36Sopenharmony_ci	}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	/* Queue sizes, capped to ensure natural alignment */
358162306a36Sopenharmony_ci	smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT,
358262306a36Sopenharmony_ci					     FIELD_GET(IDR1_CMDQS, reg));
358362306a36Sopenharmony_ci	if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) {
358462306a36Sopenharmony_ci		/*
358562306a36Sopenharmony_ci		 * We don't support splitting up batches, so one batch of
358662306a36Sopenharmony_ci		 * commands plus an extra sync needs to fit inside the command
358762306a36Sopenharmony_ci		 * queue. There's also no way we can handle the weird alignment
358862306a36Sopenharmony_ci		 * restrictions on the base pointer for a unit-length queue.
358962306a36Sopenharmony_ci		 */
359062306a36Sopenharmony_ci		dev_err(smmu->dev, "command queue size <= %d entries not supported\n",
359162306a36Sopenharmony_ci			CMDQ_BATCH_ENTRIES);
359262306a36Sopenharmony_ci		return -ENXIO;
359362306a36Sopenharmony_ci	}
359462306a36Sopenharmony_ci
359562306a36Sopenharmony_ci	smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT,
359662306a36Sopenharmony_ci					     FIELD_GET(IDR1_EVTQS, reg));
359762306a36Sopenharmony_ci	smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT,
359862306a36Sopenharmony_ci					     FIELD_GET(IDR1_PRIQS, reg));
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci	/* SID/SSID sizes */
360162306a36Sopenharmony_ci	smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg);
360262306a36Sopenharmony_ci	smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg);
360362306a36Sopenharmony_ci	smmu->iommu.max_pasids = 1UL << smmu->ssid_bits;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	/*
360662306a36Sopenharmony_ci	 * If the SMMU supports fewer bits than would fill a single L2 stream
360762306a36Sopenharmony_ci	 * table, use a linear table instead.
360862306a36Sopenharmony_ci	 */
360962306a36Sopenharmony_ci	if (smmu->sid_bits <= STRTAB_SPLIT)
361062306a36Sopenharmony_ci		smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci	/* IDR3 */
361362306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3);
361462306a36Sopenharmony_ci	if (FIELD_GET(IDR3_RIL, reg))
361562306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_RANGE_INV;
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci	/* IDR5 */
361862306a36Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	/* Maximum number of outstanding stalls */
362162306a36Sopenharmony_ci	smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg);
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci	/* Page sizes */
362462306a36Sopenharmony_ci	if (reg & IDR5_GRAN64K)
362562306a36Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_64K | SZ_512M;
362662306a36Sopenharmony_ci	if (reg & IDR5_GRAN16K)
362762306a36Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_16K | SZ_32M;
362862306a36Sopenharmony_ci	if (reg & IDR5_GRAN4K)
362962306a36Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	/* Input address size */
363262306a36Sopenharmony_ci	if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT)
363362306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_VAX;
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	/* Output address size */
363662306a36Sopenharmony_ci	switch (FIELD_GET(IDR5_OAS, reg)) {
363762306a36Sopenharmony_ci	case IDR5_OAS_32_BIT:
363862306a36Sopenharmony_ci		smmu->oas = 32;
363962306a36Sopenharmony_ci		break;
364062306a36Sopenharmony_ci	case IDR5_OAS_36_BIT:
364162306a36Sopenharmony_ci		smmu->oas = 36;
364262306a36Sopenharmony_ci		break;
364362306a36Sopenharmony_ci	case IDR5_OAS_40_BIT:
364462306a36Sopenharmony_ci		smmu->oas = 40;
364562306a36Sopenharmony_ci		break;
364662306a36Sopenharmony_ci	case IDR5_OAS_42_BIT:
364762306a36Sopenharmony_ci		smmu->oas = 42;
364862306a36Sopenharmony_ci		break;
364962306a36Sopenharmony_ci	case IDR5_OAS_44_BIT:
365062306a36Sopenharmony_ci		smmu->oas = 44;
365162306a36Sopenharmony_ci		break;
365262306a36Sopenharmony_ci	case IDR5_OAS_52_BIT:
365362306a36Sopenharmony_ci		smmu->oas = 52;
365462306a36Sopenharmony_ci		smmu->pgsize_bitmap |= 1ULL << 42; /* 4TB */
365562306a36Sopenharmony_ci		break;
365662306a36Sopenharmony_ci	default:
365762306a36Sopenharmony_ci		dev_info(smmu->dev,
365862306a36Sopenharmony_ci			"unknown output address size. Truncating to 48-bit\n");
365962306a36Sopenharmony_ci		fallthrough;
366062306a36Sopenharmony_ci	case IDR5_OAS_48_BIT:
366162306a36Sopenharmony_ci		smmu->oas = 48;
366262306a36Sopenharmony_ci	}
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	if (arm_smmu_ops.pgsize_bitmap == -1UL)
366562306a36Sopenharmony_ci		arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
366662306a36Sopenharmony_ci	else
366762306a36Sopenharmony_ci		arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	/* Set the DMA mask for our table walker */
367062306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
367162306a36Sopenharmony_ci		dev_warn(smmu->dev,
367262306a36Sopenharmony_ci			 "failed to set DMA mask for table walker\n");
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	smmu->ias = max(smmu->ias, smmu->oas);
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_ci	if ((smmu->features & ARM_SMMU_FEAT_TRANS_S1) &&
367762306a36Sopenharmony_ci	    (smmu->features & ARM_SMMU_FEAT_TRANS_S2))
367862306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_NESTING;
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	arm_smmu_device_iidr_probe(smmu);
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci	if (arm_smmu_sva_supported(smmu))
368362306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_SVA;
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
368662306a36Sopenharmony_ci		 smmu->ias, smmu->oas, smmu->features);
368762306a36Sopenharmony_ci	return 0;
368862306a36Sopenharmony_ci}
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci#ifdef CONFIG_ACPI
369162306a36Sopenharmony_cistatic void acpi_smmu_get_options(u32 model, struct arm_smmu_device *smmu)
369262306a36Sopenharmony_ci{
369362306a36Sopenharmony_ci	switch (model) {
369462306a36Sopenharmony_ci	case ACPI_IORT_SMMU_V3_CAVIUM_CN99XX:
369562306a36Sopenharmony_ci		smmu->options |= ARM_SMMU_OPT_PAGE0_REGS_ONLY;
369662306a36Sopenharmony_ci		break;
369762306a36Sopenharmony_ci	case ACPI_IORT_SMMU_V3_HISILICON_HI161X:
369862306a36Sopenharmony_ci		smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH;
369962306a36Sopenharmony_ci		break;
370062306a36Sopenharmony_ci	}
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	dev_notice(smmu->dev, "option mask 0x%x\n", smmu->options);
370362306a36Sopenharmony_ci}
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct platform_device *pdev,
370662306a36Sopenharmony_ci				      struct arm_smmu_device *smmu)
370762306a36Sopenharmony_ci{
370862306a36Sopenharmony_ci	struct acpi_iort_smmu_v3 *iort_smmu;
370962306a36Sopenharmony_ci	struct device *dev = smmu->dev;
371062306a36Sopenharmony_ci	struct acpi_iort_node *node;
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	node = *(struct acpi_iort_node **)dev_get_platdata(dev);
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	/* Retrieve SMMUv3 specific data */
371562306a36Sopenharmony_ci	iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ci	acpi_smmu_get_options(iort_smmu->model, smmu);
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
372062306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci	return 0;
372362306a36Sopenharmony_ci}
372462306a36Sopenharmony_ci#else
372562306a36Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
372662306a36Sopenharmony_ci					     struct arm_smmu_device *smmu)
372762306a36Sopenharmony_ci{
372862306a36Sopenharmony_ci	return -ENODEV;
372962306a36Sopenharmony_ci}
373062306a36Sopenharmony_ci#endif
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct platform_device *pdev,
373362306a36Sopenharmony_ci				    struct arm_smmu_device *smmu)
373462306a36Sopenharmony_ci{
373562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
373662306a36Sopenharmony_ci	u32 cells;
373762306a36Sopenharmony_ci	int ret = -EINVAL;
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
374062306a36Sopenharmony_ci		dev_err(dev, "missing #iommu-cells property\n");
374162306a36Sopenharmony_ci	else if (cells != 1)
374262306a36Sopenharmony_ci		dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
374362306a36Sopenharmony_ci	else
374462306a36Sopenharmony_ci		ret = 0;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	parse_driver_options(smmu);
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	if (of_dma_is_coherent(dev->of_node))
374962306a36Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci	return ret;
375262306a36Sopenharmony_ci}
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_cistatic unsigned long arm_smmu_resource_size(struct arm_smmu_device *smmu)
375562306a36Sopenharmony_ci{
375662306a36Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY)
375762306a36Sopenharmony_ci		return SZ_64K;
375862306a36Sopenharmony_ci	else
375962306a36Sopenharmony_ci		return SZ_128K;
376062306a36Sopenharmony_ci}
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_cistatic void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start,
376362306a36Sopenharmony_ci				      resource_size_t size)
376462306a36Sopenharmony_ci{
376562306a36Sopenharmony_ci	struct resource res = DEFINE_RES_MEM(start, size);
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	return devm_ioremap_resource(dev, &res);
376862306a36Sopenharmony_ci}
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_cistatic void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu)
377162306a36Sopenharmony_ci{
377262306a36Sopenharmony_ci	struct list_head rmr_list;
377362306a36Sopenharmony_ci	struct iommu_resv_region *e;
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_ci	INIT_LIST_HEAD(&rmr_list);
377662306a36Sopenharmony_ci	iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list);
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	list_for_each_entry(e, &rmr_list, list) {
377962306a36Sopenharmony_ci		__le64 *step;
378062306a36Sopenharmony_ci		struct iommu_iort_rmr_data *rmr;
378162306a36Sopenharmony_ci		int ret, i;
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci		rmr = container_of(e, struct iommu_iort_rmr_data, rr);
378462306a36Sopenharmony_ci		for (i = 0; i < rmr->num_sids; i++) {
378562306a36Sopenharmony_ci			ret = arm_smmu_init_sid_strtab(smmu, rmr->sids[i]);
378662306a36Sopenharmony_ci			if (ret) {
378762306a36Sopenharmony_ci				dev_err(smmu->dev, "RMR SID(0x%x) bypass failed\n",
378862306a36Sopenharmony_ci					rmr->sids[i]);
378962306a36Sopenharmony_ci				continue;
379062306a36Sopenharmony_ci			}
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci			step = arm_smmu_get_step_for_sid(smmu, rmr->sids[i]);
379362306a36Sopenharmony_ci			arm_smmu_init_bypass_stes(step, 1, true);
379462306a36Sopenharmony_ci		}
379562306a36Sopenharmony_ci	}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ci	iort_put_rmr_sids(dev_fwnode(smmu->dev), &rmr_list);
379862306a36Sopenharmony_ci}
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev)
380162306a36Sopenharmony_ci{
380262306a36Sopenharmony_ci	int irq, ret;
380362306a36Sopenharmony_ci	struct resource *res;
380462306a36Sopenharmony_ci	resource_size_t ioaddr;
380562306a36Sopenharmony_ci	struct arm_smmu_device *smmu;
380662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
380762306a36Sopenharmony_ci	bool bypass;
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
381062306a36Sopenharmony_ci	if (!smmu)
381162306a36Sopenharmony_ci		return -ENOMEM;
381262306a36Sopenharmony_ci	smmu->dev = dev;
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	if (dev->of_node) {
381562306a36Sopenharmony_ci		ret = arm_smmu_device_dt_probe(pdev, smmu);
381662306a36Sopenharmony_ci	} else {
381762306a36Sopenharmony_ci		ret = arm_smmu_device_acpi_probe(pdev, smmu);
381862306a36Sopenharmony_ci		if (ret == -ENODEV)
381962306a36Sopenharmony_ci			return ret;
382062306a36Sopenharmony_ci	}
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci	/* Set bypass mode according to firmware probing result */
382362306a36Sopenharmony_ci	bypass = !!ret;
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	/* Base address */
382662306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
382762306a36Sopenharmony_ci	if (!res)
382862306a36Sopenharmony_ci		return -EINVAL;
382962306a36Sopenharmony_ci	if (resource_size(res) < arm_smmu_resource_size(smmu)) {
383062306a36Sopenharmony_ci		dev_err(dev, "MMIO region too small (%pr)\n", res);
383162306a36Sopenharmony_ci		return -EINVAL;
383262306a36Sopenharmony_ci	}
383362306a36Sopenharmony_ci	ioaddr = res->start;
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci	/*
383662306a36Sopenharmony_ci	 * Don't map the IMPLEMENTATION DEFINED regions, since they may contain
383762306a36Sopenharmony_ci	 * the PMCG registers which are reserved by the PMU driver.
383862306a36Sopenharmony_ci	 */
383962306a36Sopenharmony_ci	smmu->base = arm_smmu_ioremap(dev, ioaddr, ARM_SMMU_REG_SZ);
384062306a36Sopenharmony_ci	if (IS_ERR(smmu->base))
384162306a36Sopenharmony_ci		return PTR_ERR(smmu->base);
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	if (arm_smmu_resource_size(smmu) > SZ_64K) {
384462306a36Sopenharmony_ci		smmu->page1 = arm_smmu_ioremap(dev, ioaddr + SZ_64K,
384562306a36Sopenharmony_ci					       ARM_SMMU_REG_SZ);
384662306a36Sopenharmony_ci		if (IS_ERR(smmu->page1))
384762306a36Sopenharmony_ci			return PTR_ERR(smmu->page1);
384862306a36Sopenharmony_ci	} else {
384962306a36Sopenharmony_ci		smmu->page1 = smmu->base;
385062306a36Sopenharmony_ci	}
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_ci	/* Interrupt lines */
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	irq = platform_get_irq_byname_optional(pdev, "combined");
385562306a36Sopenharmony_ci	if (irq > 0)
385662306a36Sopenharmony_ci		smmu->combined_irq = irq;
385762306a36Sopenharmony_ci	else {
385862306a36Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "eventq");
385962306a36Sopenharmony_ci		if (irq > 0)
386062306a36Sopenharmony_ci			smmu->evtq.q.irq = irq;
386162306a36Sopenharmony_ci
386262306a36Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "priq");
386362306a36Sopenharmony_ci		if (irq > 0)
386462306a36Sopenharmony_ci			smmu->priq.q.irq = irq;
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "gerror");
386762306a36Sopenharmony_ci		if (irq > 0)
386862306a36Sopenharmony_ci			smmu->gerr_irq = irq;
386962306a36Sopenharmony_ci	}
387062306a36Sopenharmony_ci	/* Probe the h/w */
387162306a36Sopenharmony_ci	ret = arm_smmu_device_hw_probe(smmu);
387262306a36Sopenharmony_ci	if (ret)
387362306a36Sopenharmony_ci		return ret;
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* Initialise in-memory data structures */
387662306a36Sopenharmony_ci	ret = arm_smmu_init_structures(smmu);
387762306a36Sopenharmony_ci	if (ret)
387862306a36Sopenharmony_ci		return ret;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	/* Record our private device structure */
388162306a36Sopenharmony_ci	platform_set_drvdata(pdev, smmu);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	/* Check for RMRs and install bypass STEs if any */
388462306a36Sopenharmony_ci	arm_smmu_rmr_install_bypass_ste(smmu);
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	/* Reset the device */
388762306a36Sopenharmony_ci	ret = arm_smmu_device_reset(smmu, bypass);
388862306a36Sopenharmony_ci	if (ret)
388962306a36Sopenharmony_ci		return ret;
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	/* And we're up. Go go go! */
389262306a36Sopenharmony_ci	ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
389362306a36Sopenharmony_ci				     "smmu3.%pa", &ioaddr);
389462306a36Sopenharmony_ci	if (ret)
389562306a36Sopenharmony_ci		return ret;
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci	ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev);
389862306a36Sopenharmony_ci	if (ret) {
389962306a36Sopenharmony_ci		dev_err(dev, "Failed to register iommu\n");
390062306a36Sopenharmony_ci		iommu_device_sysfs_remove(&smmu->iommu);
390162306a36Sopenharmony_ci		return ret;
390262306a36Sopenharmony_ci	}
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	return 0;
390562306a36Sopenharmony_ci}
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_cistatic void arm_smmu_device_remove(struct platform_device *pdev)
390862306a36Sopenharmony_ci{
390962306a36Sopenharmony_ci	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	iommu_device_unregister(&smmu->iommu);
391262306a36Sopenharmony_ci	iommu_device_sysfs_remove(&smmu->iommu);
391362306a36Sopenharmony_ci	arm_smmu_device_disable(smmu);
391462306a36Sopenharmony_ci	iopf_queue_free(smmu->evtq.iopf);
391562306a36Sopenharmony_ci	ida_destroy(&smmu->vmid_map);
391662306a36Sopenharmony_ci}
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev)
391962306a36Sopenharmony_ci{
392062306a36Sopenharmony_ci	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	arm_smmu_device_disable(smmu);
392362306a36Sopenharmony_ci}
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = {
392662306a36Sopenharmony_ci	{ .compatible = "arm,smmu-v3", },
392762306a36Sopenharmony_ci	{ },
392862306a36Sopenharmony_ci};
392962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_cistatic void arm_smmu_driver_unregister(struct platform_driver *drv)
393262306a36Sopenharmony_ci{
393362306a36Sopenharmony_ci	arm_smmu_sva_notifier_synchronize();
393462306a36Sopenharmony_ci	platform_driver_unregister(drv);
393562306a36Sopenharmony_ci}
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver = {
393862306a36Sopenharmony_ci	.driver	= {
393962306a36Sopenharmony_ci		.name			= "arm-smmu-v3",
394062306a36Sopenharmony_ci		.of_match_table		= arm_smmu_of_match,
394162306a36Sopenharmony_ci		.suppress_bind_attrs	= true,
394262306a36Sopenharmony_ci	},
394362306a36Sopenharmony_ci	.probe	= arm_smmu_device_probe,
394462306a36Sopenharmony_ci	.remove_new = arm_smmu_device_remove,
394562306a36Sopenharmony_ci	.shutdown = arm_smmu_device_shutdown,
394662306a36Sopenharmony_ci};
394762306a36Sopenharmony_cimodule_driver(arm_smmu_driver, platform_driver_register,
394862306a36Sopenharmony_ci	      arm_smmu_driver_unregister);
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
395162306a36Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>");
395262306a36Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu-v3");
395362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3954