[U-Boot] [PATCH v7 04/11] arm: add MMU/D-Cache support for Faraday cores

Kuo-Jung Su
2013-07-29T05:51:46Z

This updates the map_physmem()/unmap_physmem(), and use
them to implement dma_alloc_coherent() & dma_free_coherent().

It uses 1MB section for each mapping, and thus wastes lots of
address space, however this should still be good enough for
tiny systems (i.e. u-boot).

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
CC: Albert Aribaud <albert.u.boot@aribaud.net>
---
Changes for v6, v7:
- Nothing updates

Changes for v5:
- Add void dram_bank_mmu_setup() into 'arch/arm/cpu/faraday/cpu.c'
to override the weak function in "cache-cp15.c".
- Use small page (4KB) to map relocated exception table to 0x0000

Changes for v4:
- Coding Style cleanup.

Changes for v3:
- Coding Style cleanup.
- Always insert a blank line between declarations and code.
- dma-mapping.h: Have the global data ptr declared outside functions.
- dma-mapping.h: Add #if...#else...#endif to dma_free_coherent().
- Drop static non-cached region, now we use map_physmem()/unmap_physmem()
for dynamic mappings.

Changes for v2:
- Coding Style cleanup.
- cache-cp15: Enable write buffer in write-through mode.

arch/arm/include/asm/dma-mapping.h | 56 ++++++++++++-
arch/arm/include/asm/global_data.h | 4 +
arch/arm/include/asm/io.h | 159 ++++++++++++++++++++++++++++++++++--
arch/arm/include/asm/system.h | 7 +-
arch/arm/lib/cache-cp15.c | 9 ++
5 files changed, 223 insertions(+), 12 deletions(-)

diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 55a4e26..e066204 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -8,22 +8,76 @@
#ifndef __ASM_ARM_DMA_MAPPING_H
#define __ASM_ARM_DMA_MAPPING_H

+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <malloc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
+
enum dma_data_direction {
DMA_BIDIRECTIONAL = 0,
DMA_TO_DEVICE = 1,
DMA_FROM_DEVICE = 2,
};

-static void *dma_alloc_coherent(size_t len, unsigned long *handle)
+static inline void *dma_alloc_coherent(size_t len, unsigned long *handle)
{
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ void *map, *va = memalign(ARCH_DMA_MINALIGN, len);
+
+ if (va && gd->arch.cpu_mmu) {
+ invalidate_dcache_range((ulong)va, (ulong)va + len);
+ map = map_physmem((phys_addr_t)va, len, MAP_NOCACHE);
+ if (!map)
+ free(va);
+ va = map;
+ }
+
+ if (handle)
+ *handle = virt_to_phys(va);
+
+ return va;
+#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
*handle = (unsigned long)memalign(ARCH_DMA_MINALIGN, len);
return (void *)*handle;
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
+}
+
+static inline void dma_free_coherent(void *vaddr, ulong len)
+{
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ void *tmp = (void *)virt_to_phys(vaddr);
+ unmap_physmem(vaddr, len);
+ vaddr = tmp;
+#endif
+ free(vaddr);
}

static inline unsigned long dma_map_single(volatile void *vaddr, size_t len,
enum dma_data_direction dir)
{
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ if (gd->arch.cpu_mmu) {
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_TO_DEVICE:
+ flush_dcache_range((ulong)vaddr,
+ (ulong)vaddr + len);
+ break;
+
+ case DMA_FROM_DEVICE:
+ invalidate_dcache_range((ulong)vaddr,
+ (ulong)vaddr + len);
+ break;
+ }
+ }
+ return virt_to_phys((void *)vaddr);
+#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
return (unsigned long)vaddr;
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
}

static inline void dma_unmap_single(volatile void *vaddr, size_t len,
diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
index 79a9597..587ecbc 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -26,6 +26,10 @@ struct arch_global_data {
unsigned long pllb_rate_hz;
unsigned long at91_pllb_usb_init;
#endif
+#ifdef CONFIG_FARADAY
+ unsigned long cpu_id;
+ unsigned long cpu_mmu; /* has mmu */
+#endif
/* "static data" needed by most of timer.c on ARM platforms */
unsigned long timer_rate_hz;
unsigned long tbu;
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 1fbc531..6edb53a 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -28,9 +28,36 @@
#if 0 /* XXX###XXX */
#include <asm/arch/hardware.h>
#endif /* XXX###XXX */
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+#include <asm/system.h>
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
+
+#ifndef CONFIG_MMAP_START
+#define CONFIG_MMAP_START 0xd0000000
+#endif
+
+#ifndef CONFIG_MMAP_END
+#define CONFIG_MMAP_END 0xfff00000
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
+
+/* arch/$(ARCH)/lib/cache.c */
+void invalidate_icache_all(void);
+void flush_dcache_all(void);
+void flush_dcache_range(ulong start, ulong stop);

static inline void sync(void)
{
+#ifndef CONFIG_SYS_DCACHE_OFF
+ flush_dcache_all();
+#endif
+#ifndef CONFIG_SYS_ICACHE_OFF
+ invalidate_icache_all();
+#endif
}

/*
@@ -39,27 +66,143 @@ static inline void sync(void)
* properties specified by "flags".
*/
#define MAP_NOCACHE (0)
-#define MAP_WRCOMBINE (0)
-#define MAP_WRBACK (0)
-#define MAP_WRTHROUGH (0)
+#define MAP_WRCOMBINE (1)
+#define MAP_WRBACK (2)
+#define MAP_WRTHROUGH (3)
+
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+static inline void map_flush(ulong start, ulong end)
+{
+ flush_dcache_range(start, end);
+
+ /* invalidate D-TLB */
+ start &= 0xfff00000;
+ end = (end + 0x000fffff) & 0xfff00000;
+ __asm__ __volatile__ (
+ "mov r3, %0\n"
+ "1:\n"
+ "mcr p15, 0, r3, c8, c6, 1\n"
+ "add r3, r3, #4096\n"
+ "cmp r3, %1\n"
+ "blo 1b\n"
+ : /* output */
+ : "r"(start), "r"(end) /* input */
+ : "r3" /* clobber list */
+ );
+}
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */

static inline void *
-map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags)
+map_physmem(phys_addr_t paddr, ulong len, ulong flags)
{
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ u32 *page_table = (u32 *)gd->arch.tlb_addr;
+ u32 vaddr, nattr, oattr, addr, size, end;
+
+ /* 1. check if we have to create a mapping for it */
+ vaddr = paddr;
+ addr = page_table[vaddr >> 20] & 0xfff00000;
+ oattr = page_table[vaddr >> 20] & 0x1f;
+ switch (flags) {
+ case MAP_WRCOMBINE:
+ nattr = DCACHE_WRITECOMBINE;
+ break;
+ case MAP_WRTHROUGH:
+ nattr = DCACHE_WRITETHROUGH;
+ break;
+ case MAP_WRBACK:
+ nattr = DCACHE_WRITEBACK;
+ break;
+ default:
+ nattr = DCACHE_OFF;
+ break;
+ }
+ if ((nattr == oattr) && (vaddr == addr))
+ return (void *)paddr;
+
+ /* 2. find a contiguous region for it */
+ end = (paddr + len + 0x000fffff) & 0xfff00000;
+ len = end - (paddr & 0xfff00000);
+ size = 0;
+ addr = CONFIG_MMAP_START;
+ vaddr = addr;
+ while (addr < CONFIG_MMAP_END) {
+ /* if va == pa, then it's free to use */
+ if (addr == (page_table[addr >> 20] & 0xfff00000)) {
+ size += SZ_1M;
+ } else {
+ size = 0;
+ vaddr = addr + SZ_1M;
+ }
+ if (size >= len)
+ break;
+ addr += SZ_1M;
+ }
+ if (size < len)
+ return NULL;
+
+ /* 3. create the map */
+ map_flush(vaddr, vaddr + size);
+ addr = vaddr;
+ vaddr += paddr & 0x000fffff;
+ paddr &= 0xfff00000;
+ while (size) {
+ page_table[addr >> 20] = paddr | (3 << 10) | nattr;
+ size -= SZ_1M;
+ addr += SZ_1M;
+ paddr += SZ_1M;
+ }
+ return (void *)vaddr;
+#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
return (void *)paddr;
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
}

/*
* Take down a mapping set up by map_physmem().
*/
-static inline void unmap_physmem(void *vaddr, unsigned long flags)
+static inline void unmap_physmem(void *vaddr, ulong len)
{
-
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ u32 *page_table = (u32 *)gd->arch.tlb_addr;
+ u32 addr, end;
+
+ /* 1. skip on NULL pointer */
+ if (!vaddr)
+ return;
+
+ /* 2. check if it's the right address map */
+ addr = (u32)vaddr;
+ if ((page_table[addr >> 20] & 0xfff00000) == addr)
+ return;
+
+ /* 3. reset the map */
+ end = (addr + len + 0x000fffff) & 0xfff00000;
+ addr &= 0xfff00000;
+ map_flush(addr, end);
+ while (addr < end) {
+ page_table[addr >> 20] = addr | (3 << 10) | DCACHE_OFF;
+ addr += SZ_1M;
+ }
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
}

-static inline phys_addr_t virt_to_phys(void * vaddr)
+static inline phys_addr_t virt_to_phys(void *vaddr)
{
- return (phys_addr_t)(vaddr);
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ u32 *page_table = (u32 *)gd->arch.tlb_addr;
+ phys_addr_t phys = (phys_addr_t)vaddr;
+
+ if (!gd->arch.cpu_mmu || !vaddr)
+ return phys;
+
+ phys = page_table[(u32)vaddr >> 20] & 0xfff00000;
+ phys += (u32)vaddr & 0x000fffff;
+
+ return phys;
+#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
+ return (phys_addr_t)vaddr;
+#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */
}

/*
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 760345f..050b707 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -97,9 +97,10 @@ static inline void set_dacr(unsigned int val)

/* options available for data cache on each page */
enum dcache_option {
- DCACHE_OFF = 0x12,
- DCACHE_WRITETHROUGH = 0x1a,
- DCACHE_WRITEBACK = 0x1e,
+ DCACHE_OFF = 0x12, /* non-cached + non-buffered */
+ DCACHE_WRITECOMBINE = 0x16, /* non-cached + buffered */
+ DCACHE_WRITETHROUGH = 0x1a, /* cached + non-buffered */
+ DCACHE_WRITEBACK = 0x1e, /* cached + buffered */
};

/* Size of an MMU section */
diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c
index 8642010..3259a33 100644
--- a/arch/arm/lib/cache-cp15.c
+++ b/arch/arm/lib/cache-cp15.c
@@ -110,6 +110,10 @@ static inline void mmu_setup(void)

/* and enable the mmu */
reg = get_cr(); /* get control reg. */
+#ifdef CONFIG_FARADAY
+ reg |= CR_W; /* enable write buffer */
+ reg |= CR_Z; /* enable branch prediction */
+#endif
cp_delay();
set_cr(reg | CR_M);
}
@@ -124,6 +128,11 @@ static void cache_enable(uint32_t cache_bit)
{
uint32_t reg;

+#ifdef CONFIG_FARADAY
+ if (!gd->arch.cpu_mmu && (cache_bit == CR_C))
+ return;
+#endif
+
/* The data cache is not active unless the mmu is enabled too */
if ((cache_bit == CR_C) && !mmu_enabled())
mmu_setup();
--
1.7.9.5