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