diff options
author | Linaro CI <ci_notify@linaro.org> | 2020-01-06 18:27:50 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2020-01-06 18:27:50 +0000 |
commit | 1778b8d1b5b52893f582143902aa07fdce0ff419 (patch) | |
tree | ba74c7924712f6a8d1e77ae44a9734e8103b20a1 | |
parent | 53906957ab89e22e7bbdbb0444b6dcb7cd620b61 (diff) | |
parent | 430d2c7fd33abaf17066c689500be0ccce49cf71 (diff) |
Merge remote-tracking branch 'iommu/tracking-qcomlt-iommu' into integration-linux-qcomlt
-rw-r--r-- | arch/arm64/boot/dts/qcom/sdm845.dtsi | 1 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu-impl.c | 31 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu.c | 96 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu.h | 1 |
4 files changed, 118 insertions, 11 deletions
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index ddb1f23c936f..ee67977cb9fd 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -3012,6 +3012,7 @@ compatible = "qcom,sdm845-smmu-500", "arm,mmu-500"; reg = <0 0x15000000 0 0x80000>; #iommu-cells = <2>; + qcom,smmu-500-fw-impl-safe-errata; #global-interrupts = <1>; interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>, diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c index b2fe72a8f019..518c4752a7c8 100644 --- a/drivers/iommu/arm-smmu-impl.c +++ b/drivers/iommu/arm-smmu-impl.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "arm-smmu: " fmt #include <linux/bitfield.h> +#include <linux/qcom_scm.h> #include <linux/of.h> #include "arm-smmu.h" @@ -47,7 +48,6 @@ static const struct arm_smmu_impl calxeda_impl = { .write_reg = arm_smmu_write_ns, }; - struct cavium_smmu { struct arm_smmu_device smmu; u32 id_base; @@ -147,6 +147,32 @@ static const struct arm_smmu_impl arm_mmu500_impl = { .reset = arm_mmu500_reset, }; +static int qcom_mmu500_cfg_probe(struct arm_smmu_device *smmu) +{ + int err; + + if (of_property_read_bool(smmu->dev->of_node, + "qcom,smmu-500-fw-impl-safe-errata")) { + /* + * To address performance degradation in non-real time clients, + * such as USB and UFS, turn off wait-for-safe on sdm845 platforms, + * such as MTP, whose firmwares implement corresponding secure monitor + * call handlers. + */ + err = qcom_scm_qsmmu500_wait_safe_toggle(0); + if (err) + dev_warn(smmu->dev, "Failed to turn off SAFE logic\n"); + + printk(KERN_ERR "%s() ===================================\n", __func__); + } + + return 0; +} + +static const struct arm_smmu_impl qcom_mmu500_impl = { + .cfg_probe = qcom_mmu500_cfg_probe, + .reset = arm_mmu500_reset, +}; struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { @@ -160,6 +186,9 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) case ARM_MMU500: smmu->impl = &arm_mmu500_impl; break; + case QCOM_MMU500: + smmu->impl = &qcom_mmu500_impl; + break; case CAVIUM_SMMUV2: return cavium_smmu_impl_init(smmu); default: diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 4f1a350d9529..0700bdd9d43b 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -79,6 +79,7 @@ struct arm_smmu_s2cr { enum arm_smmu_s2cr_type type; enum arm_smmu_s2cr_privcfg privcfg; u8 cbndx; + bool pinned; }; #define s2cr_init_val (struct arm_smmu_s2cr){ \ @@ -220,9 +221,19 @@ static int arm_smmu_register_legacy_master(struct device *dev, return err; } -static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end) +static int __arm_smmu_alloc_cb(struct arm_smmu_device *smmu, int start, + struct device *dev) { + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + unsigned long *map = smmu->context_map; + int end = smmu->num_context_banks; int idx; + int i; + + for_each_cfg_sme(fwspec, i, idx) { + if (smmu->s2crs[idx].pinned) + return smmu->s2crs[idx].cbndx; + } do { idx = find_next_zero_bit(map, end, start); @@ -642,7 +653,8 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) } static int arm_smmu_init_domain_context(struct iommu_domain *domain, - struct arm_smmu_device *smmu) + struct arm_smmu_device *smmu, + struct device *dev) { int irq, start, ret = 0; unsigned long ias, oas; @@ -756,8 +768,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, ret = -EINVAL; goto out_unlock; } - ret = __arm_smmu_alloc_bitmap(smmu->context_map, start, - smmu->num_context_banks); + ret = __arm_smmu_alloc_cb(smmu, start, dev); if (ret < 0) goto out_unlock; @@ -946,23 +957,35 @@ static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu) { u32 smr; + int idx; if (!smmu->smrs) return; + for (idx = 0; idx < smmu->num_mapping_groups; idx++) { + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx)); + if (!(smr & SMR_VALID)) + break; + } + + if (idx == smmu->num_mapping_groups) { + dev_err(smmu->dev, "Unable to compute streamid_mask\n"); + return; + } + /* * SMR.ID bits may not be preserved if the corresponding MASK * bits are set, so check each one separately. We can reject * masters later if they try to claim IDs outside these masks. */ smr = FIELD_PREP(SMR_ID, smmu->streamid_mask); - arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr); - smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0)); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), smr); + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx)); smmu->streamid_mask = FIELD_GET(SMR_ID, smr); smr = FIELD_PREP(SMR_MASK, smmu->streamid_mask); - arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr); - smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0)); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), smr); + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx)); smmu->smr_mask_mask = FIELD_GET(SMR_MASK, smr); } @@ -1010,12 +1033,19 @@ static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask) static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx) { + bool pinned = smmu->s2crs[idx].pinned; + u8 cbndx = smmu->s2crs[idx].cbndx;; + if (--smmu->s2crs[idx].count) return false; smmu->s2crs[idx] = s2cr_init_val; - if (smmu->smrs) + if (pinned) { + smmu->s2crs[idx].pinned = true; + smmu->s2crs[idx].cbndx = cbndx; + } else if (smmu->smrs) { smmu->smrs[idx].valid = false; + } return true; } @@ -1149,7 +1179,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) return ret; /* Ensure that the domain is finalised */ - ret = arm_smmu_init_domain_context(domain, smmu); + ret = arm_smmu_init_domain_context(domain, smmu, dev); if (ret < 0) goto rpm_put; @@ -1667,6 +1697,48 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); } +static int arm_smmu_read_smr_state(struct arm_smmu_device *smmu) +{ + u32 smr, s2cr; + u32 mask; + u32 type; + u32 cbndx; + u32 privcfg; + u32 id; + int i; + + for (i = 0; i < smmu->num_mapping_groups; i++) { + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); + mask = FIELD_GET(SMR_MASK, smr); + id = FIELD_GET(SMR_ID, smr); + + s2cr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_S2CR(i)); + type = FIELD_GET(S2CR_TYPE, s2cr); + cbndx = FIELD_GET(S2CR_CBNDX, s2cr); + privcfg = FIELD_GET(S2CR_PRIVCFG, s2cr); + + smmu->smrs[i].mask = mask; + smmu->smrs[i].id = id; + smmu->smrs[i].valid = !!(smr & SMR_VALID); + + smmu->s2crs[i].group = NULL; + smmu->s2crs[i].count = 0; + smmu->s2crs[i].type = type; + smmu->s2crs[i].privcfg = privcfg; + smmu->s2crs[i].cbndx = cbndx; + + if (!(smr & SMR_VALID)) + continue; + + smmu->s2crs[i].pinned = true; + bitmap_set(smmu->context_map, cbndx, 1); + + dev_err(smmu->dev, "Handoff smr: %x s2cr: %x cb: %d\n", smr, s2cr, cbndx); + } + + return 0; +} + static int arm_smmu_id_size_to_bits(int size) { switch (size) { @@ -1880,6 +1952,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", smmu->ipa_size, smmu->pa_size); + arm_smmu_read_smr_state(smmu); + if (smmu->impl && smmu->impl->cfg_probe) return smmu->impl->cfg_probe(smmu); @@ -1900,6 +1974,7 @@ ARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU); ARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500); ARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2); ARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2); +ARM_SMMU_MATCH_DATA(qcom_mmu500, ARM_SMMU_V2, QCOM_MMU500); static const struct of_device_id arm_smmu_of_match[] = { { .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 }, @@ -1909,6 +1984,7 @@ static const struct of_device_id arm_smmu_of_match[] = { { .compatible = "arm,mmu-500", .data = &arm_mmu500 }, { .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 }, { .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 }, + { .compatible = "qcom,sdm845-smmu-500", .data = &qcom_mmu500 }, { }, }; diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index 62b9f0cec49b..df9edf7e079a 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -222,6 +222,7 @@ enum arm_smmu_implementation { ARM_MMU500, CAVIUM_SMMUV2, QCOM_SMMUV2, + QCOM_MMU500, }; struct arm_smmu_device { |