aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2019-05-10 17:26:01 -0700
committerBjorn Andersson <bjorn.andersson@linaro.org>2019-05-30 09:40:27 -0700
commit8e0efaf57b2537251d05110f38b3a07d0dba1eb5 (patch)
tree2360f9365e135e8732e4dd7e2e90ad0a95e86350
parentbf2d3733f1c4b23219982add8f708992f89eba97 (diff)
iommu: arm-smmu: handoff
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r--drivers/iommu/arm-smmu-regs.h2
-rw-r--r--drivers/iommu/arm-smmu.c81
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;
}