[U-Boot] [PATCH v2 11/12] arm: add MMU/d-cache support for Faraday cores
Kuo-Jung Su
dantesu at gmail.com
Thu Apr 18 11:25:38 CEST 2013
From: Kuo-Jung Su <dantesu at faraday-tech.com>
This patch would enable MMU for Faraday ARMv5TE cores.
Here is the abstract of this MMU design.
Assume SDRAM memory region starts at 0x10000000, and its size = 0x800000.
0x00000000 +-------------------+
| |
| UN-CACHED |
| |
| |
0x10000000 +-------------------+
| CACHED (SDRAM) | <- It's where data/bss/stack lived.
| |
| |
0x10800000 +-------------------+
| |
| |
| UN-CACHED |
| |
| |
0xFF800000 +-------------------+
| UN-CACHED (SDRAM) | <- An un-cached shadow of the SDRAM.
| | dma_alloc_coherent() always returns
| | an address in this region.
0xFFFFFFFF +-------------------+
Signed-off-by: Kuo-Jung Su <dantesu at faraday-tech.com>
CC: Albert Aribaud <albert.u.boot at aribaud.net>
---
arch/arm/include/asm/dma-mapping.h | 56 ++++++++++++++++++++++--
arch/arm/include/asm/global_data.h | 4 ++
arch/arm/include/asm/io.h | 84 +++++++++++++++++++++++++++++++++++-
arch/arm/lib/cache-cp15.c | 42 ++++++++++++++++++
common/cmd_boot.c | 4 ++
5 files changed, 186 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 5bbb0a0..53c4edf 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -3,6 +3,9 @@
* Stelian Pop <stelian at popies.net>
* Lead Tech Design <www.leadtechdesign.com>
*
+ * (C) Copyright 2010
+ * Dante Su <dantesu at faraday-tech.com>
+ *
* See file CREDITS for list of people who contributed to this
* project.
*
@@ -24,22 +27,69 @@
#ifndef __ASM_ARM_DMA_MAPPING_H
#define __ASM_ARM_DMA_MAPPING_H
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <malloc.h>
+
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)
+ DECLARE_GLOBAL_DATA_PTR;
+#endif
+ void *va = memalign(ARCH_DMA_MINALIGN, len);
+
+ if (va && handle)
+ *handle = virt_to_phys(va);
+
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+ if (gd->arch.cpu_mmu) {
+ /* invalidate the buffer, convert to un-cached address */
+ if (va != NULL) {
+ invalidate_dcache_range((ulong)va, (ulong)va + len);
+ va = virt_to_uncached(va);
+ }
+ }
+#endif
+
+ return va;
+}
+
+static inline void dma_free_coherent(void *va)
{
- *handle = (unsigned long)malloc(len);
- return (void *)*handle;
+ free(virt_to_cached(va));
}
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)
+ DECLARE_GLOBAL_DATA_PTR;
+
+ 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
return (unsigned long)vaddr;
+#endif
}
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 37ac0da..bd18ff7 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -38,6 +38,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..17d8898 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -2,6 +2,7 @@
* linux/include/asm-arm/io.h
*
* Copyright (C) 1996-2000 Russell King
+ * Copyright (C) 2009-2010 Dante Su <dantesu at faraday-tech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -23,6 +24,8 @@
#ifdef __KERNEL__
#include <linux/types.h>
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
#include <asm/byteorder.h>
#include <asm/memory.h>
#if 0 /* XXX###XXX */
@@ -57,9 +60,88 @@ static inline void unmap_physmem(void *vaddr, unsigned long flags)
}
+#ifdef CONFIG_FARADAY
+
+# ifndef CONFIG_SYS_DCACHE_OFF
+
+static inline ulong uncached_base(volatile gd_t *gd)
+{
+ return (4096 - (gd->ram_size >> 20)) << 20;
+}
+# endif
+
+static inline void *virt_to_cached(void *va)
+{
+# ifndef CONFIG_SYS_DCACHE_OFF
+ DECLARE_GLOBAL_DATA_PTR;
+ ulong base = uncached_base(gd);
+
+ if (!gd->arch.cpu_mmu)
+ return va;
+
+ if ((ulong)va >= base &&
+ (ulong)va < (base + gd->ram_size))
+ va = (void *)((ulong)va - base + CONFIG_SYS_SDRAM_BASE);
+# endif /* !CONFIG_SYS_DCACHE_OFF */
+
+ return va;
+}
+
+static inline void *virt_to_uncached(void *va)
+{
+# ifndef CONFIG_SYS_DCACHE_OFF
+ DECLARE_GLOBAL_DATA_PTR;
+ ulong base = uncached_base(gd);
+
+ if (!gd->arch.cpu_mmu)
+ return va;
+
+# ifdef CONFIG_USE_IRQ
+ if ((ulong)va < SZ_1M)
+ return (void *)(base + (ulong)va);
+# endif
+
+ if ((ulong)va >= CONFIG_SYS_SDRAM_BASE &&
+ (ulong)va < (CONFIG_SYS_SDRAM_BASE + gd->ram_size))
+ va = (void *)(base + ((ulong)va - CONFIG_SYS_SDRAM_BASE));
+# endif /* !CONFIG_SYS_DCACHE_OFF */
+
+ return va;
+}
+
+#endif /* CONFIG_FARADAY */
+
static inline phys_addr_t virt_to_phys(void * vaddr)
{
- return (phys_addr_t)(vaddr);
+#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF)
+
+ DECLARE_GLOBAL_DATA_PTR;
+ bd_t *bd = gd->bd;
+ ulong base = uncached_base(gd);
+ ulong phys = (ulong)vaddr;
+
+ if (!gd->arch.cpu_mmu)
+ return (phys_addr_t)phys;
+
+ if (phys >= base) {
+ ulong bank;
+ ulong off = phys - base;
+ for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; ++bank) {
+ if (bd->bi_dram[bank].size > off)
+ break;
+ off -= bd->bi_dram[bank].size;
+ }
+ phys = bd->bi_dram[bank].start + off;
+ }
+# ifdef CONFIG_USE_IRQ
+ else if (phys < SZ_1M && bd->bi_dram[0].start != 0)
+ phys = bd->bi_dram[0].start + phys;
+# endif
+
+ return (phys_addr_t)phys;
+#else
+ return (phys_addr_t)vaddr;
+#endif
}
/*
diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c
index 4abe1cf..eee8585 100644
--- a/arch/arm/lib/cache-cp15.c
+++ b/arch/arm/lib/cache-cp15.c
@@ -1,6 +1,8 @@
/*
* (C) Copyright 2002
* Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+ * (C) Copyright 2010
+ * Dante Su <dantesu at faraday-tech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
@@ -87,6 +89,10 @@ __weak void dram_bank_mmu_setup(int bank)
{
bd_t *bd = gd->bd;
int i;
+#ifdef CONFIG_FARADAY
+ ulong ubase, off;
+ u32 *page_table = (u32 *)gd->arch.tlb_addr;
+#endif
debug("%s: bank: %d\n", __func__, bank);
for (i = bd->bi_dram[bank].start >> 20;
@@ -98,6 +104,32 @@ __weak void dram_bank_mmu_setup(int bank)
set_section_dcache(i, DCACHE_WRITEBACK);
#endif
}
+#ifdef CONFIG_FARADAY
+# ifdef CONFIG_USE_IRQ
+ /* map the exception table to 0x00000000 if necessary */
+ if (bank == 0 && bd->bi_dram[bank].start != 0) {
+ u32 pa = bd->bi_dram[bank].start;
+#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+ page_table[0] = pa | (3 << 10) | DCACHE_WRITETHROUGH;
+#else
+ page_table[0] = pa | (3 << 10) | DCACHE_WRITEBACK;
+#endif
+ }
+# endif
+ /* calculate address offset */
+ off = 0;
+ for (i = 0; i < bank; ++i)
+ off += bd->bi_dram[bank].size;
+
+ /* create memory map */
+ ubase = (4096 - (gd->ram_size >> 20)) << 20;
+ for (i = 0; i < bd->bi_dram[bank].size >> 20; ++i) {
+ u32 pa = bd->bi_dram[bank].start + (i << 20);
+ /* create un-cached address map */
+ u32 va = ubase + off + (i << 20);
+ page_table[va >> 20] = pa | (3 << 10) | DCACHE_OFF;
+ }
+#endif
}
/* to activate the MMU we need to set up virtual memory: use 1M areas */
@@ -126,6 +158,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);
}
@@ -140,9 +176,15 @@ 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();
+
reg = get_cr(); /* get control reg. */
cp_delay();
set_cr(reg | cache_bit);
diff --git a/common/cmd_boot.c b/common/cmd_boot.c
index d3836fd..b2477e8 100644
--- a/common/cmd_boot.c
+++ b/common/cmd_boot.c
@@ -50,6 +50,10 @@ static int do_go(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf ("## Starting application at 0x%08lX ...\n", addr);
+#if defined(__ARM__) && !defined(CONFIG_SYS_DCACHE_OFF)
+ cleanup_before_linux();
+#endif
+
/*
* pass address parameter as argv[0] (aka command name),
* and all remaining args
--
1.7.9.5
More information about the U-Boot
mailing list