18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IOMMU API for ARM architected SMMUv3 implementations.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Limited
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This driver is powered by bad coffee and bombay mix.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/acpi.h>
138c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h>
148c2ecf20Sopenharmony_ci#include <linux/bitops.h>
158c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/io-pgtable.h>
218c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/msi.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci#include <linux/of_address.h>
268c2ecf20Sopenharmony_ci#include <linux/of_iommu.h>
278c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
288c2ecf20Sopenharmony_ci#include <linux/pci.h>
298c2ecf20Sopenharmony_ci#include <linux/pci-ats.h>
308c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/amba/bus.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "arm-smmu-v3.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic bool disable_bypass = 1;
378c2ecf20Sopenharmony_cimodule_param(disable_bypass, bool, 0444);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_bypass,
398c2ecf20Sopenharmony_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.");
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic bool disable_msipolling;
428c2ecf20Sopenharmony_cimodule_param(disable_msipolling, bool, 0444);
438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_msipolling,
448c2ecf20Sopenharmony_ci	"Disable MSI-based polling for CMD_SYNC completion.");
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cienum arm_smmu_msi_index {
478c2ecf20Sopenharmony_ci	EVTQ_MSI_INDEX,
488c2ecf20Sopenharmony_ci	GERROR_MSI_INDEX,
498c2ecf20Sopenharmony_ci	PRIQ_MSI_INDEX,
508c2ecf20Sopenharmony_ci	ARM_SMMU_MAX_MSIS,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
548c2ecf20Sopenharmony_ci	[EVTQ_MSI_INDEX] = {
558c2ecf20Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG0,
568c2ecf20Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG1,
578c2ecf20Sopenharmony_ci		ARM_SMMU_EVTQ_IRQ_CFG2,
588c2ecf20Sopenharmony_ci	},
598c2ecf20Sopenharmony_ci	[GERROR_MSI_INDEX] = {
608c2ecf20Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG0,
618c2ecf20Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG1,
628c2ecf20Sopenharmony_ci		ARM_SMMU_GERROR_IRQ_CFG2,
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	[PRIQ_MSI_INDEX] = {
658c2ecf20Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG0,
668c2ecf20Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG1,
678c2ecf20Sopenharmony_ci		ARM_SMMU_PRIQ_IRQ_CFG2,
688c2ecf20Sopenharmony_ci	},
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct arm_smmu_option_prop {
728c2ecf20Sopenharmony_ci	u32 opt;
738c2ecf20Sopenharmony_ci	const char *prop;
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciDEFINE_XARRAY_ALLOC1(arm_smmu_asid_xa);
778c2ecf20Sopenharmony_ciDEFINE_MUTEX(arm_smmu_asid_lock);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct arm_smmu_option_prop arm_smmu_options[] = {
808c2ecf20Sopenharmony_ci	{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
818c2ecf20Sopenharmony_ci	{ ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"},
828c2ecf20Sopenharmony_ci	{ 0, NULL},
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic inline void __iomem *arm_smmu_page1_fixup(unsigned long offset,
868c2ecf20Sopenharmony_ci						 struct arm_smmu_device *smmu)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	if (offset > SZ_64K)
898c2ecf20Sopenharmony_ci		return smmu->page1 + offset - SZ_64K;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return smmu->base + offset;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	return container_of(dom, struct arm_smmu_domain, domain);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void parse_driver_options(struct arm_smmu_device *smmu)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	int i = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	do {
1048c2ecf20Sopenharmony_ci		if (of_property_read_bool(smmu->dev->of_node,
1058c2ecf20Sopenharmony_ci						arm_smmu_options[i].prop)) {
1068c2ecf20Sopenharmony_ci			smmu->options |= arm_smmu_options[i].opt;
1078c2ecf20Sopenharmony_ci			dev_notice(smmu->dev, "option %s\n",
1088c2ecf20Sopenharmony_ci				arm_smmu_options[i].prop);
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	} while (arm_smmu_options[++i].opt);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Low-level queue manipulation functions */
1148c2ecf20Sopenharmony_cistatic bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u32 space, prod, cons;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	prod = Q_IDX(q, q->prod);
1198c2ecf20Sopenharmony_ci	cons = Q_IDX(q, q->cons);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons))
1228c2ecf20Sopenharmony_ci		space = (1 << q->max_n_shift) - (prod - cons);
1238c2ecf20Sopenharmony_ci	else
1248c2ecf20Sopenharmony_ci		space = cons - prod;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return space >= n;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic bool queue_full(struct arm_smmu_ll_queue *q)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
1328c2ecf20Sopenharmony_ci	       Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic bool queue_empty(struct arm_smmu_ll_queue *q)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
1388c2ecf20Sopenharmony_ci	       Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) &&
1448c2ecf20Sopenharmony_ci		(Q_IDX(q, q->cons) > Q_IDX(q, prod))) ||
1458c2ecf20Sopenharmony_ci	       ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) &&
1468c2ecf20Sopenharmony_ci		(Q_IDX(q, q->cons) <= Q_IDX(q, prod)));
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void queue_sync_cons_out(struct arm_smmu_queue *q)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * Ensure that all CPU accesses (reads and writes) to the queue
1538c2ecf20Sopenharmony_ci	 * are complete before we update the cons pointer.
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	__iomb();
1568c2ecf20Sopenharmony_ci	writel_relaxed(q->llq.cons, q->cons_reg);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void queue_inc_cons(struct arm_smmu_ll_queue *q)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1;
1628c2ecf20Sopenharmony_ci	q->cons = Q_OVF(q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void queue_sync_cons_ovf(struct arm_smmu_queue *q)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (likely(Q_OVF(llq->prod) == Q_OVF(llq->cons)))
1708c2ecf20Sopenharmony_ci		return;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) |
1738c2ecf20Sopenharmony_ci		      Q_IDX(llq, llq->cons);
1748c2ecf20Sopenharmony_ci	queue_sync_cons_out(q);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int queue_sync_prod_in(struct arm_smmu_queue *q)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u32 prod;
1808c2ecf20Sopenharmony_ci	int ret = 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/*
1838c2ecf20Sopenharmony_ci	 * We can't use the _relaxed() variant here, as we must prevent
1848c2ecf20Sopenharmony_ci	 * speculative reads of the queue before we have determined that
1858c2ecf20Sopenharmony_ci	 * prod has indeed moved.
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	prod = readl(q->prod_reg);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (Q_OVF(prod) != Q_OVF(q->llq.prod))
1908c2ecf20Sopenharmony_ci		ret = -EOVERFLOW;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	q->llq.prod = prod;
1938c2ecf20Sopenharmony_ci	return ret;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic u32 queue_inc_prod_n(struct arm_smmu_ll_queue *q, int n)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + n;
1998c2ecf20Sopenharmony_ci	return Q_OVF(q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void queue_poll_init(struct arm_smmu_device *smmu,
2038c2ecf20Sopenharmony_ci			    struct arm_smmu_queue_poll *qp)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	qp->delay = 1;
2068c2ecf20Sopenharmony_ci	qp->spin_cnt = 0;
2078c2ecf20Sopenharmony_ci	qp->wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
2088c2ecf20Sopenharmony_ci	qp->timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int queue_poll(struct arm_smmu_queue_poll *qp)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (ktime_compare(ktime_get(), qp->timeout) > 0)
2148c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (qp->wfe) {
2178c2ecf20Sopenharmony_ci		wfe();
2188c2ecf20Sopenharmony_ci	} else if (++qp->spin_cnt < ARM_SMMU_POLL_SPIN_COUNT) {
2198c2ecf20Sopenharmony_ci		cpu_relax();
2208c2ecf20Sopenharmony_ci	} else {
2218c2ecf20Sopenharmony_ci		udelay(qp->delay);
2228c2ecf20Sopenharmony_ci		qp->delay *= 2;
2238c2ecf20Sopenharmony_ci		qp->spin_cnt = 0;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void queue_write(__le64 *dst, u64 *src, size_t n_dwords)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int i;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	for (i = 0; i < n_dwords; ++i)
2348c2ecf20Sopenharmony_ci		*dst++ = cpu_to_le64(*src++);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void queue_read(u64 *dst, __le64 *src, size_t n_dwords)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int i;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	for (i = 0; i < n_dwords; ++i)
2428c2ecf20Sopenharmony_ci		*dst++ = le64_to_cpu(*src++);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	if (queue_empty(&q->llq))
2488c2ecf20Sopenharmony_ci		return -EAGAIN;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	queue_read(ent, Q_ENT(q, q->llq.cons), q->ent_dwords);
2518c2ecf20Sopenharmony_ci	queue_inc_cons(&q->llq);
2528c2ecf20Sopenharmony_ci	queue_sync_cons_out(q);
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/* High-level queue accessors */
2578c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	memset(cmd, 0, 1 << CMDQ_ENT_SZ_SHIFT);
2608c2ecf20Sopenharmony_ci	cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	switch (ent->opcode) {
2638c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_EL2_ALL:
2648c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_NSNH_ALL:
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	case CMDQ_OP_PREFETCH_CFG:
2678c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid);
2688c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent->prefetch.size);
2698c2ecf20Sopenharmony_ci		cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
2708c2ecf20Sopenharmony_ci		break;
2718c2ecf20Sopenharmony_ci	case CMDQ_OP_CFGI_CD:
2728c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid);
2738c2ecf20Sopenharmony_ci		fallthrough;
2748c2ecf20Sopenharmony_ci	case CMDQ_OP_CFGI_STE:
2758c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
2768c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf);
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci	case CMDQ_OP_CFGI_CD_ALL:
2798c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
2808c2ecf20Sopenharmony_ci		break;
2818c2ecf20Sopenharmony_ci	case CMDQ_OP_CFGI_ALL:
2828c2ecf20Sopenharmony_ci		/* Cover the entire SID range */
2838c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31);
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_NH_VA:
2868c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
2878c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
2888c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
2898c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
2908c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
2918c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
2928c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
2938c2ecf20Sopenharmony_ci		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_S2_IPA:
2968c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
2978c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
2988c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
2998c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
3008c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
3018c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
3028c2ecf20Sopenharmony_ci		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_NH_ASID:
3058c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
3068c2ecf20Sopenharmony_ci		fallthrough;
3078c2ecf20Sopenharmony_ci	case CMDQ_OP_TLBI_S12_VMALL:
3088c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case CMDQ_OP_ATC_INV:
3118c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
3128c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_GLOBAL, ent->atc.global);
3138c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SSID, ent->atc.ssid);
3148c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SID, ent->atc.sid);
3158c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_ATC_1_SIZE, ent->atc.size);
3168c2ecf20Sopenharmony_ci		cmd[1] |= ent->atc.addr & CMDQ_ATC_1_ADDR_MASK;
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	case CMDQ_OP_PRI_RESP:
3198c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
3208c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SSID, ent->pri.ssid);
3218c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SID, ent->pri.sid);
3228c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_PRI_1_GRPID, ent->pri.grpid);
3238c2ecf20Sopenharmony_ci		switch (ent->pri.resp) {
3248c2ecf20Sopenharmony_ci		case PRI_RESP_DENY:
3258c2ecf20Sopenharmony_ci		case PRI_RESP_FAIL:
3268c2ecf20Sopenharmony_ci		case PRI_RESP_SUCC:
3278c2ecf20Sopenharmony_ci			break;
3288c2ecf20Sopenharmony_ci		default:
3298c2ecf20Sopenharmony_ci			return -EINVAL;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci		cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp);
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	case CMDQ_OP_CMD_SYNC:
3348c2ecf20Sopenharmony_ci		if (ent->sync.msiaddr) {
3358c2ecf20Sopenharmony_ci			cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ);
3368c2ecf20Sopenharmony_ci			cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK;
3378c2ecf20Sopenharmony_ci		} else {
3388c2ecf20Sopenharmony_ci			cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV);
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH);
3418c2ecf20Sopenharmony_ci		cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB);
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	default:
3448c2ecf20Sopenharmony_ci		return -ENOENT;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,
3518c2ecf20Sopenharmony_ci					 u32 prod)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->cmdq.q;
3548c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent ent = {
3558c2ecf20Sopenharmony_ci		.opcode = CMDQ_OP_CMD_SYNC,
3568c2ecf20Sopenharmony_ci	};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/*
3598c2ecf20Sopenharmony_ci	 * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI
3608c2ecf20Sopenharmony_ci	 * payload, so the write will zero the entire command on that platform.
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_MSIPOLL) {
3638c2ecf20Sopenharmony_ci		ent.sync.msiaddr = q->base_dma + Q_IDX(&q->llq, prod) *
3648c2ecf20Sopenharmony_ci				   q->ent_dwords * 8;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	arm_smmu_cmdq_build_cmd(cmd, &ent);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	static const char *cerror_str[] = {
3738c2ecf20Sopenharmony_ci		[CMDQ_ERR_CERROR_NONE_IDX]	= "No error",
3748c2ecf20Sopenharmony_ci		[CMDQ_ERR_CERROR_ILL_IDX]	= "Illegal command",
3758c2ecf20Sopenharmony_ci		[CMDQ_ERR_CERROR_ABT_IDX]	= "Abort on command fetch",
3768c2ecf20Sopenharmony_ci		[CMDQ_ERR_CERROR_ATC_INV_IDX]	= "ATC invalidate timeout",
3778c2ecf20Sopenharmony_ci	};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	int i;
3808c2ecf20Sopenharmony_ci	u64 cmd[CMDQ_ENT_DWORDS];
3818c2ecf20Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->cmdq.q;
3828c2ecf20Sopenharmony_ci	u32 cons = readl_relaxed(q->cons_reg);
3838c2ecf20Sopenharmony_ci	u32 idx = FIELD_GET(CMDQ_CONS_ERR, cons);
3848c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd_sync = {
3858c2ecf20Sopenharmony_ci		.opcode = CMDQ_OP_CMD_SYNC,
3868c2ecf20Sopenharmony_ci	};
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons,
3898c2ecf20Sopenharmony_ci		idx < ARRAY_SIZE(cerror_str) ?  cerror_str[idx] : "Unknown");
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	switch (idx) {
3928c2ecf20Sopenharmony_ci	case CMDQ_ERR_CERROR_ABT_IDX:
3938c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "retrying command fetch\n");
3948c2ecf20Sopenharmony_ci	case CMDQ_ERR_CERROR_NONE_IDX:
3958c2ecf20Sopenharmony_ci		return;
3968c2ecf20Sopenharmony_ci	case CMDQ_ERR_CERROR_ATC_INV_IDX:
3978c2ecf20Sopenharmony_ci		/*
3988c2ecf20Sopenharmony_ci		 * ATC Invalidation Completion timeout. CONS is still pointing
3998c2ecf20Sopenharmony_ci		 * at the CMD_SYNC. Attempt to complete other pending commands
4008c2ecf20Sopenharmony_ci		 * by repeating the CMD_SYNC, though we might well end up back
4018c2ecf20Sopenharmony_ci		 * here since the ATC invalidation may still be pending.
4028c2ecf20Sopenharmony_ci		 */
4038c2ecf20Sopenharmony_ci		return;
4048c2ecf20Sopenharmony_ci	case CMDQ_ERR_CERROR_ILL_IDX:
4058c2ecf20Sopenharmony_ci	default:
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/*
4108c2ecf20Sopenharmony_ci	 * We may have concurrent producers, so we need to be careful
4118c2ecf20Sopenharmony_ci	 * not to touch any of the shadow cmdq state.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	queue_read(cmd, Q_ENT(q, cons), q->ent_dwords);
4148c2ecf20Sopenharmony_ci	dev_err(smmu->dev, "skipping command in error state:\n");
4158c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cmd); ++i)
4168c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* Convert the erroneous command into a CMD_SYNC */
4198c2ecf20Sopenharmony_ci	if (arm_smmu_cmdq_build_cmd(cmd, &cmd_sync)) {
4208c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to convert to CMD_SYNC\n");
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci/*
4288c2ecf20Sopenharmony_ci * Command queue locking.
4298c2ecf20Sopenharmony_ci * This is a form of bastardised rwlock with the following major changes:
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci * - The only LOCK routines are exclusive_trylock() and shared_lock().
4328c2ecf20Sopenharmony_ci *   Neither have barrier semantics, and instead provide only a control
4338c2ecf20Sopenharmony_ci *   dependency.
4348c2ecf20Sopenharmony_ci *
4358c2ecf20Sopenharmony_ci * - The UNLOCK routines are supplemented with shared_tryunlock(), which
4368c2ecf20Sopenharmony_ci *   fails if the caller appears to be the last lock holder (yes, this is
4378c2ecf20Sopenharmony_ci *   racy). All successful UNLOCK routines have RELEASE semantics.
4388c2ecf20Sopenharmony_ci */
4398c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_shared_lock(struct arm_smmu_cmdq *cmdq)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	int val;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/*
4448c2ecf20Sopenharmony_ci	 * We can try to avoid the cmpxchg() loop by simply incrementing the
4458c2ecf20Sopenharmony_ci	 * lock counter. When held in exclusive state, the lock counter is set
4468c2ecf20Sopenharmony_ci	 * to INT_MIN so these increments won't hurt as the value will remain
4478c2ecf20Sopenharmony_ci	 * negative.
4488c2ecf20Sopenharmony_ci	 */
4498c2ecf20Sopenharmony_ci	if (atomic_fetch_inc_relaxed(&cmdq->lock) >= 0)
4508c2ecf20Sopenharmony_ci		return;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	do {
4538c2ecf20Sopenharmony_ci		val = atomic_cond_read_relaxed(&cmdq->lock, VAL >= 0);
4548c2ecf20Sopenharmony_ci	} while (atomic_cmpxchg_relaxed(&cmdq->lock, val, val + 1) != val);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_shared_unlock(struct arm_smmu_cmdq *cmdq)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	(void)atomic_dec_return_release(&cmdq->lock);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic bool arm_smmu_cmdq_shared_tryunlock(struct arm_smmu_cmdq *cmdq)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	if (atomic_read(&cmdq->lock) == 1)
4658c2ecf20Sopenharmony_ci		return false;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	arm_smmu_cmdq_shared_unlock(cmdq);
4688c2ecf20Sopenharmony_ci	return true;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci#define arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)		\
4728c2ecf20Sopenharmony_ci({									\
4738c2ecf20Sopenharmony_ci	bool __ret;							\
4748c2ecf20Sopenharmony_ci	local_irq_save(flags);						\
4758c2ecf20Sopenharmony_ci	__ret = !atomic_cmpxchg_relaxed(&cmdq->lock, 0, INT_MIN);	\
4768c2ecf20Sopenharmony_ci	if (!__ret)							\
4778c2ecf20Sopenharmony_ci		local_irq_restore(flags);				\
4788c2ecf20Sopenharmony_ci	__ret;								\
4798c2ecf20Sopenharmony_ci})
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci#define arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags)		\
4828c2ecf20Sopenharmony_ci({									\
4838c2ecf20Sopenharmony_ci	atomic_set_release(&cmdq->lock, 0);				\
4848c2ecf20Sopenharmony_ci	local_irq_restore(flags);					\
4858c2ecf20Sopenharmony_ci})
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/*
4898c2ecf20Sopenharmony_ci * Command queue insertion.
4908c2ecf20Sopenharmony_ci * This is made fiddly by our attempts to achieve some sort of scalability
4918c2ecf20Sopenharmony_ci * since there is one queue shared amongst all of the CPUs in the system.  If
4928c2ecf20Sopenharmony_ci * you like mixed-size concurrency, dependency ordering and relaxed atomics,
4938c2ecf20Sopenharmony_ci * then you'll *love* this monstrosity.
4948c2ecf20Sopenharmony_ci *
4958c2ecf20Sopenharmony_ci * The basic idea is to split the queue up into ranges of commands that are
4968c2ecf20Sopenharmony_ci * owned by a given CPU; the owner may not have written all of the commands
4978c2ecf20Sopenharmony_ci * itself, but is responsible for advancing the hardware prod pointer when
4988c2ecf20Sopenharmony_ci * the time comes. The algorithm is roughly:
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci * 	1. Allocate some space in the queue. At this point we also discover
5018c2ecf20Sopenharmony_ci *	   whether the head of the queue is currently owned by another CPU,
5028c2ecf20Sopenharmony_ci *	   or whether we are the owner.
5038c2ecf20Sopenharmony_ci *
5048c2ecf20Sopenharmony_ci *	2. Write our commands into our allocated slots in the queue.
5058c2ecf20Sopenharmony_ci *
5068c2ecf20Sopenharmony_ci *	3. Mark our slots as valid in arm_smmu_cmdq.valid_map.
5078c2ecf20Sopenharmony_ci *
5088c2ecf20Sopenharmony_ci *	4. If we are an owner:
5098c2ecf20Sopenharmony_ci *		a. Wait for the previous owner to finish.
5108c2ecf20Sopenharmony_ci *		b. Mark the queue head as unowned, which tells us the range
5118c2ecf20Sopenharmony_ci *		   that we are responsible for publishing.
5128c2ecf20Sopenharmony_ci *		c. Wait for all commands in our owned range to become valid.
5138c2ecf20Sopenharmony_ci *		d. Advance the hardware prod pointer.
5148c2ecf20Sopenharmony_ci *		e. Tell the next owner we've finished.
5158c2ecf20Sopenharmony_ci *
5168c2ecf20Sopenharmony_ci *	5. If we are inserting a CMD_SYNC (we may or may not have been an
5178c2ecf20Sopenharmony_ci *	   owner), then we need to stick around until it has completed:
5188c2ecf20Sopenharmony_ci *		a. If we have MSIs, the SMMU can write back into the CMD_SYNC
5198c2ecf20Sopenharmony_ci *		   to clear the first 4 bytes.
5208c2ecf20Sopenharmony_ci *		b. Otherwise, we spin waiting for the hardware cons pointer to
5218c2ecf20Sopenharmony_ci *		   advance past our command.
5228c2ecf20Sopenharmony_ci *
5238c2ecf20Sopenharmony_ci * The devil is in the details, particularly the use of locking for handling
5248c2ecf20Sopenharmony_ci * SYNC completion and freeing up space in the queue before we think that it is
5258c2ecf20Sopenharmony_ci * full.
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_cistatic void __arm_smmu_cmdq_poll_set_valid_map(struct arm_smmu_cmdq *cmdq,
5288c2ecf20Sopenharmony_ci					       u32 sprod, u32 eprod, bool set)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	u32 swidx, sbidx, ewidx, ebidx;
5318c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue llq = {
5328c2ecf20Sopenharmony_ci		.max_n_shift	= cmdq->q.llq.max_n_shift,
5338c2ecf20Sopenharmony_ci		.prod		= sprod,
5348c2ecf20Sopenharmony_ci	};
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	ewidx = BIT_WORD(Q_IDX(&llq, eprod));
5378c2ecf20Sopenharmony_ci	ebidx = Q_IDX(&llq, eprod) % BITS_PER_LONG;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	while (llq.prod != eprod) {
5408c2ecf20Sopenharmony_ci		unsigned long mask;
5418c2ecf20Sopenharmony_ci		atomic_long_t *ptr;
5428c2ecf20Sopenharmony_ci		u32 limit = BITS_PER_LONG;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		swidx = BIT_WORD(Q_IDX(&llq, llq.prod));
5458c2ecf20Sopenharmony_ci		sbidx = Q_IDX(&llq, llq.prod) % BITS_PER_LONG;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		ptr = &cmdq->valid_map[swidx];
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		if ((swidx == ewidx) && (sbidx < ebidx))
5508c2ecf20Sopenharmony_ci			limit = ebidx;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		mask = GENMASK(limit - 1, sbidx);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		/*
5558c2ecf20Sopenharmony_ci		 * The valid bit is the inverse of the wrap bit. This means
5568c2ecf20Sopenharmony_ci		 * that a zero-initialised queue is invalid and, after marking
5578c2ecf20Sopenharmony_ci		 * all entries as valid, they become invalid again when we
5588c2ecf20Sopenharmony_ci		 * wrap.
5598c2ecf20Sopenharmony_ci		 */
5608c2ecf20Sopenharmony_ci		if (set) {
5618c2ecf20Sopenharmony_ci			atomic_long_xor(mask, ptr);
5628c2ecf20Sopenharmony_ci		} else { /* Poll */
5638c2ecf20Sopenharmony_ci			unsigned long valid;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			valid = (ULONG_MAX + !!Q_WRP(&llq, llq.prod)) & mask;
5668c2ecf20Sopenharmony_ci			atomic_long_cond_read_relaxed(ptr, (VAL & mask) == valid);
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		llq.prod = queue_inc_prod_n(&llq, limit - sbidx);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/* Mark all entries in the range [sprod, eprod) as valid */
5748c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_set_valid_map(struct arm_smmu_cmdq *cmdq,
5758c2ecf20Sopenharmony_ci					u32 sprod, u32 eprod)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	__arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, true);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/* Wait for all entries in the range [sprod, eprod) to become valid */
5818c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq,
5828c2ecf20Sopenharmony_ci					 u32 sprod, u32 eprod)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	__arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, false);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci/* Wait for the command queue to become non-full */
5888c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu,
5898c2ecf20Sopenharmony_ci					     struct arm_smmu_ll_queue *llq)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	unsigned long flags;
5928c2ecf20Sopenharmony_ci	struct arm_smmu_queue_poll qp;
5938c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
5948c2ecf20Sopenharmony_ci	int ret = 0;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	/*
5978c2ecf20Sopenharmony_ci	 * Try to update our copy of cons by grabbing exclusive cmdq access. If
5988c2ecf20Sopenharmony_ci	 * that fails, spin until somebody else updates it for us.
5998c2ecf20Sopenharmony_ci	 */
6008c2ecf20Sopenharmony_ci	if (arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)) {
6018c2ecf20Sopenharmony_ci		WRITE_ONCE(cmdq->q.llq.cons, readl_relaxed(cmdq->q.cons_reg));
6028c2ecf20Sopenharmony_ci		arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags);
6038c2ecf20Sopenharmony_ci		llq->val = READ_ONCE(cmdq->q.llq.val);
6048c2ecf20Sopenharmony_ci		return 0;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	queue_poll_init(smmu, &qp);
6088c2ecf20Sopenharmony_ci	do {
6098c2ecf20Sopenharmony_ci		llq->val = READ_ONCE(smmu->cmdq.q.llq.val);
6108c2ecf20Sopenharmony_ci		if (!queue_full(llq))
6118c2ecf20Sopenharmony_ci			break;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		ret = queue_poll(&qp);
6148c2ecf20Sopenharmony_ci	} while (!ret);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return ret;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/*
6208c2ecf20Sopenharmony_ci * Wait until the SMMU signals a CMD_SYNC completion MSI.
6218c2ecf20Sopenharmony_ci * Must be called with the cmdq lock held in some capacity.
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu,
6248c2ecf20Sopenharmony_ci					  struct arm_smmu_ll_queue *llq)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	int ret = 0;
6278c2ecf20Sopenharmony_ci	struct arm_smmu_queue_poll qp;
6288c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
6298c2ecf20Sopenharmony_ci	u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod));
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	queue_poll_init(smmu, &qp);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	/*
6348c2ecf20Sopenharmony_ci	 * The MSI won't generate an event, since it's being written back
6358c2ecf20Sopenharmony_ci	 * into the command queue.
6368c2ecf20Sopenharmony_ci	 */
6378c2ecf20Sopenharmony_ci	qp.wfe = false;
6388c2ecf20Sopenharmony_ci	smp_cond_load_relaxed(cmd, !VAL || (ret = queue_poll(&qp)));
6398c2ecf20Sopenharmony_ci	llq->cons = ret ? llq->prod : queue_inc_prod_n(llq, 1);
6408c2ecf20Sopenharmony_ci	return ret;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/*
6448c2ecf20Sopenharmony_ci * Wait until the SMMU cons index passes llq->prod.
6458c2ecf20Sopenharmony_ci * Must be called with the cmdq lock held in some capacity.
6468c2ecf20Sopenharmony_ci */
6478c2ecf20Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu,
6488c2ecf20Sopenharmony_ci					       struct arm_smmu_ll_queue *llq)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	struct arm_smmu_queue_poll qp;
6518c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
6528c2ecf20Sopenharmony_ci	u32 prod = llq->prod;
6538c2ecf20Sopenharmony_ci	int ret = 0;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	queue_poll_init(smmu, &qp);
6568c2ecf20Sopenharmony_ci	llq->val = READ_ONCE(smmu->cmdq.q.llq.val);
6578c2ecf20Sopenharmony_ci	do {
6588c2ecf20Sopenharmony_ci		if (queue_consumed(llq, prod))
6598c2ecf20Sopenharmony_ci			break;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		ret = queue_poll(&qp);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		/*
6648c2ecf20Sopenharmony_ci		 * This needs to be a readl() so that our subsequent call
6658c2ecf20Sopenharmony_ci		 * to arm_smmu_cmdq_shared_tryunlock() can fail accurately.
6668c2ecf20Sopenharmony_ci		 *
6678c2ecf20Sopenharmony_ci		 * Specifically, we need to ensure that we observe all
6688c2ecf20Sopenharmony_ci		 * shared_lock()s by other CMD_SYNCs that share our owner,
6698c2ecf20Sopenharmony_ci		 * so that a failing call to tryunlock() means that we're
6708c2ecf20Sopenharmony_ci		 * the last one out and therefore we can safely advance
6718c2ecf20Sopenharmony_ci		 * cmdq->q.llq.cons. Roughly speaking:
6728c2ecf20Sopenharmony_ci		 *
6738c2ecf20Sopenharmony_ci		 * CPU 0		CPU1			CPU2 (us)
6748c2ecf20Sopenharmony_ci		 *
6758c2ecf20Sopenharmony_ci		 * if (sync)
6768c2ecf20Sopenharmony_ci		 * 	shared_lock();
6778c2ecf20Sopenharmony_ci		 *
6788c2ecf20Sopenharmony_ci		 * dma_wmb();
6798c2ecf20Sopenharmony_ci		 * set_valid_map();
6808c2ecf20Sopenharmony_ci		 *
6818c2ecf20Sopenharmony_ci		 * 			if (owner) {
6828c2ecf20Sopenharmony_ci		 *				poll_valid_map();
6838c2ecf20Sopenharmony_ci		 *				<control dependency>
6848c2ecf20Sopenharmony_ci		 *				writel(prod_reg);
6858c2ecf20Sopenharmony_ci		 *
6868c2ecf20Sopenharmony_ci		 *						readl(cons_reg);
6878c2ecf20Sopenharmony_ci		 *						tryunlock();
6888c2ecf20Sopenharmony_ci		 *
6898c2ecf20Sopenharmony_ci		 * Requires us to see CPU 0's shared_lock() acquisition.
6908c2ecf20Sopenharmony_ci		 */
6918c2ecf20Sopenharmony_ci		llq->cons = readl(cmdq->q.cons_reg);
6928c2ecf20Sopenharmony_ci	} while (!ret);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	return ret;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu,
6988c2ecf20Sopenharmony_ci					 struct arm_smmu_ll_queue *llq)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_MSIPOLL)
7018c2ecf20Sopenharmony_ci		return __arm_smmu_cmdq_poll_until_msi(smmu, llq);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	return __arm_smmu_cmdq_poll_until_consumed(smmu, llq);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds,
7078c2ecf20Sopenharmony_ci					u32 prod, int n)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	int i;
7108c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue llq = {
7118c2ecf20Sopenharmony_ci		.max_n_shift	= cmdq->q.llq.max_n_shift,
7128c2ecf20Sopenharmony_ci		.prod		= prod,
7138c2ecf20Sopenharmony_ci	};
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	for (i = 0; i < n; ++i) {
7168c2ecf20Sopenharmony_ci		u64 *cmd = &cmds[i * CMDQ_ENT_DWORDS];
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		prod = queue_inc_prod_n(&llq, i);
7198c2ecf20Sopenharmony_ci		queue_write(Q_ENT(&cmdq->q, prod), cmd, CMDQ_ENT_DWORDS);
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/*
7248c2ecf20Sopenharmony_ci * This is the actual insertion function, and provides the following
7258c2ecf20Sopenharmony_ci * ordering guarantees to callers:
7268c2ecf20Sopenharmony_ci *
7278c2ecf20Sopenharmony_ci * - There is a dma_wmb() before publishing any commands to the queue.
7288c2ecf20Sopenharmony_ci *   This can be relied upon to order prior writes to data structures
7298c2ecf20Sopenharmony_ci *   in memory (such as a CD or an STE) before the command.
7308c2ecf20Sopenharmony_ci *
7318c2ecf20Sopenharmony_ci * - On completion of a CMD_SYNC, there is a control dependency.
7328c2ecf20Sopenharmony_ci *   This can be relied upon to order subsequent writes to memory (e.g.
7338c2ecf20Sopenharmony_ci *   freeing an IOVA) after completion of the CMD_SYNC.
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci * - Command insertion is totally ordered, so if two CPUs each race to
7368c2ecf20Sopenharmony_ci *   insert their own list of commands then all of the commands from one
7378c2ecf20Sopenharmony_ci *   CPU will appear before any of the commands from the other CPU.
7388c2ecf20Sopenharmony_ci */
7398c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
7408c2ecf20Sopenharmony_ci				       u64 *cmds, int n, bool sync)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	u64 cmd_sync[CMDQ_ENT_DWORDS];
7438c2ecf20Sopenharmony_ci	u32 prod;
7448c2ecf20Sopenharmony_ci	unsigned long flags;
7458c2ecf20Sopenharmony_ci	bool owner;
7468c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
7478c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue llq = {
7488c2ecf20Sopenharmony_ci		.max_n_shift = cmdq->q.llq.max_n_shift,
7498c2ecf20Sopenharmony_ci	}, head = llq;
7508c2ecf20Sopenharmony_ci	int ret = 0;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* 1. Allocate some space in the queue */
7538c2ecf20Sopenharmony_ci	local_irq_save(flags);
7548c2ecf20Sopenharmony_ci	llq.val = READ_ONCE(cmdq->q.llq.val);
7558c2ecf20Sopenharmony_ci	do {
7568c2ecf20Sopenharmony_ci		u64 old;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		while (!queue_has_space(&llq, n + sync)) {
7598c2ecf20Sopenharmony_ci			local_irq_restore(flags);
7608c2ecf20Sopenharmony_ci			if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq))
7618c2ecf20Sopenharmony_ci				dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
7628c2ecf20Sopenharmony_ci			local_irq_save(flags);
7638c2ecf20Sopenharmony_ci		}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		head.cons = llq.cons;
7668c2ecf20Sopenharmony_ci		head.prod = queue_inc_prod_n(&llq, n + sync) |
7678c2ecf20Sopenharmony_ci					     CMDQ_PROD_OWNED_FLAG;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		old = cmpxchg_relaxed(&cmdq->q.llq.val, llq.val, head.val);
7708c2ecf20Sopenharmony_ci		if (old == llq.val)
7718c2ecf20Sopenharmony_ci			break;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		llq.val = old;
7748c2ecf20Sopenharmony_ci	} while (1);
7758c2ecf20Sopenharmony_ci	owner = !(llq.prod & CMDQ_PROD_OWNED_FLAG);
7768c2ecf20Sopenharmony_ci	head.prod &= ~CMDQ_PROD_OWNED_FLAG;
7778c2ecf20Sopenharmony_ci	llq.prod &= ~CMDQ_PROD_OWNED_FLAG;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/*
7808c2ecf20Sopenharmony_ci	 * 2. Write our commands into the queue
7818c2ecf20Sopenharmony_ci	 * Dependency ordering from the cmpxchg() loop above.
7828c2ecf20Sopenharmony_ci	 */
7838c2ecf20Sopenharmony_ci	arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n);
7848c2ecf20Sopenharmony_ci	if (sync) {
7858c2ecf20Sopenharmony_ci		prod = queue_inc_prod_n(&llq, n);
7868c2ecf20Sopenharmony_ci		arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, prod);
7878c2ecf20Sopenharmony_ci		queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		/*
7908c2ecf20Sopenharmony_ci		 * In order to determine completion of our CMD_SYNC, we must
7918c2ecf20Sopenharmony_ci		 * ensure that the queue can't wrap twice without us noticing.
7928c2ecf20Sopenharmony_ci		 * We achieve that by taking the cmdq lock as shared before
7938c2ecf20Sopenharmony_ci		 * marking our slot as valid.
7948c2ecf20Sopenharmony_ci		 */
7958c2ecf20Sopenharmony_ci		arm_smmu_cmdq_shared_lock(cmdq);
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* 3. Mark our slots as valid, ensuring commands are visible first */
7998c2ecf20Sopenharmony_ci	dma_wmb();
8008c2ecf20Sopenharmony_ci	arm_smmu_cmdq_set_valid_map(cmdq, llq.prod, head.prod);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* 4. If we are the owner, take control of the SMMU hardware */
8038c2ecf20Sopenharmony_ci	if (owner) {
8048c2ecf20Sopenharmony_ci		/* a. Wait for previous owner to finish */
8058c2ecf20Sopenharmony_ci		atomic_cond_read_relaxed(&cmdq->owner_prod, VAL == llq.prod);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci		/* b. Stop gathering work by clearing the owned flag */
8088c2ecf20Sopenharmony_ci		prod = atomic_fetch_andnot_relaxed(CMDQ_PROD_OWNED_FLAG,
8098c2ecf20Sopenharmony_ci						   &cmdq->q.llq.atomic.prod);
8108c2ecf20Sopenharmony_ci		prod &= ~CMDQ_PROD_OWNED_FLAG;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci		/*
8138c2ecf20Sopenharmony_ci		 * c. Wait for any gathered work to be written to the queue.
8148c2ecf20Sopenharmony_ci		 * Note that we read our own entries so that we have the control
8158c2ecf20Sopenharmony_ci		 * dependency required by (d).
8168c2ecf20Sopenharmony_ci		 */
8178c2ecf20Sopenharmony_ci		arm_smmu_cmdq_poll_valid_map(cmdq, llq.prod, prod);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci		/*
8208c2ecf20Sopenharmony_ci		 * d. Advance the hardware prod pointer
8218c2ecf20Sopenharmony_ci		 * Control dependency ordering from the entries becoming valid.
8228c2ecf20Sopenharmony_ci		 */
8238c2ecf20Sopenharmony_ci		writel_relaxed(prod, cmdq->q.prod_reg);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		/*
8268c2ecf20Sopenharmony_ci		 * e. Tell the next owner we're done
8278c2ecf20Sopenharmony_ci		 * Make sure we've updated the hardware first, so that we don't
8288c2ecf20Sopenharmony_ci		 * race to update prod and potentially move it backwards.
8298c2ecf20Sopenharmony_ci		 */
8308c2ecf20Sopenharmony_ci		atomic_set_release(&cmdq->owner_prod, prod);
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */
8348c2ecf20Sopenharmony_ci	if (sync) {
8358c2ecf20Sopenharmony_ci		llq.prod = queue_inc_prod_n(&llq, n);
8368c2ecf20Sopenharmony_ci		ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq);
8378c2ecf20Sopenharmony_ci		if (ret) {
8388c2ecf20Sopenharmony_ci			dev_err_ratelimited(smmu->dev,
8398c2ecf20Sopenharmony_ci					    "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n",
8408c2ecf20Sopenharmony_ci					    llq.prod,
8418c2ecf20Sopenharmony_ci					    readl_relaxed(cmdq->q.prod_reg),
8428c2ecf20Sopenharmony_ci					    readl_relaxed(cmdq->q.cons_reg));
8438c2ecf20Sopenharmony_ci		}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci		/*
8468c2ecf20Sopenharmony_ci		 * Try to unlock the cmdq lock. This will fail if we're the last
8478c2ecf20Sopenharmony_ci		 * reader, in which case we can safely update cmdq->q.llq.cons
8488c2ecf20Sopenharmony_ci		 */
8498c2ecf20Sopenharmony_ci		if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) {
8508c2ecf20Sopenharmony_ci			WRITE_ONCE(cmdq->q.llq.cons, llq.cons);
8518c2ecf20Sopenharmony_ci			arm_smmu_cmdq_shared_unlock(cmdq);
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	local_irq_restore(flags);
8568c2ecf20Sopenharmony_ci	return ret;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
8608c2ecf20Sopenharmony_ci				   struct arm_smmu_cmdq_ent *ent)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	u64 cmd[CMDQ_ENT_DWORDS];
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (arm_smmu_cmdq_build_cmd(cmd, ent)) {
8658c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
8668c2ecf20Sopenharmony_ci			 ent->opcode);
8678c2ecf20Sopenharmony_ci		return -EINVAL;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, false);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	return arm_smmu_cmdq_issue_cmdlist(smmu, NULL, 0, true);
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
8798c2ecf20Sopenharmony_ci				    struct arm_smmu_cmdq_batch *cmds,
8808c2ecf20Sopenharmony_ci				    struct arm_smmu_cmdq_ent *cmd)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	if (cmds->num == CMDQ_BATCH_ENTRIES) {
8838c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false);
8848c2ecf20Sopenharmony_ci		cmds->num = 0;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci	arm_smmu_cmdq_build_cmd(&cmds->cmds[cmds->num * CMDQ_ENT_DWORDS], cmd);
8878c2ecf20Sopenharmony_ci	cmds->num++;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
8918c2ecf20Sopenharmony_ci				      struct arm_smmu_cmdq_batch *cmds)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true);
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/* Context descriptor manipulation functions */
8978c2ecf20Sopenharmony_civoid arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
9008c2ecf20Sopenharmony_ci		.opcode = CMDQ_OP_TLBI_NH_ASID,
9018c2ecf20Sopenharmony_ci		.tlbi.asid = asid,
9028c2ecf20Sopenharmony_ci	};
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
9058c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_sync(smmu);
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain,
9098c2ecf20Sopenharmony_ci			     int ssid, bool leaf)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	size_t i;
9128c2ecf20Sopenharmony_ci	unsigned long flags;
9138c2ecf20Sopenharmony_ci	struct arm_smmu_master *master;
9148c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds = {};
9158c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
9168c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
9178c2ecf20Sopenharmony_ci		.opcode	= CMDQ_OP_CFGI_CD,
9188c2ecf20Sopenharmony_ci		.cfgi	= {
9198c2ecf20Sopenharmony_ci			.ssid	= ssid,
9208c2ecf20Sopenharmony_ci			.leaf	= leaf,
9218c2ecf20Sopenharmony_ci		},
9228c2ecf20Sopenharmony_ci	};
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
9258c2ecf20Sopenharmony_ci	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
9268c2ecf20Sopenharmony_ci		for (i = 0; i < master->num_sids; i++) {
9278c2ecf20Sopenharmony_ci			cmd.cfgi.sid = master->sids[i];
9288c2ecf20Sopenharmony_ci			arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
9298c2ecf20Sopenharmony_ci		}
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	arm_smmu_cmdq_batch_submit(smmu, &cmds);
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic int arm_smmu_alloc_cd_leaf_table(struct arm_smmu_device *smmu,
9378c2ecf20Sopenharmony_ci					struct arm_smmu_l1_ctx_desc *l1_desc)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	size_t size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	l1_desc->l2ptr = dmam_alloc_coherent(smmu->dev, size,
9428c2ecf20Sopenharmony_ci					     &l1_desc->l2ptr_dma, GFP_KERNEL);
9438c2ecf20Sopenharmony_ci	if (!l1_desc->l2ptr) {
9448c2ecf20Sopenharmony_ci		dev_warn(smmu->dev,
9458c2ecf20Sopenharmony_ci			 "failed to allocate context descriptor table\n");
9468c2ecf20Sopenharmony_ci		return -ENOMEM;
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci	return 0;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic void arm_smmu_write_cd_l1_desc(__le64 *dst,
9528c2ecf20Sopenharmony_ci				      struct arm_smmu_l1_ctx_desc *l1_desc)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	u64 val = (l1_desc->l2ptr_dma & CTXDESC_L1_DESC_L2PTR_MASK) |
9558c2ecf20Sopenharmony_ci		  CTXDESC_L1_DESC_V;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
9588c2ecf20Sopenharmony_ci	WRITE_ONCE(*dst, cpu_to_le64(val));
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_domain *smmu_domain,
9628c2ecf20Sopenharmony_ci				   u32 ssid)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	__le64 *l1ptr;
9658c2ecf20Sopenharmony_ci	unsigned int idx;
9668c2ecf20Sopenharmony_ci	struct arm_smmu_l1_ctx_desc *l1_desc;
9678c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
9688c2ecf20Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (smmu_domain->s1_cfg.s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
9718c2ecf20Sopenharmony_ci		return cdcfg->cdtab + ssid * CTXDESC_CD_DWORDS;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	idx = ssid >> CTXDESC_SPLIT;
9748c2ecf20Sopenharmony_ci	l1_desc = &cdcfg->l1_desc[idx];
9758c2ecf20Sopenharmony_ci	if (!l1_desc->l2ptr) {
9768c2ecf20Sopenharmony_ci		if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
9778c2ecf20Sopenharmony_ci			return NULL;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci		l1ptr = cdcfg->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
9808c2ecf20Sopenharmony_ci		arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
9818c2ecf20Sopenharmony_ci		/* An invalid L1CD can be cached */
9828c2ecf20Sopenharmony_ci		arm_smmu_sync_cd(smmu_domain, ssid, false);
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci	idx = ssid & (CTXDESC_L2_ENTRIES - 1);
9858c2ecf20Sopenharmony_ci	return l1_desc->l2ptr + idx * CTXDESC_CD_DWORDS;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ciint arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid,
9898c2ecf20Sopenharmony_ci			    struct arm_smmu_ctx_desc *cd)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	/*
9928c2ecf20Sopenharmony_ci	 * This function handles the following cases:
9938c2ecf20Sopenharmony_ci	 *
9948c2ecf20Sopenharmony_ci	 * (1) Install primary CD, for normal DMA traffic (SSID = 0).
9958c2ecf20Sopenharmony_ci	 * (2) Install a secondary CD, for SID+SSID traffic.
9968c2ecf20Sopenharmony_ci	 * (3) Update ASID of a CD. Atomically write the first 64 bits of the
9978c2ecf20Sopenharmony_ci	 *     CD, then invalidate the old entry and mappings.
9988c2ecf20Sopenharmony_ci	 * (4) Remove a secondary CD.
9998c2ecf20Sopenharmony_ci	 */
10008c2ecf20Sopenharmony_ci	u64 val;
10018c2ecf20Sopenharmony_ci	bool cd_live;
10028c2ecf20Sopenharmony_ci	__le64 *cdptr;
10038c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg.s1cdmax)))
10068c2ecf20Sopenharmony_ci		return -E2BIG;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	cdptr = arm_smmu_get_cd_ptr(smmu_domain, ssid);
10098c2ecf20Sopenharmony_ci	if (!cdptr)
10108c2ecf20Sopenharmony_ci		return -ENOMEM;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	val = le64_to_cpu(cdptr[0]);
10138c2ecf20Sopenharmony_ci	cd_live = !!(val & CTXDESC_CD_0_V);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (!cd) { /* (4) */
10168c2ecf20Sopenharmony_ci		val = 0;
10178c2ecf20Sopenharmony_ci	} else if (cd_live) { /* (3) */
10188c2ecf20Sopenharmony_ci		val &= ~CTXDESC_CD_0_ASID;
10198c2ecf20Sopenharmony_ci		val |= FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid);
10208c2ecf20Sopenharmony_ci		/*
10218c2ecf20Sopenharmony_ci		 * Until CD+TLB invalidation, both ASIDs may be used for tagging
10228c2ecf20Sopenharmony_ci		 * this substream's traffic
10238c2ecf20Sopenharmony_ci		 */
10248c2ecf20Sopenharmony_ci	} else { /* (1) and (2) */
10258c2ecf20Sopenharmony_ci		cdptr[1] = cpu_to_le64(cd->ttbr & CTXDESC_CD_1_TTB0_MASK);
10268c2ecf20Sopenharmony_ci		cdptr[2] = 0;
10278c2ecf20Sopenharmony_ci		cdptr[3] = cpu_to_le64(cd->mair);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci		/*
10308c2ecf20Sopenharmony_ci		 * STE is live, and the SMMU might read dwords of this CD in any
10318c2ecf20Sopenharmony_ci		 * order. Ensure that it observes valid values before reading
10328c2ecf20Sopenharmony_ci		 * V=1.
10338c2ecf20Sopenharmony_ci		 */
10348c2ecf20Sopenharmony_ci		arm_smmu_sync_cd(smmu_domain, ssid, true);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci		val = cd->tcr |
10378c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
10388c2ecf20Sopenharmony_ci			CTXDESC_CD_0_ENDI |
10398c2ecf20Sopenharmony_ci#endif
10408c2ecf20Sopenharmony_ci			CTXDESC_CD_0_R | CTXDESC_CD_0_A |
10418c2ecf20Sopenharmony_ci			(cd->mm ? 0 : CTXDESC_CD_0_ASET) |
10428c2ecf20Sopenharmony_ci			CTXDESC_CD_0_AA64 |
10438c2ecf20Sopenharmony_ci			FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) |
10448c2ecf20Sopenharmony_ci			CTXDESC_CD_0_V;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci		/* STALL_MODEL==0b10 && CD.S==0 is ILLEGAL */
10478c2ecf20Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
10488c2ecf20Sopenharmony_ci			val |= CTXDESC_CD_0_S;
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/*
10528c2ecf20Sopenharmony_ci	 * The SMMU accesses 64-bit values atomically. See IHI0070Ca 3.21.3
10538c2ecf20Sopenharmony_ci	 * "Configuration structures and configuration invalidation completion"
10548c2ecf20Sopenharmony_ci	 *
10558c2ecf20Sopenharmony_ci	 *   The size of single-copy atomic reads made by the SMMU is
10568c2ecf20Sopenharmony_ci	 *   IMPLEMENTATION DEFINED but must be at least 64 bits. Any single
10578c2ecf20Sopenharmony_ci	 *   field within an aligned 64-bit span of a structure can be altered
10588c2ecf20Sopenharmony_ci	 *   without first making the structure invalid.
10598c2ecf20Sopenharmony_ci	 */
10608c2ecf20Sopenharmony_ci	WRITE_ONCE(cdptr[0], cpu_to_le64(val));
10618c2ecf20Sopenharmony_ci	arm_smmu_sync_cd(smmu_domain, ssid, true);
10628c2ecf20Sopenharmony_ci	return 0;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic int arm_smmu_alloc_cd_tables(struct arm_smmu_domain *smmu_domain)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	int ret;
10688c2ecf20Sopenharmony_ci	size_t l1size;
10698c2ecf20Sopenharmony_ci	size_t max_contexts;
10708c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
10718c2ecf20Sopenharmony_ci	struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
10728c2ecf20Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &cfg->cdcfg;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	max_contexts = 1 << cfg->s1cdmax;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) ||
10778c2ecf20Sopenharmony_ci	    max_contexts <= CTXDESC_L2_ENTRIES) {
10788c2ecf20Sopenharmony_ci		cfg->s1fmt = STRTAB_STE_0_S1FMT_LINEAR;
10798c2ecf20Sopenharmony_ci		cdcfg->num_l1_ents = max_contexts;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci		l1size = max_contexts * (CTXDESC_CD_DWORDS << 3);
10828c2ecf20Sopenharmony_ci	} else {
10838c2ecf20Sopenharmony_ci		cfg->s1fmt = STRTAB_STE_0_S1FMT_64K_L2;
10848c2ecf20Sopenharmony_ci		cdcfg->num_l1_ents = DIV_ROUND_UP(max_contexts,
10858c2ecf20Sopenharmony_ci						  CTXDESC_L2_ENTRIES);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		cdcfg->l1_desc = devm_kcalloc(smmu->dev, cdcfg->num_l1_ents,
10888c2ecf20Sopenharmony_ci					      sizeof(*cdcfg->l1_desc),
10898c2ecf20Sopenharmony_ci					      GFP_KERNEL);
10908c2ecf20Sopenharmony_ci		if (!cdcfg->l1_desc)
10918c2ecf20Sopenharmony_ci			return -ENOMEM;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3);
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	cdcfg->cdtab = dmam_alloc_coherent(smmu->dev, l1size, &cdcfg->cdtab_dma,
10978c2ecf20Sopenharmony_ci					   GFP_KERNEL);
10988c2ecf20Sopenharmony_ci	if (!cdcfg->cdtab) {
10998c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "failed to allocate context descriptor\n");
11008c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11018c2ecf20Sopenharmony_ci		goto err_free_l1;
11028c2ecf20Sopenharmony_ci	}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	return 0;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cierr_free_l1:
11078c2ecf20Sopenharmony_ci	if (cdcfg->l1_desc) {
11088c2ecf20Sopenharmony_ci		devm_kfree(smmu->dev, cdcfg->l1_desc);
11098c2ecf20Sopenharmony_ci		cdcfg->l1_desc = NULL;
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci	return ret;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	int i;
11178c2ecf20Sopenharmony_ci	size_t size, l1size;
11188c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
11198c2ecf20Sopenharmony_ci	struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (cdcfg->l1_desc) {
11228c2ecf20Sopenharmony_ci		size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		for (i = 0; i < cdcfg->num_l1_ents; i++) {
11258c2ecf20Sopenharmony_ci			if (!cdcfg->l1_desc[i].l2ptr)
11268c2ecf20Sopenharmony_ci				continue;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci			dmam_free_coherent(smmu->dev, size,
11298c2ecf20Sopenharmony_ci					   cdcfg->l1_desc[i].l2ptr,
11308c2ecf20Sopenharmony_ci					   cdcfg->l1_desc[i].l2ptr_dma);
11318c2ecf20Sopenharmony_ci		}
11328c2ecf20Sopenharmony_ci		devm_kfree(smmu->dev, cdcfg->l1_desc);
11338c2ecf20Sopenharmony_ci		cdcfg->l1_desc = NULL;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3);
11368c2ecf20Sopenharmony_ci	} else {
11378c2ecf20Sopenharmony_ci		l1size = cdcfg->num_l1_ents * (CTXDESC_CD_DWORDS << 3);
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	dmam_free_coherent(smmu->dev, l1size, cdcfg->cdtab, cdcfg->cdtab_dma);
11418c2ecf20Sopenharmony_ci	cdcfg->cdtab_dma = 0;
11428c2ecf20Sopenharmony_ci	cdcfg->cdtab = NULL;
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cibool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	bool free;
11488c2ecf20Sopenharmony_ci	struct arm_smmu_ctx_desc *old_cd;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (!cd->asid)
11518c2ecf20Sopenharmony_ci		return false;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	free = refcount_dec_and_test(&cd->refs);
11548c2ecf20Sopenharmony_ci	if (free) {
11558c2ecf20Sopenharmony_ci		old_cd = xa_erase(&arm_smmu_asid_xa, cd->asid);
11568c2ecf20Sopenharmony_ci		WARN_ON(old_cd != cd);
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci	return free;
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci/* Stream table manipulation functions */
11628c2ecf20Sopenharmony_cistatic void
11638c2ecf20Sopenharmony_ciarm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	u64 val = 0;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span);
11688c2ecf20Sopenharmony_ci	val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
11718c2ecf20Sopenharmony_ci	WRITE_ONCE(*dst, cpu_to_le64(val));
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
11778c2ecf20Sopenharmony_ci		.opcode	= CMDQ_OP_CFGI_STE,
11788c2ecf20Sopenharmony_ci		.cfgi	= {
11798c2ecf20Sopenharmony_ci			.sid	= sid,
11808c2ecf20Sopenharmony_ci			.leaf	= true,
11818c2ecf20Sopenharmony_ci		},
11828c2ecf20Sopenharmony_ci	};
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
11858c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_sync(smmu);
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
11898c2ecf20Sopenharmony_ci				      __le64 *dst)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	/*
11928c2ecf20Sopenharmony_ci	 * This is hideously complicated, but we only really care about
11938c2ecf20Sopenharmony_ci	 * three cases at the moment:
11948c2ecf20Sopenharmony_ci	 *
11958c2ecf20Sopenharmony_ci	 * 1. Invalid (all zero) -> bypass/fault (init)
11968c2ecf20Sopenharmony_ci	 * 2. Bypass/fault -> translation/bypass (attach)
11978c2ecf20Sopenharmony_ci	 * 3. Translation/bypass -> bypass/fault (detach)
11988c2ecf20Sopenharmony_ci	 *
11998c2ecf20Sopenharmony_ci	 * Given that we can't update the STE atomically and the SMMU
12008c2ecf20Sopenharmony_ci	 * doesn't read the thing in a defined order, that leaves us
12018c2ecf20Sopenharmony_ci	 * with the following maintenance requirements:
12028c2ecf20Sopenharmony_ci	 *
12038c2ecf20Sopenharmony_ci	 * 1. Update Config, return (init time STEs aren't live)
12048c2ecf20Sopenharmony_ci	 * 2. Write everything apart from dword 0, sync, write dword 0, sync
12058c2ecf20Sopenharmony_ci	 * 3. Update Config, sync
12068c2ecf20Sopenharmony_ci	 */
12078c2ecf20Sopenharmony_ci	u64 val = le64_to_cpu(dst[0]);
12088c2ecf20Sopenharmony_ci	bool ste_live = false;
12098c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = NULL;
12108c2ecf20Sopenharmony_ci	struct arm_smmu_s1_cfg *s1_cfg = NULL;
12118c2ecf20Sopenharmony_ci	struct arm_smmu_s2_cfg *s2_cfg = NULL;
12128c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = NULL;
12138c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent prefetch_cmd = {
12148c2ecf20Sopenharmony_ci		.opcode		= CMDQ_OP_PREFETCH_CFG,
12158c2ecf20Sopenharmony_ci		.prefetch	= {
12168c2ecf20Sopenharmony_ci			.sid	= sid,
12178c2ecf20Sopenharmony_ci		},
12188c2ecf20Sopenharmony_ci	};
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	if (master) {
12218c2ecf20Sopenharmony_ci		smmu_domain = master->domain;
12228c2ecf20Sopenharmony_ci		smmu = master->smmu;
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	if (smmu_domain) {
12268c2ecf20Sopenharmony_ci		switch (smmu_domain->stage) {
12278c2ecf20Sopenharmony_ci		case ARM_SMMU_DOMAIN_S1:
12288c2ecf20Sopenharmony_ci			s1_cfg = &smmu_domain->s1_cfg;
12298c2ecf20Sopenharmony_ci			break;
12308c2ecf20Sopenharmony_ci		case ARM_SMMU_DOMAIN_S2:
12318c2ecf20Sopenharmony_ci		case ARM_SMMU_DOMAIN_NESTED:
12328c2ecf20Sopenharmony_ci			s2_cfg = &smmu_domain->s2_cfg;
12338c2ecf20Sopenharmony_ci			break;
12348c2ecf20Sopenharmony_ci		default:
12358c2ecf20Sopenharmony_ci			break;
12368c2ecf20Sopenharmony_ci		}
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	if (val & STRTAB_STE_0_V) {
12408c2ecf20Sopenharmony_ci		switch (FIELD_GET(STRTAB_STE_0_CFG, val)) {
12418c2ecf20Sopenharmony_ci		case STRTAB_STE_0_CFG_BYPASS:
12428c2ecf20Sopenharmony_ci			break;
12438c2ecf20Sopenharmony_ci		case STRTAB_STE_0_CFG_S1_TRANS:
12448c2ecf20Sopenharmony_ci		case STRTAB_STE_0_CFG_S2_TRANS:
12458c2ecf20Sopenharmony_ci			ste_live = true;
12468c2ecf20Sopenharmony_ci			break;
12478c2ecf20Sopenharmony_ci		case STRTAB_STE_0_CFG_ABORT:
12488c2ecf20Sopenharmony_ci			BUG_ON(!disable_bypass);
12498c2ecf20Sopenharmony_ci			break;
12508c2ecf20Sopenharmony_ci		default:
12518c2ecf20Sopenharmony_ci			BUG(); /* STE corruption */
12528c2ecf20Sopenharmony_ci		}
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	/* Nuke the existing STE_0 value, as we're going to rewrite it */
12568c2ecf20Sopenharmony_ci	val = STRTAB_STE_0_V;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* Bypass/fault */
12598c2ecf20Sopenharmony_ci	if (!smmu_domain || !(s1_cfg || s2_cfg)) {
12608c2ecf20Sopenharmony_ci		if (!smmu_domain && disable_bypass)
12618c2ecf20Sopenharmony_ci			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
12628c2ecf20Sopenharmony_ci		else
12638c2ecf20Sopenharmony_ci			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci		dst[0] = cpu_to_le64(val);
12668c2ecf20Sopenharmony_ci		dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
12678c2ecf20Sopenharmony_ci						STRTAB_STE_1_SHCFG_INCOMING));
12688c2ecf20Sopenharmony_ci		dst[2] = 0; /* Nuke the VMID */
12698c2ecf20Sopenharmony_ci		/*
12708c2ecf20Sopenharmony_ci		 * The SMMU can perform negative caching, so we must sync
12718c2ecf20Sopenharmony_ci		 * the STE regardless of whether the old value was live.
12728c2ecf20Sopenharmony_ci		 */
12738c2ecf20Sopenharmony_ci		if (smmu)
12748c2ecf20Sopenharmony_ci			arm_smmu_sync_ste_for_sid(smmu, sid);
12758c2ecf20Sopenharmony_ci		return;
12768c2ecf20Sopenharmony_ci	}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	if (s1_cfg) {
12798c2ecf20Sopenharmony_ci		BUG_ON(ste_live);
12808c2ecf20Sopenharmony_ci		dst[1] = cpu_to_le64(
12818c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
12828c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
12838c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
12848c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
12858c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_1_STRW, STRTAB_STE_1_STRW_NSEL1));
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_STALLS &&
12888c2ecf20Sopenharmony_ci		   !(smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
12898c2ecf20Sopenharmony_ci			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci		val |= (s1_cfg->cdcfg.cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) |
12928c2ecf20Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) |
12938c2ecf20Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_S1CDMAX, s1_cfg->s1cdmax) |
12948c2ecf20Sopenharmony_ci			FIELD_PREP(STRTAB_STE_0_S1FMT, s1_cfg->s1fmt);
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	if (s2_cfg) {
12988c2ecf20Sopenharmony_ci		BUG_ON(ste_live);
12998c2ecf20Sopenharmony_ci		dst[2] = cpu_to_le64(
13008c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
13018c2ecf20Sopenharmony_ci			 FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) |
13028c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
13038c2ecf20Sopenharmony_ci			 STRTAB_STE_2_S2ENDI |
13048c2ecf20Sopenharmony_ci#endif
13058c2ecf20Sopenharmony_ci			 STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
13068c2ecf20Sopenharmony_ci			 STRTAB_STE_2_S2R);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	if (master->ats_enabled)
13148c2ecf20Sopenharmony_ci		dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS,
13158c2ecf20Sopenharmony_ci						 STRTAB_STE_1_EATS_TRANS));
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	arm_smmu_sync_ste_for_sid(smmu, sid);
13188c2ecf20Sopenharmony_ci	/* See comment in arm_smmu_write_ctx_desc() */
13198c2ecf20Sopenharmony_ci	WRITE_ONCE(dst[0], cpu_to_le64(val));
13208c2ecf20Sopenharmony_ci	arm_smmu_sync_ste_for_sid(smmu, sid);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	/* It's likely that we'll want to use the new STE soon */
13238c2ecf20Sopenharmony_ci	if (!(smmu->options & ARM_SMMU_OPT_SKIP_PREFETCH))
13248c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cistatic void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent)
13288c2ecf20Sopenharmony_ci{
13298c2ecf20Sopenharmony_ci	unsigned int i;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	for (i = 0; i < nent; ++i) {
13328c2ecf20Sopenharmony_ci		arm_smmu_write_strtab_ent(NULL, -1, strtab);
13338c2ecf20Sopenharmony_ci		strtab += STRTAB_STE_DWORDS;
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_cistatic int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
13388c2ecf20Sopenharmony_ci{
13398c2ecf20Sopenharmony_ci	size_t size;
13408c2ecf20Sopenharmony_ci	void *strtab;
13418c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
13428c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	if (desc->l2ptr)
13458c2ecf20Sopenharmony_ci		return 0;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
13488c2ecf20Sopenharmony_ci	strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	desc->span = STRTAB_SPLIT + 1;
13518c2ecf20Sopenharmony_ci	desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
13528c2ecf20Sopenharmony_ci					  GFP_KERNEL);
13538c2ecf20Sopenharmony_ci	if (!desc->l2ptr) {
13548c2ecf20Sopenharmony_ci		dev_err(smmu->dev,
13558c2ecf20Sopenharmony_ci			"failed to allocate l2 stream table for SID %u\n",
13568c2ecf20Sopenharmony_ci			sid);
13578c2ecf20Sopenharmony_ci		return -ENOMEM;
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);
13618c2ecf20Sopenharmony_ci	arm_smmu_write_strtab_l1_desc(strtab, desc);
13628c2ecf20Sopenharmony_ci	return 0;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci/* IRQ and event handlers */
13668c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
13678c2ecf20Sopenharmony_ci{
13688c2ecf20Sopenharmony_ci	int i;
13698c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
13708c2ecf20Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->evtq.q;
13718c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
13728c2ecf20Sopenharmony_ci	u64 evt[EVTQ_ENT_DWORDS];
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	do {
13758c2ecf20Sopenharmony_ci		while (!queue_remove_raw(q, evt)) {
13768c2ecf20Sopenharmony_ci			u8 id = FIELD_GET(EVTQ_0_ID, evt[0]);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci			dev_info(smmu->dev, "event 0x%02x received:\n", id);
13798c2ecf20Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(evt); ++i)
13808c2ecf20Sopenharmony_ci				dev_info(smmu->dev, "\t0x%016llx\n",
13818c2ecf20Sopenharmony_ci					 (unsigned long long)evt[i]);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci			cond_resched();
13848c2ecf20Sopenharmony_ci		}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		/*
13878c2ecf20Sopenharmony_ci		 * Not much we can do on overflow, so scream and pretend we're
13888c2ecf20Sopenharmony_ci		 * trying harder.
13898c2ecf20Sopenharmony_ci		 */
13908c2ecf20Sopenharmony_ci		if (queue_sync_prod_in(q) == -EOVERFLOW)
13918c2ecf20Sopenharmony_ci			dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n");
13928c2ecf20Sopenharmony_ci	} while (!queue_empty(llq));
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	/* Sync our overflow flag, as we believe we're up to speed */
13958c2ecf20Sopenharmony_ci	queue_sync_cons_ovf(q);
13968c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	u32 sid, ssid;
14028c2ecf20Sopenharmony_ci	u16 grpid;
14038c2ecf20Sopenharmony_ci	bool ssv, last;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	sid = FIELD_GET(PRIQ_0_SID, evt[0]);
14068c2ecf20Sopenharmony_ci	ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]);
14078c2ecf20Sopenharmony_ci	ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : 0;
14088c2ecf20Sopenharmony_ci	last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]);
14098c2ecf20Sopenharmony_ci	grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	dev_info(smmu->dev, "unexpected PRI request received:\n");
14128c2ecf20Sopenharmony_ci	dev_info(smmu->dev,
14138c2ecf20Sopenharmony_ci		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
14148c2ecf20Sopenharmony_ci		 sid, ssid, grpid, last ? "L" : "",
14158c2ecf20Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
14168c2ecf20Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_READ ? "R" : "",
14178c2ecf20Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
14188c2ecf20Sopenharmony_ci		 evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
14198c2ecf20Sopenharmony_ci		 evt[1] & PRIQ_1_ADDR_MASK);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	if (last) {
14228c2ecf20Sopenharmony_ci		struct arm_smmu_cmdq_ent cmd = {
14238c2ecf20Sopenharmony_ci			.opcode			= CMDQ_OP_PRI_RESP,
14248c2ecf20Sopenharmony_ci			.substream_valid	= ssv,
14258c2ecf20Sopenharmony_ci			.pri			= {
14268c2ecf20Sopenharmony_ci				.sid	= sid,
14278c2ecf20Sopenharmony_ci				.ssid	= ssid,
14288c2ecf20Sopenharmony_ci				.grpid	= grpid,
14298c2ecf20Sopenharmony_ci				.resp	= PRI_RESP_DENY,
14308c2ecf20Sopenharmony_ci			},
14318c2ecf20Sopenharmony_ci		};
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
14408c2ecf20Sopenharmony_ci	struct arm_smmu_queue *q = &smmu->priq.q;
14418c2ecf20Sopenharmony_ci	struct arm_smmu_ll_queue *llq = &q->llq;
14428c2ecf20Sopenharmony_ci	u64 evt[PRIQ_ENT_DWORDS];
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	do {
14458c2ecf20Sopenharmony_ci		while (!queue_remove_raw(q, evt))
14468c2ecf20Sopenharmony_ci			arm_smmu_handle_ppr(smmu, evt);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		if (queue_sync_prod_in(q) == -EOVERFLOW)
14498c2ecf20Sopenharmony_ci			dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n");
14508c2ecf20Sopenharmony_ci	} while (!queue_empty(llq));
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	/* Sync our overflow flag, as we believe we're up to speed */
14538c2ecf20Sopenharmony_ci	queue_sync_cons_ovf(q);
14548c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	u32 gerror, gerrorn, active;
14628c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
14658c2ecf20Sopenharmony_ci	gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	active = gerror ^ gerrorn;
14688c2ecf20Sopenharmony_ci	if (!(active & GERROR_ERR_MASK))
14698c2ecf20Sopenharmony_ci		return IRQ_NONE; /* No errors pending */
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	dev_warn(smmu->dev,
14728c2ecf20Sopenharmony_ci		 "unexpected global error reported (0x%08x), this could be serious\n",
14738c2ecf20Sopenharmony_ci		 active);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	if (active & GERROR_SFM_ERR) {
14768c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
14778c2ecf20Sopenharmony_ci		arm_smmu_device_disable(smmu);
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (active & GERROR_MSI_GERROR_ABT_ERR)
14818c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "GERROR MSI write aborted\n");
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (active & GERROR_MSI_PRIQ_ABT_ERR)
14848c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "PRIQ MSI write aborted\n");
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	if (active & GERROR_MSI_EVTQ_ABT_ERR)
14878c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "EVTQ MSI write aborted\n");
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	if (active & GERROR_MSI_CMDQ_ABT_ERR)
14908c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	if (active & GERROR_PRIQ_ABT_ERR)
14938c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n");
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	if (active & GERROR_EVTQ_ABT_ERR)
14968c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n");
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	if (active & GERROR_CMDQ_ERR)
14998c2ecf20Sopenharmony_ci		arm_smmu_cmdq_skip_err(smmu);
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	writel(gerror, smmu->base + ARM_SMMU_GERRORN);
15028c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev)
15068c2ecf20Sopenharmony_ci{
15078c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = dev;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	arm_smmu_evtq_thread(irq, dev);
15108c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
15118c2ecf20Sopenharmony_ci		arm_smmu_priq_thread(irq, dev);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
15148c2ecf20Sopenharmony_ci}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev)
15178c2ecf20Sopenharmony_ci{
15188c2ecf20Sopenharmony_ci	arm_smmu_gerror_handler(irq, dev);
15198c2ecf20Sopenharmony_ci	return IRQ_WAKE_THREAD;
15208c2ecf20Sopenharmony_ci}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_cistatic void
15238c2ecf20Sopenharmony_ciarm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size,
15248c2ecf20Sopenharmony_ci			struct arm_smmu_cmdq_ent *cmd)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	size_t log2_span;
15278c2ecf20Sopenharmony_ci	size_t span_mask;
15288c2ecf20Sopenharmony_ci	/* ATC invalidates are always on 4096-bytes pages */
15298c2ecf20Sopenharmony_ci	size_t inval_grain_shift = 12;
15308c2ecf20Sopenharmony_ci	unsigned long page_start, page_end;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	*cmd = (struct arm_smmu_cmdq_ent) {
15338c2ecf20Sopenharmony_ci		.opcode			= CMDQ_OP_ATC_INV,
15348c2ecf20Sopenharmony_ci		.substream_valid	= !!ssid,
15358c2ecf20Sopenharmony_ci		.atc.ssid		= ssid,
15368c2ecf20Sopenharmony_ci	};
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	if (!size) {
15398c2ecf20Sopenharmony_ci		cmd->atc.size = ATC_INV_SIZE_ALL;
15408c2ecf20Sopenharmony_ci		return;
15418c2ecf20Sopenharmony_ci	}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	page_start	= iova >> inval_grain_shift;
15448c2ecf20Sopenharmony_ci	page_end	= (iova + size - 1) >> inval_grain_shift;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	/*
15478c2ecf20Sopenharmony_ci	 * In an ATS Invalidate Request, the address must be aligned on the
15488c2ecf20Sopenharmony_ci	 * range size, which must be a power of two number of page sizes. We
15498c2ecf20Sopenharmony_ci	 * thus have to choose between grossly over-invalidating the region, or
15508c2ecf20Sopenharmony_ci	 * splitting the invalidation into multiple commands. For simplicity
15518c2ecf20Sopenharmony_ci	 * we'll go with the first solution, but should refine it in the future
15528c2ecf20Sopenharmony_ci	 * if multiple commands are shown to be more efficient.
15538c2ecf20Sopenharmony_ci	 *
15548c2ecf20Sopenharmony_ci	 * Find the smallest power of two that covers the range. The most
15558c2ecf20Sopenharmony_ci	 * significant differing bit between the start and end addresses,
15568c2ecf20Sopenharmony_ci	 * fls(start ^ end), indicates the required span. For example:
15578c2ecf20Sopenharmony_ci	 *
15588c2ecf20Sopenharmony_ci	 * We want to invalidate pages [8; 11]. This is already the ideal range:
15598c2ecf20Sopenharmony_ci	 *		x = 0b1000 ^ 0b1011 = 0b11
15608c2ecf20Sopenharmony_ci	 *		span = 1 << fls(x) = 4
15618c2ecf20Sopenharmony_ci	 *
15628c2ecf20Sopenharmony_ci	 * To invalidate pages [7; 10], we need to invalidate [0; 15]:
15638c2ecf20Sopenharmony_ci	 *		x = 0b0111 ^ 0b1010 = 0b1101
15648c2ecf20Sopenharmony_ci	 *		span = 1 << fls(x) = 16
15658c2ecf20Sopenharmony_ci	 */
15668c2ecf20Sopenharmony_ci	log2_span	= fls_long(page_start ^ page_end);
15678c2ecf20Sopenharmony_ci	span_mask	= (1ULL << log2_span) - 1;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	page_start	&= ~span_mask;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	cmd->atc.addr	= page_start << inval_grain_shift;
15728c2ecf20Sopenharmony_ci	cmd->atc.size	= log2_span;
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_cistatic int arm_smmu_atc_inv_master(struct arm_smmu_master *master)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	int i;
15788c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd);
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	for (i = 0; i < master->num_sids; i++) {
15838c2ecf20Sopenharmony_ci		cmd.atc.sid = master->sids[i];
15848c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(master->smmu, &cmd);
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	return arm_smmu_cmdq_issue_sync(master->smmu);
15888c2ecf20Sopenharmony_ci}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_cistatic int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
15918c2ecf20Sopenharmony_ci				   int ssid, unsigned long iova, size_t size)
15928c2ecf20Sopenharmony_ci{
15938c2ecf20Sopenharmony_ci	int i;
15948c2ecf20Sopenharmony_ci	unsigned long flags;
15958c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
15968c2ecf20Sopenharmony_ci	struct arm_smmu_master *master;
15978c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds = {};
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
16008c2ecf20Sopenharmony_ci		return 0;
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	/*
16038c2ecf20Sopenharmony_ci	 * Ensure that we've completed prior invalidation of the main TLBs
16048c2ecf20Sopenharmony_ci	 * before we read 'nr_ats_masters' in case of a concurrent call to
16058c2ecf20Sopenharmony_ci	 * arm_smmu_enable_ats():
16068c2ecf20Sopenharmony_ci	 *
16078c2ecf20Sopenharmony_ci	 *	// unmap()			// arm_smmu_enable_ats()
16088c2ecf20Sopenharmony_ci	 *	TLBI+SYNC			atomic_inc(&nr_ats_masters);
16098c2ecf20Sopenharmony_ci	 *	smp_mb();			[...]
16108c2ecf20Sopenharmony_ci	 *	atomic_read(&nr_ats_masters);	pci_enable_ats() // writel()
16118c2ecf20Sopenharmony_ci	 *
16128c2ecf20Sopenharmony_ci	 * Ensures that we always see the incremented 'nr_ats_masters' count if
16138c2ecf20Sopenharmony_ci	 * ATS was enabled at the PCI device before completion of the TLBI.
16148c2ecf20Sopenharmony_ci	 */
16158c2ecf20Sopenharmony_ci	smp_mb();
16168c2ecf20Sopenharmony_ci	if (!atomic_read(&smmu_domain->nr_ats_masters))
16178c2ecf20Sopenharmony_ci		return 0;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
16228c2ecf20Sopenharmony_ci	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
16238c2ecf20Sopenharmony_ci		if (!master->ats_enabled)
16248c2ecf20Sopenharmony_ci			continue;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci		for (i = 0; i < master->num_sids; i++) {
16278c2ecf20Sopenharmony_ci			cmd.atc.sid = master->sids[i];
16288c2ecf20Sopenharmony_ci			arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd);
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci	}
16318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	return arm_smmu_cmdq_batch_submit(smmu_domain->smmu, &cmds);
16348c2ecf20Sopenharmony_ci}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci/* IO_PGTABLE API */
16378c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_context(void *cookie)
16388c2ecf20Sopenharmony_ci{
16398c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = cookie;
16408c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
16418c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	/*
16448c2ecf20Sopenharmony_ci	 * NOTE: when io-pgtable is in non-strict mode, we may get here with
16458c2ecf20Sopenharmony_ci	 * PTEs previously cleared by unmaps on the current CPU not yet visible
16468c2ecf20Sopenharmony_ci	 * to the SMMU. We are relying on the dma_wmb() implicit during cmd
16478c2ecf20Sopenharmony_ci	 * insertion to guarantee those are observed before the TLBI. Do be
16488c2ecf20Sopenharmony_ci	 * careful, 007.
16498c2ecf20Sopenharmony_ci	 */
16508c2ecf20Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
16518c2ecf20Sopenharmony_ci		arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid);
16528c2ecf20Sopenharmony_ci	} else {
16538c2ecf20Sopenharmony_ci		cmd.opcode	= CMDQ_OP_TLBI_S12_VMALL;
16548c2ecf20Sopenharmony_ci		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
16558c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
16568c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_sync(smmu);
16578c2ecf20Sopenharmony_ci	}
16588c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0);
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
16628c2ecf20Sopenharmony_ci				   size_t granule, bool leaf,
16638c2ecf20Sopenharmony_ci				   struct arm_smmu_domain *smmu_domain)
16648c2ecf20Sopenharmony_ci{
16658c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
16668c2ecf20Sopenharmony_ci	unsigned long start = iova, end = iova + size, num_pages = 0, tg = 0;
16678c2ecf20Sopenharmony_ci	size_t inv_range = granule;
16688c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_batch cmds = {};
16698c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd = {
16708c2ecf20Sopenharmony_ci		.tlbi = {
16718c2ecf20Sopenharmony_ci			.leaf	= leaf,
16728c2ecf20Sopenharmony_ci		},
16738c2ecf20Sopenharmony_ci	};
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	if (!size)
16768c2ecf20Sopenharmony_ci		return;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
16798c2ecf20Sopenharmony_ci		cmd.opcode	= CMDQ_OP_TLBI_NH_VA;
16808c2ecf20Sopenharmony_ci		cmd.tlbi.asid	= smmu_domain->s1_cfg.cd.asid;
16818c2ecf20Sopenharmony_ci	} else {
16828c2ecf20Sopenharmony_ci		cmd.opcode	= CMDQ_OP_TLBI_S2_IPA;
16838c2ecf20Sopenharmony_ci		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
16878c2ecf20Sopenharmony_ci		/* Get the leaf page size */
16888c2ecf20Sopenharmony_ci		tg = __ffs(smmu_domain->domain.pgsize_bitmap);
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci		/* Convert page size of 12,14,16 (log2) to 1,2,3 */
16918c2ecf20Sopenharmony_ci		cmd.tlbi.tg = (tg - 10) / 2;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci		/* Determine what level the granule is at */
16948c2ecf20Sopenharmony_ci		cmd.tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3));
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci		num_pages = size >> tg;
16978c2ecf20Sopenharmony_ci	}
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	while (iova < end) {
17008c2ecf20Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
17018c2ecf20Sopenharmony_ci			/*
17028c2ecf20Sopenharmony_ci			 * On each iteration of the loop, the range is 5 bits
17038c2ecf20Sopenharmony_ci			 * worth of the aligned size remaining.
17048c2ecf20Sopenharmony_ci			 * The range in pages is:
17058c2ecf20Sopenharmony_ci			 *
17068c2ecf20Sopenharmony_ci			 * range = (num_pages & (0x1f << __ffs(num_pages)))
17078c2ecf20Sopenharmony_ci			 */
17088c2ecf20Sopenharmony_ci			unsigned long scale, num;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci			/* Determine the power of 2 multiple number of pages */
17118c2ecf20Sopenharmony_ci			scale = __ffs(num_pages);
17128c2ecf20Sopenharmony_ci			cmd.tlbi.scale = scale;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci			/* Determine how many chunks of 2^scale size we have */
17158c2ecf20Sopenharmony_ci			num = (num_pages >> scale) & CMDQ_TLBI_RANGE_NUM_MAX;
17168c2ecf20Sopenharmony_ci			cmd.tlbi.num = num - 1;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci			/* range is num * 2^scale * pgsize */
17198c2ecf20Sopenharmony_ci			inv_range = num << (scale + tg);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci			/* Clear out the lower order bits for the next iteration */
17228c2ecf20Sopenharmony_ci			num_pages -= num << scale;
17238c2ecf20Sopenharmony_ci		}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci		cmd.tlbi.addr = iova;
17268c2ecf20Sopenharmony_ci		arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
17278c2ecf20Sopenharmony_ci		iova += inv_range;
17288c2ecf20Sopenharmony_ci	}
17298c2ecf20Sopenharmony_ci	arm_smmu_cmdq_batch_submit(smmu, &cmds);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	/*
17328c2ecf20Sopenharmony_ci	 * Unfortunately, this can't be leaf-only since we may have
17338c2ecf20Sopenharmony_ci	 * zapped an entire table.
17348c2ecf20Sopenharmony_ci	 */
17358c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, 0, start, size);
17368c2ecf20Sopenharmony_ci}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather,
17398c2ecf20Sopenharmony_ci					 unsigned long iova, size_t granule,
17408c2ecf20Sopenharmony_ci					 void *cookie)
17418c2ecf20Sopenharmony_ci{
17428c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = cookie;
17438c2ecf20Sopenharmony_ci	struct iommu_domain *domain = &smmu_domain->domain;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	iommu_iotlb_gather_add_page(domain, gather, iova, granule);
17468c2ecf20Sopenharmony_ci}
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size,
17498c2ecf20Sopenharmony_ci				  size_t granule, void *cookie)
17508c2ecf20Sopenharmony_ci{
17518c2ecf20Sopenharmony_ci	arm_smmu_tlb_inv_range(iova, size, granule, false, cookie);
17528c2ecf20Sopenharmony_ci}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_leaf(unsigned long iova, size_t size,
17558c2ecf20Sopenharmony_ci				  size_t granule, void *cookie)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	arm_smmu_tlb_inv_range(iova, size, granule, true, cookie);
17588c2ecf20Sopenharmony_ci}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_flush_ops = {
17618c2ecf20Sopenharmony_ci	.tlb_flush_all	= arm_smmu_tlb_inv_context,
17628c2ecf20Sopenharmony_ci	.tlb_flush_walk = arm_smmu_tlb_inv_walk,
17638c2ecf20Sopenharmony_ci	.tlb_flush_leaf = arm_smmu_tlb_inv_leaf,
17648c2ecf20Sopenharmony_ci	.tlb_add_page	= arm_smmu_tlb_inv_page_nosync,
17658c2ecf20Sopenharmony_ci};
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci/* IOMMU API */
17688c2ecf20Sopenharmony_cistatic bool arm_smmu_capable(enum iommu_cap cap)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	switch (cap) {
17718c2ecf20Sopenharmony_ci	case IOMMU_CAP_CACHE_COHERENCY:
17728c2ecf20Sopenharmony_ci		return true;
17738c2ecf20Sopenharmony_ci	case IOMMU_CAP_NOEXEC:
17748c2ecf20Sopenharmony_ci		return true;
17758c2ecf20Sopenharmony_ci	default:
17768c2ecf20Sopenharmony_ci		return false;
17778c2ecf20Sopenharmony_ci	}
17788c2ecf20Sopenharmony_ci}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
17818c2ecf20Sopenharmony_ci{
17828c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	if (type != IOMMU_DOMAIN_UNMANAGED &&
17858c2ecf20Sopenharmony_ci	    type != IOMMU_DOMAIN_DMA &&
17868c2ecf20Sopenharmony_ci	    type != IOMMU_DOMAIN_IDENTITY)
17878c2ecf20Sopenharmony_ci		return NULL;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	/*
17908c2ecf20Sopenharmony_ci	 * Allocate the domain and initialise some of its data structures.
17918c2ecf20Sopenharmony_ci	 * We can't really do anything meaningful until we've added a
17928c2ecf20Sopenharmony_ci	 * master.
17938c2ecf20Sopenharmony_ci	 */
17948c2ecf20Sopenharmony_ci	smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
17958c2ecf20Sopenharmony_ci	if (!smmu_domain)
17968c2ecf20Sopenharmony_ci		return NULL;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	if (type == IOMMU_DOMAIN_DMA &&
17998c2ecf20Sopenharmony_ci	    iommu_get_dma_cookie(&smmu_domain->domain)) {
18008c2ecf20Sopenharmony_ci		kfree(smmu_domain);
18018c2ecf20Sopenharmony_ci		return NULL;
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	mutex_init(&smmu_domain->init_mutex);
18058c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&smmu_domain->devices);
18068c2ecf20Sopenharmony_ci	spin_lock_init(&smmu_domain->devices_lock);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	return &smmu_domain->domain;
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic int arm_smmu_bitmap_alloc(unsigned long *map, int span)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	int idx, size = 1 << span;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	do {
18168c2ecf20Sopenharmony_ci		idx = find_first_zero_bit(map, size);
18178c2ecf20Sopenharmony_ci		if (idx == size)
18188c2ecf20Sopenharmony_ci			return -ENOSPC;
18198c2ecf20Sopenharmony_ci	} while (test_and_set_bit(idx, map));
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	return idx;
18228c2ecf20Sopenharmony_ci}
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_cistatic void arm_smmu_bitmap_free(unsigned long *map, int idx)
18258c2ecf20Sopenharmony_ci{
18268c2ecf20Sopenharmony_ci	clear_bit(idx, map);
18278c2ecf20Sopenharmony_ci}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain)
18308c2ecf20Sopenharmony_ci{
18318c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
18328c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	iommu_put_dma_cookie(domain);
18358c2ecf20Sopenharmony_ci	free_io_pgtable_ops(smmu_domain->pgtbl_ops);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	/* Free the CD and ASID, if we allocated them */
18388c2ecf20Sopenharmony_ci	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
18398c2ecf20Sopenharmony_ci		struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci		/* Prevent SVA from touching the CD while we're freeing it */
18428c2ecf20Sopenharmony_ci		mutex_lock(&arm_smmu_asid_lock);
18438c2ecf20Sopenharmony_ci		if (cfg->cdcfg.cdtab)
18448c2ecf20Sopenharmony_ci			arm_smmu_free_cd_tables(smmu_domain);
18458c2ecf20Sopenharmony_ci		arm_smmu_free_asid(&cfg->cd);
18468c2ecf20Sopenharmony_ci		mutex_unlock(&arm_smmu_asid_lock);
18478c2ecf20Sopenharmony_ci	} else {
18488c2ecf20Sopenharmony_ci		struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
18498c2ecf20Sopenharmony_ci		if (cfg->vmid)
18508c2ecf20Sopenharmony_ci			arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
18518c2ecf20Sopenharmony_ci	}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	kfree(smmu_domain);
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
18578c2ecf20Sopenharmony_ci				       struct arm_smmu_master *master,
18588c2ecf20Sopenharmony_ci				       struct io_pgtable_cfg *pgtbl_cfg)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	int ret;
18618c2ecf20Sopenharmony_ci	u32 asid;
18628c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
18638c2ecf20Sopenharmony_ci	struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
18648c2ecf20Sopenharmony_ci	typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	refcount_set(&cfg->cd.refs, 1);
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	/* Prevent SVA from modifying the ASID until it is written to the CD */
18698c2ecf20Sopenharmony_ci	mutex_lock(&arm_smmu_asid_lock);
18708c2ecf20Sopenharmony_ci	ret = xa_alloc(&arm_smmu_asid_xa, &asid, &cfg->cd,
18718c2ecf20Sopenharmony_ci		       XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL);
18728c2ecf20Sopenharmony_ci	if (ret)
18738c2ecf20Sopenharmony_ci		goto out_unlock;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	cfg->s1cdmax = master->ssid_bits;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	ret = arm_smmu_alloc_cd_tables(smmu_domain);
18788c2ecf20Sopenharmony_ci	if (ret)
18798c2ecf20Sopenharmony_ci		goto out_free_asid;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	cfg->cd.asid	= (u16)asid;
18828c2ecf20Sopenharmony_ci	cfg->cd.ttbr	= pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
18838c2ecf20Sopenharmony_ci	cfg->cd.tcr	= FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, tcr->tsz) |
18848c2ecf20Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_TG0, tcr->tg) |
18858c2ecf20Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, tcr->irgn) |
18868c2ecf20Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, tcr->orgn) |
18878c2ecf20Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_SH0, tcr->sh) |
18888c2ecf20Sopenharmony_ci			  FIELD_PREP(CTXDESC_CD_0_TCR_IPS, tcr->ips) |
18898c2ecf20Sopenharmony_ci			  CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64;
18908c2ecf20Sopenharmony_ci	cfg->cd.mair	= pgtbl_cfg->arm_lpae_s1_cfg.mair;
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	/*
18938c2ecf20Sopenharmony_ci	 * Note that this will end up calling arm_smmu_sync_cd() before
18948c2ecf20Sopenharmony_ci	 * the master has been added to the devices list for this domain.
18958c2ecf20Sopenharmony_ci	 * This isn't an issue because the STE hasn't been installed yet.
18968c2ecf20Sopenharmony_ci	 */
18978c2ecf20Sopenharmony_ci	ret = arm_smmu_write_ctx_desc(smmu_domain, 0, &cfg->cd);
18988c2ecf20Sopenharmony_ci	if (ret)
18998c2ecf20Sopenharmony_ci		goto out_free_cd_tables;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	mutex_unlock(&arm_smmu_asid_lock);
19028c2ecf20Sopenharmony_ci	return 0;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ciout_free_cd_tables:
19058c2ecf20Sopenharmony_ci	arm_smmu_free_cd_tables(smmu_domain);
19068c2ecf20Sopenharmony_ciout_free_asid:
19078c2ecf20Sopenharmony_ci	arm_smmu_free_asid(&cfg->cd);
19088c2ecf20Sopenharmony_ciout_unlock:
19098c2ecf20Sopenharmony_ci	mutex_unlock(&arm_smmu_asid_lock);
19108c2ecf20Sopenharmony_ci	return ret;
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
19148c2ecf20Sopenharmony_ci				       struct arm_smmu_master *master,
19158c2ecf20Sopenharmony_ci				       struct io_pgtable_cfg *pgtbl_cfg)
19168c2ecf20Sopenharmony_ci{
19178c2ecf20Sopenharmony_ci	int vmid;
19188c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
19198c2ecf20Sopenharmony_ci	struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
19208c2ecf20Sopenharmony_ci	typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
19238c2ecf20Sopenharmony_ci	if (vmid < 0)
19248c2ecf20Sopenharmony_ci		return vmid;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	vtcr = &pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
19278c2ecf20Sopenharmony_ci	cfg->vmid	= (u16)vmid;
19288c2ecf20Sopenharmony_ci	cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
19298c2ecf20Sopenharmony_ci	cfg->vtcr	= FIELD_PREP(STRTAB_STE_2_VTCR_S2T0SZ, vtcr->tsz) |
19308c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2SL0, vtcr->sl) |
19318c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2IR0, vtcr->irgn) |
19328c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2OR0, vtcr->orgn) |
19338c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2SH0, vtcr->sh) |
19348c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) |
19358c2ecf20Sopenharmony_ci			  FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps);
19368c2ecf20Sopenharmony_ci	return 0;
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise(struct iommu_domain *domain,
19408c2ecf20Sopenharmony_ci				    struct arm_smmu_master *master)
19418c2ecf20Sopenharmony_ci{
19428c2ecf20Sopenharmony_ci	int ret;
19438c2ecf20Sopenharmony_ci	unsigned long ias, oas;
19448c2ecf20Sopenharmony_ci	enum io_pgtable_fmt fmt;
19458c2ecf20Sopenharmony_ci	struct io_pgtable_cfg pgtbl_cfg;
19468c2ecf20Sopenharmony_ci	struct io_pgtable_ops *pgtbl_ops;
19478c2ecf20Sopenharmony_ci	int (*finalise_stage_fn)(struct arm_smmu_domain *,
19488c2ecf20Sopenharmony_ci				 struct arm_smmu_master *,
19498c2ecf20Sopenharmony_ci				 struct io_pgtable_cfg *);
19508c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
19518c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
19548c2ecf20Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
19558c2ecf20Sopenharmony_ci		return 0;
19568c2ecf20Sopenharmony_ci	}
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	/* Restrict the stage to what we can actually support */
19598c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
19608c2ecf20Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
19618c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
19628c2ecf20Sopenharmony_ci		smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	switch (smmu_domain->stage) {
19658c2ecf20Sopenharmony_ci	case ARM_SMMU_DOMAIN_S1:
19668c2ecf20Sopenharmony_ci		ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
19678c2ecf20Sopenharmony_ci		ias = min_t(unsigned long, ias, VA_BITS);
19688c2ecf20Sopenharmony_ci		oas = smmu->ias;
19698c2ecf20Sopenharmony_ci		fmt = ARM_64_LPAE_S1;
19708c2ecf20Sopenharmony_ci		finalise_stage_fn = arm_smmu_domain_finalise_s1;
19718c2ecf20Sopenharmony_ci		break;
19728c2ecf20Sopenharmony_ci	case ARM_SMMU_DOMAIN_NESTED:
19738c2ecf20Sopenharmony_ci	case ARM_SMMU_DOMAIN_S2:
19748c2ecf20Sopenharmony_ci		ias = smmu->ias;
19758c2ecf20Sopenharmony_ci		oas = smmu->oas;
19768c2ecf20Sopenharmony_ci		fmt = ARM_64_LPAE_S2;
19778c2ecf20Sopenharmony_ci		finalise_stage_fn = arm_smmu_domain_finalise_s2;
19788c2ecf20Sopenharmony_ci		break;
19798c2ecf20Sopenharmony_ci	default:
19808c2ecf20Sopenharmony_ci		return -EINVAL;
19818c2ecf20Sopenharmony_ci	}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	pgtbl_cfg = (struct io_pgtable_cfg) {
19848c2ecf20Sopenharmony_ci		.pgsize_bitmap	= smmu->pgsize_bitmap,
19858c2ecf20Sopenharmony_ci		.ias		= ias,
19868c2ecf20Sopenharmony_ci		.oas		= oas,
19878c2ecf20Sopenharmony_ci		.coherent_walk	= smmu->features & ARM_SMMU_FEAT_COHERENCY,
19888c2ecf20Sopenharmony_ci		.tlb		= &arm_smmu_flush_ops,
19898c2ecf20Sopenharmony_ci		.iommu_dev	= smmu->dev,
19908c2ecf20Sopenharmony_ci	};
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	if (smmu_domain->non_strict)
19938c2ecf20Sopenharmony_ci		pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
19968c2ecf20Sopenharmony_ci	if (!pgtbl_ops)
19978c2ecf20Sopenharmony_ci		return -ENOMEM;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
20008c2ecf20Sopenharmony_ci	domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
20018c2ecf20Sopenharmony_ci	domain->geometry.force_aperture = true;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg);
20048c2ecf20Sopenharmony_ci	if (ret < 0) {
20058c2ecf20Sopenharmony_ci		free_io_pgtable_ops(pgtbl_ops);
20068c2ecf20Sopenharmony_ci		return ret;
20078c2ecf20Sopenharmony_ci	}
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	smmu_domain->pgtbl_ops = pgtbl_ops;
20108c2ecf20Sopenharmony_ci	return 0;
20118c2ecf20Sopenharmony_ci}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_cistatic __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	__le64 *step;
20168c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
20198c2ecf20Sopenharmony_ci		struct arm_smmu_strtab_l1_desc *l1_desc;
20208c2ecf20Sopenharmony_ci		int idx;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci		/* Two-level walk */
20238c2ecf20Sopenharmony_ci		idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
20248c2ecf20Sopenharmony_ci		l1_desc = &cfg->l1_desc[idx];
20258c2ecf20Sopenharmony_ci		idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
20268c2ecf20Sopenharmony_ci		step = &l1_desc->l2ptr[idx];
20278c2ecf20Sopenharmony_ci	} else {
20288c2ecf20Sopenharmony_ci		/* Simple linear lookup */
20298c2ecf20Sopenharmony_ci		step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	return step;
20338c2ecf20Sopenharmony_ci}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_cistatic void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
20368c2ecf20Sopenharmony_ci{
20378c2ecf20Sopenharmony_ci	int i, j;
20388c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	for (i = 0; i < master->num_sids; ++i) {
20418c2ecf20Sopenharmony_ci		u32 sid = master->sids[i];
20428c2ecf20Sopenharmony_ci		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci		/* Bridged PCI devices may end up with duplicated IDs */
20458c2ecf20Sopenharmony_ci		for (j = 0; j < i; j++)
20468c2ecf20Sopenharmony_ci			if (master->sids[j] == sid)
20478c2ecf20Sopenharmony_ci				break;
20488c2ecf20Sopenharmony_ci		if (j < i)
20498c2ecf20Sopenharmony_ci			continue;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci		arm_smmu_write_strtab_ent(master, sid, step);
20528c2ecf20Sopenharmony_ci	}
20538c2ecf20Sopenharmony_ci}
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_cistatic bool arm_smmu_ats_supported(struct arm_smmu_master *master)
20568c2ecf20Sopenharmony_ci{
20578c2ecf20Sopenharmony_ci	struct device *dev = master->dev;
20588c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
20598c2ecf20Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_ATS))
20628c2ecf20Sopenharmony_ci		return false;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS))
20658c2ecf20Sopenharmony_ci		return false;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev));
20688c2ecf20Sopenharmony_ci}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_cistatic void arm_smmu_enable_ats(struct arm_smmu_master *master)
20718c2ecf20Sopenharmony_ci{
20728c2ecf20Sopenharmony_ci	size_t stu;
20738c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
20748c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = master->smmu;
20758c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	/* Don't enable ATS at the endpoint if it's not enabled in the STE */
20788c2ecf20Sopenharmony_ci	if (!master->ats_enabled)
20798c2ecf20Sopenharmony_ci		return;
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	/* Smallest Translation Unit: log2 of the smallest supported granule */
20828c2ecf20Sopenharmony_ci	stu = __ffs(smmu->pgsize_bitmap);
20838c2ecf20Sopenharmony_ci	pdev = to_pci_dev(master->dev);
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	atomic_inc(&smmu_domain->nr_ats_masters);
20868c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0);
20878c2ecf20Sopenharmony_ci	if (pci_enable_ats(pdev, stu))
20888c2ecf20Sopenharmony_ci		dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu);
20898c2ecf20Sopenharmony_ci}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_cistatic void arm_smmu_disable_ats(struct arm_smmu_master *master)
20928c2ecf20Sopenharmony_ci{
20938c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	if (!master->ats_enabled)
20968c2ecf20Sopenharmony_ci		return;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	pci_disable_ats(to_pci_dev(master->dev));
20998c2ecf20Sopenharmony_ci	/*
21008c2ecf20Sopenharmony_ci	 * Ensure ATS is disabled at the endpoint before we issue the
21018c2ecf20Sopenharmony_ci	 * ATC invalidation via the SMMU.
21028c2ecf20Sopenharmony_ci	 */
21038c2ecf20Sopenharmony_ci	wmb();
21048c2ecf20Sopenharmony_ci	arm_smmu_atc_inv_master(master);
21058c2ecf20Sopenharmony_ci	atomic_dec(&smmu_domain->nr_ats_masters);
21068c2ecf20Sopenharmony_ci}
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_cistatic int arm_smmu_enable_pasid(struct arm_smmu_master *master)
21098c2ecf20Sopenharmony_ci{
21108c2ecf20Sopenharmony_ci	int ret;
21118c2ecf20Sopenharmony_ci	int features;
21128c2ecf20Sopenharmony_ci	int num_pasids;
21138c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	if (!dev_is_pci(master->dev))
21168c2ecf20Sopenharmony_ci		return -ENODEV;
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	pdev = to_pci_dev(master->dev);
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	features = pci_pasid_features(pdev);
21218c2ecf20Sopenharmony_ci	if (features < 0)
21228c2ecf20Sopenharmony_ci		return features;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	num_pasids = pci_max_pasids(pdev);
21258c2ecf20Sopenharmony_ci	if (num_pasids <= 0)
21268c2ecf20Sopenharmony_ci		return num_pasids;
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	ret = pci_enable_pasid(pdev, features);
21298c2ecf20Sopenharmony_ci	if (ret) {
21308c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable PASID\n");
21318c2ecf20Sopenharmony_ci		return ret;
21328c2ecf20Sopenharmony_ci	}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	master->ssid_bits = min_t(u8, ilog2(num_pasids),
21358c2ecf20Sopenharmony_ci				  master->smmu->ssid_bits);
21368c2ecf20Sopenharmony_ci	return 0;
21378c2ecf20Sopenharmony_ci}
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_cistatic void arm_smmu_disable_pasid(struct arm_smmu_master *master)
21408c2ecf20Sopenharmony_ci{
21418c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	if (!dev_is_pci(master->dev))
21448c2ecf20Sopenharmony_ci		return;
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	pdev = to_pci_dev(master->dev);
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	if (!pdev->pasid_enabled)
21498c2ecf20Sopenharmony_ci		return;
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci	master->ssid_bits = 0;
21528c2ecf20Sopenharmony_ci	pci_disable_pasid(pdev);
21538c2ecf20Sopenharmony_ci}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_cistatic void arm_smmu_detach_dev(struct arm_smmu_master *master)
21568c2ecf20Sopenharmony_ci{
21578c2ecf20Sopenharmony_ci	unsigned long flags;
21588c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = master->domain;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	if (!smmu_domain)
21618c2ecf20Sopenharmony_ci		return;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	arm_smmu_disable_ats(master);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
21668c2ecf20Sopenharmony_ci	list_del(&master->domain_head);
21678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	master->domain = NULL;
21708c2ecf20Sopenharmony_ci	master->ats_enabled = false;
21718c2ecf20Sopenharmony_ci	arm_smmu_install_ste_for_dev(master);
21728c2ecf20Sopenharmony_ci}
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
21758c2ecf20Sopenharmony_ci{
21768c2ecf20Sopenharmony_ci	int ret = 0;
21778c2ecf20Sopenharmony_ci	unsigned long flags;
21788c2ecf20Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
21798c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu;
21808c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
21818c2ecf20Sopenharmony_ci	struct arm_smmu_master *master;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	if (!fwspec)
21848c2ecf20Sopenharmony_ci		return -ENOENT;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	master = dev_iommu_priv_get(dev);
21878c2ecf20Sopenharmony_ci	smmu = master->smmu;
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	/*
21908c2ecf20Sopenharmony_ci	 * Checking that SVA is disabled ensures that this device isn't bound to
21918c2ecf20Sopenharmony_ci	 * any mm, and can be safely detached from its old domain. Bonds cannot
21928c2ecf20Sopenharmony_ci	 * be removed concurrently since we're holding the group mutex.
21938c2ecf20Sopenharmony_ci	 */
21948c2ecf20Sopenharmony_ci	if (arm_smmu_master_sva_enabled(master)) {
21958c2ecf20Sopenharmony_ci		dev_err(dev, "cannot attach - SVA enabled\n");
21968c2ecf20Sopenharmony_ci		return -EBUSY;
21978c2ecf20Sopenharmony_ci	}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	arm_smmu_detach_dev(master);
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	mutex_lock(&smmu_domain->init_mutex);
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	if (!smmu_domain->smmu) {
22048c2ecf20Sopenharmony_ci		smmu_domain->smmu = smmu;
22058c2ecf20Sopenharmony_ci		ret = arm_smmu_domain_finalise(domain, master);
22068c2ecf20Sopenharmony_ci		if (ret) {
22078c2ecf20Sopenharmony_ci			smmu_domain->smmu = NULL;
22088c2ecf20Sopenharmony_ci			goto out_unlock;
22098c2ecf20Sopenharmony_ci		}
22108c2ecf20Sopenharmony_ci	} else if (smmu_domain->smmu != smmu) {
22118c2ecf20Sopenharmony_ci		dev_err(dev,
22128c2ecf20Sopenharmony_ci			"cannot attach to SMMU %s (upstream of %s)\n",
22138c2ecf20Sopenharmony_ci			dev_name(smmu_domain->smmu->dev),
22148c2ecf20Sopenharmony_ci			dev_name(smmu->dev));
22158c2ecf20Sopenharmony_ci		ret = -ENXIO;
22168c2ecf20Sopenharmony_ci		goto out_unlock;
22178c2ecf20Sopenharmony_ci	} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 &&
22188c2ecf20Sopenharmony_ci		   master->ssid_bits != smmu_domain->s1_cfg.s1cdmax) {
22198c2ecf20Sopenharmony_ci		dev_err(dev,
22208c2ecf20Sopenharmony_ci			"cannot attach to incompatible domain (%u SSID bits != %u)\n",
22218c2ecf20Sopenharmony_ci			smmu_domain->s1_cfg.s1cdmax, master->ssid_bits);
22228c2ecf20Sopenharmony_ci		ret = -EINVAL;
22238c2ecf20Sopenharmony_ci		goto out_unlock;
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	master->domain = smmu_domain;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS)
22298c2ecf20Sopenharmony_ci		master->ats_enabled = arm_smmu_ats_supported(master);
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	arm_smmu_install_ste_for_dev(master);
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
22348c2ecf20Sopenharmony_ci	list_add(&master->domain_head, &smmu_domain->devices);
22358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	arm_smmu_enable_ats(master);
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ciout_unlock:
22408c2ecf20Sopenharmony_ci	mutex_unlock(&smmu_domain->init_mutex);
22418c2ecf20Sopenharmony_ci	return ret;
22428c2ecf20Sopenharmony_ci}
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_cistatic int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
22458c2ecf20Sopenharmony_ci			phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
22468c2ecf20Sopenharmony_ci{
22478c2ecf20Sopenharmony_ci	struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (!ops)
22508c2ecf20Sopenharmony_ci		return -ENODEV;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	return ops->map(ops, iova, paddr, size, prot, gfp);
22538c2ecf20Sopenharmony_ci}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_cistatic size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
22568c2ecf20Sopenharmony_ci			     size_t size, struct iommu_iotlb_gather *gather)
22578c2ecf20Sopenharmony_ci{
22588c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
22598c2ecf20Sopenharmony_ci	struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	if (!ops)
22628c2ecf20Sopenharmony_ci		return 0;
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	return ops->unmap(ops, iova, size, gather);
22658c2ecf20Sopenharmony_ci}
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain)
22688c2ecf20Sopenharmony_ci{
22698c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	if (smmu_domain->smmu)
22728c2ecf20Sopenharmony_ci		arm_smmu_tlb_inv_context(smmu_domain);
22738c2ecf20Sopenharmony_ci}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain,
22768c2ecf20Sopenharmony_ci				struct iommu_iotlb_gather *gather)
22778c2ecf20Sopenharmony_ci{
22788c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	arm_smmu_tlb_inv_range(gather->start, gather->end - gather->start + 1,
22818c2ecf20Sopenharmony_ci			       gather->pgsize, true, smmu_domain);
22828c2ecf20Sopenharmony_ci}
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_cistatic phys_addr_t
22858c2ecf20Sopenharmony_ciarm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	if (domain->type == IOMMU_DOMAIN_IDENTITY)
22908c2ecf20Sopenharmony_ci		return iova;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	if (!ops)
22938c2ecf20Sopenharmony_ci		return 0;
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	return ops->iova_to_phys(ops, iova);
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver;
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_cistatic
23018c2ecf20Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
23028c2ecf20Sopenharmony_ci{
23038c2ecf20Sopenharmony_ci	struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver,
23048c2ecf20Sopenharmony_ci							  fwnode);
23058c2ecf20Sopenharmony_ci	put_device(dev);
23068c2ecf20Sopenharmony_ci	return dev ? dev_get_drvdata(dev) : NULL;
23078c2ecf20Sopenharmony_ci}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_cistatic bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
23108c2ecf20Sopenharmony_ci{
23118c2ecf20Sopenharmony_ci	unsigned long limit = smmu->strtab_cfg.num_l1_ents;
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
23148c2ecf20Sopenharmony_ci		limit *= 1UL << STRTAB_SPLIT;
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	return sid < limit;
23178c2ecf20Sopenharmony_ci}
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops;
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev)
23228c2ecf20Sopenharmony_ci{
23238c2ecf20Sopenharmony_ci	int i, ret;
23248c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu;
23258c2ecf20Sopenharmony_ci	struct arm_smmu_master *master;
23268c2ecf20Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	if (!fwspec || fwspec->ops != &arm_smmu_ops)
23298c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(dev_iommu_priv_get(dev)))
23328c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
23358c2ecf20Sopenharmony_ci	if (!smmu)
23368c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	master = kzalloc(sizeof(*master), GFP_KERNEL);
23398c2ecf20Sopenharmony_ci	if (!master)
23408c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	master->dev = dev;
23438c2ecf20Sopenharmony_ci	master->smmu = smmu;
23448c2ecf20Sopenharmony_ci	master->sids = fwspec->ids;
23458c2ecf20Sopenharmony_ci	master->num_sids = fwspec->num_ids;
23468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&master->bonds);
23478c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, master);
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci	/* Check the SIDs are in range of the SMMU and our stream table */
23508c2ecf20Sopenharmony_ci	for (i = 0; i < master->num_sids; i++) {
23518c2ecf20Sopenharmony_ci		u32 sid = master->sids[i];
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci		if (!arm_smmu_sid_in_range(smmu, sid)) {
23548c2ecf20Sopenharmony_ci			ret = -ERANGE;
23558c2ecf20Sopenharmony_ci			goto err_free_master;
23568c2ecf20Sopenharmony_ci		}
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci		/* Ensure l2 strtab is initialised */
23598c2ecf20Sopenharmony_ci		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
23608c2ecf20Sopenharmony_ci			ret = arm_smmu_init_l2_strtab(smmu, sid);
23618c2ecf20Sopenharmony_ci			if (ret)
23628c2ecf20Sopenharmony_ci				goto err_free_master;
23638c2ecf20Sopenharmony_ci		}
23648c2ecf20Sopenharmony_ci	}
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	master->ssid_bits = min(smmu->ssid_bits, fwspec->num_pasid_bits);
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci	/*
23698c2ecf20Sopenharmony_ci	 * Note that PASID must be enabled before, and disabled after ATS:
23708c2ecf20Sopenharmony_ci	 * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register
23718c2ecf20Sopenharmony_ci	 *
23728c2ecf20Sopenharmony_ci	 *   Behavior is undefined if this bit is Set and the value of the PASID
23738c2ecf20Sopenharmony_ci	 *   Enable, Execute Requested Enable, or Privileged Mode Requested bits
23748c2ecf20Sopenharmony_ci	 *   are changed.
23758c2ecf20Sopenharmony_ci	 */
23768c2ecf20Sopenharmony_ci	arm_smmu_enable_pasid(master);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
23798c2ecf20Sopenharmony_ci		master->ssid_bits = min_t(u8, master->ssid_bits,
23808c2ecf20Sopenharmony_ci					  CTXDESC_LINEAR_CDMAX);
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	return &smmu->iommu;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_cierr_free_master:
23858c2ecf20Sopenharmony_ci	kfree(master);
23868c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, NULL);
23878c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
23888c2ecf20Sopenharmony_ci}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev)
23918c2ecf20Sopenharmony_ci{
23928c2ecf20Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
23938c2ecf20Sopenharmony_ci	struct arm_smmu_master *master;
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	if (!fwspec || fwspec->ops != &arm_smmu_ops)
23968c2ecf20Sopenharmony_ci		return;
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	master = dev_iommu_priv_get(dev);
23998c2ecf20Sopenharmony_ci	WARN_ON(arm_smmu_master_sva_enabled(master));
24008c2ecf20Sopenharmony_ci	arm_smmu_detach_dev(master);
24018c2ecf20Sopenharmony_ci	arm_smmu_disable_pasid(master);
24028c2ecf20Sopenharmony_ci	kfree(master);
24038c2ecf20Sopenharmony_ci	iommu_fwspec_free(dev);
24048c2ecf20Sopenharmony_ci}
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev)
24078c2ecf20Sopenharmony_ci{
24088c2ecf20Sopenharmony_ci	struct iommu_group *group;
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci	/*
24118c2ecf20Sopenharmony_ci	 * We don't support devices sharing stream IDs other than PCI RID
24128c2ecf20Sopenharmony_ci	 * aliases, since the necessary ID-to-device lookup becomes rather
24138c2ecf20Sopenharmony_ci	 * impractical given a potential sparse 32-bit stream ID space.
24148c2ecf20Sopenharmony_ci	 */
24158c2ecf20Sopenharmony_ci	if (dev_is_pci(dev))
24168c2ecf20Sopenharmony_ci		group = pci_device_group(dev);
24178c2ecf20Sopenharmony_ci	else
24188c2ecf20Sopenharmony_ci		group = generic_device_group(dev);
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci	return group;
24218c2ecf20Sopenharmony_ci}
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_cistatic int arm_smmu_domain_get_attr(struct iommu_domain *domain,
24248c2ecf20Sopenharmony_ci				    enum iommu_attr attr, void *data)
24258c2ecf20Sopenharmony_ci{
24268c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	switch (domain->type) {
24298c2ecf20Sopenharmony_ci	case IOMMU_DOMAIN_UNMANAGED:
24308c2ecf20Sopenharmony_ci		switch (attr) {
24318c2ecf20Sopenharmony_ci		case DOMAIN_ATTR_NESTING:
24328c2ecf20Sopenharmony_ci			*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
24338c2ecf20Sopenharmony_ci			return 0;
24348c2ecf20Sopenharmony_ci		default:
24358c2ecf20Sopenharmony_ci			return -ENODEV;
24368c2ecf20Sopenharmony_ci		}
24378c2ecf20Sopenharmony_ci		break;
24388c2ecf20Sopenharmony_ci	case IOMMU_DOMAIN_DMA:
24398c2ecf20Sopenharmony_ci		switch (attr) {
24408c2ecf20Sopenharmony_ci		case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
24418c2ecf20Sopenharmony_ci			*(int *)data = smmu_domain->non_strict;
24428c2ecf20Sopenharmony_ci			return 0;
24438c2ecf20Sopenharmony_ci		default:
24448c2ecf20Sopenharmony_ci			return -ENODEV;
24458c2ecf20Sopenharmony_ci		}
24468c2ecf20Sopenharmony_ci		break;
24478c2ecf20Sopenharmony_ci	default:
24488c2ecf20Sopenharmony_ci		return -EINVAL;
24498c2ecf20Sopenharmony_ci	}
24508c2ecf20Sopenharmony_ci}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_cistatic int arm_smmu_domain_set_attr(struct iommu_domain *domain,
24538c2ecf20Sopenharmony_ci				    enum iommu_attr attr, void *data)
24548c2ecf20Sopenharmony_ci{
24558c2ecf20Sopenharmony_ci	int ret = 0;
24568c2ecf20Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	mutex_lock(&smmu_domain->init_mutex);
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	switch (domain->type) {
24618c2ecf20Sopenharmony_ci	case IOMMU_DOMAIN_UNMANAGED:
24628c2ecf20Sopenharmony_ci		switch (attr) {
24638c2ecf20Sopenharmony_ci		case DOMAIN_ATTR_NESTING:
24648c2ecf20Sopenharmony_ci			if (smmu_domain->smmu) {
24658c2ecf20Sopenharmony_ci				ret = -EPERM;
24668c2ecf20Sopenharmony_ci				goto out_unlock;
24678c2ecf20Sopenharmony_ci			}
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci			if (*(int *)data)
24708c2ecf20Sopenharmony_ci				smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
24718c2ecf20Sopenharmony_ci			else
24728c2ecf20Sopenharmony_ci				smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
24738c2ecf20Sopenharmony_ci			break;
24748c2ecf20Sopenharmony_ci		default:
24758c2ecf20Sopenharmony_ci			ret = -ENODEV;
24768c2ecf20Sopenharmony_ci		}
24778c2ecf20Sopenharmony_ci		break;
24788c2ecf20Sopenharmony_ci	case IOMMU_DOMAIN_DMA:
24798c2ecf20Sopenharmony_ci		switch(attr) {
24808c2ecf20Sopenharmony_ci		case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
24818c2ecf20Sopenharmony_ci			smmu_domain->non_strict = *(int *)data;
24828c2ecf20Sopenharmony_ci			break;
24838c2ecf20Sopenharmony_ci		default:
24848c2ecf20Sopenharmony_ci			ret = -ENODEV;
24858c2ecf20Sopenharmony_ci		}
24868c2ecf20Sopenharmony_ci		break;
24878c2ecf20Sopenharmony_ci	default:
24888c2ecf20Sopenharmony_ci		ret = -EINVAL;
24898c2ecf20Sopenharmony_ci	}
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ciout_unlock:
24928c2ecf20Sopenharmony_ci	mutex_unlock(&smmu_domain->init_mutex);
24938c2ecf20Sopenharmony_ci	return ret;
24948c2ecf20Sopenharmony_ci}
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
24978c2ecf20Sopenharmony_ci{
24988c2ecf20Sopenharmony_ci	return iommu_fwspec_add_ids(dev, args->args, 1);
24998c2ecf20Sopenharmony_ci}
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev,
25028c2ecf20Sopenharmony_ci				      struct list_head *head)
25038c2ecf20Sopenharmony_ci{
25048c2ecf20Sopenharmony_ci	struct iommu_resv_region *region;
25058c2ecf20Sopenharmony_ci	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
25088c2ecf20Sopenharmony_ci					 prot, IOMMU_RESV_SW_MSI);
25098c2ecf20Sopenharmony_ci	if (!region)
25108c2ecf20Sopenharmony_ci		return;
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	list_add_tail(&region->list, head);
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	iommu_dma_get_resv_regions(dev, head);
25158c2ecf20Sopenharmony_ci}
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_cistatic bool arm_smmu_dev_has_feature(struct device *dev,
25188c2ecf20Sopenharmony_ci				     enum iommu_dev_features feat)
25198c2ecf20Sopenharmony_ci{
25208c2ecf20Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	if (!master)
25238c2ecf20Sopenharmony_ci		return false;
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	switch (feat) {
25268c2ecf20Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
25278c2ecf20Sopenharmony_ci		return arm_smmu_master_sva_supported(master);
25288c2ecf20Sopenharmony_ci	default:
25298c2ecf20Sopenharmony_ci		return false;
25308c2ecf20Sopenharmony_ci	}
25318c2ecf20Sopenharmony_ci}
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_cistatic bool arm_smmu_dev_feature_enabled(struct device *dev,
25348c2ecf20Sopenharmony_ci					 enum iommu_dev_features feat)
25358c2ecf20Sopenharmony_ci{
25368c2ecf20Sopenharmony_ci	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_ci	if (!master)
25398c2ecf20Sopenharmony_ci		return false;
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	switch (feat) {
25428c2ecf20Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
25438c2ecf20Sopenharmony_ci		return arm_smmu_master_sva_enabled(master);
25448c2ecf20Sopenharmony_ci	default:
25458c2ecf20Sopenharmony_ci		return false;
25468c2ecf20Sopenharmony_ci	}
25478c2ecf20Sopenharmony_ci}
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_cistatic int arm_smmu_dev_enable_feature(struct device *dev,
25508c2ecf20Sopenharmony_ci				       enum iommu_dev_features feat)
25518c2ecf20Sopenharmony_ci{
25528c2ecf20Sopenharmony_ci	if (!arm_smmu_dev_has_feature(dev, feat))
25538c2ecf20Sopenharmony_ci		return -ENODEV;
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	if (arm_smmu_dev_feature_enabled(dev, feat))
25568c2ecf20Sopenharmony_ci		return -EBUSY;
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	switch (feat) {
25598c2ecf20Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
25608c2ecf20Sopenharmony_ci		return arm_smmu_master_enable_sva(dev_iommu_priv_get(dev));
25618c2ecf20Sopenharmony_ci	default:
25628c2ecf20Sopenharmony_ci		return -EINVAL;
25638c2ecf20Sopenharmony_ci	}
25648c2ecf20Sopenharmony_ci}
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_cistatic int arm_smmu_dev_disable_feature(struct device *dev,
25678c2ecf20Sopenharmony_ci					enum iommu_dev_features feat)
25688c2ecf20Sopenharmony_ci{
25698c2ecf20Sopenharmony_ci	if (!arm_smmu_dev_feature_enabled(dev, feat))
25708c2ecf20Sopenharmony_ci		return -EINVAL;
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	switch (feat) {
25738c2ecf20Sopenharmony_ci	case IOMMU_DEV_FEAT_SVA:
25748c2ecf20Sopenharmony_ci		return arm_smmu_master_disable_sva(dev_iommu_priv_get(dev));
25758c2ecf20Sopenharmony_ci	default:
25768c2ecf20Sopenharmony_ci		return -EINVAL;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci}
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = {
25818c2ecf20Sopenharmony_ci	.capable		= arm_smmu_capable,
25828c2ecf20Sopenharmony_ci	.domain_alloc		= arm_smmu_domain_alloc,
25838c2ecf20Sopenharmony_ci	.domain_free		= arm_smmu_domain_free,
25848c2ecf20Sopenharmony_ci	.attach_dev		= arm_smmu_attach_dev,
25858c2ecf20Sopenharmony_ci	.map			= arm_smmu_map,
25868c2ecf20Sopenharmony_ci	.unmap			= arm_smmu_unmap,
25878c2ecf20Sopenharmony_ci	.flush_iotlb_all	= arm_smmu_flush_iotlb_all,
25888c2ecf20Sopenharmony_ci	.iotlb_sync		= arm_smmu_iotlb_sync,
25898c2ecf20Sopenharmony_ci	.iova_to_phys		= arm_smmu_iova_to_phys,
25908c2ecf20Sopenharmony_ci	.probe_device		= arm_smmu_probe_device,
25918c2ecf20Sopenharmony_ci	.release_device		= arm_smmu_release_device,
25928c2ecf20Sopenharmony_ci	.device_group		= arm_smmu_device_group,
25938c2ecf20Sopenharmony_ci	.domain_get_attr	= arm_smmu_domain_get_attr,
25948c2ecf20Sopenharmony_ci	.domain_set_attr	= arm_smmu_domain_set_attr,
25958c2ecf20Sopenharmony_ci	.of_xlate		= arm_smmu_of_xlate,
25968c2ecf20Sopenharmony_ci	.get_resv_regions	= arm_smmu_get_resv_regions,
25978c2ecf20Sopenharmony_ci	.put_resv_regions	= generic_iommu_put_resv_regions,
25988c2ecf20Sopenharmony_ci	.dev_has_feat		= arm_smmu_dev_has_feature,
25998c2ecf20Sopenharmony_ci	.dev_feat_enabled	= arm_smmu_dev_feature_enabled,
26008c2ecf20Sopenharmony_ci	.dev_enable_feat	= arm_smmu_dev_enable_feature,
26018c2ecf20Sopenharmony_ci	.dev_disable_feat	= arm_smmu_dev_disable_feature,
26028c2ecf20Sopenharmony_ci	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
26038c2ecf20Sopenharmony_ci};
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci/* Probing and initialisation functions */
26068c2ecf20Sopenharmony_cistatic int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
26078c2ecf20Sopenharmony_ci				   struct arm_smmu_queue *q,
26088c2ecf20Sopenharmony_ci				   unsigned long prod_off,
26098c2ecf20Sopenharmony_ci				   unsigned long cons_off,
26108c2ecf20Sopenharmony_ci				   size_t dwords, const char *name)
26118c2ecf20Sopenharmony_ci{
26128c2ecf20Sopenharmony_ci	size_t qsz;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	do {
26158c2ecf20Sopenharmony_ci		qsz = ((1 << q->llq.max_n_shift) * dwords) << 3;
26168c2ecf20Sopenharmony_ci		q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma,
26178c2ecf20Sopenharmony_ci					      GFP_KERNEL);
26188c2ecf20Sopenharmony_ci		if (q->base || qsz < PAGE_SIZE)
26198c2ecf20Sopenharmony_ci			break;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci		q->llq.max_n_shift--;
26228c2ecf20Sopenharmony_ci	} while (1);
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	if (!q->base) {
26258c2ecf20Sopenharmony_ci		dev_err(smmu->dev,
26268c2ecf20Sopenharmony_ci			"failed to allocate queue (0x%zx bytes) for %s\n",
26278c2ecf20Sopenharmony_ci			qsz, name);
26288c2ecf20Sopenharmony_ci		return -ENOMEM;
26298c2ecf20Sopenharmony_ci	}
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	if (!WARN_ON(q->base_dma & (qsz - 1))) {
26328c2ecf20Sopenharmony_ci		dev_info(smmu->dev, "allocated %u entries for %s\n",
26338c2ecf20Sopenharmony_ci			 1 << q->llq.max_n_shift, name);
26348c2ecf20Sopenharmony_ci	}
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	q->prod_reg	= arm_smmu_page1_fixup(prod_off, smmu);
26378c2ecf20Sopenharmony_ci	q->cons_reg	= arm_smmu_page1_fixup(cons_off, smmu);
26388c2ecf20Sopenharmony_ci	q->ent_dwords	= dwords;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	q->q_base  = Q_BASE_RWA;
26418c2ecf20Sopenharmony_ci	q->q_base |= q->base_dma & Q_BASE_ADDR_MASK;
26428c2ecf20Sopenharmony_ci	q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->llq.max_n_shift);
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci	q->llq.prod = q->llq.cons = 0;
26458c2ecf20Sopenharmony_ci	return 0;
26468c2ecf20Sopenharmony_ci}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_free_bitmap(void *data)
26498c2ecf20Sopenharmony_ci{
26508c2ecf20Sopenharmony_ci	unsigned long *bitmap = data;
26518c2ecf20Sopenharmony_ci	bitmap_free(bitmap);
26528c2ecf20Sopenharmony_ci}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_init(struct arm_smmu_device *smmu)
26558c2ecf20Sopenharmony_ci{
26568c2ecf20Sopenharmony_ci	int ret = 0;
26578c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
26588c2ecf20Sopenharmony_ci	unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
26598c2ecf20Sopenharmony_ci	atomic_long_t *bitmap;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	atomic_set(&cmdq->owner_prod, 0);
26628c2ecf20Sopenharmony_ci	atomic_set(&cmdq->lock, 0);
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	bitmap = (atomic_long_t *)bitmap_zalloc(nents, GFP_KERNEL);
26658c2ecf20Sopenharmony_ci	if (!bitmap) {
26668c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to allocate cmdq bitmap\n");
26678c2ecf20Sopenharmony_ci		ret = -ENOMEM;
26688c2ecf20Sopenharmony_ci	} else {
26698c2ecf20Sopenharmony_ci		cmdq->valid_map = bitmap;
26708c2ecf20Sopenharmony_ci		devm_add_action(smmu->dev, arm_smmu_cmdq_free_bitmap, bitmap);
26718c2ecf20Sopenharmony_ci	}
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci	return ret;
26748c2ecf20Sopenharmony_ci}
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_cistatic int arm_smmu_init_queues(struct arm_smmu_device *smmu)
26778c2ecf20Sopenharmony_ci{
26788c2ecf20Sopenharmony_ci	int ret;
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	/* cmdq */
26818c2ecf20Sopenharmony_ci	ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
26828c2ecf20Sopenharmony_ci				      ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS,
26838c2ecf20Sopenharmony_ci				      "cmdq");
26848c2ecf20Sopenharmony_ci	if (ret)
26858c2ecf20Sopenharmony_ci		return ret;
26868c2ecf20Sopenharmony_ci
26878c2ecf20Sopenharmony_ci	ret = arm_smmu_cmdq_init(smmu);
26888c2ecf20Sopenharmony_ci	if (ret)
26898c2ecf20Sopenharmony_ci		return ret;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	/* evtq */
26928c2ecf20Sopenharmony_ci	ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
26938c2ecf20Sopenharmony_ci				      ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS,
26948c2ecf20Sopenharmony_ci				      "evtq");
26958c2ecf20Sopenharmony_ci	if (ret)
26968c2ecf20Sopenharmony_ci		return ret;
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	/* priq */
26998c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_PRI))
27008c2ecf20Sopenharmony_ci		return 0;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	return arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD,
27038c2ecf20Sopenharmony_ci				       ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS,
27048c2ecf20Sopenharmony_ci				       "priq");
27058c2ecf20Sopenharmony_ci}
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_cistatic int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
27088c2ecf20Sopenharmony_ci{
27098c2ecf20Sopenharmony_ci	unsigned int i;
27108c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
27118c2ecf20Sopenharmony_ci	size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
27128c2ecf20Sopenharmony_ci	void *strtab = smmu->strtab_cfg.strtab;
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);
27158c2ecf20Sopenharmony_ci	if (!cfg->l1_desc) {
27168c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to allocate l1 stream table desc\n");
27178c2ecf20Sopenharmony_ci		return -ENOMEM;
27188c2ecf20Sopenharmony_ci	}
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	for (i = 0; i < cfg->num_l1_ents; ++i) {
27218c2ecf20Sopenharmony_ci		arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
27228c2ecf20Sopenharmony_ci		strtab += STRTAB_L1_DESC_DWORDS << 3;
27238c2ecf20Sopenharmony_ci	}
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci	return 0;
27268c2ecf20Sopenharmony_ci}
27278c2ecf20Sopenharmony_ci
27288c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
27298c2ecf20Sopenharmony_ci{
27308c2ecf20Sopenharmony_ci	void *strtab;
27318c2ecf20Sopenharmony_ci	u64 reg;
27328c2ecf20Sopenharmony_ci	u32 size, l1size;
27338c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	/* Calculate the L1 size, capped to the SIDSIZE. */
27368c2ecf20Sopenharmony_ci	size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
27378c2ecf20Sopenharmony_ci	size = min(size, smmu->sid_bits - STRTAB_SPLIT);
27388c2ecf20Sopenharmony_ci	cfg->num_l1_ents = 1 << size;
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	size += STRTAB_SPLIT;
27418c2ecf20Sopenharmony_ci	if (size < smmu->sid_bits)
27428c2ecf20Sopenharmony_ci		dev_warn(smmu->dev,
27438c2ecf20Sopenharmony_ci			 "2-level strtab only covers %u/%u bits of SID\n",
27448c2ecf20Sopenharmony_ci			 size, smmu->sid_bits);
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
27478c2ecf20Sopenharmony_ci	strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma,
27488c2ecf20Sopenharmony_ci				     GFP_KERNEL);
27498c2ecf20Sopenharmony_ci	if (!strtab) {
27508c2ecf20Sopenharmony_ci		dev_err(smmu->dev,
27518c2ecf20Sopenharmony_ci			"failed to allocate l1 stream table (%u bytes)\n",
27528c2ecf20Sopenharmony_ci			l1size);
27538c2ecf20Sopenharmony_ci		return -ENOMEM;
27548c2ecf20Sopenharmony_ci	}
27558c2ecf20Sopenharmony_ci	cfg->strtab = strtab;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	/* Configure strtab_base_cfg for 2 levels */
27588c2ecf20Sopenharmony_ci	reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL);
27598c2ecf20Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size);
27608c2ecf20Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT);
27618c2ecf20Sopenharmony_ci	cfg->strtab_base_cfg = reg;
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	return arm_smmu_init_l1_strtab(smmu);
27648c2ecf20Sopenharmony_ci}
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
27678c2ecf20Sopenharmony_ci{
27688c2ecf20Sopenharmony_ci	void *strtab;
27698c2ecf20Sopenharmony_ci	u64 reg;
27708c2ecf20Sopenharmony_ci	u32 size;
27718c2ecf20Sopenharmony_ci	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
27748c2ecf20Sopenharmony_ci	strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma,
27758c2ecf20Sopenharmony_ci				     GFP_KERNEL);
27768c2ecf20Sopenharmony_ci	if (!strtab) {
27778c2ecf20Sopenharmony_ci		dev_err(smmu->dev,
27788c2ecf20Sopenharmony_ci			"failed to allocate linear stream table (%u bytes)\n",
27798c2ecf20Sopenharmony_ci			size);
27808c2ecf20Sopenharmony_ci		return -ENOMEM;
27818c2ecf20Sopenharmony_ci	}
27828c2ecf20Sopenharmony_ci	cfg->strtab = strtab;
27838c2ecf20Sopenharmony_ci	cfg->num_l1_ents = 1 << smmu->sid_bits;
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci	/* Configure strtab_base_cfg for a linear table covering all SIDs */
27868c2ecf20Sopenharmony_ci	reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR);
27878c2ecf20Sopenharmony_ci	reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits);
27888c2ecf20Sopenharmony_ci	cfg->strtab_base_cfg = reg;
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
27918c2ecf20Sopenharmony_ci	return 0;
27928c2ecf20Sopenharmony_ci}
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
27958c2ecf20Sopenharmony_ci{
27968c2ecf20Sopenharmony_ci	u64 reg;
27978c2ecf20Sopenharmony_ci	int ret;
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
28008c2ecf20Sopenharmony_ci		ret = arm_smmu_init_strtab_2lvl(smmu);
28018c2ecf20Sopenharmony_ci	else
28028c2ecf20Sopenharmony_ci		ret = arm_smmu_init_strtab_linear(smmu);
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	if (ret)
28058c2ecf20Sopenharmony_ci		return ret;
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci	/* Set the strtab base address */
28088c2ecf20Sopenharmony_ci	reg  = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK;
28098c2ecf20Sopenharmony_ci	reg |= STRTAB_BASE_RA;
28108c2ecf20Sopenharmony_ci	smmu->strtab_cfg.strtab_base = reg;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	/* Allocate the first VMID for stage-2 bypass STEs */
28138c2ecf20Sopenharmony_ci	set_bit(0, smmu->vmid_map);
28148c2ecf20Sopenharmony_ci	return 0;
28158c2ecf20Sopenharmony_ci}
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_cistatic int arm_smmu_init_structures(struct arm_smmu_device *smmu)
28188c2ecf20Sopenharmony_ci{
28198c2ecf20Sopenharmony_ci	int ret;
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	ret = arm_smmu_init_queues(smmu);
28228c2ecf20Sopenharmony_ci	if (ret)
28238c2ecf20Sopenharmony_ci		return ret;
28248c2ecf20Sopenharmony_ci
28258c2ecf20Sopenharmony_ci	return arm_smmu_init_strtab(smmu);
28268c2ecf20Sopenharmony_ci}
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_cistatic int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
28298c2ecf20Sopenharmony_ci				   unsigned int reg_off, unsigned int ack_off)
28308c2ecf20Sopenharmony_ci{
28318c2ecf20Sopenharmony_ci	u32 reg;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	writel_relaxed(val, smmu->base + reg_off);
28348c2ecf20Sopenharmony_ci	return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
28358c2ecf20Sopenharmony_ci					  1, ARM_SMMU_POLL_TIMEOUT_US);
28368c2ecf20Sopenharmony_ci}
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci/* GBPA is "special" */
28398c2ecf20Sopenharmony_cistatic int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
28408c2ecf20Sopenharmony_ci{
28418c2ecf20Sopenharmony_ci	int ret;
28428c2ecf20Sopenharmony_ci	u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_ci	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
28458c2ecf20Sopenharmony_ci					 1, ARM_SMMU_POLL_TIMEOUT_US);
28468c2ecf20Sopenharmony_ci	if (ret)
28478c2ecf20Sopenharmony_ci		return ret;
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_ci	reg &= ~clr;
28508c2ecf20Sopenharmony_ci	reg |= set;
28518c2ecf20Sopenharmony_ci	writel_relaxed(reg | GBPA_UPDATE, gbpa);
28528c2ecf20Sopenharmony_ci	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
28538c2ecf20Sopenharmony_ci					 1, ARM_SMMU_POLL_TIMEOUT_US);
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	if (ret)
28568c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "GBPA not responding to update\n");
28578c2ecf20Sopenharmony_ci	return ret;
28588c2ecf20Sopenharmony_ci}
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_cistatic void arm_smmu_free_msis(void *data)
28618c2ecf20Sopenharmony_ci{
28628c2ecf20Sopenharmony_ci	struct device *dev = data;
28638c2ecf20Sopenharmony_ci	platform_msi_domain_free_irqs(dev);
28648c2ecf20Sopenharmony_ci}
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_cistatic void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
28678c2ecf20Sopenharmony_ci{
28688c2ecf20Sopenharmony_ci	phys_addr_t doorbell;
28698c2ecf20Sopenharmony_ci	struct device *dev = msi_desc_to_dev(desc);
28708c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = dev_get_drvdata(dev);
28718c2ecf20Sopenharmony_ci	phys_addr_t *cfg = arm_smmu_msi_cfg[desc->platform.msi_index];
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
28748c2ecf20Sopenharmony_ci	doorbell &= MSI_CFG0_ADDR_MASK;
28758c2ecf20Sopenharmony_ci
28768c2ecf20Sopenharmony_ci	writeq_relaxed(doorbell, smmu->base + cfg[0]);
28778c2ecf20Sopenharmony_ci	writel_relaxed(msg->data, smmu->base + cfg[1]);
28788c2ecf20Sopenharmony_ci	writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
28798c2ecf20Sopenharmony_ci}
28808c2ecf20Sopenharmony_ci
28818c2ecf20Sopenharmony_cistatic void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
28828c2ecf20Sopenharmony_ci{
28838c2ecf20Sopenharmony_ci	struct msi_desc *desc;
28848c2ecf20Sopenharmony_ci	int ret, nvec = ARM_SMMU_MAX_MSIS;
28858c2ecf20Sopenharmony_ci	struct device *dev = smmu->dev;
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	/* Clear the MSI address regs */
28888c2ecf20Sopenharmony_ci	writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
28898c2ecf20Sopenharmony_ci	writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
28928c2ecf20Sopenharmony_ci		writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
28938c2ecf20Sopenharmony_ci	else
28948c2ecf20Sopenharmony_ci		nvec--;
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ci	if (!(smmu->features & ARM_SMMU_FEAT_MSI))
28978c2ecf20Sopenharmony_ci		return;
28988c2ecf20Sopenharmony_ci
28998c2ecf20Sopenharmony_ci	if (!dev->msi_domain) {
29008c2ecf20Sopenharmony_ci		dev_info(smmu->dev, "msi_domain absent - falling back to wired irqs\n");
29018c2ecf20Sopenharmony_ci		return;
29028c2ecf20Sopenharmony_ci	}
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
29058c2ecf20Sopenharmony_ci	ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
29068c2ecf20Sopenharmony_ci	if (ret) {
29078c2ecf20Sopenharmony_ci		dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n");
29088c2ecf20Sopenharmony_ci		return;
29098c2ecf20Sopenharmony_ci	}
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	for_each_msi_entry(desc, dev) {
29128c2ecf20Sopenharmony_ci		switch (desc->platform.msi_index) {
29138c2ecf20Sopenharmony_ci		case EVTQ_MSI_INDEX:
29148c2ecf20Sopenharmony_ci			smmu->evtq.q.irq = desc->irq;
29158c2ecf20Sopenharmony_ci			break;
29168c2ecf20Sopenharmony_ci		case GERROR_MSI_INDEX:
29178c2ecf20Sopenharmony_ci			smmu->gerr_irq = desc->irq;
29188c2ecf20Sopenharmony_ci			break;
29198c2ecf20Sopenharmony_ci		case PRIQ_MSI_INDEX:
29208c2ecf20Sopenharmony_ci			smmu->priq.q.irq = desc->irq;
29218c2ecf20Sopenharmony_ci			break;
29228c2ecf20Sopenharmony_ci		default:	/* Unknown */
29238c2ecf20Sopenharmony_ci			continue;
29248c2ecf20Sopenharmony_ci		}
29258c2ecf20Sopenharmony_ci	}
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci	/* Add callback to free MSIs on teardown */
29288c2ecf20Sopenharmony_ci	devm_add_action(dev, arm_smmu_free_msis, dev);
29298c2ecf20Sopenharmony_ci}
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_cistatic void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
29328c2ecf20Sopenharmony_ci{
29338c2ecf20Sopenharmony_ci	int irq, ret;
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	arm_smmu_setup_msis(smmu);
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_ci	/* Request interrupt lines */
29388c2ecf20Sopenharmony_ci	irq = smmu->evtq.q.irq;
29398c2ecf20Sopenharmony_ci	if (irq) {
29408c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
29418c2ecf20Sopenharmony_ci						arm_smmu_evtq_thread,
29428c2ecf20Sopenharmony_ci						IRQF_ONESHOT,
29438c2ecf20Sopenharmony_ci						"arm-smmu-v3-evtq", smmu);
29448c2ecf20Sopenharmony_ci		if (ret < 0)
29458c2ecf20Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable evtq irq\n");
29468c2ecf20Sopenharmony_ci	} else {
29478c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "no evtq irq - events will not be reported!\n");
29488c2ecf20Sopenharmony_ci	}
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	irq = smmu->gerr_irq;
29518c2ecf20Sopenharmony_ci	if (irq) {
29528c2ecf20Sopenharmony_ci		ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
29538c2ecf20Sopenharmony_ci				       0, "arm-smmu-v3-gerror", smmu);
29548c2ecf20Sopenharmony_ci		if (ret < 0)
29558c2ecf20Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable gerror irq\n");
29568c2ecf20Sopenharmony_ci	} else {
29578c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "no gerr irq - errors will not be reported!\n");
29588c2ecf20Sopenharmony_ci	}
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI) {
29618c2ecf20Sopenharmony_ci		irq = smmu->priq.q.irq;
29628c2ecf20Sopenharmony_ci		if (irq) {
29638c2ecf20Sopenharmony_ci			ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
29648c2ecf20Sopenharmony_ci							arm_smmu_priq_thread,
29658c2ecf20Sopenharmony_ci							IRQF_ONESHOT,
29668c2ecf20Sopenharmony_ci							"arm-smmu-v3-priq",
29678c2ecf20Sopenharmony_ci							smmu);
29688c2ecf20Sopenharmony_ci			if (ret < 0)
29698c2ecf20Sopenharmony_ci				dev_warn(smmu->dev,
29708c2ecf20Sopenharmony_ci					 "failed to enable priq irq\n");
29718c2ecf20Sopenharmony_ci		} else {
29728c2ecf20Sopenharmony_ci			dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
29738c2ecf20Sopenharmony_ci		}
29748c2ecf20Sopenharmony_ci	}
29758c2ecf20Sopenharmony_ci}
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_cistatic int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
29788c2ecf20Sopenharmony_ci{
29798c2ecf20Sopenharmony_ci	int ret, irq;
29808c2ecf20Sopenharmony_ci	u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	/* Disable IRQs first */
29838c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
29848c2ecf20Sopenharmony_ci				      ARM_SMMU_IRQ_CTRLACK);
29858c2ecf20Sopenharmony_ci	if (ret) {
29868c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to disable irqs\n");
29878c2ecf20Sopenharmony_ci		return ret;
29888c2ecf20Sopenharmony_ci	}
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_ci	irq = smmu->combined_irq;
29918c2ecf20Sopenharmony_ci	if (irq) {
29928c2ecf20Sopenharmony_ci		/*
29938c2ecf20Sopenharmony_ci		 * Cavium ThunderX2 implementation doesn't support unique irq
29948c2ecf20Sopenharmony_ci		 * lines. Use a single irq line for all the SMMUv3 interrupts.
29958c2ecf20Sopenharmony_ci		 */
29968c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(smmu->dev, irq,
29978c2ecf20Sopenharmony_ci					arm_smmu_combined_irq_handler,
29988c2ecf20Sopenharmony_ci					arm_smmu_combined_irq_thread,
29998c2ecf20Sopenharmony_ci					IRQF_ONESHOT,
30008c2ecf20Sopenharmony_ci					"arm-smmu-v3-combined-irq", smmu);
30018c2ecf20Sopenharmony_ci		if (ret < 0)
30028c2ecf20Sopenharmony_ci			dev_warn(smmu->dev, "failed to enable combined irq\n");
30038c2ecf20Sopenharmony_ci	} else
30048c2ecf20Sopenharmony_ci		arm_smmu_setup_unique_irqs(smmu);
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI)
30078c2ecf20Sopenharmony_ci		irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	/* Enable interrupt generation on the SMMU */
30108c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
30118c2ecf20Sopenharmony_ci				      ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
30128c2ecf20Sopenharmony_ci	if (ret)
30138c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "failed to enable irqs\n");
30148c2ecf20Sopenharmony_ci
30158c2ecf20Sopenharmony_ci	return 0;
30168c2ecf20Sopenharmony_ci}
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu)
30198c2ecf20Sopenharmony_ci{
30208c2ecf20Sopenharmony_ci	int ret;
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
30238c2ecf20Sopenharmony_ci	if (ret)
30248c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to clear cr0\n");
30258c2ecf20Sopenharmony_ci
30268c2ecf20Sopenharmony_ci	return ret;
30278c2ecf20Sopenharmony_ci}
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_cistatic int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
30308c2ecf20Sopenharmony_ci{
30318c2ecf20Sopenharmony_ci	int ret;
30328c2ecf20Sopenharmony_ci	u32 reg, enables;
30338c2ecf20Sopenharmony_ci	struct arm_smmu_cmdq_ent cmd;
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci	/* Clear CR0 and sync (disables SMMU and queue processing) */
30368c2ecf20Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
30378c2ecf20Sopenharmony_ci	if (reg & CR0_SMMUEN) {
30388c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
30398c2ecf20Sopenharmony_ci		WARN_ON(is_kdump_kernel() && !disable_bypass);
30408c2ecf20Sopenharmony_ci		arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
30418c2ecf20Sopenharmony_ci	}
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci	ret = arm_smmu_device_disable(smmu);
30448c2ecf20Sopenharmony_ci	if (ret)
30458c2ecf20Sopenharmony_ci		return ret;
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci	/* CR1 (table and queue memory attributes) */
30488c2ecf20Sopenharmony_ci	reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) |
30498c2ecf20Sopenharmony_ci	      FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) |
30508c2ecf20Sopenharmony_ci	      FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) |
30518c2ecf20Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) |
30528c2ecf20Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) |
30538c2ecf20Sopenharmony_ci	      FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB);
30548c2ecf20Sopenharmony_ci	writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
30558c2ecf20Sopenharmony_ci
30568c2ecf20Sopenharmony_ci	/* CR2 (random crap) */
30578c2ecf20Sopenharmony_ci	reg = CR2_PTM | CR2_RECINVSID | CR2_E2H;
30588c2ecf20Sopenharmony_ci	writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_ci	/* Stream table */
30618c2ecf20Sopenharmony_ci	writeq_relaxed(smmu->strtab_cfg.strtab_base,
30628c2ecf20Sopenharmony_ci		       smmu->base + ARM_SMMU_STRTAB_BASE);
30638c2ecf20Sopenharmony_ci	writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
30648c2ecf20Sopenharmony_ci		       smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci	/* Command queue */
30678c2ecf20Sopenharmony_ci	writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
30688c2ecf20Sopenharmony_ci	writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
30698c2ecf20Sopenharmony_ci	writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_ci	enables = CR0_CMDQEN;
30728c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
30738c2ecf20Sopenharmony_ci				      ARM_SMMU_CR0ACK);
30748c2ecf20Sopenharmony_ci	if (ret) {
30758c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to enable command queue\n");
30768c2ecf20Sopenharmony_ci		return ret;
30778c2ecf20Sopenharmony_ci	}
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_ci	/* Invalidate any cached configuration */
30808c2ecf20Sopenharmony_ci	cmd.opcode = CMDQ_OP_CFGI_ALL;
30818c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
30828c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_sync(smmu);
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	/* Invalidate any stale TLB entries */
30858c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_HYP) {
30868c2ecf20Sopenharmony_ci		cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
30878c2ecf20Sopenharmony_ci		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
30888c2ecf20Sopenharmony_ci	}
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
30918c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
30928c2ecf20Sopenharmony_ci	arm_smmu_cmdq_issue_sync(smmu);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	/* Event queue */
30958c2ecf20Sopenharmony_ci	writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
30968c2ecf20Sopenharmony_ci	writel_relaxed(smmu->evtq.q.llq.prod,
30978c2ecf20Sopenharmony_ci		       arm_smmu_page1_fixup(ARM_SMMU_EVTQ_PROD, smmu));
30988c2ecf20Sopenharmony_ci	writel_relaxed(smmu->evtq.q.llq.cons,
30998c2ecf20Sopenharmony_ci		       arm_smmu_page1_fixup(ARM_SMMU_EVTQ_CONS, smmu));
31008c2ecf20Sopenharmony_ci
31018c2ecf20Sopenharmony_ci	enables |= CR0_EVTQEN;
31028c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
31038c2ecf20Sopenharmony_ci				      ARM_SMMU_CR0ACK);
31048c2ecf20Sopenharmony_ci	if (ret) {
31058c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to enable event queue\n");
31068c2ecf20Sopenharmony_ci		return ret;
31078c2ecf20Sopenharmony_ci	}
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_ci	/* PRI queue */
31108c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_PRI) {
31118c2ecf20Sopenharmony_ci		writeq_relaxed(smmu->priq.q.q_base,
31128c2ecf20Sopenharmony_ci			       smmu->base + ARM_SMMU_PRIQ_BASE);
31138c2ecf20Sopenharmony_ci		writel_relaxed(smmu->priq.q.llq.prod,
31148c2ecf20Sopenharmony_ci			       arm_smmu_page1_fixup(ARM_SMMU_PRIQ_PROD, smmu));
31158c2ecf20Sopenharmony_ci		writel_relaxed(smmu->priq.q.llq.cons,
31168c2ecf20Sopenharmony_ci			       arm_smmu_page1_fixup(ARM_SMMU_PRIQ_CONS, smmu));
31178c2ecf20Sopenharmony_ci
31188c2ecf20Sopenharmony_ci		enables |= CR0_PRIQEN;
31198c2ecf20Sopenharmony_ci		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
31208c2ecf20Sopenharmony_ci					      ARM_SMMU_CR0ACK);
31218c2ecf20Sopenharmony_ci		if (ret) {
31228c2ecf20Sopenharmony_ci			dev_err(smmu->dev, "failed to enable PRI queue\n");
31238c2ecf20Sopenharmony_ci			return ret;
31248c2ecf20Sopenharmony_ci		}
31258c2ecf20Sopenharmony_ci	}
31268c2ecf20Sopenharmony_ci
31278c2ecf20Sopenharmony_ci	if (smmu->features & ARM_SMMU_FEAT_ATS) {
31288c2ecf20Sopenharmony_ci		enables |= CR0_ATSCHK;
31298c2ecf20Sopenharmony_ci		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
31308c2ecf20Sopenharmony_ci					      ARM_SMMU_CR0ACK);
31318c2ecf20Sopenharmony_ci		if (ret) {
31328c2ecf20Sopenharmony_ci			dev_err(smmu->dev, "failed to enable ATS check\n");
31338c2ecf20Sopenharmony_ci			return ret;
31348c2ecf20Sopenharmony_ci		}
31358c2ecf20Sopenharmony_ci	}
31368c2ecf20Sopenharmony_ci
31378c2ecf20Sopenharmony_ci	ret = arm_smmu_setup_irqs(smmu);
31388c2ecf20Sopenharmony_ci	if (ret) {
31398c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to setup irqs\n");
31408c2ecf20Sopenharmony_ci		return ret;
31418c2ecf20Sopenharmony_ci	}
31428c2ecf20Sopenharmony_ci
31438c2ecf20Sopenharmony_ci	if (is_kdump_kernel())
31448c2ecf20Sopenharmony_ci		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
31458c2ecf20Sopenharmony_ci
31468c2ecf20Sopenharmony_ci	/* Enable the SMMU interface, or ensure bypass */
31478c2ecf20Sopenharmony_ci	if (!bypass || disable_bypass) {
31488c2ecf20Sopenharmony_ci		enables |= CR0_SMMUEN;
31498c2ecf20Sopenharmony_ci	} else {
31508c2ecf20Sopenharmony_ci		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
31518c2ecf20Sopenharmony_ci		if (ret)
31528c2ecf20Sopenharmony_ci			return ret;
31538c2ecf20Sopenharmony_ci	}
31548c2ecf20Sopenharmony_ci	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
31558c2ecf20Sopenharmony_ci				      ARM_SMMU_CR0ACK);
31568c2ecf20Sopenharmony_ci	if (ret) {
31578c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "failed to enable SMMU interface\n");
31588c2ecf20Sopenharmony_ci		return ret;
31598c2ecf20Sopenharmony_ci	}
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci	return 0;
31628c2ecf20Sopenharmony_ci}
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_cistatic int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
31658c2ecf20Sopenharmony_ci{
31668c2ecf20Sopenharmony_ci	u32 reg;
31678c2ecf20Sopenharmony_ci	bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci	/* IDR0 */
31708c2ecf20Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
31718c2ecf20Sopenharmony_ci
31728c2ecf20Sopenharmony_ci	/* 2-level structures */
31738c2ecf20Sopenharmony_ci	if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL)
31748c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci	if (reg & IDR0_CD2L)
31778c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB;
31788c2ecf20Sopenharmony_ci
31798c2ecf20Sopenharmony_ci	/*
31808c2ecf20Sopenharmony_ci	 * Translation table endianness.
31818c2ecf20Sopenharmony_ci	 * We currently require the same endianness as the CPU, but this
31828c2ecf20Sopenharmony_ci	 * could be changed later by adding a new IO_PGTABLE_QUIRK.
31838c2ecf20Sopenharmony_ci	 */
31848c2ecf20Sopenharmony_ci	switch (FIELD_GET(IDR0_TTENDIAN, reg)) {
31858c2ecf20Sopenharmony_ci	case IDR0_TTENDIAN_MIXED:
31868c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
31878c2ecf20Sopenharmony_ci		break;
31888c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
31898c2ecf20Sopenharmony_ci	case IDR0_TTENDIAN_BE:
31908c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_BE;
31918c2ecf20Sopenharmony_ci		break;
31928c2ecf20Sopenharmony_ci#else
31938c2ecf20Sopenharmony_ci	case IDR0_TTENDIAN_LE:
31948c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TT_LE;
31958c2ecf20Sopenharmony_ci		break;
31968c2ecf20Sopenharmony_ci#endif
31978c2ecf20Sopenharmony_ci	default:
31988c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "unknown/unsupported TT endianness!\n");
31998c2ecf20Sopenharmony_ci		return -ENXIO;
32008c2ecf20Sopenharmony_ci	}
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	/* Boolean feature flags */
32038c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI)
32048c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_PRI;
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS)
32078c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_ATS;
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_ci	if (reg & IDR0_SEV)
32108c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_SEV;
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_ci	if (reg & IDR0_MSI) {
32138c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_MSI;
32148c2ecf20Sopenharmony_ci		if (coherent && !disable_msipolling)
32158c2ecf20Sopenharmony_ci			smmu->options |= ARM_SMMU_OPT_MSIPOLL;
32168c2ecf20Sopenharmony_ci	}
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	if (reg & IDR0_HYP)
32198c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_HYP;
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci	/*
32228c2ecf20Sopenharmony_ci	 * The coherency feature as set by FW is used in preference to the ID
32238c2ecf20Sopenharmony_ci	 * register, but warn on mismatch.
32248c2ecf20Sopenharmony_ci	 */
32258c2ecf20Sopenharmony_ci	if (!!(reg & IDR0_COHACC) != coherent)
32268c2ecf20Sopenharmony_ci		dev_warn(smmu->dev, "IDR0.COHACC overridden by FW configuration (%s)\n",
32278c2ecf20Sopenharmony_ci			 coherent ? "true" : "false");
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	switch (FIELD_GET(IDR0_STALL_MODEL, reg)) {
32308c2ecf20Sopenharmony_ci	case IDR0_STALL_MODEL_FORCE:
32318c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_STALL_FORCE;
32328c2ecf20Sopenharmony_ci		fallthrough;
32338c2ecf20Sopenharmony_ci	case IDR0_STALL_MODEL_STALL:
32348c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_STALLS;
32358c2ecf20Sopenharmony_ci	}
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_ci	if (reg & IDR0_S1P)
32388c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	if (reg & IDR0_S2P)
32418c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci	if (!(reg & (IDR0_S1P | IDR0_S2P))) {
32448c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "no translation support!\n");
32458c2ecf20Sopenharmony_ci		return -ENXIO;
32468c2ecf20Sopenharmony_ci	}
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci	/* We only support the AArch64 table format at present */
32498c2ecf20Sopenharmony_ci	switch (FIELD_GET(IDR0_TTF, reg)) {
32508c2ecf20Sopenharmony_ci	case IDR0_TTF_AARCH32_64:
32518c2ecf20Sopenharmony_ci		smmu->ias = 40;
32528c2ecf20Sopenharmony_ci		fallthrough;
32538c2ecf20Sopenharmony_ci	case IDR0_TTF_AARCH64:
32548c2ecf20Sopenharmony_ci		break;
32558c2ecf20Sopenharmony_ci	default:
32568c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "AArch64 table format not supported!\n");
32578c2ecf20Sopenharmony_ci		return -ENXIO;
32588c2ecf20Sopenharmony_ci	}
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci	/* ASID/VMID sizes */
32618c2ecf20Sopenharmony_ci	smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
32628c2ecf20Sopenharmony_ci	smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
32638c2ecf20Sopenharmony_ci
32648c2ecf20Sopenharmony_ci	/* IDR1 */
32658c2ecf20Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
32668c2ecf20Sopenharmony_ci	if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
32678c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "embedded implementation not supported\n");
32688c2ecf20Sopenharmony_ci		return -ENXIO;
32698c2ecf20Sopenharmony_ci	}
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci	/* Queue sizes, capped to ensure natural alignment */
32728c2ecf20Sopenharmony_ci	smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT,
32738c2ecf20Sopenharmony_ci					     FIELD_GET(IDR1_CMDQS, reg));
32748c2ecf20Sopenharmony_ci	if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) {
32758c2ecf20Sopenharmony_ci		/*
32768c2ecf20Sopenharmony_ci		 * We don't support splitting up batches, so one batch of
32778c2ecf20Sopenharmony_ci		 * commands plus an extra sync needs to fit inside the command
32788c2ecf20Sopenharmony_ci		 * queue. There's also no way we can handle the weird alignment
32798c2ecf20Sopenharmony_ci		 * restrictions on the base pointer for a unit-length queue.
32808c2ecf20Sopenharmony_ci		 */
32818c2ecf20Sopenharmony_ci		dev_err(smmu->dev, "command queue size <= %d entries not supported\n",
32828c2ecf20Sopenharmony_ci			CMDQ_BATCH_ENTRIES);
32838c2ecf20Sopenharmony_ci		return -ENXIO;
32848c2ecf20Sopenharmony_ci	}
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci	smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT,
32878c2ecf20Sopenharmony_ci					     FIELD_GET(IDR1_EVTQS, reg));
32888c2ecf20Sopenharmony_ci	smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT,
32898c2ecf20Sopenharmony_ci					     FIELD_GET(IDR1_PRIQS, reg));
32908c2ecf20Sopenharmony_ci
32918c2ecf20Sopenharmony_ci	/* SID/SSID sizes */
32928c2ecf20Sopenharmony_ci	smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg);
32938c2ecf20Sopenharmony_ci	smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg);
32948c2ecf20Sopenharmony_ci
32958c2ecf20Sopenharmony_ci	/*
32968c2ecf20Sopenharmony_ci	 * If the SMMU supports fewer bits than would fill a single L2 stream
32978c2ecf20Sopenharmony_ci	 * table, use a linear table instead.
32988c2ecf20Sopenharmony_ci	 */
32998c2ecf20Sopenharmony_ci	if (smmu->sid_bits <= STRTAB_SPLIT)
33008c2ecf20Sopenharmony_ci		smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci	/* IDR3 */
33038c2ecf20Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3);
33048c2ecf20Sopenharmony_ci	if (FIELD_GET(IDR3_RIL, reg))
33058c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_RANGE_INV;
33068c2ecf20Sopenharmony_ci
33078c2ecf20Sopenharmony_ci	/* IDR5 */
33088c2ecf20Sopenharmony_ci	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	/* Maximum number of outstanding stalls */
33118c2ecf20Sopenharmony_ci	smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg);
33128c2ecf20Sopenharmony_ci
33138c2ecf20Sopenharmony_ci	/* Page sizes */
33148c2ecf20Sopenharmony_ci	if (reg & IDR5_GRAN64K)
33158c2ecf20Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_64K | SZ_512M;
33168c2ecf20Sopenharmony_ci	if (reg & IDR5_GRAN16K)
33178c2ecf20Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_16K | SZ_32M;
33188c2ecf20Sopenharmony_ci	if (reg & IDR5_GRAN4K)
33198c2ecf20Sopenharmony_ci		smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
33208c2ecf20Sopenharmony_ci
33218c2ecf20Sopenharmony_ci	/* Input address size */
33228c2ecf20Sopenharmony_ci	if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT)
33238c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_VAX;
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci	/* Output address size */
33268c2ecf20Sopenharmony_ci	switch (FIELD_GET(IDR5_OAS, reg)) {
33278c2ecf20Sopenharmony_ci	case IDR5_OAS_32_BIT:
33288c2ecf20Sopenharmony_ci		smmu->oas = 32;
33298c2ecf20Sopenharmony_ci		break;
33308c2ecf20Sopenharmony_ci	case IDR5_OAS_36_BIT:
33318c2ecf20Sopenharmony_ci		smmu->oas = 36;
33328c2ecf20Sopenharmony_ci		break;
33338c2ecf20Sopenharmony_ci	case IDR5_OAS_40_BIT:
33348c2ecf20Sopenharmony_ci		smmu->oas = 40;
33358c2ecf20Sopenharmony_ci		break;
33368c2ecf20Sopenharmony_ci	case IDR5_OAS_42_BIT:
33378c2ecf20Sopenharmony_ci		smmu->oas = 42;
33388c2ecf20Sopenharmony_ci		break;
33398c2ecf20Sopenharmony_ci	case IDR5_OAS_44_BIT:
33408c2ecf20Sopenharmony_ci		smmu->oas = 44;
33418c2ecf20Sopenharmony_ci		break;
33428c2ecf20Sopenharmony_ci	case IDR5_OAS_52_BIT:
33438c2ecf20Sopenharmony_ci		smmu->oas = 52;
33448c2ecf20Sopenharmony_ci		smmu->pgsize_bitmap |= 1ULL << 42; /* 4TB */
33458c2ecf20Sopenharmony_ci		break;
33468c2ecf20Sopenharmony_ci	default:
33478c2ecf20Sopenharmony_ci		dev_info(smmu->dev,
33488c2ecf20Sopenharmony_ci			"unknown output address size. Truncating to 48-bit\n");
33498c2ecf20Sopenharmony_ci		fallthrough;
33508c2ecf20Sopenharmony_ci	case IDR5_OAS_48_BIT:
33518c2ecf20Sopenharmony_ci		smmu->oas = 48;
33528c2ecf20Sopenharmony_ci	}
33538c2ecf20Sopenharmony_ci
33548c2ecf20Sopenharmony_ci	if (arm_smmu_ops.pgsize_bitmap == -1UL)
33558c2ecf20Sopenharmony_ci		arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
33568c2ecf20Sopenharmony_ci	else
33578c2ecf20Sopenharmony_ci		arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ci	/* Set the DMA mask for our table walker */
33608c2ecf20Sopenharmony_ci	if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
33618c2ecf20Sopenharmony_ci		dev_warn(smmu->dev,
33628c2ecf20Sopenharmony_ci			 "failed to set DMA mask for table walker\n");
33638c2ecf20Sopenharmony_ci
33648c2ecf20Sopenharmony_ci	smmu->ias = max(smmu->ias, smmu->oas);
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_ci	if (arm_smmu_sva_supported(smmu))
33678c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_SVA;
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
33708c2ecf20Sopenharmony_ci		 smmu->ias, smmu->oas, smmu->features);
33718c2ecf20Sopenharmony_ci	return 0;
33728c2ecf20Sopenharmony_ci}
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
33758c2ecf20Sopenharmony_cistatic void acpi_smmu_get_options(u32 model, struct arm_smmu_device *smmu)
33768c2ecf20Sopenharmony_ci{
33778c2ecf20Sopenharmony_ci	switch (model) {
33788c2ecf20Sopenharmony_ci	case ACPI_IORT_SMMU_V3_CAVIUM_CN99XX:
33798c2ecf20Sopenharmony_ci		smmu->options |= ARM_SMMU_OPT_PAGE0_REGS_ONLY;
33808c2ecf20Sopenharmony_ci		break;
33818c2ecf20Sopenharmony_ci	case ACPI_IORT_SMMU_V3_HISILICON_HI161X:
33828c2ecf20Sopenharmony_ci		smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH;
33838c2ecf20Sopenharmony_ci		break;
33848c2ecf20Sopenharmony_ci	}
33858c2ecf20Sopenharmony_ci
33868c2ecf20Sopenharmony_ci	dev_notice(smmu->dev, "option mask 0x%x\n", smmu->options);
33878c2ecf20Sopenharmony_ci}
33888c2ecf20Sopenharmony_ci
33898c2ecf20Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct platform_device *pdev,
33908c2ecf20Sopenharmony_ci				      struct arm_smmu_device *smmu)
33918c2ecf20Sopenharmony_ci{
33928c2ecf20Sopenharmony_ci	struct acpi_iort_smmu_v3 *iort_smmu;
33938c2ecf20Sopenharmony_ci	struct device *dev = smmu->dev;
33948c2ecf20Sopenharmony_ci	struct acpi_iort_node *node;
33958c2ecf20Sopenharmony_ci
33968c2ecf20Sopenharmony_ci	node = *(struct acpi_iort_node **)dev_get_platdata(dev);
33978c2ecf20Sopenharmony_ci
33988c2ecf20Sopenharmony_ci	/* Retrieve SMMUv3 specific data */
33998c2ecf20Sopenharmony_ci	iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	acpi_smmu_get_options(iort_smmu->model, smmu);
34028c2ecf20Sopenharmony_ci
34038c2ecf20Sopenharmony_ci	if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
34048c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_ci	return 0;
34078c2ecf20Sopenharmony_ci}
34088c2ecf20Sopenharmony_ci#else
34098c2ecf20Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
34108c2ecf20Sopenharmony_ci					     struct arm_smmu_device *smmu)
34118c2ecf20Sopenharmony_ci{
34128c2ecf20Sopenharmony_ci	return -ENODEV;
34138c2ecf20Sopenharmony_ci}
34148c2ecf20Sopenharmony_ci#endif
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct platform_device *pdev,
34178c2ecf20Sopenharmony_ci				    struct arm_smmu_device *smmu)
34188c2ecf20Sopenharmony_ci{
34198c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
34208c2ecf20Sopenharmony_ci	u32 cells;
34218c2ecf20Sopenharmony_ci	int ret = -EINVAL;
34228c2ecf20Sopenharmony_ci
34238c2ecf20Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
34248c2ecf20Sopenharmony_ci		dev_err(dev, "missing #iommu-cells property\n");
34258c2ecf20Sopenharmony_ci	else if (cells != 1)
34268c2ecf20Sopenharmony_ci		dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
34278c2ecf20Sopenharmony_ci	else
34288c2ecf20Sopenharmony_ci		ret = 0;
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci	parse_driver_options(smmu);
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	if (of_dma_is_coherent(dev->of_node))
34338c2ecf20Sopenharmony_ci		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci	return ret;
34368c2ecf20Sopenharmony_ci}
34378c2ecf20Sopenharmony_ci
34388c2ecf20Sopenharmony_cistatic unsigned long arm_smmu_resource_size(struct arm_smmu_device *smmu)
34398c2ecf20Sopenharmony_ci{
34408c2ecf20Sopenharmony_ci	if (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY)
34418c2ecf20Sopenharmony_ci		return SZ_64K;
34428c2ecf20Sopenharmony_ci	else
34438c2ecf20Sopenharmony_ci		return SZ_128K;
34448c2ecf20Sopenharmony_ci}
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_cistatic int arm_smmu_set_bus_ops(struct iommu_ops *ops)
34478c2ecf20Sopenharmony_ci{
34488c2ecf20Sopenharmony_ci	int err;
34498c2ecf20Sopenharmony_ci
34508c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
34518c2ecf20Sopenharmony_ci	if (pci_bus_type.iommu_ops != ops) {
34528c2ecf20Sopenharmony_ci		err = bus_set_iommu(&pci_bus_type, ops);
34538c2ecf20Sopenharmony_ci		if (err)
34548c2ecf20Sopenharmony_ci			return err;
34558c2ecf20Sopenharmony_ci	}
34568c2ecf20Sopenharmony_ci#endif
34578c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA
34588c2ecf20Sopenharmony_ci	if (amba_bustype.iommu_ops != ops) {
34598c2ecf20Sopenharmony_ci		err = bus_set_iommu(&amba_bustype, ops);
34608c2ecf20Sopenharmony_ci		if (err)
34618c2ecf20Sopenharmony_ci			goto err_reset_pci_ops;
34628c2ecf20Sopenharmony_ci	}
34638c2ecf20Sopenharmony_ci#endif
34648c2ecf20Sopenharmony_ci	if (platform_bus_type.iommu_ops != ops) {
34658c2ecf20Sopenharmony_ci		err = bus_set_iommu(&platform_bus_type, ops);
34668c2ecf20Sopenharmony_ci		if (err)
34678c2ecf20Sopenharmony_ci			goto err_reset_amba_ops;
34688c2ecf20Sopenharmony_ci	}
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_ci	return 0;
34718c2ecf20Sopenharmony_ci
34728c2ecf20Sopenharmony_cierr_reset_amba_ops:
34738c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA
34748c2ecf20Sopenharmony_ci	bus_set_iommu(&amba_bustype, NULL);
34758c2ecf20Sopenharmony_ci#endif
34768c2ecf20Sopenharmony_cierr_reset_pci_ops: __maybe_unused;
34778c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
34788c2ecf20Sopenharmony_ci	bus_set_iommu(&pci_bus_type, NULL);
34798c2ecf20Sopenharmony_ci#endif
34808c2ecf20Sopenharmony_ci	return err;
34818c2ecf20Sopenharmony_ci}
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_cistatic void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start,
34848c2ecf20Sopenharmony_ci				      resource_size_t size)
34858c2ecf20Sopenharmony_ci{
34868c2ecf20Sopenharmony_ci	struct resource res = {
34878c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM,
34888c2ecf20Sopenharmony_ci		.start = start,
34898c2ecf20Sopenharmony_ci		.end = start + size - 1,
34908c2ecf20Sopenharmony_ci	};
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	return devm_ioremap_resource(dev, &res);
34938c2ecf20Sopenharmony_ci}
34948c2ecf20Sopenharmony_ci
34958c2ecf20Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev)
34968c2ecf20Sopenharmony_ci{
34978c2ecf20Sopenharmony_ci	int irq, ret;
34988c2ecf20Sopenharmony_ci	struct resource *res;
34998c2ecf20Sopenharmony_ci	resource_size_t ioaddr;
35008c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu;
35018c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
35028c2ecf20Sopenharmony_ci	bool bypass;
35038c2ecf20Sopenharmony_ci
35048c2ecf20Sopenharmony_ci	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
35058c2ecf20Sopenharmony_ci	if (!smmu) {
35068c2ecf20Sopenharmony_ci		dev_err(dev, "failed to allocate arm_smmu_device\n");
35078c2ecf20Sopenharmony_ci		return -ENOMEM;
35088c2ecf20Sopenharmony_ci	}
35098c2ecf20Sopenharmony_ci	smmu->dev = dev;
35108c2ecf20Sopenharmony_ci
35118c2ecf20Sopenharmony_ci	if (dev->of_node) {
35128c2ecf20Sopenharmony_ci		ret = arm_smmu_device_dt_probe(pdev, smmu);
35138c2ecf20Sopenharmony_ci	} else {
35148c2ecf20Sopenharmony_ci		ret = arm_smmu_device_acpi_probe(pdev, smmu);
35158c2ecf20Sopenharmony_ci		if (ret == -ENODEV)
35168c2ecf20Sopenharmony_ci			return ret;
35178c2ecf20Sopenharmony_ci	}
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_ci	/* Set bypass mode according to firmware probing result */
35208c2ecf20Sopenharmony_ci	bypass = !!ret;
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci	/* Base address */
35238c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
35248c2ecf20Sopenharmony_ci	if (!res)
35258c2ecf20Sopenharmony_ci		return -EINVAL;
35268c2ecf20Sopenharmony_ci	if (resource_size(res) < arm_smmu_resource_size(smmu)) {
35278c2ecf20Sopenharmony_ci		dev_err(dev, "MMIO region too small (%pr)\n", res);
35288c2ecf20Sopenharmony_ci		return -EINVAL;
35298c2ecf20Sopenharmony_ci	}
35308c2ecf20Sopenharmony_ci	ioaddr = res->start;
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	/*
35338c2ecf20Sopenharmony_ci	 * Don't map the IMPLEMENTATION DEFINED regions, since they may contain
35348c2ecf20Sopenharmony_ci	 * the PMCG registers which are reserved by the PMU driver.
35358c2ecf20Sopenharmony_ci	 */
35368c2ecf20Sopenharmony_ci	smmu->base = arm_smmu_ioremap(dev, ioaddr, ARM_SMMU_REG_SZ);
35378c2ecf20Sopenharmony_ci	if (IS_ERR(smmu->base))
35388c2ecf20Sopenharmony_ci		return PTR_ERR(smmu->base);
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ci	if (arm_smmu_resource_size(smmu) > SZ_64K) {
35418c2ecf20Sopenharmony_ci		smmu->page1 = arm_smmu_ioremap(dev, ioaddr + SZ_64K,
35428c2ecf20Sopenharmony_ci					       ARM_SMMU_REG_SZ);
35438c2ecf20Sopenharmony_ci		if (IS_ERR(smmu->page1))
35448c2ecf20Sopenharmony_ci			return PTR_ERR(smmu->page1);
35458c2ecf20Sopenharmony_ci	} else {
35468c2ecf20Sopenharmony_ci		smmu->page1 = smmu->base;
35478c2ecf20Sopenharmony_ci	}
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_ci	/* Interrupt lines */
35508c2ecf20Sopenharmony_ci
35518c2ecf20Sopenharmony_ci	irq = platform_get_irq_byname_optional(pdev, "combined");
35528c2ecf20Sopenharmony_ci	if (irq > 0)
35538c2ecf20Sopenharmony_ci		smmu->combined_irq = irq;
35548c2ecf20Sopenharmony_ci	else {
35558c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "eventq");
35568c2ecf20Sopenharmony_ci		if (irq > 0)
35578c2ecf20Sopenharmony_ci			smmu->evtq.q.irq = irq;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "priq");
35608c2ecf20Sopenharmony_ci		if (irq > 0)
35618c2ecf20Sopenharmony_ci			smmu->priq.q.irq = irq;
35628c2ecf20Sopenharmony_ci
35638c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, "gerror");
35648c2ecf20Sopenharmony_ci		if (irq > 0)
35658c2ecf20Sopenharmony_ci			smmu->gerr_irq = irq;
35668c2ecf20Sopenharmony_ci	}
35678c2ecf20Sopenharmony_ci	/* Probe the h/w */
35688c2ecf20Sopenharmony_ci	ret = arm_smmu_device_hw_probe(smmu);
35698c2ecf20Sopenharmony_ci	if (ret)
35708c2ecf20Sopenharmony_ci		return ret;
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ci	/* Initialise in-memory data structures */
35738c2ecf20Sopenharmony_ci	ret = arm_smmu_init_structures(smmu);
35748c2ecf20Sopenharmony_ci	if (ret)
35758c2ecf20Sopenharmony_ci		return ret;
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	/* Record our private device structure */
35788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, smmu);
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci	/* Reset the device */
35818c2ecf20Sopenharmony_ci	ret = arm_smmu_device_reset(smmu, bypass);
35828c2ecf20Sopenharmony_ci	if (ret)
35838c2ecf20Sopenharmony_ci		return ret;
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	/* And we're up. Go go go! */
35868c2ecf20Sopenharmony_ci	ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
35878c2ecf20Sopenharmony_ci				     "smmu3.%pa", &ioaddr);
35888c2ecf20Sopenharmony_ci	if (ret)
35898c2ecf20Sopenharmony_ci		return ret;
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci	iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);
35928c2ecf20Sopenharmony_ci	iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_ci	ret = iommu_device_register(&smmu->iommu);
35958c2ecf20Sopenharmony_ci	if (ret) {
35968c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register iommu\n");
35978c2ecf20Sopenharmony_ci		return ret;
35988c2ecf20Sopenharmony_ci	}
35998c2ecf20Sopenharmony_ci
36008c2ecf20Sopenharmony_ci	return arm_smmu_set_bus_ops(&arm_smmu_ops);
36018c2ecf20Sopenharmony_ci}
36028c2ecf20Sopenharmony_ci
36038c2ecf20Sopenharmony_cistatic int arm_smmu_device_remove(struct platform_device *pdev)
36048c2ecf20Sopenharmony_ci{
36058c2ecf20Sopenharmony_ci	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
36068c2ecf20Sopenharmony_ci
36078c2ecf20Sopenharmony_ci	arm_smmu_set_bus_ops(NULL);
36088c2ecf20Sopenharmony_ci	iommu_device_unregister(&smmu->iommu);
36098c2ecf20Sopenharmony_ci	iommu_device_sysfs_remove(&smmu->iommu);
36108c2ecf20Sopenharmony_ci	arm_smmu_device_disable(smmu);
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_ci	return 0;
36138c2ecf20Sopenharmony_ci}
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev)
36168c2ecf20Sopenharmony_ci{
36178c2ecf20Sopenharmony_ci	arm_smmu_device_remove(pdev);
36188c2ecf20Sopenharmony_ci}
36198c2ecf20Sopenharmony_ci
36208c2ecf20Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = {
36218c2ecf20Sopenharmony_ci	{ .compatible = "arm,smmu-v3", },
36228c2ecf20Sopenharmony_ci	{ },
36238c2ecf20Sopenharmony_ci};
36248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match);
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver = {
36278c2ecf20Sopenharmony_ci	.driver	= {
36288c2ecf20Sopenharmony_ci		.name			= "arm-smmu-v3",
36298c2ecf20Sopenharmony_ci		.of_match_table		= arm_smmu_of_match,
36308c2ecf20Sopenharmony_ci		.suppress_bind_attrs	= true,
36318c2ecf20Sopenharmony_ci	},
36328c2ecf20Sopenharmony_ci	.probe	= arm_smmu_device_probe,
36338c2ecf20Sopenharmony_ci	.remove	= arm_smmu_device_remove,
36348c2ecf20Sopenharmony_ci	.shutdown = arm_smmu_device_shutdown,
36358c2ecf20Sopenharmony_ci};
36368c2ecf20Sopenharmony_cimodule_platform_driver(arm_smmu_driver);
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
36398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>");
36408c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu-v3");
36418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3642