diff options
author | Haavard Skinnemoen <haavard.skinnemoen@atmel.com> | 2010-08-12 13:52:54 +0700 |
---|---|---|
committer | Reinhard Meyer <u-boot@emk-elektronik.de> | 2010-09-03 15:13:02 +0200 |
commit | 1f36f73fe70560a2bd286a7abc8530fdc93af9ae (patch) | |
tree | 23ba61ced5254af43107e7d0de5b25b566ac78bb /arch/avr32/cpu | |
parent | 9cec2fc209a000655af77256a39ede7c7d441e56 (diff) |
avr32: Add simple paging support
Use the MMU hardware to set up 1:1 mappings between physical and virtual
addresses. This allows us to bypass the cache when accessing the flash
without having to do any physical-to-virtual address mapping in the CFI
driver.
The virtual memory mappings are defined at compile time through a sorted
array of virtual memory range objects. When a TLB miss exception
happens, the exception handler does a binary search through the array
until it finds a matching entry and loads it into the TLB. The u-boot
image itself is covered by a fixed TLB entry which is never replaced.
This makes the 'saveenv' command work again on ATNGW100 and other boards
using the CFI driver, hopefully without breaking any rules.
Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32/cpu')
-rw-r--r-- | arch/avr32/cpu/at32ap700x/Makefile | 2 | ||||
-rw-r--r-- | arch/avr32/cpu/at32ap700x/mmu.c | 78 | ||||
-rw-r--r-- | arch/avr32/cpu/start.S | 19 |
3 files changed, 92 insertions, 7 deletions
diff --git a/arch/avr32/cpu/at32ap700x/Makefile b/arch/avr32/cpu/at32ap700x/Makefile index 46e6ef661a..30ea92590d 100644 --- a/arch/avr32/cpu/at32ap700x/Makefile +++ b/arch/avr32/cpu/at32ap700x/Makefile @@ -24,7 +24,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)lib$(SOC).a -COBJS := portmux.o clk.o +COBJS := portmux.o clk.o mmu.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) diff --git a/arch/avr32/cpu/at32ap700x/mmu.c b/arch/avr32/cpu/at32ap700x/mmu.c new file mode 100644 index 0000000000..c3a1b93679 --- /dev/null +++ b/arch/avr32/cpu/at32ap700x/mmu.c @@ -0,0 +1,78 @@ +#include <common.h> +#include <asm/arch/mmu.h> +#include <asm/sysreg.h> + +void mmu_init_r(unsigned long dest_addr) +{ + uintptr_t vmr_table_addr; + + /* Round monitor address down to the nearest page boundary */ + dest_addr &= PAGE_ADDR_MASK; + + /* Initialize TLB entry 0 to cover the monitor, and lock it */ + sysreg_write(TLBEHI, dest_addr | SYSREG_BIT(TLBEHI_V)); + sysreg_write(TLBELO, dest_addr | MMU_VMR_CACHE_WRBACK); + sysreg_write(MMUCR, SYSREG_BF(DRP, 0) | SYSREG_BF(DLA, 1) + | SYSREG_BIT(MMUCR_S) | SYSREG_BIT(M)); + __builtin_tlbw(); + + /* + * Calculate the address of the VM range table in a PC-relative + * manner to make sure we hit the SDRAM and not the flash. + */ + vmr_table_addr = (uintptr_t)&mmu_vmr_table; + sysreg_write(PTBR, vmr_table_addr); + printf("VMR table @ 0x%08x\n", vmr_table_addr); + + /* Enable paging */ + sysreg_write(MMUCR, SYSREG_BF(DRP, 1) | SYSREG_BF(DLA, 1) + | SYSREG_BIT(MMUCR_S) | SYSREG_BIT(M) | SYSREG_BIT(E)); +} + +int mmu_handle_tlb_miss(void) +{ + const struct mmu_vm_range *vmr_table; + const struct mmu_vm_range *vmr; + unsigned int fault_pgno; + int first, last; + + fault_pgno = sysreg_read(TLBEAR) >> PAGE_SHIFT; + vmr_table = (const struct mmu_vm_range *)sysreg_read(PTBR); + + /* Do a binary search through the VM ranges */ + first = 0; + last = CONFIG_SYS_NR_VM_REGIONS; + while (first < last) { + unsigned int start; + int middle; + + /* Pick the entry in the middle of the remaining range */ + middle = (first + last) >> 1; + vmr = &vmr_table[middle]; + start = vmr->virt_pgno; + + /* Do the bisection thing */ + if (fault_pgno < start) { + last = middle; + } else if (fault_pgno >= (start + vmr->nr_pages)) { + first = middle + 1; + } else { + /* Got it; let's slam it into the TLB */ + uint32_t tlbelo; + + tlbelo = vmr->phys & ~PAGE_ADDR_MASK; + tlbelo |= fault_pgno << PAGE_SHIFT; + sysreg_write(TLBELO, tlbelo); + __builtin_tlbw(); + + /* Zero means success */ + return 0; + } + } + + /* + * Didn't find any matching entries. Return a nonzero value to + * indicate that this should be treated as a fatal exception. + */ + return -1; +} diff --git a/arch/avr32/cpu/start.S b/arch/avr32/cpu/start.S index 99c9e06cb8..06bf4c692d 100644 --- a/arch/avr32/cpu/start.S +++ b/arch/avr32/cpu/start.S @@ -82,12 +82,19 @@ _evba: .org 0x44 rjmp unknown_exception /* DTLB Modified */ - .org 0x50 - rjmp unknown_exception /* ITLB Miss */ - .org 0x60 - rjmp unknown_exception /* DTLB Miss (read) */ - .org 0x70 - rjmp unknown_exception /* DTLB Miss (write) */ + .org 0x50 /* ITLB Miss */ + pushm r8-r12,lr + rjmp 1f + .org 0x60 /* DTLB Miss (read) */ + pushm r8-r12,lr + rjmp 1f + .org 0x70 /* DTLB Miss (write) */ + pushm r8-r12,lr +1: mov r12, sp + rcall mmu_handle_tlb_miss + popm r8-r12,lr + brne unknown_exception + rete .size _evba, . - _evba |