1 /* 2 * r2300.c: R2000 and R3000 specific mmu/cache code. 3 * 4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) 5 * 6 * with a lot of changes to make this thing work for R3000s 7 * Tx39XX R4k style caches added. HK 8 * Copyright (C) 1998, 1999, 2000 Harald Koerfgen 9 * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov 10 * Copyright (C) 2002 Ralf Baechle 11 * Copyright (C) 2002 Maciej W. Rozycki 12 */ 13 #include <linux/init.h> 14 #include <linux/kernel.h> 15 #include <linux/sched.h> 16 #include <linux/mm.h> 17 18 #include <asm/page.h> 19 #include <asm/pgtable.h> 20 #include <asm/mmu_context.h> 21 #include <asm/system.h> 22 #include <asm/isadep.h> 23 #include <asm/io.h> 24 #include <asm/bootinfo.h> 25 #include <asm/cpu.h> 26 27 #undef DEBUG_TLB 28 29 extern void build_tlb_refill_handler(void); 30 31 /* CP0 hazard avoidance. */ 32 #define BARRIER \ 33 __asm__ __volatile__( \ 34 ".set push\n\t" \ 35 ".set noreorder\n\t" \ 36 "nop\n\t" \ 37 ".set pop\n\t") 38 39 int r3k_have_wired_reg; /* should be in cpu_data? */ 40 41 /* TLB operations. */ 42 void local_flush_tlb_all(void) 43 { 44 unsigned long flags; 45 unsigned long old_ctx; 46 int entry; 47 48 #ifdef DEBUG_TLB 49 printk("[tlball]"); 50 #endif 51 52 local_irq_save(flags); 53 old_ctx = read_c0_entryhi() & ASID_MASK; 54 write_c0_entrylo0(0); 55 entry = r3k_have_wired_reg ? read_c0_wired() : 8; 56 for (; entry < current_cpu_data.tlbsize; entry++) { 57 write_c0_index(entry << 8); 58 write_c0_entryhi((entry | 0x80000) << 12); 59 BARRIER; 60 tlb_write_indexed(); 61 } 62 write_c0_entryhi(old_ctx); 63 local_irq_restore(flags); 64 } 65 66 void local_flush_tlb_mm(struct mm_struct *mm) 67 { 68 int cpu = smp_processor_id(); 69 70 if (cpu_context(cpu, mm) != 0) { 71 #ifdef DEBUG_TLB 72 printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm)); 73 #endif 74 drop_mmu_context(mm, cpu); 75 } 76 } 77 78 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 79 unsigned long end) 80 { 81 struct mm_struct *mm = vma->vm_mm; 82 int cpu = smp_processor_id(); 83 84 if (cpu_context(cpu, mm) != 0) { 85 unsigned long flags; 86 int size; 87 88 #ifdef DEBUG_TLB 89 printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", 90 cpu_context(cpu, mm) & ASID_MASK, start, end); 91 #endif 92 local_irq_save(flags); 93 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 94 if (size <= current_cpu_data.tlbsize) { 95 int oldpid = read_c0_entryhi() & ASID_MASK; 96 int newpid = cpu_context(cpu, mm) & ASID_MASK; 97 98 start &= PAGE_MASK; 99 end += PAGE_SIZE - 1; 100 end &= PAGE_MASK; 101 while (start < end) { 102 int idx; 103 104 write_c0_entryhi(start | newpid); 105 start += PAGE_SIZE; /* BARRIER */ 106 tlb_probe(); 107 idx = read_c0_index(); 108 write_c0_entrylo0(0); 109 write_c0_entryhi(KSEG0); 110 if (idx < 0) /* BARRIER */ 111 continue; 112 tlb_write_indexed(); 113 } 114 write_c0_entryhi(oldpid); 115 } else { 116 drop_mmu_context(mm, cpu); 117 } 118 local_irq_restore(flags); 119 } 120 } 121 122 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) 123 { 124 unsigned long flags; 125 int size; 126 127 #ifdef DEBUG_TLB 128 printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end); 129 #endif 130 local_irq_save(flags); 131 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 132 if (size <= current_cpu_data.tlbsize) { 133 int pid = read_c0_entryhi(); 134 135 start &= PAGE_MASK; 136 end += PAGE_SIZE - 1; 137 end &= PAGE_MASK; 138 139 while (start < end) { 140 int idx; 141 142 write_c0_entryhi(start); 143 start += PAGE_SIZE; /* BARRIER */ 144 tlb_probe(); 145 idx = read_c0_index(); 146 write_c0_entrylo0(0); 147 write_c0_entryhi(KSEG0); 148 if (idx < 0) /* BARRIER */ 149 continue; 150 tlb_write_indexed(); 151 } 152 write_c0_entryhi(pid); 153 } else { 154 local_flush_tlb_all(); 155 } 156 local_irq_restore(flags); 157 } 158 159 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 160 { 161 int cpu = smp_processor_id(); 162 163 if (!vma || cpu_context(cpu, vma->vm_mm) != 0) { 164 unsigned long flags; 165 int oldpid, newpid, idx; 166 167 #ifdef DEBUG_TLB 168 printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page); 169 #endif 170 newpid = cpu_context(cpu, vma->vm_mm) & ASID_MASK; 171 page &= PAGE_MASK; 172 local_irq_save(flags); 173 oldpid = read_c0_entryhi() & ASID_MASK; 174 write_c0_entryhi(page | newpid); 175 BARRIER; 176 tlb_probe(); 177 idx = read_c0_index(); 178 write_c0_entrylo0(0); 179 write_c0_entryhi(KSEG0); 180 if (idx < 0) /* BARRIER */ 181 goto finish; 182 tlb_write_indexed(); 183 184 finish: 185 write_c0_entryhi(oldpid); 186 local_irq_restore(flags); 187 } 188 } 189 190 void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) 191 { 192 unsigned long flags; 193 int idx, pid; 194 195 /* 196 * Handle debugger faulting in for debugee. 197 */ 198 if (current->active_mm != vma->vm_mm) 199 return; 200 201 pid = read_c0_entryhi() & ASID_MASK; 202 203 #ifdef DEBUG_TLB 204 if ((pid != (cpu_context(cpu, vma->vm_mm) & ASID_MASK)) || (cpu_context(cpu, vma->vm_mm) == 0)) { 205 printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n", 206 (cpu_context(cpu, vma->vm_mm)), pid); 207 } 208 #endif 209 210 local_irq_save(flags); 211 address &= PAGE_MASK; 212 write_c0_entryhi(address | pid); 213 BARRIER; 214 tlb_probe(); 215 idx = read_c0_index(); 216 write_c0_entrylo0(pte_val(pte)); 217 write_c0_entryhi(address | pid); 218 if (idx < 0) { /* BARRIER */ 219 tlb_write_random(); 220 } else { 221 tlb_write_indexed(); 222 } 223 write_c0_entryhi(pid); 224 local_irq_restore(flags); 225 } 226 227 void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, 228 unsigned long entryhi, unsigned long pagemask) 229 { 230 unsigned long flags; 231 unsigned long old_ctx; 232 static unsigned long wired = 0; 233 234 if (r3k_have_wired_reg) { /* TX39XX */ 235 unsigned long old_pagemask; 236 unsigned long w; 237 238 #ifdef DEBUG_TLB 239 printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n", 240 entrylo0, entryhi, pagemask); 241 #endif 242 243 local_irq_save(flags); 244 /* Save old context and create impossible VPN2 value */ 245 old_ctx = read_c0_entryhi() & ASID_MASK; 246 old_pagemask = read_c0_pagemask(); 247 w = read_c0_wired(); 248 write_c0_wired(w + 1); 249 write_c0_index(w << 8); 250 write_c0_pagemask(pagemask); 251 write_c0_entryhi(entryhi); 252 write_c0_entrylo0(entrylo0); 253 BARRIER; 254 tlb_write_indexed(); 255 256 write_c0_entryhi(old_ctx); 257 write_c0_pagemask(old_pagemask); 258 local_flush_tlb_all(); 259 local_irq_restore(flags); 260 261 } else if (wired < 8) { 262 #ifdef DEBUG_TLB 263 printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n", 264 entrylo0, entryhi); 265 #endif 266 267 local_irq_save(flags); 268 old_ctx = read_c0_entryhi() & ASID_MASK; 269 write_c0_entrylo0(entrylo0); 270 write_c0_entryhi(entryhi); 271 write_c0_index(wired); 272 wired++; /* BARRIER */ 273 tlb_write_indexed(); 274 write_c0_entryhi(old_ctx); 275 local_flush_tlb_all(); 276 local_irq_restore(flags); 277 } 278 } 279 280 void __cpuinit tlb_init(void) 281 { 282 local_flush_tlb_all(); 283 284 build_tlb_refill_handler(); 285 } 286