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