diff --git a/drivers/iommu/arm-smmu-qcom.c b/drivers/iommu/arm-smmu-qcom.c index 24c071c1d8b0..06e5799dcb87 100644 --- a/drivers/iommu/arm-smmu-qcom.c +++ b/drivers/iommu/arm-smmu-qcom.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ +#include #include #include "arm-smmu.h" @@ -11,6 +12,39 @@ struct qcom_smmu { struct arm_smmu_device smmu; }; +static int qcom_sdm845_smmu500_cfg_probe(struct arm_smmu_device *smmu) +{ + u32 s2cr; + u32 smr; + int i; + + for (i = 0; i < smmu->num_mapping_groups; i++) { + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); + s2cr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_S2CR(i)); + + smmu->smrs[i].mask = FIELD_GET(SMR_MASK, smr); + smmu->smrs[i].id = FIELD_GET(SMR_ID, smr); + if (smmu->features & ARM_SMMU_FEAT_EXIDS) + smmu->smrs[i].valid = FIELD_GET(S2CR_EXIDVALID, s2cr); + else + smmu->smrs[i].valid = FIELD_GET(SMR_VALID, smr); + + smmu->s2crs[i].group = NULL; + smmu->s2crs[i].count = 0; + smmu->s2crs[i].type = FIELD_GET(S2CR_TYPE, s2cr); + smmu->s2crs[i].privcfg = FIELD_GET(S2CR_PRIVCFG, s2cr); + smmu->s2crs[i].cbndx = FIELD_GET(S2CR_CBNDX, s2cr); + + if (!smmu->smrs[i].valid) + continue; + + smmu->s2crs[i].pinned = true; + bitmap_set(smmu->context_map, smmu->s2crs[i].cbndx, 1); + } + + return 0; +} + static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) { int ret; @@ -31,6 +65,7 @@ static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) } static const struct arm_smmu_impl qcom_smmu_impl = { + .cfg_probe = qcom_sdm845_smmu500_cfg_probe, .reset = qcom_sdm845_smmu500_reset, }; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index f2620ead9fac..badd637e96b5 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -225,9 +225,19 @@ static int arm_smmu_register_legacy_master(struct device *dev, } #endif /* CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS */ -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); @@ -647,7 +657,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; @@ -761,8 +772,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; @@ -1034,12 +1044,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; } @@ -1173,7 +1190,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; diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index 73f94579b926..0701e6875964 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -230,6 +230,7 @@ struct arm_smmu_s2cr { enum arm_smmu_s2cr_type type; enum arm_smmu_s2cr_privcfg privcfg; u8 cbndx; + bool pinned; }; struct arm_smmu_smr {