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.herrm...@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

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to