iommu/arm-smmu-v3: Don't reserve implementation defined register space
authorJean-Philippe Brucker <jean-philippe@linaro.org>
Wed, 13 May 2020 11:02:57 +0000 (13:02 +0200)
committerWill Deacon <will@kernel.org>
Mon, 18 May 2020 14:36:01 +0000 (15:36 +0100)
Some SMMUv3 implementation embed the Perf Monitor Group Registers (PMCG)
inside the first 64kB region of the SMMU. Since PMCG are managed by a
separate driver, this layout causes resource reservation conflicts
during boot.

To avoid this conflict, don't reserve the MMIO regions that are
implementation defined. Although devm_ioremap_resource() still works on
full pages under the hood, this way we benefit from resource conflict
checks.

Fixes: 7d839b4b9e00 ("perf/smmuv3: Add arm64 smmuv3 pmu driver")
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Link: https://lore.kernel.org/r/20200513110255.597203-1-jean-philippe@linaro.org
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/arm-smmu-v3.c

index 89ee9c5..5eec8eb 100644 (file)
 #define ARM_SMMU_PRIQ_IRQ_CFG1         0xd8
 #define ARM_SMMU_PRIQ_IRQ_CFG2         0xdc
 
+#define ARM_SMMU_REG_SZ                        0xe00
+
 /* Common MSI config fields */
 #define MSI_CFG0_ADDR_MASK             GENMASK_ULL(51, 2)
 #define MSI_CFG2_SH                    GENMASK(5, 4)
@@ -628,6 +630,7 @@ struct arm_smmu_strtab_cfg {
 struct arm_smmu_device {
        struct device                   *dev;
        void __iomem                    *base;
+       void __iomem                    *page1;
 
 #define ARM_SMMU_FEAT_2_LVL_STRTAB     (1 << 0)
 #define ARM_SMMU_FEAT_2_LVL_CDTAB      (1 << 1)
@@ -733,9 +736,8 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
 static inline void __iomem *arm_smmu_page1_fixup(unsigned long offset,
                                                 struct arm_smmu_device *smmu)
 {
-       if ((offset > SZ_64K) &&
-           (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY))
-               offset -= SZ_64K;
+       if (offset > SZ_64K)
+               return smmu->page1 + offset - SZ_64K;
 
        return smmu->base + offset;
 }
@@ -4001,6 +4003,18 @@ err_reset_pci_ops: __maybe_unused;
        return err;
 }
 
+static void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start,
+                                     resource_size_t size)
+{
+       struct resource res = {
+               .flags = IORESOURCE_MEM,
+               .start = start,
+               .end = start + size - 1,
+       };
+
+       return devm_ioremap_resource(dev, &res);
+}
+
 static int arm_smmu_device_probe(struct platform_device *pdev)
 {
        int irq, ret;
@@ -4036,10 +4050,23 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
        }
        ioaddr = res->start;
 
-       smmu->base = devm_ioremap_resource(dev, res);
+       /*
+        * Don't map the IMPLEMENTATION DEFINED regions, since they may contain
+        * the PMCG registers which are reserved by the PMU driver.
+        */
+       smmu->base = arm_smmu_ioremap(dev, ioaddr, ARM_SMMU_REG_SZ);
        if (IS_ERR(smmu->base))
                return PTR_ERR(smmu->base);
 
+       if (arm_smmu_resource_size(smmu) > SZ_64K) {
+               smmu->page1 = arm_smmu_ioremap(dev, ioaddr + SZ_64K,
+                                              ARM_SMMU_REG_SZ);
+               if (IS_ERR(smmu->page1))
+                       return PTR_ERR(smmu->page1);
+       } else {
+               smmu->page1 = smmu->base;
+       }
+
        /* Interrupt lines */
 
        irq = platform_get_irq_byname_optional(pdev, "combined");