Merge branch 'for-5.14/intel-ish' into for-linus
[linux-2.6-microblaze.git] / arch / mips / mm / tlb-r3k.c
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(&current_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(&current_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(&current_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(&current_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(&current_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 }