arm64: use both ZONE_DMA and ZONE_DMA32

So far all arm64 devices have supported 32 bit DMA masks for their
peripherals. This is not true anymore for the Raspberry Pi 4 as most of
it's peripherals can only address the first GB of memory on a total of
up to 4 GB.

This goes against ZONE_DMA32's intent, as it's expected for ZONE_DMA32
to be addressable with a 32 bit mask. So it was decided to re-introduce
ZONE_DMA in arm64.

ZONE_DMA will contain the lower 1G of memory, which is currently the
memory area addressable by any peripheral on an arm64 device.
ZONE_DMA32 will contain the rest of the 32 bit addressable memory.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Nicolas Saenz Julienne 2019-09-11 20:25:45 +02:00 committed by Catalin Marinas
parent a573cdd797
commit 1a8e1cef76
3 changed files with 45 additions and 15 deletions

View File

@ -266,6 +266,10 @@ config GENERIC_CSUM
config GENERIC_CALIBRATE_DELAY config GENERIC_CALIBRATE_DELAY
def_bool y def_bool y
config ZONE_DMA
bool "Support DMA zone" if EXPERT
default y
config ZONE_DMA32 config ZONE_DMA32
bool "Support DMA32 zone" if EXPERT bool "Support DMA32 zone" if EXPERT
default y default y

View File

@ -38,4 +38,6 @@ extern int pfn_valid(unsigned long);
#include <asm-generic/getorder.h> #include <asm-generic/getorder.h>
#define ARCH_ZONE_DMA_BITS 30
#endif #endif

View File

@ -56,6 +56,13 @@ EXPORT_SYMBOL(physvirt_offset);
struct page *vmemmap __ro_after_init; struct page *vmemmap __ro_after_init;
EXPORT_SYMBOL(vmemmap); EXPORT_SYMBOL(vmemmap);
/*
* We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
* memory as some devices, namely the Raspberry Pi 4, have peripherals with
* this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
* bit addressable memory area.
*/
phys_addr_t arm64_dma_phys_limit __ro_after_init;
phys_addr_t arm64_dma32_phys_limit __ro_after_init; phys_addr_t arm64_dma32_phys_limit __ro_after_init;
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE
@ -169,15 +176,16 @@ static void __init reserve_elfcorehdr(void)
{ {
} }
#endif /* CONFIG_CRASH_DUMP */ #endif /* CONFIG_CRASH_DUMP */
/* /*
* Return the maximum physical address for ZONE_DMA32 (DMA_BIT_MASK(32)). It * Return the maximum physical address for a zone with a given address size
* currently assumes that for memory starting above 4G, 32-bit devices will * limit. It currently assumes that for memory starting above 4G, 32-bit
* use a DMA offset. * devices will use a DMA offset.
*/ */
static phys_addr_t __init max_zone_dma32_phys(void) static phys_addr_t __init max_zone_phys(unsigned int zone_bits)
{ {
phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32); phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits);
return min(offset + (1ULL << 32), memblock_end_of_DRAM()); return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM());
} }
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
@ -186,6 +194,9 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
{ {
unsigned long max_zone_pfns[MAX_NR_ZONES] = {0}; unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
#ifdef CONFIG_ZONE_DMA
max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
#endif
#ifdef CONFIG_ZONE_DMA32 #ifdef CONFIG_ZONE_DMA32
max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit); max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
#endif #endif
@ -201,13 +212,18 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
struct memblock_region *reg; struct memblock_region *reg;
unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
unsigned long max_dma32 = min; unsigned long max_dma32 = min;
unsigned long max_dma = min;
memset(zone_size, 0, sizeof(zone_size)); memset(zone_size, 0, sizeof(zone_size));
/* 4GB maximum for 32-bit only capable devices */ #ifdef CONFIG_ZONE_DMA
max_dma = PFN_DOWN(arm64_dma_phys_limit);
zone_size[ZONE_DMA] = max_dma - min;
max_dma32 = max_dma;
#endif
#ifdef CONFIG_ZONE_DMA32 #ifdef CONFIG_ZONE_DMA32
max_dma32 = PFN_DOWN(arm64_dma32_phys_limit); max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
zone_size[ZONE_DMA32] = max_dma32 - min; zone_size[ZONE_DMA32] = max_dma32 - max_dma;
#endif #endif
zone_size[ZONE_NORMAL] = max - max_dma32; zone_size[ZONE_NORMAL] = max - max_dma32;
@ -219,11 +235,17 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
if (start >= max) if (start >= max)
continue; continue;
#ifdef CONFIG_ZONE_DMA
if (start < max_dma) {
unsigned long dma_end = min_not_zero(end, max_dma);
zhole_size[ZONE_DMA] -= dma_end - start;
}
#endif
#ifdef CONFIG_ZONE_DMA32 #ifdef CONFIG_ZONE_DMA32
if (start < max_dma32) { if (start < max_dma32) {
unsigned long dma_end = min(end, max_dma32); unsigned long dma32_end = min(end, max_dma32);
zhole_size[ZONE_DMA32] -= dma_end - start; unsigned long dma32_start = max(start, max_dma);
zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
} }
#endif #endif
if (end > max_dma32) { if (end > max_dma32) {
@ -418,9 +440,11 @@ void __init arm64_memblock_init(void)
early_init_fdt_scan_reserved_mem(); early_init_fdt_scan_reserved_mem();
/* 4GB maximum for 32-bit only capable devices */ if (IS_ENABLED(CONFIG_ZONE_DMA))
arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
if (IS_ENABLED(CONFIG_ZONE_DMA32)) if (IS_ENABLED(CONFIG_ZONE_DMA32))
arm64_dma32_phys_limit = max_zone_dma32_phys(); arm64_dma32_phys_limit = max_zone_phys(32);
else else
arm64_dma32_phys_limit = PHYS_MASK + 1; arm64_dma32_phys_limit = PHYS_MASK + 1;
@ -430,7 +454,7 @@ void __init arm64_memblock_init(void)
high_memory = __va(memblock_end_of_DRAM() - 1) + 1; high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
dma_contiguous_reserve(arm64_dma32_phys_limit); dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
} }
void __init bootmem_init(void) void __init bootmem_init(void)
@ -534,7 +558,7 @@ static void __init free_unused_memmap(void)
void __init mem_init(void) void __init mem_init(void)
{ {
if (swiotlb_force == SWIOTLB_FORCE || if (swiotlb_force == SWIOTLB_FORCE ||
max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT)) max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
swiotlb_init(1); swiotlb_init(1);
else else
swiotlb_force = SWIOTLB_NO_FORCE; swiotlb_force = SWIOTLB_NO_FORCE;