162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2019, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/acpi.h>
762306a36Sopenharmony_ci#include <linux/adreno-smmu-priv.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/of_device.h>
1062306a36Sopenharmony_ci#include <linux/firmware/qcom/qcom_scm.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "arm-smmu.h"
1362306a36Sopenharmony_ci#include "arm-smmu-qcom.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define QCOM_DUMMY_VAL	-1
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	return container_of(smmu, struct qcom_smmu, smmu);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic void qcom_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
2362306a36Sopenharmony_ci				int sync, int status)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	unsigned int spin_cnt, delay;
2662306a36Sopenharmony_ci	u32 reg;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL);
2962306a36Sopenharmony_ci	for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) {
3062306a36Sopenharmony_ci		for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) {
3162306a36Sopenharmony_ci			reg = arm_smmu_readl(smmu, page, status);
3262306a36Sopenharmony_ci			if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE))
3362306a36Sopenharmony_ci				return;
3462306a36Sopenharmony_ci			cpu_relax();
3562306a36Sopenharmony_ci		}
3662306a36Sopenharmony_ci		udelay(delay);
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	qcom_smmu_tlb_sync_debug(smmu);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void qcom_adreno_smmu_write_sctlr(struct arm_smmu_device *smmu, int idx,
4362306a36Sopenharmony_ci		u32 reg)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/*
4862306a36Sopenharmony_ci	 * On the GPU device we want to process subsequent transactions after a
4962306a36Sopenharmony_ci	 * fault to keep the GPU from hanging
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	reg |= ARM_SMMU_SCTLR_HUPCF;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (qsmmu->stall_enabled & BIT(idx))
5462306a36Sopenharmony_ci		reg |= ARM_SMMU_SCTLR_CFCFG;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void qcom_adreno_smmu_get_fault_info(const void *cookie,
6062306a36Sopenharmony_ci		struct adreno_smmu_fault_info *info)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = (void *)cookie;
6362306a36Sopenharmony_ci	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
6462306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	info->fsr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSR);
6762306a36Sopenharmony_ci	info->fsynr0 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR0);
6862306a36Sopenharmony_ci	info->fsynr1 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR1);
6962306a36Sopenharmony_ci	info->far = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_FAR);
7062306a36Sopenharmony_ci	info->cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx));
7162306a36Sopenharmony_ci	info->ttbr0 = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_TTBR0);
7262306a36Sopenharmony_ci	info->contextidr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_CONTEXTIDR);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void qcom_adreno_smmu_set_stall(const void *cookie, bool enabled)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = (void *)cookie;
7862306a36Sopenharmony_ci	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
7962306a36Sopenharmony_ci	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu_domain->smmu);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (enabled)
8262306a36Sopenharmony_ci		qsmmu->stall_enabled |= BIT(cfg->cbndx);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		qsmmu->stall_enabled &= ~BIT(cfg->cbndx);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void qcom_adreno_smmu_resume_translation(const void *cookie, bool terminate)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = (void *)cookie;
9062306a36Sopenharmony_ci	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
9162306a36Sopenharmony_ci	struct arm_smmu_device *smmu = smmu_domain->smmu;
9262306a36Sopenharmony_ci	u32 reg = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (terminate)
9562306a36Sopenharmony_ci		reg |= ARM_SMMU_RESUME_TERMINATE;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	arm_smmu_cb_write(smmu, cfg->cbndx, ARM_SMMU_CB_RESUME, reg);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define QCOM_ADRENO_SMMU_GPU_SID 0
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic bool qcom_adreno_smmu_is_gpu_device(struct device *dev)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
10562306a36Sopenharmony_ci	int i;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/*
10862306a36Sopenharmony_ci	 * The GPU will always use SID 0 so that is a handy way to uniquely
10962306a36Sopenharmony_ci	 * identify it and configure it for per-instance pagetables
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	for (i = 0; i < fwspec->num_ids; i++) {
11262306a36Sopenharmony_ci		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (sid == QCOM_ADRENO_SMMU_GPU_SID)
11562306a36Sopenharmony_ci			return true;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return false;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic const struct io_pgtable_cfg *qcom_adreno_smmu_get_ttbr1_cfg(
12262306a36Sopenharmony_ci		const void *cookie)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = (void *)cookie;
12562306a36Sopenharmony_ci	struct io_pgtable *pgtable =
12662306a36Sopenharmony_ci		io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops);
12762306a36Sopenharmony_ci	return &pgtable->cfg;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * Local implementation to configure TTBR0 with the specified pagetable config.
13262306a36Sopenharmony_ci * The GPU driver will call this to enable TTBR0 when per-instance pagetables
13362306a36Sopenharmony_ci * are active
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int qcom_adreno_smmu_set_ttbr0_cfg(const void *cookie,
13762306a36Sopenharmony_ci		const struct io_pgtable_cfg *pgtbl_cfg)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct arm_smmu_domain *smmu_domain = (void *)cookie;
14062306a36Sopenharmony_ci	struct io_pgtable *pgtable = io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops);
14162306a36Sopenharmony_ci	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
14262306a36Sopenharmony_ci	struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* The domain must have split pagetables already enabled */
14562306a36Sopenharmony_ci	if (cb->tcr[0] & ARM_SMMU_TCR_EPD1)
14662306a36Sopenharmony_ci		return -EINVAL;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* If the pagetable config is NULL, disable TTBR0 */
14962306a36Sopenharmony_ci	if (!pgtbl_cfg) {
15062306a36Sopenharmony_ci		/* Do nothing if it is already disabled */
15162306a36Sopenharmony_ci		if ((cb->tcr[0] & ARM_SMMU_TCR_EPD0))
15262306a36Sopenharmony_ci			return -EINVAL;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		/* Set TCR to the original configuration */
15562306a36Sopenharmony_ci		cb->tcr[0] = arm_smmu_lpae_tcr(&pgtable->cfg);
15662306a36Sopenharmony_ci		cb->ttbr[0] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid);
15762306a36Sopenharmony_ci	} else {
15862306a36Sopenharmony_ci		u32 tcr = cb->tcr[0];
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		/* Don't call this again if TTBR0 is already enabled */
16162306a36Sopenharmony_ci		if (!(cb->tcr[0] & ARM_SMMU_TCR_EPD0))
16262306a36Sopenharmony_ci			return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		tcr |= arm_smmu_lpae_tcr(pgtbl_cfg);
16562306a36Sopenharmony_ci		tcr &= ~(ARM_SMMU_TCR_EPD0 | ARM_SMMU_TCR_EPD1);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		cb->tcr[0] = tcr;
16862306a36Sopenharmony_ci		cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
16962306a36Sopenharmony_ci		cb->ttbr[0] |= FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	arm_smmu_write_context_bank(smmu_domain->smmu, cb->cfg->cbndx);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int qcom_adreno_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain,
17862306a36Sopenharmony_ci					       struct arm_smmu_device *smmu,
17962306a36Sopenharmony_ci					       struct device *dev, int start)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int count;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Assign context bank 0 to the GPU device so the GPU hardware can
18562306a36Sopenharmony_ci	 * switch pagetables
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	if (qcom_adreno_smmu_is_gpu_device(dev)) {
18862306a36Sopenharmony_ci		start = 0;
18962306a36Sopenharmony_ci		count = 1;
19062306a36Sopenharmony_ci	} else {
19162306a36Sopenharmony_ci		start = 1;
19262306a36Sopenharmony_ci		count = smmu->num_context_banks;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return __arm_smmu_alloc_bitmap(smmu->context_map, start, count);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic bool qcom_adreno_can_do_ttbr1(struct arm_smmu_device *smmu)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	const struct device_node *np = smmu->dev->of_node;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2"))
20362306a36Sopenharmony_ci		return false;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return true;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain,
20962306a36Sopenharmony_ci		struct io_pgtable_cfg *pgtbl_cfg, struct device *dev)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct adreno_smmu_priv *priv;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	smmu_domain->cfg.flush_walk_prefer_tlbiasid = true;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Only enable split pagetables for the GPU device (SID 0) */
21662306a36Sopenharmony_ci	if (!qcom_adreno_smmu_is_gpu_device(dev))
21762306a36Sopenharmony_ci		return 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/*
22062306a36Sopenharmony_ci	 * All targets that use the qcom,adreno-smmu compatible string *should*
22162306a36Sopenharmony_ci	 * be AARCH64 stage 1 but double check because the arm-smmu code assumes
22262306a36Sopenharmony_ci	 * that is the case when the TTBR1 quirk is enabled
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	if (qcom_adreno_can_do_ttbr1(smmu_domain->smmu) &&
22562306a36Sopenharmony_ci	    (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) &&
22662306a36Sopenharmony_ci	    (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64))
22762306a36Sopenharmony_ci		pgtbl_cfg->quirks |= IO_PGTABLE_QUIRK_ARM_TTBR1;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Initialize private interface with GPU:
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	priv = dev_get_drvdata(dev);
23462306a36Sopenharmony_ci	priv->cookie = smmu_domain;
23562306a36Sopenharmony_ci	priv->get_ttbr1_cfg = qcom_adreno_smmu_get_ttbr1_cfg;
23662306a36Sopenharmony_ci	priv->set_ttbr0_cfg = qcom_adreno_smmu_set_ttbr0_cfg;
23762306a36Sopenharmony_ci	priv->get_fault_info = qcom_adreno_smmu_get_fault_info;
23862306a36Sopenharmony_ci	priv->set_stall = qcom_adreno_smmu_set_stall;
23962306a36Sopenharmony_ci	priv->resume_translation = qcom_adreno_smmu_resume_translation;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
24562306a36Sopenharmony_ci	{ .compatible = "qcom,adreno" },
24662306a36Sopenharmony_ci	{ .compatible = "qcom,adreno-gmu" },
24762306a36Sopenharmony_ci	{ .compatible = "qcom,mdp4" },
24862306a36Sopenharmony_ci	{ .compatible = "qcom,mdss" },
24962306a36Sopenharmony_ci	{ .compatible = "qcom,sc7180-mdss" },
25062306a36Sopenharmony_ci	{ .compatible = "qcom,sc7180-mss-pil" },
25162306a36Sopenharmony_ci	{ .compatible = "qcom,sc7280-mdss" },
25262306a36Sopenharmony_ci	{ .compatible = "qcom,sc7280-mss-pil" },
25362306a36Sopenharmony_ci	{ .compatible = "qcom,sc8180x-mdss" },
25462306a36Sopenharmony_ci	{ .compatible = "qcom,sc8280xp-mdss" },
25562306a36Sopenharmony_ci	{ .compatible = "qcom,sdm845-mdss" },
25662306a36Sopenharmony_ci	{ .compatible = "qcom,sdm845-mss-pil" },
25762306a36Sopenharmony_ci	{ .compatible = "qcom,sm6350-mdss" },
25862306a36Sopenharmony_ci	{ .compatible = "qcom,sm6375-mdss" },
25962306a36Sopenharmony_ci	{ .compatible = "qcom,sm8150-mdss" },
26062306a36Sopenharmony_ci	{ .compatible = "qcom,sm8250-mdss" },
26162306a36Sopenharmony_ci	{ }
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int qcom_smmu_init_context(struct arm_smmu_domain *smmu_domain,
26562306a36Sopenharmony_ci		struct io_pgtable_cfg *pgtbl_cfg, struct device *dev)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	smmu_domain->cfg.flush_walk_prefer_tlbiasid = true;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
27562306a36Sopenharmony_ci	unsigned int last_s2cr;
27662306a36Sopenharmony_ci	u32 reg;
27762306a36Sopenharmony_ci	u32 smr;
27862306a36Sopenharmony_ci	int i;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * Some platforms support more than the Arm SMMU architected maximum of
28262306a36Sopenharmony_ci	 * 128 stream matching groups. For unknown reasons, the additional
28362306a36Sopenharmony_ci	 * groups don't exhibit the same behavior as the architected registers,
28462306a36Sopenharmony_ci	 * so limit the groups to 128 until the behavior is fixed for the other
28562306a36Sopenharmony_ci	 * groups.
28662306a36Sopenharmony_ci	 */
28762306a36Sopenharmony_ci	if (smmu->num_mapping_groups > 128) {
28862306a36Sopenharmony_ci		dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n");
28962306a36Sopenharmony_ci		smmu->num_mapping_groups = 128;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/*
29562306a36Sopenharmony_ci	 * With some firmware versions writes to S2CR of type FAULT are
29662306a36Sopenharmony_ci	 * ignored, and writing BYPASS will end up written as FAULT in the
29762306a36Sopenharmony_ci	 * register. Perform a write to S2CR to detect if this is the case and
29862306a36Sopenharmony_ci	 * if so reserve a context bank to emulate bypass streams.
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) |
30162306a36Sopenharmony_ci	      FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) |
30262306a36Sopenharmony_ci	      FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT);
30362306a36Sopenharmony_ci	arm_smmu_gr0_write(smmu, last_s2cr, reg);
30462306a36Sopenharmony_ci	reg = arm_smmu_gr0_read(smmu, last_s2cr);
30562306a36Sopenharmony_ci	if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) {
30662306a36Sopenharmony_ci		qsmmu->bypass_quirk = true;
30762306a36Sopenharmony_ci		qsmmu->bypass_cbndx = smmu->num_context_banks - 1;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		set_bit(qsmmu->bypass_cbndx, smmu->context_map);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
31462306a36Sopenharmony_ci		arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	for (i = 0; i < smmu->num_mapping_groups; i++) {
31862306a36Sopenharmony_ci		smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		if (FIELD_GET(ARM_SMMU_SMR_VALID, smr)) {
32162306a36Sopenharmony_ci			/* Ignore valid bit for SMR mask extraction. */
32262306a36Sopenharmony_ci			smr &= ~ARM_SMMU_SMR_VALID;
32362306a36Sopenharmony_ci			smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
32462306a36Sopenharmony_ci			smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
32562306a36Sopenharmony_ci			smmu->smrs[i].valid = true;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci			smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
32862306a36Sopenharmony_ci			smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
32962306a36Sopenharmony_ci			smmu->s2crs[i].cbndx = 0xff;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void qcom_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
33962306a36Sopenharmony_ci	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
34062306a36Sopenharmony_ci	u32 cbndx = s2cr->cbndx;
34162306a36Sopenharmony_ci	u32 type = s2cr->type;
34262306a36Sopenharmony_ci	u32 reg;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (qsmmu->bypass_quirk) {
34562306a36Sopenharmony_ci		if (type == S2CR_TYPE_BYPASS) {
34662306a36Sopenharmony_ci			/*
34762306a36Sopenharmony_ci			 * Firmware with quirky S2CR handling will substitute
34862306a36Sopenharmony_ci			 * BYPASS writes with FAULT, so point the stream to the
34962306a36Sopenharmony_ci			 * reserved context bank and ask for translation on the
35062306a36Sopenharmony_ci			 * stream
35162306a36Sopenharmony_ci			 */
35262306a36Sopenharmony_ci			type = S2CR_TYPE_TRANS;
35362306a36Sopenharmony_ci			cbndx = qsmmu->bypass_cbndx;
35462306a36Sopenharmony_ci		} else if (type == S2CR_TYPE_FAULT) {
35562306a36Sopenharmony_ci			/*
35662306a36Sopenharmony_ci			 * Firmware with quirky S2CR handling will ignore FAULT
35762306a36Sopenharmony_ci			 * writes, so trick it to write FAULT by asking for a
35862306a36Sopenharmony_ci			 * BYPASS.
35962306a36Sopenharmony_ci			 */
36062306a36Sopenharmony_ci			type = S2CR_TYPE_BYPASS;
36162306a36Sopenharmony_ci			cbndx = 0xff;
36262306a36Sopenharmony_ci		}
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, type) |
36662306a36Sopenharmony_ci	      FIELD_PREP(ARM_SMMU_S2CR_CBNDX, cbndx) |
36762306a36Sopenharmony_ci	      FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg);
36862306a36Sopenharmony_ci	arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int qcom_smmu_def_domain_type(struct device *dev)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	const struct of_device_id *match =
37462306a36Sopenharmony_ci		of_match_device(qcom_smmu_client_of_match, dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return match ? IOMMU_DOMAIN_IDENTITY : 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int ret;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	arm_mmu500_reset(smmu);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/*
38662306a36Sopenharmony_ci	 * To address performance degradation in non-real time clients,
38762306a36Sopenharmony_ci	 * such as USB and UFS, turn off wait-for-safe on sdm845 based boards,
38862306a36Sopenharmony_ci	 * such as MTP and db845, whose firmwares implement secure monitor
38962306a36Sopenharmony_ci	 * call handlers to turn on/off the wait-for-safe logic.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	ret = qcom_scm_qsmmu500_wait_safe_toggle(0);
39262306a36Sopenharmony_ci	if (ret)
39362306a36Sopenharmony_ci		dev_warn(smmu->dev, "Failed to turn off SAFE logic\n");
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return ret;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic const struct arm_smmu_impl qcom_smmu_v2_impl = {
39962306a36Sopenharmony_ci	.init_context = qcom_smmu_init_context,
40062306a36Sopenharmony_ci	.cfg_probe = qcom_smmu_cfg_probe,
40162306a36Sopenharmony_ci	.def_domain_type = qcom_smmu_def_domain_type,
40262306a36Sopenharmony_ci	.write_s2cr = qcom_smmu_write_s2cr,
40362306a36Sopenharmony_ci	.tlb_sync = qcom_smmu_tlb_sync,
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic const struct arm_smmu_impl qcom_smmu_500_impl = {
40762306a36Sopenharmony_ci	.init_context = qcom_smmu_init_context,
40862306a36Sopenharmony_ci	.cfg_probe = qcom_smmu_cfg_probe,
40962306a36Sopenharmony_ci	.def_domain_type = qcom_smmu_def_domain_type,
41062306a36Sopenharmony_ci	.reset = arm_mmu500_reset,
41162306a36Sopenharmony_ci	.write_s2cr = qcom_smmu_write_s2cr,
41262306a36Sopenharmony_ci	.tlb_sync = qcom_smmu_tlb_sync,
41362306a36Sopenharmony_ci};
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic const struct arm_smmu_impl sdm845_smmu_500_impl = {
41662306a36Sopenharmony_ci	.init_context = qcom_smmu_init_context,
41762306a36Sopenharmony_ci	.cfg_probe = qcom_smmu_cfg_probe,
41862306a36Sopenharmony_ci	.def_domain_type = qcom_smmu_def_domain_type,
41962306a36Sopenharmony_ci	.reset = qcom_sdm845_smmu500_reset,
42062306a36Sopenharmony_ci	.write_s2cr = qcom_smmu_write_s2cr,
42162306a36Sopenharmony_ci	.tlb_sync = qcom_smmu_tlb_sync,
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic const struct arm_smmu_impl qcom_adreno_smmu_v2_impl = {
42562306a36Sopenharmony_ci	.init_context = qcom_adreno_smmu_init_context,
42662306a36Sopenharmony_ci	.def_domain_type = qcom_smmu_def_domain_type,
42762306a36Sopenharmony_ci	.alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
42862306a36Sopenharmony_ci	.write_sctlr = qcom_adreno_smmu_write_sctlr,
42962306a36Sopenharmony_ci	.tlb_sync = qcom_smmu_tlb_sync,
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic const struct arm_smmu_impl qcom_adreno_smmu_500_impl = {
43362306a36Sopenharmony_ci	.init_context = qcom_adreno_smmu_init_context,
43462306a36Sopenharmony_ci	.def_domain_type = qcom_smmu_def_domain_type,
43562306a36Sopenharmony_ci	.reset = arm_mmu500_reset,
43662306a36Sopenharmony_ci	.alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
43762306a36Sopenharmony_ci	.write_sctlr = qcom_adreno_smmu_write_sctlr,
43862306a36Sopenharmony_ci	.tlb_sync = qcom_smmu_tlb_sync,
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu,
44262306a36Sopenharmony_ci		const struct qcom_smmu_match_data *data)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	const struct device_node *np = smmu->dev->of_node;
44562306a36Sopenharmony_ci	const struct arm_smmu_impl *impl;
44662306a36Sopenharmony_ci	struct qcom_smmu *qsmmu;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (!data)
44962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (np && of_device_is_compatible(np, "qcom,adreno-smmu"))
45262306a36Sopenharmony_ci		impl = data->adreno_impl;
45362306a36Sopenharmony_ci	else
45462306a36Sopenharmony_ci		impl = data->impl;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!impl)
45762306a36Sopenharmony_ci		return smmu;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Check to make sure qcom_scm has finished probing */
46062306a36Sopenharmony_ci	if (!qcom_scm_is_available())
46162306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	qsmmu = devm_krealloc(smmu->dev, smmu, sizeof(*qsmmu), GFP_KERNEL);
46462306a36Sopenharmony_ci	if (!qsmmu)
46562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	qsmmu->smmu.impl = impl;
46862306a36Sopenharmony_ci	qsmmu->cfg = data->cfg;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return &qsmmu->smmu;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/* Implementation Defined Register Space 0 register offsets */
47462306a36Sopenharmony_cistatic const u32 qcom_smmu_impl0_reg_offset[] = {
47562306a36Sopenharmony_ci	[QCOM_SMMU_TBU_PWR_STATUS]		= 0x2204,
47662306a36Sopenharmony_ci	[QCOM_SMMU_STATS_SYNC_INV_TBU_ACK]	= 0x25dc,
47762306a36Sopenharmony_ci	[QCOM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR]	= 0x2670,
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const struct qcom_smmu_config qcom_smmu_impl0_cfg = {
48162306a36Sopenharmony_ci	.reg_offset = qcom_smmu_impl0_reg_offset,
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/*
48562306a36Sopenharmony_ci * It is not yet possible to use MDP SMMU with the bypass quirk on the msm8996,
48662306a36Sopenharmony_ci * there are not enough context banks.
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cistatic const struct qcom_smmu_match_data msm8996_smmu_data = {
48962306a36Sopenharmony_ci	.impl = NULL,
49062306a36Sopenharmony_ci	.adreno_impl = &qcom_adreno_smmu_v2_impl,
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic const struct qcom_smmu_match_data qcom_smmu_v2_data = {
49462306a36Sopenharmony_ci	.impl = &qcom_smmu_v2_impl,
49562306a36Sopenharmony_ci	.adreno_impl = &qcom_adreno_smmu_v2_impl,
49662306a36Sopenharmony_ci};
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic const struct qcom_smmu_match_data sdm845_smmu_500_data = {
49962306a36Sopenharmony_ci	.impl = &sdm845_smmu_500_impl,
50062306a36Sopenharmony_ci	/*
50162306a36Sopenharmony_ci	 * No need for adreno impl here. On sdm845 the Adreno SMMU is handled
50262306a36Sopenharmony_ci	 * by the separate sdm845-smmu-v2 device.
50362306a36Sopenharmony_ci	 */
50462306a36Sopenharmony_ci	/* Also no debug configuration. */
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic const struct qcom_smmu_match_data qcom_smmu_500_impl0_data = {
50862306a36Sopenharmony_ci	.impl = &qcom_smmu_500_impl,
50962306a36Sopenharmony_ci	.adreno_impl = &qcom_adreno_smmu_500_impl,
51062306a36Sopenharmony_ci	.cfg = &qcom_smmu_impl0_cfg,
51162306a36Sopenharmony_ci};
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/*
51462306a36Sopenharmony_ci * Do not add any more qcom,SOC-smmu-500 entries to this list, unless they need
51562306a36Sopenharmony_ci * special handling and can not be covered by the qcom,smmu-500 entry.
51662306a36Sopenharmony_ci */
51762306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
51862306a36Sopenharmony_ci	{ .compatible = "qcom,msm8996-smmu-v2", .data = &msm8996_smmu_data },
51962306a36Sopenharmony_ci	{ .compatible = "qcom,msm8998-smmu-v2", .data = &qcom_smmu_v2_data },
52062306a36Sopenharmony_ci	{ .compatible = "qcom,qcm2290-smmu-500", .data = &qcom_smmu_500_impl0_data },
52162306a36Sopenharmony_ci	{ .compatible = "qcom,qdu1000-smmu-500", .data = &qcom_smmu_500_impl0_data  },
52262306a36Sopenharmony_ci	{ .compatible = "qcom,sc7180-smmu-500", .data = &qcom_smmu_500_impl0_data },
52362306a36Sopenharmony_ci	{ .compatible = "qcom,sc7180-smmu-v2", .data = &qcom_smmu_v2_data },
52462306a36Sopenharmony_ci	{ .compatible = "qcom,sc7280-smmu-500", .data = &qcom_smmu_500_impl0_data },
52562306a36Sopenharmony_ci	{ .compatible = "qcom,sc8180x-smmu-500", .data = &qcom_smmu_500_impl0_data },
52662306a36Sopenharmony_ci	{ .compatible = "qcom,sc8280xp-smmu-500", .data = &qcom_smmu_500_impl0_data },
52762306a36Sopenharmony_ci	{ .compatible = "qcom,sdm630-smmu-v2", .data = &qcom_smmu_v2_data },
52862306a36Sopenharmony_ci	{ .compatible = "qcom,sdm845-smmu-v2", .data = &qcom_smmu_v2_data },
52962306a36Sopenharmony_ci	{ .compatible = "qcom,sdm845-smmu-500", .data = &sdm845_smmu_500_data },
53062306a36Sopenharmony_ci	{ .compatible = "qcom,sm6115-smmu-500", .data = &qcom_smmu_500_impl0_data},
53162306a36Sopenharmony_ci	{ .compatible = "qcom,sm6125-smmu-500", .data = &qcom_smmu_500_impl0_data },
53262306a36Sopenharmony_ci	{ .compatible = "qcom,sm6350-smmu-v2", .data = &qcom_smmu_v2_data },
53362306a36Sopenharmony_ci	{ .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data },
53462306a36Sopenharmony_ci	{ .compatible = "qcom,sm6375-smmu-v2", .data = &qcom_smmu_v2_data },
53562306a36Sopenharmony_ci	{ .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data },
53662306a36Sopenharmony_ci	{ .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data },
53762306a36Sopenharmony_ci	{ .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data },
53862306a36Sopenharmony_ci	{ .compatible = "qcom,sm8350-smmu-500", .data = &qcom_smmu_500_impl0_data },
53962306a36Sopenharmony_ci	{ .compatible = "qcom,sm8450-smmu-500", .data = &qcom_smmu_500_impl0_data },
54062306a36Sopenharmony_ci	{ .compatible = "qcom,smmu-500", .data = &qcom_smmu_500_impl0_data },
54162306a36Sopenharmony_ci	{ }
54262306a36Sopenharmony_ci};
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci#ifdef CONFIG_ACPI
54562306a36Sopenharmony_cistatic struct acpi_platform_list qcom_acpi_platlist[] = {
54662306a36Sopenharmony_ci	{ "LENOVO", "CB-01   ", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" },
54762306a36Sopenharmony_ci	{ "QCOM  ", "QCOMEDK2", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" },
54862306a36Sopenharmony_ci	{ }
54962306a36Sopenharmony_ci};
55062306a36Sopenharmony_ci#endif
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistruct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	const struct device_node *np = smmu->dev->of_node;
55562306a36Sopenharmony_ci	const struct of_device_id *match;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci#ifdef CONFIG_ACPI
55862306a36Sopenharmony_ci	if (np == NULL) {
55962306a36Sopenharmony_ci		/* Match platform for ACPI boot */
56062306a36Sopenharmony_ci		if (acpi_match_platform_list(qcom_acpi_platlist) >= 0)
56162306a36Sopenharmony_ci			return qcom_smmu_create(smmu, &qcom_smmu_500_impl0_data);
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci#endif
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	match = of_match_node(qcom_smmu_impl_of_match, np);
56662306a36Sopenharmony_ci	if (match)
56762306a36Sopenharmony_ci		return qcom_smmu_create(smmu, match->data);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/*
57062306a36Sopenharmony_ci	 * If you hit this WARN_ON() you are missing an entry in the
57162306a36Sopenharmony_ci	 * qcom_smmu_impl_of_match[] table, and GPU per-process page-
57262306a36Sopenharmony_ci	 * tables will be broken.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	WARN(of_device_is_compatible(np, "qcom,adreno-smmu"),
57562306a36Sopenharmony_ci	     "Missing qcom_smmu_impl_of_match entry for: %s",
57662306a36Sopenharmony_ci	     dev_name(smmu->dev));
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return smmu;
57962306a36Sopenharmony_ci}
580