ARM: Implement non-cached memory support
authorThierry Reding <treding@nvidia.com>
Wed, 10 Dec 2014 05:25:22 +0000 (22:25 -0700)
committerAlbert ARIBAUD <albert.u.boot@aribaud.net>
Thu, 18 Dec 2014 20:18:43 +0000 (21:18 +0100)
Implement an API that can be used by drivers to allocate memory from a
pool that is mapped uncached. This is useful if drivers would otherwise
need to do extensive cache maintenance (or explicitly maintaining the
cache isn't safe).

The API is protected using the new CONFIG_SYS_NONCACHED_MEMORY setting.
Boards can set this to the size to be used for the non-cached area. The
area will typically be right below the malloc() area, but architectures
should take care of aligning the beginning and end of the area to honor
any mapping restrictions. Architectures must also ensure that mappings
established for this area do not overlap with the malloc() area (which
should remain cached for improved performance).

While the API is currently only implemented for ARM v7, it should be
generic enough to allow other architectures to implement it as well.

Signed-off-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
README
arch/arm/include/asm/system.h
arch/arm/lib/cache.c
common/board_r.c

diff --git a/README b/README
index 4ca04d0489ed3dcd3f04f403cc527f633746f1c2..5af345b0860959c94451383b8389a833f83ec159 100644 (file)
--- a/README
+++ b/README
@@ -4007,6 +4007,25 @@ Configuration Settings:
                boards which do not use the full malloc in SPL (which is
                enabled with CONFIG_SYS_SPL_MALLOC_START).
 
+- CONFIG_SYS_NONCACHED_MEMORY:
+               Size of non-cached memory area. This area of memory will be
+               typically located right below the malloc() area and mapped
+               uncached in the MMU. This is useful for drivers that would
+               otherwise require a lot of explicit cache maintenance. For
+               some drivers it's also impossible to properly maintain the
+               cache. For example if the regions that need to be flushed
+               are not a multiple of the cache-line size, *and* padding
+               cannot be allocated between the regions to align them (i.e.
+               if the HW requires a contiguous array of regions, and the
+               size of each region is not cache-aligned), then a flush of
+               one region may result in overwriting data that hardware has
+               written to another region in the same cache-line. This can
+               happen for example in network drivers where descriptors for
+               buffers are typically smaller than the CPU cache-line (e.g.
+               16 bytes vs. 32 or 64 bytes).
+
+               Non-cached memory is only supported on 32-bit ARM at present.
+
 - CONFIG_SYS_BOOTM_LEN:
                Normally compressed uImages are limited to an
                uncompressed size of 8 MBytes. If this is not enough,
index 61e2914d44b44a1d3be2e92b74f52d6a2a765b12..89f22946895301b09a183ead75503ae4dff0f1b2 100644 (file)
@@ -212,6 +212,11 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
  */
 void mmu_page_table_flush(unsigned long start, unsigned long stop);
 
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+void noncached_init(void);
+phys_addr_t noncached_alloc(size_t size, size_t align);
+#endif /* CONFIG_SYS_NONCACHED_MEMORY */
+
 #endif /* __ASSEMBLY__ */
 
 #define arch_align_stack(x) (x)
index f1c0792ce8da0afcf1b17b68f6cd9b7547b07822..9cedeac6d641eb7b8548fb8571bcd6a261388fdb 100644 (file)
@@ -8,6 +8,7 @@
 /* for now: just dummy functions to satisfy the linker */
 
 #include <common.h>
+#include <malloc.h>
 
 __weak void flush_cache(unsigned long start, unsigned long size)
 {
@@ -49,3 +50,46 @@ __weak void enable_caches(void)
 {
        puts("WARNING: Caches not enabled\n");
 }
+
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+/*
+ * Reserve one MMU section worth of address space below the malloc() area that
+ * will be mapped uncached.
+ */
+static unsigned long noncached_start;
+static unsigned long noncached_end;
+static unsigned long noncached_next;
+
+void noncached_init(void)
+{
+       phys_addr_t start, end;
+       size_t size;
+
+       end = ALIGN(mem_malloc_start, MMU_SECTION_SIZE) - MMU_SECTION_SIZE;
+       size = ALIGN(CONFIG_SYS_NONCACHED_MEMORY, MMU_SECTION_SIZE);
+       start = end - size;
+
+       debug("mapping memory %pa-%pa non-cached\n", &start, &end);
+
+       noncached_start = start;
+       noncached_end = end;
+       noncached_next = start;
+
+#ifndef CONFIG_SYS_DCACHE_OFF
+       mmu_set_region_dcache_behaviour(noncached_start, size, DCACHE_OFF);
+#endif
+}
+
+phys_addr_t noncached_alloc(size_t size, size_t align)
+{
+       phys_addr_t next = ALIGN(noncached_next, align);
+
+       if (next >= noncached_end || (noncached_end - next) < size)
+               return 0;
+
+       debug("allocated %zu bytes of uncached memory @%pa\n", size, &next);
+       noncached_next = next + size;
+
+       return next;
+}
+#endif /* CONFIG_SYS_NONCACHED_MEMORY */
index 4eb7a023d4eaf5e2c1bdfee9c48bf65b49fb7c9e..a301cc226f1e69fb4f6229382391c80218f12460 100644 (file)
@@ -265,6 +265,14 @@ static int initr_malloc(void)
        return 0;
 }
 
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+static int initr_noncached(void)
+{
+       noncached_init();
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_DM
 static int initr_dm(void)
 {
@@ -687,6 +695,9 @@ init_fnc_t init_sequence_r[] = {
 #endif
        initr_barrier,
        initr_malloc,
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
+       initr_noncached,
+#endif
        bootstage_relocate,
 #ifdef CONFIG_DM
        initr_dm,