1 /* 2 * r2300.c: R2000 and R3000 specific mmu/cache code. 3 * 4 * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 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/smp.h> 17 #include <linux/mm.h> 18 19 #include <asm/page.h> 20 #include <asm/pgtable.h> 21 #include <asm/mmu_context.h> 22 #include <asm/system.h> 23 #include <asm/tlbmisc.h> 24 #include <asm/isadep.h> 25 #include <asm/io.h> 26 #include <asm/bootinfo.h> 27 #include <asm/cpu.h> 28 29 #undef DEBUG_TLB 30 31 extern void build_tlb_refill_handler(void); 32 33 /* CP0 hazard avoidance. */ 34 #define BARRIER \ 35 __asm__ __volatile__( \ 36 ".set push\n\t" \ 37 ".set noreorder\n\t" \ 38 "nop\n\t" \ 39 ".set pop\n\t") 40 41 int r3k_have_wired_reg; /* should be in cpu_data? */ 42 43 /* TLB operations. */ 44 void local_flush_tlb_all(void) 45 { 46 unsigned long flags; 47 unsigned long old_ctx; 48 int entry; 49 50 #ifdef DEBUG_TLB 51 printk("[tlball]"); 52 #endif 53 54 local_irq_save(flags); 55 old_ctx = read_c0_entryhi() & ASID_MASK; 56 write_c0_entrylo0(0); 57 entry = r3k_have_wired_reg ? read_c0_wired() : 8; 58 for (; entry < current_cpu_data.tlbsize; entry++) { 59 write_c0_index(entry << 8); 60 write_c0_entryhi((entry | 0x80000) << 12); 61 BARRIER; 62 tlb_write_indexed(); 63 } 64 write_c0_entryhi(old_ctx); 65 local_irq_restore(flags); 66 } 67 68 void local_flush_tlb_mm(struct mm_struct *mm) 69 { 70 int cpu = smp_processor_id(); 71 72 if (cpu_context(cpu, mm) != 0) { 73 #ifdef DEBUG_TLB 74 printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm)); 75 #endif 76 drop_mmu_context(mm, cpu); 77 } 78 } 79 80 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 81 unsigned long end) 82 { 83 struct mm_struct *mm = vma->vm_mm; 84 int cpu = smp_processor_id(); 85 86 if (cpu_context(cpu, mm) != 0) { 87 unsigned long size, flags; 88 89 #ifdef DEBUG_TLB 90 printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", 91 cpu_context(cpu, mm) & ASID_MASK, start, end); 92 #endif 93 local_irq_save(flags); 94 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 95 if (size <= current_cpu_data.tlbsize) { 96 int oldpid = read_c0_entryhi() & ASID_MASK; 97 int newpid = cpu_context(cpu, mm) & ASID_MASK; 98 99 start &= PAGE_MASK; 100 end += PAGE_SIZE - 1; 101 end &= PAGE_MASK; 102 while (start < end) { 103 int idx; 104 105 write_c0_entryhi(start | newpid); 106 start += PAGE_SIZE; /* BARRIER */ 107 tlb_probe(); 108 idx = read_c0_index(); 109 write_c0_entrylo0(0); 110 write_c0_entryhi(KSEG0); 111 if (idx < 0) /* BARRIER */ 112 continue; 113 tlb_write_indexed(); 114 } 115 write_c0_entryhi(oldpid); 116 } else { 117 drop_mmu_context(mm, cpu); 118 } 119 local_irq_restore(flags); 120 } 121 } 122 123 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) 124 { 125 unsigned long size, flags; 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 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