xref: /OK3568_Linux_fs/kernel/arch/mips/mm/tlb-r3k.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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(&current_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(&current_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(&current_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(&current_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(&current_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