diff options
author | Bjorn Andersson <bjorn.andersson@linaro.org> | 2019-05-10 17:26:01 -0700 |
---|---|---|
committer | Bjorn Andersson <bjorn.andersson@linaro.org> | 2019-05-30 09:40:27 -0700 |
commit | 8e0efaf57b2537251d05110f38b3a07d0dba1eb5 (patch) | |
tree | 2360f9365e135e8732e4dd7e2e90ad0a95e86350 | |
parent | bf2d3733f1c4b23219982add8f708992f89eba97 (diff) |
iommu: arm-smmu: handoff
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r-- | drivers/iommu/arm-smmu-regs.h | 2 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu.c | 81 |
2 files changed, 78 insertions, 5 deletions
diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h index 71662cae9806..670491d04352 100644 --- a/drivers/iommu/arm-smmu-regs.h +++ b/drivers/iommu/arm-smmu-regs.h @@ -105,7 +105,9 @@ #define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) #define SMR_VALID (1 << 31) #define SMR_MASK_SHIFT 16 +#define SMR_MASK_MASK 0x7fff #define SMR_ID_SHIFT 0 +#define SMR_ID_MASK 0xffff #define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) #define S2CR_CBNDX_SHIFT 0 diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 740439cd7030..88d757c8cf4e 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -135,6 +135,7 @@ struct arm_smmu_s2cr { enum arm_smmu_s2cr_type type; enum arm_smmu_s2cr_privcfg privcfg; u8 cbndx; + bool handoff; }; #define s2cr_init_val (struct arm_smmu_s2cr){ \ @@ -401,9 +402,22 @@ 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 cbndx; int idx; + int i; + + for_each_cfg_sme(fwspec, i, idx) { + if (smmu->s2crs[idx].handoff) { + cbndx = smmu->s2crs[idx].cbndx; + goto found_handoff; + } + } do { idx = find_next_zero_bit(map, end, start); @@ -412,6 +426,18 @@ static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end) } while (test_and_set_bit(idx, map)); return idx; + +found_handoff: + + for (i = 0; i < smmu->num_mapping_groups; i++) { + if (smmu->s2crs[i].cbndx == cbndx) { + smmu->s2crs[i].cbndx = 0; + smmu->s2crs[i].handoff = false; + smmu->s2crs[i].count -= 1; + } + } + + return cbndx; } static void __arm_smmu_free_bitmap(unsigned long *map, int idx) @@ -881,7 +907,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; @@ -999,8 +1026,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; @@ -1390,7 +1416,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; @@ -1924,6 +1950,49 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); } +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 = readl_relaxed(ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(i)); + mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK; + id = smr & SMR_ID_MASK; + if (!(smr & SMR_VALID)) + continue; + + s2cr = readl_relaxed(ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(i)); + type = (s2cr >> S2CR_TYPE_SHIFT) & S2CR_TYPE_MASK; + cbndx = (s2cr >> S2CR_CBNDX_SHIFT) & S2CR_CBNDX_MASK; + privcfg = (s2cr >> S2CR_PRIVCFG_SHIFT) & S2CR_PRIVCFG_MASK; + if (type != S2CR_TYPE_TRANS) + continue; + + smmu->smrs[i].mask = mask; + smmu->smrs[i].id = id; + smmu->smrs[i].valid = true; + + smmu->s2crs[i].group = NULL; + smmu->s2crs[i].count = 1; + smmu->s2crs[i].type = type; + smmu->s2crs[i].privcfg = privcfg; + smmu->s2crs[i].cbndx = cbndx; + smmu->s2crs[i].handoff = 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) { @@ -2149,6 +2218,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); + return 0; } |