xref: /openbmc/linux/arch/sh/mm/tlbflush_32.c (revision 7effbd18)
1 /*
2  * TLB flushing operations for SH with an MMU.
3  *
4  *  Copyright (C) 1999  Niibe Yutaka
5  *  Copyright (C) 2003  Paul Mundt
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 #include <linux/mm.h>
12 #include <asm/mmu_context.h>
13 #include <asm/tlbflush.h>
14 
15 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
16 {
17 	unsigned int cpu = smp_processor_id();
18 
19 	if (vma->vm_mm && cpu_context(cpu, vma->vm_mm) != NO_CONTEXT) {
20 		unsigned long flags;
21 		unsigned long asid;
22 		unsigned long saved_asid = MMU_NO_ASID;
23 
24 		asid = cpu_asid(cpu, vma->vm_mm);
25 		page &= PAGE_MASK;
26 
27 		local_irq_save(flags);
28 		if (vma->vm_mm != current->mm) {
29 			saved_asid = get_asid();
30 			set_asid(asid);
31 		}
32 		local_flush_tlb_one(asid, page);
33 		if (saved_asid != MMU_NO_ASID)
34 			set_asid(saved_asid);
35 		local_irq_restore(flags);
36 	}
37 }
38 
39 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
40 			   unsigned long end)
41 {
42 	struct mm_struct *mm = vma->vm_mm;
43 	unsigned int cpu = smp_processor_id();
44 
45 	if (cpu_context(cpu, mm) != NO_CONTEXT) {
46 		unsigned long flags;
47 		int size;
48 
49 		local_irq_save(flags);
50 		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
51 		if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
52 			cpu_context(cpu, mm) = NO_CONTEXT;
53 			if (mm == current->mm)
54 				activate_context(mm, cpu);
55 		} else {
56 			unsigned long asid;
57 			unsigned long saved_asid = MMU_NO_ASID;
58 
59 			asid = cpu_asid(cpu, mm);
60 			start &= PAGE_MASK;
61 			end += (PAGE_SIZE - 1);
62 			end &= PAGE_MASK;
63 			if (mm != current->mm) {
64 				saved_asid = get_asid();
65 				set_asid(asid);
66 			}
67 			while (start < end) {
68 				local_flush_tlb_one(asid, start);
69 				start += PAGE_SIZE;
70 			}
71 			if (saved_asid != MMU_NO_ASID)
72 				set_asid(saved_asid);
73 		}
74 		local_irq_restore(flags);
75 	}
76 }
77 
78 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
79 {
80 	unsigned int cpu = smp_processor_id();
81 	unsigned long flags;
82 	int size;
83 
84 	local_irq_save(flags);
85 	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
86 	if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
87 		local_flush_tlb_all();
88 	} else {
89 		unsigned long asid;
90 		unsigned long saved_asid = get_asid();
91 
92 		asid = cpu_asid(cpu, &init_mm);
93 		start &= PAGE_MASK;
94 		end += (PAGE_SIZE - 1);
95 		end &= PAGE_MASK;
96 		set_asid(asid);
97 		while (start < end) {
98 			local_flush_tlb_one(asid, start);
99 			start += PAGE_SIZE;
100 		}
101 		set_asid(saved_asid);
102 	}
103 	local_irq_restore(flags);
104 }
105 
106 void local_flush_tlb_mm(struct mm_struct *mm)
107 {
108 	unsigned int cpu = smp_processor_id();
109 
110 	/* Invalidate all TLB of this process. */
111 	/* Instead of invalidating each TLB, we get new MMU context. */
112 	if (cpu_context(cpu, mm) != NO_CONTEXT) {
113 		unsigned long flags;
114 
115 		local_irq_save(flags);
116 		cpu_context(cpu, mm) = NO_CONTEXT;
117 		if (mm == current->mm)
118 			activate_context(mm, cpu);
119 		local_irq_restore(flags);
120 	}
121 }
122 
123 void __flush_tlb_global(void)
124 {
125 	unsigned long flags;
126 
127 	local_irq_save(flags);
128 
129 	/*
130 	 * This is the most destructive of the TLB flushing options,
131 	 * and will tear down all of the UTLB/ITLB mappings, including
132 	 * wired entries.
133 	 */
134 	__raw_writel(__raw_readl(MMUCR) | MMUCR_TI, MMUCR);
135 
136 	local_irq_restore(flags);
137 }
138