18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/of_device.h> 78c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "arm-smmu.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct qcom_smmu { 128c2ecf20Sopenharmony_ci struct arm_smmu_device smmu; 138c2ecf20Sopenharmony_ci bool bypass_quirk; 148c2ecf20Sopenharmony_ci u8 bypass_cbndx; 158c2ecf20Sopenharmony_ci}; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci return container_of(smmu, struct qcom_smmu, smmu); 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { 238c2ecf20Sopenharmony_ci { .compatible = "qcom,adreno" }, 248c2ecf20Sopenharmony_ci { .compatible = "qcom,adreno-gmu" }, 258c2ecf20Sopenharmony_ci { .compatible = "qcom,mdp4" }, 268c2ecf20Sopenharmony_ci { .compatible = "qcom,mdss" }, 278c2ecf20Sopenharmony_ci { .compatible = "qcom,sc7180-mdss" }, 288c2ecf20Sopenharmony_ci { .compatible = "qcom,sc7180-mss-pil" }, 298c2ecf20Sopenharmony_ci { .compatible = "qcom,sdm845-mdss" }, 308c2ecf20Sopenharmony_ci { .compatible = "qcom,sdm845-mss-pil" }, 318c2ecf20Sopenharmony_ci { } 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); 378c2ecf20Sopenharmony_ci unsigned int last_s2cr; 388c2ecf20Sopenharmony_ci u32 reg; 398c2ecf20Sopenharmony_ci u32 smr; 408c2ecf20Sopenharmony_ci int i; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * Some platforms support more than the Arm SMMU architected maximum of 448c2ecf20Sopenharmony_ci * 128 stream matching groups. For unknown reasons, the additional 458c2ecf20Sopenharmony_ci * groups don't exhibit the same behavior as the architected registers, 468c2ecf20Sopenharmony_ci * so limit the groups to 128 until the behavior is fixed for the other 478c2ecf20Sopenharmony_ci * groups. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci if (smmu->num_mapping_groups > 128) { 508c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n"); 518c2ecf20Sopenharmony_ci smmu->num_mapping_groups = 128; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * With some firmware versions writes to S2CR of type FAULT are 588c2ecf20Sopenharmony_ci * ignored, and writing BYPASS will end up written as FAULT in the 598c2ecf20Sopenharmony_ci * register. Perform a write to S2CR to detect if this is the case and 608c2ecf20Sopenharmony_ci * if so reserve a context bank to emulate bypass streams. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) | 638c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) | 648c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT); 658c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, last_s2cr, reg); 668c2ecf20Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, last_s2cr); 678c2ecf20Sopenharmony_ci if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) { 688c2ecf20Sopenharmony_ci qsmmu->bypass_quirk = true; 698c2ecf20Sopenharmony_ci qsmmu->bypass_cbndx = smmu->num_context_banks - 1; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci set_bit(qsmmu->bypass_cbndx, smmu->context_map); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS); 768c2ecf20Sopenharmony_ci arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_mapping_groups; i++) { 808c2ecf20Sopenharmony_ci smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (FIELD_GET(ARM_SMMU_SMR_VALID, smr)) { 838c2ecf20Sopenharmony_ci /* Ignore valid bit for SMR mask extraction. */ 848c2ecf20Sopenharmony_ci smr &= ~ARM_SMMU_SMR_VALID; 858c2ecf20Sopenharmony_ci smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr); 868c2ecf20Sopenharmony_ci smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr); 878c2ecf20Sopenharmony_ci smmu->smrs[i].valid = true; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci smmu->s2crs[i].type = S2CR_TYPE_BYPASS; 908c2ecf20Sopenharmony_ci smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT; 918c2ecf20Sopenharmony_ci smmu->s2crs[i].cbndx = 0xff; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void qcom_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; 1018c2ecf20Sopenharmony_ci struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); 1028c2ecf20Sopenharmony_ci u32 cbndx = s2cr->cbndx; 1038c2ecf20Sopenharmony_ci u32 type = s2cr->type; 1048c2ecf20Sopenharmony_ci u32 reg; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (qsmmu->bypass_quirk) { 1078c2ecf20Sopenharmony_ci if (type == S2CR_TYPE_BYPASS) { 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Firmware with quirky S2CR handling will substitute 1108c2ecf20Sopenharmony_ci * BYPASS writes with FAULT, so point the stream to the 1118c2ecf20Sopenharmony_ci * reserved context bank and ask for translation on the 1128c2ecf20Sopenharmony_ci * stream 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci type = S2CR_TYPE_TRANS; 1158c2ecf20Sopenharmony_ci cbndx = qsmmu->bypass_cbndx; 1168c2ecf20Sopenharmony_ci } else if (type == S2CR_TYPE_FAULT) { 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * Firmware with quirky S2CR handling will ignore FAULT 1198c2ecf20Sopenharmony_ci * writes, so trick it to write FAULT by asking for a 1208c2ecf20Sopenharmony_ci * BYPASS. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci type = S2CR_TYPE_BYPASS; 1238c2ecf20Sopenharmony_ci cbndx = 0xff; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, type) | 1288c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_CBNDX, cbndx) | 1298c2ecf20Sopenharmony_ci FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg); 1308c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int qcom_smmu_def_domain_type(struct device *dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci const struct of_device_id *match = 1368c2ecf20Sopenharmony_ci of_match_device(qcom_smmu_client_of_match, dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return match ? IOMMU_DOMAIN_IDENTITY : 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * To address performance degradation in non-real time clients, 1478c2ecf20Sopenharmony_ci * such as USB and UFS, turn off wait-for-safe on sdm845 based boards, 1488c2ecf20Sopenharmony_ci * such as MTP and db845, whose firmwares implement secure monitor 1498c2ecf20Sopenharmony_ci * call handlers to turn on/off the wait-for-safe logic. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci ret = qcom_scm_qsmmu500_wait_safe_toggle(0); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "Failed to turn off SAFE logic\n"); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int qcom_smmu500_reset(struct arm_smmu_device *smmu) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci const struct device_node *np = smmu->dev->of_node; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci arm_mmu500_reset(smmu); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "qcom,sdm845-smmu-500")) 1658c2ecf20Sopenharmony_ci return qcom_sdm845_smmu500_reset(smmu); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl qcom_smmu_impl = { 1718c2ecf20Sopenharmony_ci .cfg_probe = qcom_smmu_cfg_probe, 1728c2ecf20Sopenharmony_ci .def_domain_type = qcom_smmu_def_domain_type, 1738c2ecf20Sopenharmony_ci .reset = qcom_smmu500_reset, 1748c2ecf20Sopenharmony_ci .write_s2cr = qcom_smmu_write_s2cr, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistruct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct qcom_smmu *qsmmu; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Check to make sure qcom_scm has finished probing */ 1828c2ecf20Sopenharmony_ci if (!qcom_scm_is_available()) 1838c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci qsmmu = devm_kzalloc(smmu->dev, sizeof(*qsmmu), GFP_KERNEL); 1868c2ecf20Sopenharmony_ci if (!qsmmu) 1878c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci qsmmu->smmu = *smmu; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci qsmmu->smmu.impl = &qcom_smmu_impl; 1928c2ecf20Sopenharmony_ci devm_kfree(smmu->dev, smmu); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return &qsmmu->smmu; 1958c2ecf20Sopenharmony_ci} 196