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