[PATCH 7/7] iommu/arm-smmu: Introduce automatic stream-id-masking
Andreas Herrmann
andreas.herrmann at calxeda.com
Wed Oct 9 18:38:06 EDT 2013
Try to determine a mask that can be used for all StreamIDs of a master
device. This allows to use just one SMR group instead of
number-of-streamids SMR groups for a master device.
Signed-off-by: Andreas Herrmann <andreas.herrmann at calxeda.com>
---
drivers/iommu/arm-smmu.c | 79 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 91316a8..4e8ceab 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -28,6 +28,7 @@
* - Context fault reporting
*/
+#define DEBUG
#define pr_fmt(fmt) "arm-smmu: " fmt
#include <linux/delay.h>
@@ -341,6 +342,9 @@ struct arm_smmu_master {
struct rb_node node;
int num_streamids;
u16 streamids[MAX_MASTER_STREAMIDS];
+ int num_smrs;
+ u16 smr_mask;
+ u16 smr_id;
/*
* We only need to allocate these on the root SMMU, as we
@@ -530,14 +534,11 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
ARM_SMMU_OPT_MASK_STREAM_IDS));
return -EINVAL;
}
- /* set fixed streamid (0) that will be used for masking */
- master->num_streamids = 1;
- master->streamids[0] = 0;
- } else {
- for (i = 0; i < master->num_streamids; ++i)
- master->streamids[i] = masterspec->args[i];
}
+ for (i = 0; i < master->num_streamids; ++i)
+ master->streamids[i] = masterspec->args[i];
+
return insert_smmu_master(smmu, master);
}
@@ -1049,6 +1050,41 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
kfree(smmu_domain);
}
+/*
+ * no duplicates streamids please
+ */
+static void determine_smr_mapping(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ int nr_sid;
+ u16 i, v1, v2, const_mask;
+
+ if (smmu->options & ARM_SMMU_OPT_MASK_STREAM_IDS) {
+ master->smr_mask = smmu->smr_mask_bits;
+ master->smr_id = 0;
+ return;
+ }
+
+ nr_sid = master->num_streamids;
+ if (!is_power_of_2(nr_sid))
+ return;
+
+ v1 = 0;
+ v2 = -1;
+ for (i = 0; i < nr_sid; i++) {
+ v1 |= master->streamids[i]; /* for const 0 bits */
+ v2 &= ~(master->streamids[i]); /* const 1 bits */
+ }
+ const_mask = (~v1) | v2; /* const bits (either 0 or 1) */
+
+ v1 = hweight16(~const_mask);
+ if ((1 << v1) == nr_sid) {
+ /* if smr_mask is set, only 1 SMR group is used smr[0] = 0 */
+ master->smr_mask = ~const_mask;
+ master->smr_id = v1 & const_mask;
+ }
+}
+
static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
struct arm_smmu_master *master)
{
@@ -1062,15 +1098,22 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
if (master->smrs)
return -EEXIST;
- smrs = kmalloc(sizeof(*smrs) * master->num_streamids, GFP_KERNEL);
+ determine_smr_mapping(smmu, master);
+
+ if (master->smr_mask)
+ master->num_smrs = 1;
+ else
+ master->num_smrs = master->num_streamids;
+
+ smrs = kmalloc(sizeof(*smrs) * master->num_smrs, GFP_KERNEL);
if (!smrs) {
dev_err(smmu->dev, "failed to allocate %d SMRs for master %s\n",
- master->num_streamids, master->of_node->name);
+ master->num_smrs, master->of_node->name);
return -ENOMEM;
}
/* Allocate the SMRs on the root SMMU */
- for (i = 0; i < master->num_streamids; ++i) {
+ for (i = 0; i < master->num_smrs; ++i) {
int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
smmu->num_mapping_groups);
if (IS_ERR_VALUE(idx)) {
@@ -1079,16 +1122,18 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
}
smrs[i].idx = idx;
- smrs[i].id = master->streamids[i];
- if (smmu->options & ARM_SMMU_OPT_MASK_STREAM_IDS)
- smrs[i].mask = smmu->smr_mask_bits;
- else
+ if (master->smr_mask) {
+ smrs[i].mask = master->smr_mask;
+ smrs[i].id = master->smr_id;
+ } else {
smrs[i].mask = 0;
+ smrs[i].id = master->streamids[i];
+ }
}
/* It worked! Now, poke the actual hardware */
- for (i = 0; i < master->num_streamids; ++i) {
+ for (i = 0; i < master->num_smrs; ++i) {
u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
smrs[i].mask << SMR_MASK_SHIFT;
dev_dbg(smmu->dev, "SMR%d: 0x%x\n", smrs[i].idx, reg);
@@ -1139,7 +1184,7 @@ static void arm_smmu_bypass_stream_mapping(struct arm_smmu_device *smmu,
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_master *master)
{
- int i, ret;
+ int i, ret, num_s2crs;
struct arm_smmu_device *parent, *smmu = smmu_domain->root_cfg.smmu;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
@@ -1163,11 +1208,13 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
}
/* Now we're at the root, time to point at our context bank */
- for (i = 0; i < master->num_streamids; ++i) {
+ num_s2crs = master->num_smrs ? master->num_smrs : master->num_streamids;
+ for (i = 0; i < num_s2crs; ++i) {
u32 idx, s2cr;
idx = master->smrs ? master->smrs[i].idx : master->streamids[i];
s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) |
(smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT);
+ dev_dbg(smmu->dev, "S2CR%d: 0x%x\n", idx, s2cr);
writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
}
--
1.7.9.5
More information about the linux-arm-kernel
mailing list