diff options
Diffstat (limited to 'arch/mips/mm/tlb-r3k.c')
-rw-r--r-- | arch/mips/mm/tlb-r3k.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/arch/mips/mm/tlb-r3k.c b/arch/mips/mm/tlb-r3k.c new file mode 100644 index 000000000..a36622ebe --- /dev/null +++ b/arch/mips/mm/tlb-r3k.c | |||
@@ -0,0 +1,284 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * r2300.c: R2000 and R3000 specific mmu/cache code. | ||
4 | * | ||
5 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
6 | * | ||
7 | * with a lot of changes to make this thing work for R3000s | ||
8 | * Tx39XX R4k style caches added. HK | ||
9 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen | ||
10 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov | ||
11 | * Copyright (C) 2002 Ralf Baechle | ||
12 | * Copyright (C) 2002 Maciej W. Rozycki | ||
13 | */ | ||
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/mmu_context.h> | ||
21 | #include <asm/tlbmisc.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 | static void local_flush_tlb_from(int entry) | ||
43 | { | ||
44 | unsigned long old_ctx; | ||
45 | |||
46 | old_ctx = read_c0_entryhi() & cpu_asid_mask(¤t_cpu_data); | ||
47 | write_c0_entrylo0(0); | ||
48 | while (entry < current_cpu_data.tlbsize) { | ||
49 | write_c0_index(entry << 8); | ||
50 | write_c0_entryhi((entry | 0x80000) << 12); | ||
51 | entry++; /* BARRIER */ | ||
52 | tlb_write_indexed(); | ||
53 | } | ||
54 | write_c0_entryhi(old_ctx); | ||
55 | } | ||
56 | |||
57 | void local_flush_tlb_all(void) | ||
58 | { | ||
59 | unsigned long flags; | ||
60 | |||
61 | #ifdef DEBUG_TLB | ||
62 | printk("[tlball]"); | ||
63 | #endif | ||
64 | local_irq_save(flags); | ||
65 | local_flush_tlb_from(r3k_have_wired_reg ? read_c0_wired() : 8); | ||
66 | local_irq_restore(flags); | ||
67 | } | ||
68 | |||
69 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
70 | unsigned long end) | ||
71 | { | ||
72 | unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); | ||
73 | struct mm_struct *mm = vma->vm_mm; | ||
74 | int cpu = smp_processor_id(); | ||
75 | |||
76 | if (cpu_context(cpu, mm) != 0) { | ||
77 | unsigned long size, flags; | ||
78 | |||
79 | #ifdef DEBUG_TLB | ||
80 | printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", | ||
81 | cpu_context(cpu, mm) & asid_mask, start, end); | ||
82 | #endif | ||
83 | local_irq_save(flags); | ||
84 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
85 | if (size <= current_cpu_data.tlbsize) { | ||
86 | int oldpid = read_c0_entryhi() & asid_mask; | ||
87 | int newpid = cpu_context(cpu, mm) & asid_mask; | ||
88 | |||
89 | start &= PAGE_MASK; | ||
90 | end += PAGE_SIZE - 1; | ||
91 | end &= PAGE_MASK; | ||
92 | while (start < end) { | ||
93 | int idx; | ||
94 | |||
95 | write_c0_entryhi(start | newpid); | ||
96 | start += PAGE_SIZE; /* BARRIER */ | ||
97 | tlb_probe(); | ||
98 | idx = read_c0_index(); | ||
99 | write_c0_entrylo0(0); | ||
100 | write_c0_entryhi(KSEG0); | ||
101 | if (idx < 0) /* BARRIER */ | ||
102 | continue; | ||
103 | tlb_write_indexed(); | ||
104 | } | ||
105 | write_c0_entryhi(oldpid); | ||
106 | } else { | ||
107 | drop_mmu_context(mm); | ||
108 | } | ||
109 | local_irq_restore(flags); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
114 | { | ||
115 | unsigned long size, flags; | ||
116 | |||
117 | #ifdef DEBUG_TLB | ||
118 | printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end); | ||
119 | #endif | ||
120 | local_irq_save(flags); | ||
121 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
122 | if (size <= current_cpu_data.tlbsize) { | ||
123 | int pid = read_c0_entryhi(); | ||
124 | |||
125 | start &= PAGE_MASK; | ||
126 | end += PAGE_SIZE - 1; | ||
127 | end &= PAGE_MASK; | ||
128 | |||
129 | while (start < end) { | ||
130 | int idx; | ||
131 | |||
132 | write_c0_entryhi(start); | ||
133 | start += PAGE_SIZE; /* BARRIER */ | ||
134 | tlb_probe(); | ||
135 | idx = read_c0_index(); | ||
136 | write_c0_entrylo0(0); | ||
137 | write_c0_entryhi(KSEG0); | ||
138 | if (idx < 0) /* BARRIER */ | ||
139 | continue; | ||
140 | tlb_write_indexed(); | ||
141 | } | ||
142 | write_c0_entryhi(pid); | ||
143 | } else { | ||
144 | local_flush_tlb_all(); | ||
145 | } | ||
146 | local_irq_restore(flags); | ||
147 | } | ||
148 | |||
149 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
150 | { | ||
151 | unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); | ||
152 | int cpu = smp_processor_id(); | ||
153 | |||
154 | if (cpu_context(cpu, vma->vm_mm) != 0) { | ||
155 | unsigned long flags; | ||
156 | int oldpid, newpid, idx; | ||
157 | |||
158 | #ifdef DEBUG_TLB | ||
159 | printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page); | ||
160 | #endif | ||
161 | newpid = cpu_context(cpu, vma->vm_mm) & asid_mask; | ||
162 | page &= PAGE_MASK; | ||
163 | local_irq_save(flags); | ||
164 | oldpid = read_c0_entryhi() & asid_mask; | ||
165 | write_c0_entryhi(page | newpid); | ||
166 | BARRIER; | ||
167 | tlb_probe(); | ||
168 | idx = read_c0_index(); | ||
169 | write_c0_entrylo0(0); | ||
170 | write_c0_entryhi(KSEG0); | ||
171 | if (idx < 0) /* BARRIER */ | ||
172 | goto finish; | ||
173 | tlb_write_indexed(); | ||
174 | |||
175 | finish: | ||
176 | write_c0_entryhi(oldpid); | ||
177 | local_irq_restore(flags); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) | ||
182 | { | ||
183 | unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); | ||
184 | unsigned long flags; | ||
185 | int idx, pid; | ||
186 | |||
187 | /* | ||
188 | * Handle debugger faulting in for debugee. | ||
189 | */ | ||
190 | if (current->active_mm != vma->vm_mm) | ||
191 | return; | ||
192 | |||
193 | pid = read_c0_entryhi() & asid_mask; | ||
194 | |||
195 | #ifdef DEBUG_TLB | ||
196 | if ((pid != (cpu_context(cpu, vma->vm_mm) & asid_mask)) || (cpu_context(cpu, vma->vm_mm) == 0)) { | ||
197 | printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n", | ||
198 | (cpu_context(cpu, vma->vm_mm)), pid); | ||
199 | } | ||
200 | #endif | ||
201 | |||
202 | local_irq_save(flags); | ||
203 | address &= PAGE_MASK; | ||
204 | write_c0_entryhi(address | pid); | ||
205 | BARRIER; | ||
206 | tlb_probe(); | ||
207 | idx = read_c0_index(); | ||
208 | write_c0_entrylo0(pte_val(pte)); | ||
209 | write_c0_entryhi(address | pid); | ||
210 | if (idx < 0) { /* BARRIER */ | ||
211 | tlb_write_random(); | ||
212 | } else { | ||
213 | tlb_write_indexed(); | ||
214 | } | ||
215 | write_c0_entryhi(pid); | ||
216 | local_irq_restore(flags); | ||
217 | } | ||
218 | |||
219 | void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, | ||
220 | unsigned long entryhi, unsigned long pagemask) | ||
221 | { | ||
222 | unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); | ||
223 | unsigned long flags; | ||
224 | unsigned long old_ctx; | ||
225 | static unsigned long wired = 0; | ||
226 | |||
227 | if (r3k_have_wired_reg) { /* TX39XX */ | ||
228 | unsigned long old_pagemask; | ||
229 | unsigned long w; | ||
230 | |||
231 | #ifdef DEBUG_TLB | ||
232 | printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n", | ||
233 | entrylo0, entryhi, pagemask); | ||
234 | #endif | ||
235 | |||
236 | local_irq_save(flags); | ||
237 | /* Save old context and create impossible VPN2 value */ | ||
238 | old_ctx = read_c0_entryhi() & asid_mask; | ||
239 | old_pagemask = read_c0_pagemask(); | ||
240 | w = read_c0_wired(); | ||
241 | write_c0_wired(w + 1); | ||
242 | write_c0_index(w << 8); | ||
243 | write_c0_pagemask(pagemask); | ||
244 | write_c0_entryhi(entryhi); | ||
245 | write_c0_entrylo0(entrylo0); | ||
246 | BARRIER; | ||
247 | tlb_write_indexed(); | ||
248 | |||
249 | write_c0_entryhi(old_ctx); | ||
250 | write_c0_pagemask(old_pagemask); | ||
251 | local_flush_tlb_all(); | ||
252 | local_irq_restore(flags); | ||
253 | |||
254 | } else if (wired < 8) { | ||
255 | #ifdef DEBUG_TLB | ||
256 | printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n", | ||
257 | entrylo0, entryhi); | ||
258 | #endif | ||
259 | |||
260 | local_irq_save(flags); | ||
261 | old_ctx = read_c0_entryhi() & asid_mask; | ||
262 | write_c0_entrylo0(entrylo0); | ||
263 | write_c0_entryhi(entryhi); | ||
264 | write_c0_index(wired); | ||
265 | wired++; /* BARRIER */ | ||
266 | tlb_write_indexed(); | ||
267 | write_c0_entryhi(old_ctx); | ||
268 | local_flush_tlb_all(); | ||
269 | local_irq_restore(flags); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | void tlb_init(void) | ||
274 | { | ||
275 | switch (current_cpu_type()) { | ||
276 | case CPU_TX3922: | ||
277 | case CPU_TX3927: | ||
278 | r3k_have_wired_reg = 1; | ||
279 | write_c0_wired(0); /* Set to 8 on reset... */ | ||
280 | break; | ||
281 | } | ||
282 | local_flush_tlb_from(0); | ||
283 | build_tlb_refill_handler(); | ||
284 | } | ||