xref: /openbmc/linux/arch/csky/mm/tlb.c (revision 5a170e9e)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3 
4 #include <linux/init.h>
5 #include <linux/mm.h>
6 #include <linux/module.h>
7 #include <linux/sched.h>
8 
9 #include <asm/mmu_context.h>
10 #include <asm/pgtable.h>
11 #include <asm/setup.h>
12 
13 #define CSKY_TLB_SIZE CONFIG_CPU_TLB_SIZE
14 
15 void flush_tlb_all(void)
16 {
17 	tlb_invalid_all();
18 }
19 
20 void flush_tlb_mm(struct mm_struct *mm)
21 {
22 	int cpu = smp_processor_id();
23 
24 	if (cpu_context(cpu, mm) != 0)
25 		drop_mmu_context(mm, cpu);
26 
27 	tlb_invalid_all();
28 }
29 
30 #define restore_asid_inv_utlb(oldpid, newpid) \
31 do { \
32 	if ((oldpid & ASID_MASK) == newpid) \
33 		write_mmu_entryhi(oldpid + 1); \
34 	write_mmu_entryhi(oldpid); \
35 } while (0)
36 
37 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
38 			   unsigned long end)
39 {
40 	struct mm_struct *mm = vma->vm_mm;
41 	int cpu = smp_processor_id();
42 
43 	if (cpu_context(cpu, mm) != 0) {
44 		unsigned long size, flags;
45 		int newpid = cpu_asid(cpu, mm);
46 
47 		local_irq_save(flags);
48 		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
49 		size = (size + 1) >> 1;
50 		if (size <= CSKY_TLB_SIZE/2) {
51 			start &= (PAGE_MASK << 1);
52 			end += ((PAGE_SIZE << 1) - 1);
53 			end &= (PAGE_MASK << 1);
54 #ifdef CONFIG_CPU_HAS_TLBI
55 			while (start < end) {
56 				asm volatile("tlbi.vaas %0"
57 					     ::"r"(start | newpid));
58 				start += (PAGE_SIZE << 1);
59 			}
60 			sync_is();
61 #else
62 			{
63 			int oldpid = read_mmu_entryhi();
64 
65 			while (start < end) {
66 				int idx;
67 
68 				write_mmu_entryhi(start | newpid);
69 				start += (PAGE_SIZE << 1);
70 				tlb_probe();
71 				idx = read_mmu_index();
72 				if (idx >= 0)
73 					tlb_invalid_indexed();
74 			}
75 			restore_asid_inv_utlb(oldpid, newpid);
76 			}
77 #endif
78 		} else {
79 			drop_mmu_context(mm, cpu);
80 		}
81 		local_irq_restore(flags);
82 	}
83 }
84 
85 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
86 {
87 	unsigned long size, flags;
88 
89 	local_irq_save(flags);
90 	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
91 	if (size <= CSKY_TLB_SIZE) {
92 		start &= (PAGE_MASK << 1);
93 		end += ((PAGE_SIZE << 1) - 1);
94 		end &= (PAGE_MASK << 1);
95 #ifdef CONFIG_CPU_HAS_TLBI
96 		while (start < end) {
97 			asm volatile("tlbi.vaas %0"::"r"(start));
98 			start += (PAGE_SIZE << 1);
99 		}
100 		sync_is();
101 #else
102 		{
103 		int oldpid = read_mmu_entryhi();
104 
105 		while (start < end) {
106 			int idx;
107 
108 			write_mmu_entryhi(start);
109 			start += (PAGE_SIZE << 1);
110 			tlb_probe();
111 			idx = read_mmu_index();
112 			if (idx >= 0)
113 				tlb_invalid_indexed();
114 		}
115 		restore_asid_inv_utlb(oldpid, 0);
116 		}
117 #endif
118 	} else {
119 		flush_tlb_all();
120 	}
121 
122 	local_irq_restore(flags);
123 }
124 
125 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
126 {
127 	int cpu = smp_processor_id();
128 	int newpid = cpu_asid(cpu, vma->vm_mm);
129 
130 	if (!vma || cpu_context(cpu, vma->vm_mm) != 0) {
131 		page &= (PAGE_MASK << 1);
132 
133 #ifdef CONFIG_CPU_HAS_TLBI
134 		asm volatile("tlbi.vaas %0"::"r"(page | newpid));
135 		sync_is();
136 #else
137 		{
138 		int oldpid, idx;
139 		unsigned long flags;
140 
141 		local_irq_save(flags);
142 		oldpid = read_mmu_entryhi();
143 		write_mmu_entryhi(page | newpid);
144 		tlb_probe();
145 		idx = read_mmu_index();
146 		if (idx >= 0)
147 			tlb_invalid_indexed();
148 
149 		restore_asid_inv_utlb(oldpid, newpid);
150 		local_irq_restore(flags);
151 		}
152 #endif
153 	}
154 }
155 
156 /*
157  * Remove one kernel space TLB entry.  This entry is assumed to be marked
158  * global so we don't do the ASID thing.
159  */
160 void flush_tlb_one(unsigned long page)
161 {
162 	int oldpid;
163 
164 	oldpid = read_mmu_entryhi();
165 	page &= (PAGE_MASK << 1);
166 
167 #ifdef CONFIG_CPU_HAS_TLBI
168 	page = page | (oldpid & 0xfff);
169 	asm volatile("tlbi.vaas %0"::"r"(page));
170 	sync_is();
171 #else
172 	{
173 	int idx;
174 	unsigned long flags;
175 
176 	page = page | (oldpid & 0xff);
177 
178 	local_irq_save(flags);
179 	write_mmu_entryhi(page);
180 	tlb_probe();
181 	idx = read_mmu_index();
182 	if (idx >= 0)
183 		tlb_invalid_indexed();
184 	restore_asid_inv_utlb(oldpid, oldpid);
185 	local_irq_restore(flags);
186 	}
187 #endif
188 }
189 EXPORT_SYMBOL(flush_tlb_one);
190 
191 /* show current 32 jtlbs */
192 void show_jtlb_table(void)
193 {
194 	unsigned long flags;
195 	int entryhi, entrylo0, entrylo1;
196 	int entry;
197 	int oldpid;
198 
199 	local_irq_save(flags);
200 	entry = 0;
201 	pr_info("\n\n\n");
202 
203 	oldpid = read_mmu_entryhi();
204 	while (entry < CSKY_TLB_SIZE) {
205 		write_mmu_index(entry);
206 		tlb_read();
207 		entryhi = read_mmu_entryhi();
208 		entrylo0 = read_mmu_entrylo0();
209 		entrylo0 = entrylo0;
210 		entrylo1 = read_mmu_entrylo1();
211 		entrylo1 = entrylo1;
212 		pr_info("jtlb[%d]:	entryhi - 0x%x;	entrylo0 - 0x%x;"
213 			"	entrylo1 - 0x%x\n",
214 			entry, entryhi, entrylo0, entrylo1);
215 		entry++;
216 	}
217 	write_mmu_entryhi(oldpid);
218 	local_irq_restore(flags);
219 }
220