1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 7 * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org 8 * Carsten Langgaard, carstenl@mips.com 9 * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved. 10 */ 11#include <linux/sched.h> 12#include <linux/smp.h> 13#include <linux/mm.h> 14 15#include <asm/cpu.h> 16#include <asm/bootinfo.h> 17#include <asm/mmu_context.h> 18#include <asm/pgtable.h> 19 20extern void build_tlb_refill_handler(void); 21 22#define TFP_TLB_SIZE 384 23#define TFP_TLB_SET_SHIFT 7 24 25/* CP0 hazard avoidance. */ 26#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ 27 "nop; nop; nop; nop; nop; nop;\n\t" \ 28 ".set reorder\n\t") 29 30void local_flush_tlb_all(void) 31{ 32 unsigned long flags; 33 unsigned long old_ctx; 34 int entry; 35 36 local_irq_save(flags); 37 /* Save old context and create impossible VPN2 value */ 38 old_ctx = read_c0_entryhi(); 39 write_c0_entrylo(0); 40 41 for (entry = 0; entry < TFP_TLB_SIZE; entry++) { 42 write_c0_tlbset(entry >> TFP_TLB_SET_SHIFT); 43 write_c0_vaddr(entry << PAGE_SHIFT); 44 write_c0_entryhi(CKSEG0 + (entry << (PAGE_SHIFT + 1))); 45 mtc0_tlbw_hazard(); 46 tlb_write(); 47 } 48 tlbw_use_hazard(); 49 write_c0_entryhi(old_ctx); 50 local_irq_restore(flags); 51} 52 53void local_flush_tlb_mm(struct mm_struct *mm) 54{ 55 int cpu = smp_processor_id(); 56 57 if (cpu_context(cpu, mm) != 0) 58 drop_mmu_context(mm, cpu); 59} 60 61void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 62 unsigned long end) 63{ 64 struct mm_struct *mm = vma->vm_mm; 65 int cpu = smp_processor_id(); 66 unsigned long flags; 67 int oldpid, newpid, size; 68 69 if (!cpu_context(cpu, mm)) 70 return; 71 72 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 73 size = (size + 1) >> 1; 74 75 local_irq_save(flags); 76 77 if (size > TFP_TLB_SIZE / 2) { 78 drop_mmu_context(mm, cpu); 79 goto out_restore; 80 } 81 82 oldpid = read_c0_entryhi(); 83 newpid = cpu_asid(cpu, mm); 84 85 write_c0_entrylo(0); 86 87 start &= PAGE_MASK; 88 end += (PAGE_SIZE - 1); 89 end &= PAGE_MASK; 90 while (start < end) { 91 signed long idx; 92 93 write_c0_vaddr(start); 94 write_c0_entryhi(start); 95 start += PAGE_SIZE; 96 tlb_probe(); 97 idx = read_c0_tlbset(); 98 if (idx < 0) 99 continue; 100 101 write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); 102 tlb_write(); 103 } 104 write_c0_entryhi(oldpid); 105 106out_restore: 107 local_irq_restore(flags); 108} 109 110/* Usable for KV1 addresses only! */ 111void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) 112{ 113 unsigned long size, flags; 114 115 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 116 size = (size + 1) >> 1; 117 118 if (size > TFP_TLB_SIZE / 2) { 119 local_flush_tlb_all(); 120 return; 121 } 122 123 local_irq_save(flags); 124 125 write_c0_entrylo(0); 126 127 start &= PAGE_MASK; 128 end += (PAGE_SIZE - 1); 129 end &= PAGE_MASK; 130 while (start < end) { 131 signed long idx; 132 133 write_c0_vaddr(start); 134 write_c0_entryhi(start); 135 start += PAGE_SIZE; 136 tlb_probe(); 137 idx = read_c0_tlbset(); 138 if (idx < 0) 139 continue; 140 141 write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); 142 tlb_write(); 143 } 144 145 local_irq_restore(flags); 146} 147 148void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 149{ 150 int cpu = smp_processor_id(); 151 unsigned long flags; 152 int oldpid, newpid; 153 signed long idx; 154 155 if (!cpu_context(cpu, vma->vm_mm)) 156 return; 157 158 newpid = cpu_asid(cpu, vma->vm_mm); 159 page &= PAGE_MASK; 160 local_irq_save(flags); 161 oldpid = read_c0_entryhi(); 162 write_c0_vaddr(page); 163 write_c0_entryhi(newpid); 164 tlb_probe(); 165 idx = read_c0_tlbset(); 166 if (idx < 0) 167 goto finish; 168 169 write_c0_entrylo(0); 170 write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); 171 tlb_write(); 172 173finish: 174 write_c0_entryhi(oldpid); 175 local_irq_restore(flags); 176} 177 178/* 179 * We will need multiple versions of update_mmu_cache(), one that just 180 * updates the TLB with the new pte(s), and another which also checks 181 * for the R4k "end of page" hardware bug and does the needy. 182 */ 183void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) 184{ 185 unsigned long flags; 186 pgd_t *pgdp; 187 pmd_t *pmdp; 188 pte_t *ptep; 189 int pid; 190 191 /* 192 * Handle debugger faulting in for debugee. 193 */ 194 if (current->active_mm != vma->vm_mm) 195 return; 196 197 pid = read_c0_entryhi() & ASID_MASK; 198 199 local_irq_save(flags); 200 address &= PAGE_MASK; 201 write_c0_vaddr(address); 202 write_c0_entryhi(pid); 203 pgdp = pgd_offset(vma->vm_mm, address); 204 pmdp = pmd_offset(pgdp, address); 205 ptep = pte_offset_map(pmdp, address); 206 tlb_probe(); 207 208 write_c0_entrylo(pte_val(*ptep++) >> 6); 209 tlb_write(); 210 211 write_c0_entryhi(pid); 212 local_irq_restore(flags); 213} 214 215static void probe_tlb(unsigned long config) 216{ 217 struct cpuinfo_mips *c = ¤t_cpu_data; 218 219 c->tlbsize = 3 * 128; /* 3 sets each 128 entries */ 220} 221 222void tlb_init(void) 223{ 224 unsigned int config = read_c0_config(); 225 unsigned long status; 226 227 probe_tlb(config); 228 229 status = read_c0_status(); 230 status &= ~(ST0_UPS | ST0_KPS); 231#ifdef CONFIG_PAGE_SIZE_4KB 232 status |= (TFP_PAGESIZE_4K << 32) | (TFP_PAGESIZE_4K << 36); 233#elif defined(CONFIG_PAGE_SIZE_8KB) 234 status |= (TFP_PAGESIZE_8K << 32) | (TFP_PAGESIZE_8K << 36); 235#elif defined(CONFIG_PAGE_SIZE_16KB) 236 status |= (TFP_PAGESIZE_16K << 32) | (TFP_PAGESIZE_16K << 36); 237#elif defined(CONFIG_PAGE_SIZE_64KB) 238 status |= (TFP_PAGESIZE_64K << 32) | (TFP_PAGESIZE_64K << 36); 239#endif 240 write_c0_status(status); 241 242 write_c0_wired(0); 243 244 local_flush_tlb_all(); 245 246 build_tlb_refill_handler(); 247} 248