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 #include "paging.h" 28 29 void flush_icache_all(void) 30 { 31 u32 addr, t = 0; 32 33 asm volatile ("mtc0 $0, $28"); /* Clear Taglo */ 34 asm volatile ("mtc0 $0, $29"); /* Clear TagHi */ 35 36 for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_ICACHE_SIZE; 37 addr += CONFIG_SYS_CACHELINE_SIZE) { 38 asm volatile ( 39 ".set mips3\n\t" 40 " cache %0, 0(%1)\n\t" 41 ".set mips2\n\t" 42 : 43 : "I" (Index_Store_Tag_I), "r"(addr)); 44 } 45 46 /* invalicate btb */ 47 asm volatile ( 48 ".set mips32\n\t" 49 "mfc0 %0, $16, 7\n\t" 50 "nop\n\t" 51 "ori %0,2\n\t" 52 "mtc0 %0, $16, 7\n\t" 53 ".set mips2\n\t" 54 : 55 : "r" (t)); 56 } 57 58 void flush_dcache_all(void) 59 { 60 u32 addr; 61 62 for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_DCACHE_SIZE; 63 addr += CONFIG_SYS_CACHELINE_SIZE) { 64 asm volatile ( 65 ".set mips3\n\t" 66 " cache %0, 0(%1)\n\t" 67 ".set mips2\n\t" 68 : 69 : "I" (Index_Writeback_Inv_D), "r"(addr)); 70 } 71 72 asm volatile ("sync"); 73 } 74 75 void flush_cache_all(void) 76 { 77 flush_dcache_all(); 78 flush_icache_all(); 79 } 80 81 void handle_error_level(void) 82 { 83 asm volatile( 84 "mfc0 $t3, $12\n" /* CP0_STATUS */ 85 "li $t4, 0xfffffffb\n" /* ERL = 0 */ 86 "and $t3, $t3, $t4\n" 87 "mtc0 $t3, $12\n" 88 "nop\n"); 89 } 90 91 void enable_interrupts(void) 92 { 93 asm volatile( 94 "mfc0 $t3, $12\n" /* CP0_STATUS */ 95 "li $t4, 0x0000fc01\n" /* IE = enable interrupts */ 96 "or $t3, $t3, $t4\n" 97 "mtc0 $t3, $12\n" 98 "nop\n"); 99 } 100 101 void init_interrupts(void) 102 { 103 /* Set exception registers. */ 104 105 asm volatile( 106 "mtc0 $zero, $18\n" /* CP0_WATCHLO */ 107 "li $t3, 0x00800000\n" /* IV = 1 (use 0x80000200 for interrupts) */ 108 "mtc0 $t3, $13\n" /* CP0_CAUSE */ 109 "mfc0 $t4, $12\n" /* CP0_STATUS */ 110 "li $t3, 0xffbfffff\n" /* BEV=0 */ 111 "and $t3, $t3, $t4\n" 112 "mtc0 $t3, $12\n" 113 "nop\n"); 114 } 115 116 void init_registers(u32 *base, u32 got, void (*function)(), u32 args[], u8 nargs) 117 { 118 u8 i; 119 120 /* Provide arguments to the function. */ 121 122 for (i = 0; i < nargs; i++) 123 { 124 base[i+4] = args[i]; 125 } 126 127 /* Store essential data for the function environment. */ 128 129 base[25] = (u32) function - 0x80000000; /* store the function address as t9 */ 130 base[26] = got - 0x80000000; /* store the global pointer */ 131 base[29] = (u32) function - 0x80000000; /* store the function address as EPC (for the handler) */ 132 } 133 134 void invoke_task(u8 asid, u32 *base, u32 *stack_pointer) 135 { 136 asm volatile( 137 "mtc0 %0, $10\n" /* CP0_ENTRYHI */ 138 "nop\n" 139 "move $t4, %1\n" /* use arguments before they are overwritten */ 140 "lw $sp, 0($t4)\n" /* set the stack pointer */ 141 "move $t3, %2\n" /* load parameters from the stored registers */ 142 "lw $a0, 16($t3)\n" 143 "lw $a1, 20($t3)\n" 144 "lw $a2, 24($t3)\n" 145 "lw $a3, 28($t3)\n" 146 "lw $t9, 100($t3)\n" 147 "lw $gp, 104($t3)\n" 148 "mtc0 $t9, $14\n" /* CP0_EPC */ 149 "mfc0 $t3, $12\n" /* CP0_STATUS */ 150 "ori $t3, $t3, 0x2\n" /* EXL = 1 */ 151 "mtc0 $t3, $12\n" /* CP0_STATUS */ 152 ".set mips3\n" 153 "eret\n" 154 ".set mips2\n" 155 "nop" 156 : 157 : "r" (asid), "r" (stack_pointer), "r" (base) 158 ); 159 } 160 161 void init_tlb(void) 162 { 163 unsigned short first_random = 0, i, limit; 164 165 asm volatile( 166 "mtc0 $zero, $4\n" /* CP0_CONTEXT */ 167 "mtc0 %1, $6\n" /* CP0_WIRED */ 168 "mfc0 %0, $16\n" /* CP0_CONFIG1 */ 169 "nop" 170 : "=r" (limit) 171 : "r" (first_random) 172 ); 173 174 /* Reset the mappings. The total number is bits 30..25 of Config1. */ 175 176 for (i = 0; i < ((limit >> 25) & 0x3f); i++) 177 { 178 map_page_index(0, 0, 4096, 0, 0, i); 179 } 180 } 181 182 void map_page_index(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid, u32 index) 183 { 184 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 185 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 186 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 187 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 188 189 asm volatile( 190 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 191 192 /* Set the index. */ 193 194 "mtc0 %4, $0\n" /* CP0_INDEX */ 195 196 /* Set physical address. */ 197 198 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 199 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 200 201 /* Set virtual address. */ 202 203 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 204 "nop\n" 205 206 "tlbwi\n" 207 "nop" 208 : 209 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask), "r" (index) 210 ); 211 } 212 213 void init_page_table(u32 page_table, u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 214 { 215 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 216 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 217 218 /* 219 With a complete address space mapping involving pairs of 4KB pages 220 described by two values for each entry, there would be... 221 222 an address space of 0x100000000 requiring... 223 224 0x100000000 / (8 * 1024) == 0x100000000 >> 13 225 == 524288 entries 226 == 0x80000 entries 227 228 Thus, each task's entries would require... 229 230 0x80000 * 8 == 0x400000 bytes 231 232 The kseg2 region thus permits 256 tasks occupying 0x40000000 bytes. 233 234 However, for more modest address spaces occupying as much as 32MB there 235 would be... 236 237 an address space of 0x02000000 requiring... 238 239 0x02000000 / (8 * 1024) == 0x02000000 >> 13 240 == 4096 entries 241 == 0x1000 entries 242 243 Thus, each task's entries would only require... 244 245 0x1000 * 8 == 0x8000 bytes 246 */ 247 248 u32 base = page_table + page_table_task_size * asid; 249 250 /* Each page table entry corresponds to a pair of 4KB pages and holds two values. */ 251 252 u32 entry = ((virtual & 0xffffe000) >> 13) * 8; 253 u32 address = base + entry; 254 255 /* The page tables should be permanently mapped to avoid hierarchical TLB miss handling. */ 256 257 asm volatile( 258 "sw %1, 0(%0)\n" 259 "sw %2, 4(%0)\n" 260 : 261 : "r" (address), "r" (lower), "r" (upper) 262 ); 263 } 264 265 void map_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 266 { 267 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 268 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 269 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 270 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 271 272 asm volatile( 273 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 274 275 /* Set physical address. */ 276 277 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 278 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 279 280 /* Set virtual address. */ 281 282 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 283 "nop\n" 284 285 "tlbwr\n" 286 "nop" 287 : 288 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 289 ); 290 } 291 292 void map_page_miss(u32 physical, u32 pagesize, u8 flags) 293 { 294 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 295 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 296 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 297 298 asm volatile( 299 "mtc0 %2, $5\n" /* CP0_PAGEMASK */ 300 301 /* Set physical address. */ 302 303 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 304 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 305 "nop\n" 306 307 "tlbwr\n" 308 "nop" 309 : 310 : "r" (lower), "r" (upper), "r" (pagemask) 311 ); 312 } 313 314 void unmap_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 315 { 316 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 317 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 318 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 319 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 320 u32 index = 0; 321 322 asm volatile( 323 "mtc0 %4, $5\n" /* CP0_PAGEMASK */ 324 325 /* Set physical address. */ 326 327 "mtc0 %1, $2\n" /* CP0_ENTRYLO0 */ 328 "mtc0 %2, $3\n" /* CP0_ENTRYLO1 */ 329 330 /* Set virtual address. */ 331 332 "mtc0 %3, $10\n" /* CP0_ENTRYHI */ 333 "nop\n" 334 335 /* Find an existing mapping. */ 336 337 "tlbp\n" 338 "nop\n" 339 340 /* Read the index register to see if a match was found. */ 341 342 "mfc0 %0, $0\n" /* CP0_INDEX */ 343 "nop" 344 : "=r" (index) 345 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 346 ); 347 348 /* Return if the page is not mapped. */ 349 350 if (index & 0x80000000) 351 return; 352 353 /* Otherwise, invalidate the mapping. */ 354 355 map_page_index(virtual, physical, pagesize, flags & 0xfd, asid, index); 356 }