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