arm64: Create non-empty ZONE_DMA when DRAM starts above 4GB
authorCatalin Marinas <catalin.marinas@arm.com>
Fri, 18 Jul 2014 10:54:37 +0000 (11:54 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 21 Jan 2015 23:10:57 +0000 (23:10 +0000)
ZONE_DMA is created to allow 32-bit only devices to access memory in the
absence of an IOMMU. On systems where the memory starts above 4GB, it is
expected that some devices have a DMA offset hardwired to be able to
access the bottom of the memory. Linux currently supports DT bindings
for the DMA offsets but they are not (easily) available early during
boot.

This patch tries to guess a DMA offset and assumes that ZONE_DMA
corresponds to the 32-bit mask above the start of DRAM.

Fixes: 2d5a5612bc (arm64: Limit the CMA buffer to 32-bit if ZONE_DMA)
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Reported-by: Mark Salter <msalter@redhat.com>
Tested-by: Mark Salter <msalter@redhat.com>
Tested-by: Anup Patel <anup.patel@linaro.org>
(cherry picked from commit d50314a6b0702c630c35b88148c1acb76d2e4ede)
Signed-off-by: Mark Brown <broonie@kernel.org>
Conflicts:
arch/arm64/mm/init.c

arch/arm64/mm/init.c

index 81bdd29df3a42c4f71a74759c32d8219d39fdaa3..6757d02d697631ebc8c4a9d9aed9e800c2975b51 100644 (file)
@@ -68,6 +68,17 @@ static int __init early_initrd(char *p)
 }
 early_param("initrd", early_initrd);
 
+/*
+ * Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It
+ * currently assumes that for memory starting above 4G, 32-bit devices will
+ * use a DMA offset.
+ */
+static phys_addr_t max_zone_dma_phys(void)
+{
+       phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
+       return min(offset + (1ULL << 32), memblock_end_of_DRAM());
+}
+
 static void __init zone_sizes_init(unsigned long min, unsigned long max)
 {
        struct memblock_region *reg;
@@ -78,9 +89,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
 
        /* 4GB maximum for 32-bit only capable devices */
        if (IS_ENABLED(CONFIG_ZONE_DMA)) {
-               unsigned long max_dma_phys =
-                       (unsigned long)dma_to_phys(NULL, DMA_BIT_MASK(32) + 1);
-               max_dma = max(min, min(max, max_dma_phys >> PAGE_SHIFT));
+               max_dma = PFN_DOWN(max_zone_dma_phys());
                zone_size[ZONE_DMA] = max_dma - min;
        }
        zone_size[ZONE_NORMAL] = max - max_dma;
@@ -171,7 +180,11 @@ void __init arm64_memblock_init(void)
        }
 
        early_init_fdt_scan_reserved_mem();
-       dma_contiguous_reserve(0);
+
+       /* 4GB maximum for 32-bit only capable devices */
+       if (IS_ENABLED(CONFIG_ZONE_DMA))
+               dma_phys_limit = max_zone_dma_phys();
+       dma_contiguous_reserve(dma_phys_limit);
 
        memblock_allow_resize();
        memblock_dump_all();