iommu/arm-smmu-v3: Add SMMUv3.2 range invalidation support
authorRob Herring <robh@kernel.org>
Mon, 24 Feb 2020 22:31:29 +0000 (16:31 -0600)
committerWill Deacon <will@kernel.org>
Wed, 18 Mar 2020 21:37:10 +0000 (21:37 +0000)
Arm SMMUv3.2 adds support for TLB range invalidate operations.
Support for range invalidate is determined by the RIL bit in the IDR3
register.

The range invalidate is in units of the leaf page size and operates on
1-32 chunks of a power of 2 multiple pages. First, we determine from the
size what power of 2 multiple we can use. Then we calculate how many
chunks (1-31) of the power of 2 size for the range on the iteration. On
each iteration, we move up in size by at least 5 bits.

Cc: Jean-Philippe Brucker <jean-philippe@linaro.org>
Cc: Will Deacon <will@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Joerg Roedel <joro@8bytes.org>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/arm-smmu-v3.c

index 4f0a38dae6dba610e0255c156da25ab02769a71f..a7222dd5b11704f9e4a0f5fa635f9a2714f27974 100644 (file)
@@ -69,6 +69,9 @@
 #define IDR1_SSIDSIZE                  GENMASK(10, 6)
 #define IDR1_SIDSIZE                   GENMASK(5, 0)
 
+#define ARM_SMMU_IDR3                  0xc
+#define IDR3_RIL                       (1 << 10)
+
 #define ARM_SMMU_IDR5                  0x14
 #define IDR5_STALL_MAX                 GENMASK(31, 16)
 #define IDR5_GRAN64K                   (1 << 6)
 #define CMDQ_CFGI_1_LEAF               (1UL << 0)
 #define CMDQ_CFGI_1_RANGE              GENMASK_ULL(4, 0)
 
+#define CMDQ_TLBI_0_NUM                        GENMASK_ULL(16, 12)
+#define CMDQ_TLBI_RANGE_NUM_MAX                31
+#define CMDQ_TLBI_0_SCALE              GENMASK_ULL(24, 20)
 #define CMDQ_TLBI_0_VMID               GENMASK_ULL(47, 32)
 #define CMDQ_TLBI_0_ASID               GENMASK_ULL(63, 48)
 #define CMDQ_TLBI_1_LEAF               (1UL << 0)
+#define CMDQ_TLBI_1_TTL                        GENMASK_ULL(9, 8)
+#define CMDQ_TLBI_1_TG                 GENMASK_ULL(11, 10)
 #define CMDQ_TLBI_1_VA_MASK            GENMASK_ULL(63, 12)
 #define CMDQ_TLBI_1_IPA_MASK           GENMASK_ULL(51, 12)
 
@@ -473,9 +481,13 @@ struct arm_smmu_cmdq_ent {
                #define CMDQ_OP_TLBI_S2_IPA     0x2a
                #define CMDQ_OP_TLBI_NSNH_ALL   0x30
                struct {
+                       u8                      num;
+                       u8                      scale;
                        u16                     asid;
                        u16                     vmid;
                        bool                    leaf;
+                       u8                      ttl;
+                       u8                      tg;
                        u64                     addr;
                } tlbi;
 
@@ -632,6 +644,7 @@ struct arm_smmu_device {
 #define ARM_SMMU_FEAT_HYP              (1 << 12)
 #define ARM_SMMU_FEAT_STALL_FORCE      (1 << 13)
 #define ARM_SMMU_FEAT_VAX              (1 << 14)
+#define ARM_SMMU_FEAT_RANGE_INV                (1 << 15)
        u32                             features;
 
 #define ARM_SMMU_OPT_SKIP_PREFETCH     (1 << 0)
@@ -900,14 +913,22 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
                cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31);
                break;
        case CMDQ_OP_TLBI_NH_VA:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
                cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
                cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
                cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
                cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
                break;
        case CMDQ_OP_TLBI_S2_IPA:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
                cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
                cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg);
                cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
                break;
        case CMDQ_OP_TLBI_NH_ASID:
@@ -2252,7 +2273,8 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
                                   struct arm_smmu_domain *smmu_domain)
 {
        struct arm_smmu_device *smmu = smmu_domain->smmu;
-       unsigned long start = iova, end = iova + size;
+       unsigned long start = iova, end = iova + size, num_pages = 0, tg = 0;
+       size_t inv_range = granule;
        struct arm_smmu_cmdq_batch cmds = {};
        struct arm_smmu_cmdq_ent cmd = {
                .tlbi = {
@@ -2271,10 +2293,48 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
                cmd.tlbi.vmid   = smmu_domain->s2_cfg.vmid;
        }
 
+       if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
+               /* Get the leaf page size */
+               tg = __ffs(smmu_domain->domain.pgsize_bitmap);
+
+               /* Convert page size of 12,14,16 (log2) to 1,2,3 */
+               cmd.tlbi.tg = (tg - 10) / 2;
+
+               /* Determine what level the granule is at */
+               cmd.tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3));
+
+               num_pages = size >> tg;
+       }
+
        while (iova < end) {
+               if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
+                       /*
+                        * On each iteration of the loop, the range is 5 bits
+                        * worth of the aligned size remaining.
+                        * The range in pages is:
+                        *
+                        * range = (num_pages & (0x1f << __ffs(num_pages)))
+                        */
+                       unsigned long scale, num;
+
+                       /* Determine the power of 2 multiple number of pages */
+                       scale = __ffs(num_pages);
+                       cmd.tlbi.scale = scale;
+
+                       /* Determine how many chunks of 2^scale size we have */
+                       num = (num_pages >> scale) & CMDQ_TLBI_RANGE_NUM_MAX;
+                       cmd.tlbi.num = num - 1;
+
+                       /* range is num * 2^scale * pgsize */
+                       inv_range = num << (scale + tg);
+
+                       /* Clear out the lower order bits for the next iteration */
+                       num_pages -= num << scale;
+               }
+
                cmd.tlbi.addr = iova;
                arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
-               iova += granule;
+               iova += inv_range;
        }
        arm_smmu_cmdq_batch_submit(smmu, &cmds);
 
@@ -3783,6 +3843,11 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
        if (smmu->sid_bits <= STRTAB_SPLIT)
                smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
 
+       /* IDR3 */
+       reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3);
+       if (FIELD_GET(IDR3_RIL, reg))
+               smmu->features |= ARM_SMMU_FEAT_RANGE_INV;
+
        /* IDR5 */
        reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);