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 "jr $t9\n" 149 "nop" 150 : 151 : "r" (asid), "r" (stack_pointer), "r" (base) 152 ); 153 } 154 155 void init_tlb(void) 156 { 157 unsigned short first_random = 0, i, limit; 158 159 asm volatile( 160 "mtc0 $zero, $4\n" /* CP0_CONTEXT */ 161 "mtc0 %1, $6\n" /* CP0_WIRED */ 162 "mfc0 %0, $16\n" /* CP0_CONFIG1 */ 163 "nop" 164 : "=r" (limit) 165 : "r" (first_random) 166 ); 167 168 /* Reset the mappings. The total number is bits 30..25 of Config1. */ 169 170 for (i = 0; i < ((limit >> 25) & 0x3f); i++) 171 { 172 map_page_index(0, 0, 4096, 0, 0, i); 173 } 174 } 175 176 void map_page_index(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid, u32 index) 177 { 178 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 179 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 180 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 181 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 182 183 asm volatile( 184 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 185 186 /* Set the index. */ 187 188 "mtc0 %4, $0\n" /* CP0_INDEX */ 189 190 /* Set physical address. */ 191 192 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 193 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 194 195 /* Set virtual address. */ 196 197 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 198 "nop\n" 199 200 "tlbwi\n" 201 "nop" 202 : 203 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask), "r" (index) 204 ); 205 } 206 207 void init_page_table(u32 page_table, u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 208 { 209 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 210 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 211 212 /* 213 With a complete address space mapping involving pairs of 4KB pages 214 described by two values for each entry, there would be... 215 216 an address space of 0x100000000 requiring... 217 218 0x100000000 / (8 * 1024) == 0x100000000 >> 13 219 == 524288 entries 220 == 0x80000 entries 221 222 Thus, each task's entries would require... 223 224 0x80000 * 8 == 0x400000 bytes 225 226 The kseg2 region thus permits 256 tasks occupying 0x40000000 bytes. 227 228 However, for more modest address spaces occupying as much as 32MB there 229 would be... 230 231 an address space of 0x02000000 requiring... 232 233 0x02000000 / (8 * 1024) == 0x02000000 >> 13 234 == 4096 entries 235 == 0x1000 entries 236 237 Thus, each task's entries would only require... 238 239 0x1000 * 8 == 0x8000 bytes 240 */ 241 242 u32 base = page_table + page_table_task_size * asid; 243 244 /* Each page table entry corresponds to a pair of 4KB pages and holds two values. */ 245 246 u32 entry = ((virtual & 0xffffe000) >> 13) * 8; 247 u32 address = base + entry; 248 249 /* The page tables should be permanently mapped to avoid hierarchical TLB miss handling. */ 250 251 asm volatile( 252 "sw %1, 0(%0)\n" 253 "sw %2, 4(%0)\n" 254 : 255 : "r" (address), "r" (lower), "r" (upper) 256 ); 257 } 258 259 void map_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 260 { 261 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 262 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 263 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 264 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 265 266 asm volatile( 267 "mtc0 %3, $5\n" /* CP0_PAGEMASK */ 268 269 /* Set physical address. */ 270 271 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 272 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 273 274 /* Set virtual address. */ 275 276 "mtc0 %2, $10\n" /* CP0_ENTRYHI */ 277 "nop\n" 278 279 "tlbwr\n" 280 "nop" 281 : 282 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 283 ); 284 } 285 286 void map_page_miss(u32 physical, u32 pagesize, u8 flags) 287 { 288 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 289 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 290 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 291 292 asm volatile( 293 "mtc0 %2, $5\n" /* CP0_PAGEMASK */ 294 295 /* Set physical address. */ 296 297 "mtc0 %0, $2\n" /* CP0_ENTRYLO0 */ 298 "mtc0 %1, $3\n" /* CP0_ENTRYLO1 */ 299 "nop\n" 300 301 "tlbwr\n" 302 "nop" 303 : 304 : "r" (lower), "r" (upper), "r" (pagemask) 305 ); 306 } 307 308 void unmap_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) 309 { 310 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ 311 u32 lower = ((physical & 0xfffff000) >> 6) | flags; 312 u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; 313 u32 pagemask = ((pagesize - 1) & 0xfffff000) << 1; 314 u32 index = 0; 315 316 asm volatile( 317 "mtc0 %4, $5\n" /* CP0_PAGEMASK */ 318 319 /* Set physical address. */ 320 321 "mtc0 %1, $2\n" /* CP0_ENTRYLO0 */ 322 "mtc0 %2, $3\n" /* CP0_ENTRYLO1 */ 323 324 /* Set virtual address. */ 325 326 "mtc0 %3, $10\n" /* CP0_ENTRYHI */ 327 "nop\n" 328 329 /* Find an existing mapping. */ 330 331 "tlbp\n" 332 "nop\n" 333 334 /* Read the index register to see if a match was found. */ 335 336 "mfc0 %0, $0\n" /* CP0_INDEX */ 337 "nop" 338 : "=r" (index) 339 : "r" (lower), "r" (upper), "r" (start), "r" (pagemask) 340 ); 341 342 /* Return if the page is not mapped. */ 343 344 if (index & 0x80000000) 345 return; 346 347 /* Otherwise, invalidate the mapping. */ 348 349 map_page_index(virtual, physical, pagesize, flags & 0xfd, asid, index); 350 }