1 /* 2 * linux/arch/sparc/mm/leon_m.c 3 * 4 * Copyright (C) 2004 Konrad Eisele (eiselekd@web.de, konrad@gaisler.com) Gaisler Research 5 * Copyright (C) 2009 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB 6 * Copyright (C) 2009 Konrad Eisele (konrad@gaisler.com) Aeroflex Gaisler AB 7 * 8 * do srmmu probe in software 9 * 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/mm.h> 14 #include <asm/asi.h> 15 #include <asm/leon.h> 16 #include <asm/tlbflush.h> 17 18 #include "mm_32.h" 19 20 int leon_flush_during_switch = 1; 21 static int srmmu_swprobe_trace; 22 23 static inline unsigned long leon_get_ctable_ptr(void) 24 { 25 unsigned int retval; 26 27 __asm__ __volatile__("lda [%1] %2, %0\n\t" : 28 "=r" (retval) : 29 "r" (SRMMU_CTXTBL_PTR), 30 "i" (ASI_LEON_MMUREGS)); 31 return (retval & SRMMU_CTX_PMASK) << 4; 32 } 33 34 35 unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr) 36 { 37 38 unsigned int ctxtbl; 39 unsigned int pgd, pmd, ped; 40 unsigned int ptr; 41 unsigned int lvl, pte, paddrbase; 42 unsigned int ctx; 43 unsigned int paddr_calc; 44 45 paddrbase = 0; 46 47 if (srmmu_swprobe_trace) 48 printk(KERN_INFO "swprobe: trace on\n"); 49 50 ctxtbl = leon_get_ctable_ptr(); 51 if (!(ctxtbl)) { 52 if (srmmu_swprobe_trace) 53 printk(KERN_INFO "swprobe: leon_get_ctable_ptr returned 0=>0\n"); 54 return 0; 55 } 56 if (!_pfn_valid(PFN(ctxtbl))) { 57 if (srmmu_swprobe_trace) 58 printk(KERN_INFO 59 "swprobe: !_pfn_valid(%x)=>0\n", 60 PFN(ctxtbl)); 61 return 0; 62 } 63 64 ctx = srmmu_get_context(); 65 if (srmmu_swprobe_trace) 66 printk(KERN_INFO "swprobe: --- ctx (%x) ---\n", ctx); 67 68 pgd = LEON_BYPASS_LOAD_PA(ctxtbl + (ctx * 4)); 69 70 if (((pgd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { 71 if (srmmu_swprobe_trace) 72 printk(KERN_INFO "swprobe: pgd is entry level 3\n"); 73 lvl = 3; 74 pte = pgd; 75 paddrbase = pgd & _SRMMU_PTE_PMASK_LEON; 76 goto ready; 77 } 78 if (((pgd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { 79 if (srmmu_swprobe_trace) 80 printk(KERN_INFO "swprobe: pgd is invalid => 0\n"); 81 return 0; 82 } 83 84 if (srmmu_swprobe_trace) 85 printk(KERN_INFO "swprobe: --- pgd (%x) ---\n", pgd); 86 87 ptr = (pgd & SRMMU_PTD_PMASK) << 4; 88 ptr += ((((vaddr) >> LEON_PGD_SH) & LEON_PGD_M) * 4); 89 if (!_pfn_valid(PFN(ptr))) 90 return 0; 91 92 pmd = LEON_BYPASS_LOAD_PA(ptr); 93 if (((pmd & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { 94 if (srmmu_swprobe_trace) 95 printk(KERN_INFO "swprobe: pmd is entry level 2\n"); 96 lvl = 2; 97 pte = pmd; 98 paddrbase = pmd & _SRMMU_PTE_PMASK_LEON; 99 goto ready; 100 } 101 if (((pmd & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { 102 if (srmmu_swprobe_trace) 103 printk(KERN_INFO "swprobe: pmd is invalid => 0\n"); 104 return 0; 105 } 106 107 if (srmmu_swprobe_trace) 108 printk(KERN_INFO "swprobe: --- pmd (%x) ---\n", pmd); 109 110 ptr = (pmd & SRMMU_PTD_PMASK) << 4; 111 ptr += (((vaddr >> LEON_PMD_SH) & LEON_PMD_M) * 4); 112 if (!_pfn_valid(PFN(ptr))) { 113 if (srmmu_swprobe_trace) 114 printk(KERN_INFO "swprobe: !_pfn_valid(%x)=>0\n", 115 PFN(ptr)); 116 return 0; 117 } 118 119 ped = LEON_BYPASS_LOAD_PA(ptr); 120 121 if (((ped & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { 122 if (srmmu_swprobe_trace) 123 printk(KERN_INFO "swprobe: ped is entry level 1\n"); 124 lvl = 1; 125 pte = ped; 126 paddrbase = ped & _SRMMU_PTE_PMASK_LEON; 127 goto ready; 128 } 129 if (((ped & SRMMU_ET_MASK) != SRMMU_ET_PTD)) { 130 if (srmmu_swprobe_trace) 131 printk(KERN_INFO "swprobe: ped is invalid => 0\n"); 132 return 0; 133 } 134 135 if (srmmu_swprobe_trace) 136 printk(KERN_INFO "swprobe: --- ped (%x) ---\n", ped); 137 138 ptr = (ped & SRMMU_PTD_PMASK) << 4; 139 ptr += (((vaddr >> LEON_PTE_SH) & LEON_PTE_M) * 4); 140 if (!_pfn_valid(PFN(ptr))) 141 return 0; 142 143 ptr = LEON_BYPASS_LOAD_PA(ptr); 144 if (((ptr & SRMMU_ET_MASK) == SRMMU_ET_PTE)) { 145 if (srmmu_swprobe_trace) 146 printk(KERN_INFO "swprobe: ptr is entry level 0\n"); 147 lvl = 0; 148 pte = ptr; 149 paddrbase = ptr & _SRMMU_PTE_PMASK_LEON; 150 goto ready; 151 } 152 if (srmmu_swprobe_trace) 153 printk(KERN_INFO "swprobe: ptr is invalid => 0\n"); 154 return 0; 155 156 ready: 157 switch (lvl) { 158 case 0: 159 paddr_calc = 160 (vaddr & ~(-1 << LEON_PTE_SH)) | ((pte & ~0xff) << 4); 161 break; 162 case 1: 163 paddr_calc = 164 (vaddr & ~(-1 << LEON_PMD_SH)) | ((pte & ~0xff) << 4); 165 break; 166 case 2: 167 paddr_calc = 168 (vaddr & ~(-1 << LEON_PGD_SH)) | ((pte & ~0xff) << 4); 169 break; 170 default: 171 case 3: 172 paddr_calc = vaddr; 173 break; 174 } 175 if (srmmu_swprobe_trace) 176 printk(KERN_INFO "swprobe: padde %x\n", paddr_calc); 177 if (paddr) 178 *paddr = paddr_calc; 179 return pte; 180 } 181 182 void leon_flush_icache_all(void) 183 { 184 __asm__ __volatile__(" flush "); /*iflush*/ 185 } 186 187 void leon_flush_dcache_all(void) 188 { 189 __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : 190 "i"(ASI_LEON_DFLUSH) : "memory"); 191 } 192 193 void leon_flush_pcache_all(struct vm_area_struct *vma, unsigned long page) 194 { 195 if (vma->vm_flags & VM_EXEC) 196 leon_flush_icache_all(); 197 leon_flush_dcache_all(); 198 } 199 200 void leon_flush_cache_all(void) 201 { 202 __asm__ __volatile__(" flush "); /*iflush*/ 203 __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : 204 "i"(ASI_LEON_DFLUSH) : "memory"); 205 } 206 207 void leon_flush_tlb_all(void) 208 { 209 leon_flush_cache_all(); 210 __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r"(0x400), 211 "i"(ASI_LEON_MMUFLUSH) : "memory"); 212 } 213 214 /* get all cache regs */ 215 void leon3_getCacheRegs(struct leon3_cacheregs *regs) 216 { 217 unsigned long ccr, iccr, dccr; 218 219 if (!regs) 220 return; 221 /* Get Cache regs from "Cache ASI" address 0x0, 0x8 and 0xC */ 222 __asm__ __volatile__("lda [%%g0] %3, %0\n\t" 223 "mov 0x08, %%g1\n\t" 224 "lda [%%g1] %3, %1\n\t" 225 "mov 0x0c, %%g1\n\t" 226 "lda [%%g1] %3, %2\n\t" 227 : "=r"(ccr), "=r"(iccr), "=r"(dccr) 228 /* output */ 229 : "i"(ASI_LEON_CACHEREGS) /* input */ 230 : "g1" /* clobber list */ 231 ); 232 regs->ccr = ccr; 233 regs->iccr = iccr; 234 regs->dccr = dccr; 235 } 236 237 /* Due to virtual cache we need to check cache configuration if 238 * it is possible to skip flushing in some cases. 239 * 240 * Leon2 and Leon3 differ in their way of telling cache information 241 * 242 */ 243 int __init leon_flush_needed(void) 244 { 245 int flush_needed = -1; 246 unsigned int ssize, sets; 247 char *setStr[4] = 248 { "direct mapped", "2-way associative", "3-way associative", 249 "4-way associative" 250 }; 251 /* leon 3 */ 252 struct leon3_cacheregs cregs; 253 leon3_getCacheRegs(&cregs); 254 sets = (cregs.dccr & LEON3_XCCR_SETS_MASK) >> 24; 255 /* (ssize=>realsize) 0=>1k, 1=>2k, 2=>4k, 3=>8k ... */ 256 ssize = 1 << ((cregs.dccr & LEON3_XCCR_SSIZE_MASK) >> 20); 257 258 printk(KERN_INFO "CACHE: %s cache, set size %dk\n", 259 sets > 3 ? "unknown" : setStr[sets], ssize); 260 if ((ssize <= (PAGE_SIZE / 1024)) && (sets == 0)) { 261 /* Set Size <= Page size ==> 262 flush on every context switch not needed. */ 263 flush_needed = 0; 264 printk(KERN_INFO "CACHE: not flushing on every context switch\n"); 265 } 266 return flush_needed; 267 } 268 269 void leon_switch_mm(void) 270 { 271 flush_tlb_mm((void *)0); 272 if (leon_flush_during_switch) 273 leon_flush_cache_all(); 274 } 275 276 static void leon_flush_cache_mm(struct mm_struct *mm) 277 { 278 leon_flush_cache_all(); 279 } 280 281 static void leon_flush_cache_page(struct vm_area_struct *vma, unsigned long page) 282 { 283 leon_flush_pcache_all(vma, page); 284 } 285 286 static void leon_flush_cache_range(struct vm_area_struct *vma, 287 unsigned long start, 288 unsigned long end) 289 { 290 leon_flush_cache_all(); 291 } 292 293 static void leon_flush_tlb_mm(struct mm_struct *mm) 294 { 295 leon_flush_tlb_all(); 296 } 297 298 static void leon_flush_tlb_page(struct vm_area_struct *vma, 299 unsigned long page) 300 { 301 leon_flush_tlb_all(); 302 } 303 304 static void leon_flush_tlb_range(struct vm_area_struct *vma, 305 unsigned long start, 306 unsigned long end) 307 { 308 leon_flush_tlb_all(); 309 } 310 311 static void leon_flush_page_to_ram(unsigned long page) 312 { 313 leon_flush_cache_all(); 314 } 315 316 static void leon_flush_sig_insns(struct mm_struct *mm, unsigned long page) 317 { 318 leon_flush_cache_all(); 319 } 320 321 static void leon_flush_page_for_dma(unsigned long page) 322 { 323 leon_flush_dcache_all(); 324 } 325 326 void __init poke_leonsparc(void) 327 { 328 } 329 330 static const struct sparc32_cachetlb_ops leon_ops = { 331 .cache_all = leon_flush_cache_all, 332 .cache_mm = leon_flush_cache_mm, 333 .cache_page = leon_flush_cache_page, 334 .cache_range = leon_flush_cache_range, 335 .tlb_all = leon_flush_tlb_all, 336 .tlb_mm = leon_flush_tlb_mm, 337 .tlb_page = leon_flush_tlb_page, 338 .tlb_range = leon_flush_tlb_range, 339 .page_to_ram = leon_flush_page_to_ram, 340 .sig_insns = leon_flush_sig_insns, 341 .page_for_dma = leon_flush_page_for_dma, 342 }; 343 344 void __init init_leon(void) 345 { 346 srmmu_name = "LEON"; 347 sparc32_cachetlb_ops = &leon_ops; 348 poke_srmmu = poke_leonsparc; 349 350 leon_flush_during_switch = leon_flush_needed(); 351 } 352