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