Commit d50314a6 authored by Catalin Marinas's avatar Catalin Marinas

arm64: Create non-empty ZONE_DMA when DRAM starts above 4GB

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: 2d5a5612 (arm64: Limit the CMA buffer to 32-bit if ZONE_DMA)
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Reported-by: default avatarMark Salter <msalter@redhat.com>
Tested-by: default avatarMark Salter <msalter@redhat.com>
Tested-by: default avatarAnup Patel <anup.patel@linaro.org>
parent fa2ec3ea
...@@ -60,6 +60,17 @@ static int __init early_initrd(char *p) ...@@ -60,6 +60,17 @@ static int __init early_initrd(char *p)
early_param("initrd", early_initrd); early_param("initrd", early_initrd);
#endif #endif
/*
* 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) static void __init zone_sizes_init(unsigned long min, unsigned long max)
{ {
struct memblock_region *reg; struct memblock_region *reg;
...@@ -70,9 +81,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max) ...@@ -70,9 +81,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
/* 4GB maximum for 32-bit only capable devices */ /* 4GB maximum for 32-bit only capable devices */
if (IS_ENABLED(CONFIG_ZONE_DMA)) { if (IS_ENABLED(CONFIG_ZONE_DMA)) {
unsigned long max_dma_phys = max_dma = PFN_DOWN(max_zone_dma_phys());
(unsigned long)(dma_to_phys(NULL, DMA_BIT_MASK(32)) + 1);
max_dma = max(min, min(max, max_dma_phys >> PAGE_SHIFT));
zone_size[ZONE_DMA] = max_dma - min; zone_size[ZONE_DMA] = max_dma - min;
} }
zone_size[ZONE_NORMAL] = max - max_dma; zone_size[ZONE_NORMAL] = max - max_dma;
...@@ -146,7 +155,7 @@ void __init arm64_memblock_init(void) ...@@ -146,7 +155,7 @@ void __init arm64_memblock_init(void)
/* 4GB maximum for 32-bit only capable devices */ /* 4GB maximum for 32-bit only capable devices */
if (IS_ENABLED(CONFIG_ZONE_DMA)) if (IS_ENABLED(CONFIG_ZONE_DMA))
dma_phys_limit = dma_to_phys(NULL, DMA_BIT_MASK(32)) + 1; dma_phys_limit = max_zone_dma_phys();
dma_contiguous_reserve(dma_phys_limit); dma_contiguous_reserve(dma_phys_limit);
memblock_allow_resize(); memblock_allow_resize();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment