| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2006,2007 by Greg White |
| * |
| * All files in this archive are subject to the GNU General Public License. |
| * See the file COPYING in the source tree root for full license agreement. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| #include "cpu.h" |
| #include "mmu-arm.h" |
| #include "panic.h" |
| |
| #define SECTION_ADDRESS_MASK (-1 << 20) |
| #define MB (1 << 20) |
| |
| void ttb_init(void) { |
| unsigned int* ttbPtr; |
| |
| /* must be 16Kb (0x4000) aligned - clear out the TTB */ |
| for (ttbPtr=TTB_BASE; ttbPtr<(TTB_SIZE+TTB_BASE); ttbPtr++) |
| { |
| *ttbPtr = 0; |
| } |
| |
| /* Set the TTB base address */ |
| asm volatile("mcr p15, 0, %0, c2, c0, 0" : : "r" (TTB_BASE)); |
| |
| /* Set all domains to manager status */ |
| asm volatile("mcr p15, 0, %0, c3, c0, 0" : : "r" (0xFFFFFFFF)); |
| } |
| |
| void map_section(unsigned int pa, unsigned int va, int mb, int cache_flags) { |
| unsigned int* ttbPtr; |
| int i; |
| int section_no; |
| |
| section_no = va >> 20; /* sections are 1Mb size */ |
| ttbPtr = TTB_BASE + section_no; |
| pa &= SECTION_ADDRESS_MASK; /* align to 1Mb */ |
| for(i=0; i<mb; i++, pa += MB) { |
| *(ttbPtr + i) = |
| pa | |
| 1 << 10 | /* superuser - r/w, user - no access */ |
| 0 << 5 | /* domain 0th */ |
| 1 << 4 | /* should be "1" */ |
| cache_flags | |
| 1 << 1; /* Section signature */ |
| } |
| } |
| |
| void enable_mmu(void) { |
| int regread; |
| |
| asm volatile( |
| "MRC p15, 0, %r0, c1, c0, 0\n" /* Read reg1, control register */ |
| : /* outputs */ |
| "=r"(regread) |
| : /* inputs */ |
| : /* clobbers */ |
| "r0" |
| ); |
| |
| if ( !(regread & 0x04) || !(regread & 0x00001000) ) /* Was the ICache or DCache Enabled? */ |
| clean_dcache(); /* If so we need to clean the DCache before invalidating below */ |
| |
| asm volatile("mov r0, #0\n" |
| "mcr p15, 0, r0, c8, c7, 0\n" /* invalidate TLB */ |
| |
| "mcr p15, 0, r0, c7, c7,0\n" /* invalidate both icache and dcache */ |
| |
| "mrc p15, 0, r0, c1, c0, 0\n" |
| "orr r0, r0, #1<<0\n" /* enable mmu bit, icache and dcache */ |
| "orr r0, r0, #1<<2\n" /* enable dcache */ |
| "orr r0, r0, #1<<12\n" /* enable icache */ |
| "mcr p15, 0, r0, c1, c0, 0" : : : "r0"); |
| asm volatile("nop \n nop \n nop \n nop"); |
| } |
| |
| /* Invalidate DCache for this range */ |
| /* Will do write back */ |
| void invalidate_dcache_range(const void *base, unsigned int size) { |
| unsigned int addr = (((int) base) & ~31); /* Align start to cache line*/ |
| unsigned int end = ((addr+size) & ~31)+64; /* Align end to cache line, pad */ |
| asm volatile( |
| "inv_start: \n" |
| "mcr p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "add %0, %0, #32 \n" |
| "cmp %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line */ |
| "addne %0, %0, #32 \n" |
| "cmpne %0, %1 \n" |
| "bne inv_start \n" |
| "mov %0, #0\n" |
| "mcr p15,0,%0,c7,c10,4\n" /* Drain write buffer */ |
| : : "r" (addr), "r" (end)); |
| } |
| |
| /* clean DCache for this range */ |
| /* forces DCache writeback for the specified range */ |
| void clean_dcache_range(const void *base, unsigned int size) { |
| unsigned int addr = (int) base; |
| unsigned int end = addr+size+32; |
| asm volatile( |
| "bic %0, %0, #31 \n" |
| "clean_start: \n" |
| "mcr p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "add %0, %0, #32 \n" |
| "cmp %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "mcrlo p15, 0, %0, c7, c10, 1 \n" /* Clean this line */ |
| "addlo %0, %0, #32 \n" |
| "cmplo %0, %1 \n" |
| "blo clean_start \n" |
| "mov %0, #0\n" |
| "mcr p15,0,%0,c7,c10,4 \n" /* Drain write buffer */ |
| : : "r" (addr), "r" (end)); |
| } |
| |
| /* Dump DCache for this range */ |
| /* Will *NOT* do write back */ |
| void dump_dcache_range(const void *base, unsigned int size) { |
| unsigned int addr = (int) base; |
| unsigned int end = addr+size; |
| asm volatile( |
| "tst %0, #31 \n" /* Check to see if low five bits are set */ |
| "bic %0, %0, #31 \n" /* Clear them */ |
| "mcrne p15, 0, %0, c7, c14, 1 \n" /* Clean and invalidate this line, if those bits were set */ |
| "add %0, %0, #32 \n" /* Move to the next cache line */ |
| "tst %1, #31 \n" /* Check last line for bits set */ |
| "bic %1, %1, #31 \n" /* Clear those bits */ |
| "mcrne p15, 0, %1, c7, c14, 1 \n" /* Clean and invalidate this line, if not cache aligned */ |
| "dump_start: \n" |
| "mcr p15, 0, %0, c7, c6, 1 \n" /* Invalidate this line */ |
| "add %0, %0, #32 \n" /* Next cache line */ |
| "cmp %0, %1 \n" |
| "bne dump_start \n" |
| "dump_end: \n" |
| "mcr p15,0,%0,c7,c10,4 \n" /* Drain write buffer */ |
| : : "r" (addr), "r" (end)); |
| } |
| /* Cleans entire DCache */ |
| void clean_dcache(void) |
| { |
| unsigned int index, addr; |
| |
| for(index = 0; index <= 63; index++) { |
| addr = (0 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (1 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (2 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (3 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (4 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (5 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (6 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| addr = (7 << 5) | (index << 26); |
| asm volatile( |
| "mcr p15, 0, %0, c7, c10, 2 \n" /* Clean this entry by index */ |
| : : "r" (addr)); |
| } |
| } |
| |