1 /* 2 * CPU-specific routines originally from U-Boot. 3 * See: uboot-xburst/files/arch/mips/cpu/xburst/cpu.c 4 * See: u-boot/arch/mips/include/asm/cacheops.h 5 * 6 * Copyright (C) 2000-2009 Wolfgang Denk, DENX Software Engineering, <wd@denx.de> 7 * Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of 12 * the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA 23 */ 24 25 #include "cpu.h" 26 #include "sdram.h" 27 28 void flush_icache_all(void) 29 { 30 u32 addr, t = 0; 31 32 asm volatile ("mtc0 $0, $28"); /* Clear Taglo */ 33 asm volatile ("mtc0 $0, $29"); /* Clear TagHi */ 34 35 for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_ICACHE_SIZE; 36 addr += CONFIG_SYS_CACHELINE_SIZE) { 37 asm volatile ( 38 ".set mips3\n\t" 39 " cache %0, 0(%1)\n\t" 40 ".set mips2\n\t" 41 : 42 : "I" (Index_Store_Tag_I), "r"(addr)); 43 } 44 45 /* invalicate btb */ 46 asm volatile ( 47 ".set mips32\n\t" 48 "mfc0 %0, $16, 7\n\t" 49 "nop\n\t" 50 "ori %0,2\n\t" 51 "mtc0 %0, $16, 7\n\t" 52 ".set mips2\n\t" 53 : 54 : "r" (t)); 55 } 56 57 void flush_dcache_all(void) 58 { 59 u32 addr; 60 61 for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_DCACHE_SIZE; 62 addr += CONFIG_SYS_CACHELINE_SIZE) { 63 asm volatile ( 64 ".set mips3\n\t" 65 " cache %0, 0(%1)\n\t" 66 ".set mips2\n\t" 67 : 68 : "I" (Index_Writeback_Inv_D), "r"(addr)); 69 } 70 71 asm volatile ("sync"); 72 } 73 74 void flush_cache_all(void) 75 { 76 flush_dcache_all(); 77 flush_icache_all(); 78 } 79 80 void handle_error_level(void) 81 { 82 asm volatile( 83 "mfc0 $t3, $12\n" /* CP0_STATUS */ 84 "li $t4, 0xfffffffb\n" /* ERL = 0 */ 85 "and $t3, $t3, $t4\n" 86 "mtc0 $t3, $12\n" 87 "nop\n"); 88 } 89 90 void enable_interrupts(void) 91 { 92 asm volatile( 93 "mfc0 $t3, $12\n" /* CP0_STATUS */ 94 "li $t4, 0x0000fc01\n" /* IE = enable interrupts */ 95 "or $t3, $t3, $t4\n" 96 "mtc0 $t3, $12\n" 97 "nop\n"); 98 } 99 100 void init_interrupts(void) 101 { 102 /* Set exception registers. */ 103 104 asm volatile( 105 "mtc0 $zero, $18\n" /* CP0_WATCHLO */ 106 "li $t3, 0x00800000\n" /* IV = 1 (use 0x80000200 for interrupts) */ 107 "mtc0 $t3, $13\n" /* CP0_CAUSE */ 108 "mfc0 $t4, $12\n" /* CP0_STATUS */ 109 "li $t3, 0xffbfffff\n" /* BEV=0 */ 110 "and $t3, $t3, $t4\n" 111 "mtc0 $t3, $12\n" 112 "nop\n"); 113 } 114 115 void set_task(u8 asid) 116 { 117 asm volatile( 118 119 /* Set the ASID. */ 120 121 "mtc0 %0, $10\n" /* CP0_ENTRYHI */ 122 "nop" 123 : 124 : "r" (asid) 125 ); 126 } 127 128 void init_stack(u32 top, u32 got, void (*function)(), u32 args[], u8 nargs) 129 { 130 u8 i; 131 132 asm volatile( 133 "move $t3, %0\n" /* refer to the stack frame */ 134 "addi $t3, $t3, -16\n" /* refer to the first parameter */ 135 : 136 : "r" (top) 137 ); 138 139 /* Provide arguments to the function. */ 140 141 for (i = 0; i < nargs; i++) 142 { 143 asm volatile( 144 "sw %0, 0($t3)\n" 145 "addi $t3, $t3, -4\n" 146 : 147 : "r" (args[i]) 148 ); 149 } 150 151 /* Store essential data for the function environment. */ 152 153 asm volatile( 154 "subu %1, %1, 0x80000000\n" /* obtain user mode addresses */ 155 "subu %2, %2, 0x80000000\n" 156 "sw %2, -100(%0)\n" /* store the function address */ 157 "sw %1, -104(%0)\n" /* store the global pointer */ 158 "sw %2, -112(%0)\n" /* store the function address */ 159 : 160 : "r" (top), "r" (got), "r" (function) 161 ); 162 } 163 164 void enter_user_mode(void) 165 { 166 asm volatile( 167 "mfc0 $t3, $12\n" /* CP0_STATUS */ 168 "li $t4, 0x00000010\n" /* KSU = 2 (UM = 1) */ 169 "or $t3, $t3, $t4\n" 170 "mtc0 $t3, $12\n" 171 "nop\n"); 172 } 173 174 void init_tlb(void) 175 { 176 asm volatile( 177 "li $t1, 1\n" /* index of first randomly-replaced entry */ 178 "mtc0 $t1, $6\n" /* CP0_WIRED */ 179 "nop\n"); 180 181 map_page_index(0x80000000, 0x00000000, 16 * 1024 * 1024, 0x1f, 0, 0); 182 } 183 184 void map_page_index(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid, u32 index) 185 { 186 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 187 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 188 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 189 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 190 191 asm volatile( 192 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 193 194 /* Set the index. */ 195 196 "mtc0 %4, $0\n" /* CP0_INDEX */ 197 198 /* Set physical address. */ 199 200 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 201 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 202 203 /* Set virtual address. */ 204 205 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 206 "nop\n" 207 208 "tlbwi\n" 209 "nop" 210 : 211 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask), "r" (index) 212 ); 213 } 214 215 void map_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 216 { 217 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 218 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 219 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 220 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 221 222 asm volatile( 223 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 224 225 /* Set physical address. */ 226 227 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 228 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 229 230 /* Set virtual address. */ 231 232 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 233 "nop\n" 234 235 "tlbwr\n" 236 "nop" 237 : 238 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 239 ); 240 } 241 242 void map_page_miss(u32 physical, u32 pagesize, u8 flags) 243 { 244 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 245 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 246 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 247 248 asm volatile( 249 "mtc0 %2, $5\n" /* CP0_PAGEMASK */ 250 251 /* Set physical address. */ 252 253 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 254 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 255 "nop\n" 256 257 "tlbwr\n" 258 "nop" 259 : 260 : "r" (lower), "r" (upper), "r" (pagemask) 261 ); 262 } 263 264 void unmap_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 265 { 266 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 267 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 268 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 269 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 270 u32 index = 0; 271 272 asm volatile( 273 "mtc0 %4, $5\n" /* CP0_PAGEMASK */ 274 275 /* Set physical address. */ 276 277 "mtc0 %1, $2\n" /* CP0_ENTRYLO0 */ 278 "mtc0 %2, $3\n" /* CP0_ENTRYLO1 */ 279 280 /* Set virtual address. */ 281 282 "mtc0 %3, $10\n" /* CP0_ENTRYHI */ 283 "nop\n" 284 285 /* Find an existing mapping. */ 286 287 "tlbp\n" 288 "nop\n" 289 290 /* Read the index register to see if a match was found. */ 291 292 "mfc0 %0, $0\n" /* CP0_INDEX */ 293 "nop" 294 : "=r" (index) 295 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 296 ); 297 298 /* Return if the page is not mapped. */ 299 300 if (index & 0x80000000) 301 return; 302 303 /* Otherwise, invalidate the mapping. */ 304 305 map_page_index(virtual, physical, pagesize, flags & 0xfd, asid, index); 306 }