diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/mm/tlb-r3k.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/mm/tlb-r3k.c')
-rw-r--r-- | arch/mips/mm/tlb-r3k.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/arch/mips/mm/tlb-r3k.c b/arch/mips/mm/tlb-r3k.c new file mode 100644 index 000000000000..7948e9a5e372 --- /dev/null +++ b/arch/mips/mm/tlb-r3k.c | |||
@@ -0,0 +1,289 @@ | |||
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 | if (read_c0_wired() != w + 1) { | ||
250 | printk("[tlbwired] No WIRED reg?\n"); | ||
251 | return; | ||
252 | } | ||
253 | write_c0_index(w << 8); | ||
254 | write_c0_pagemask(pagemask); | ||
255 | write_c0_entryhi(entryhi); | ||
256 | write_c0_entrylo0(entrylo0); | ||
257 | BARRIER; | ||
258 | tlb_write_indexed(); | ||
259 | |||
260 | write_c0_entryhi(old_ctx); | ||
261 | write_c0_pagemask(old_pagemask); | ||
262 | local_flush_tlb_all(); | ||
263 | local_irq_restore(flags); | ||
264 | |||
265 | } else if (wired < 8) { | ||
266 | #ifdef DEBUG_TLB | ||
267 | printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n", | ||
268 | entrylo0, entryhi); | ||
269 | #endif | ||
270 | |||
271 | local_irq_save(flags); | ||
272 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
273 | write_c0_entrylo0(entrylo0); | ||
274 | write_c0_entryhi(entryhi); | ||
275 | write_c0_index(wired); | ||
276 | wired++; /* BARRIER */ | ||
277 | tlb_write_indexed(); | ||
278 | write_c0_entryhi(old_ctx); | ||
279 | local_flush_tlb_all(); | ||
280 | local_irq_restore(flags); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | void __init tlb_init(void) | ||
285 | { | ||
286 | local_flush_tlb_all(); | ||
287 | |||
288 | build_tlb_refill_handler(); | ||
289 | } | ||