]> asedeno.scripts.mit.edu Git - linux.git/blob - arch/nios2/mm/tlb.c
Merge tag 'gvt-fixes-2020-02-26' of https://github.com/intel/gvt-linux into drm-intel...
[linux.git] / arch / nios2 / mm / tlb.c
1 /*
2  * Nios2 TLB handling
3  *
4  * Copyright (C) 2009, Wind River Systems Inc
5  *   Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
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
12 #include <linux/init.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <linux/pagemap.h>
16
17 #include <asm/tlb.h>
18 #include <asm/mmu_context.h>
19 #include <asm/pgtable.h>
20 #include <asm/cpuinfo.h>
21
22 #define TLB_INDEX_MASK          \
23         ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \
24                 << PAGE_SHIFT)
25
26 static void get_misc_and_pid(unsigned long *misc, unsigned long *pid)
27 {
28         *misc  = RDCTL(CTL_TLBMISC);
29         *misc &= (TLBMISC_PID | TLBMISC_WAY);
30         *pid  = *misc & TLBMISC_PID;
31 }
32
33 /*
34  * This provides a PTEADDR value for addr that will cause a TLB miss
35  * (fast TLB miss). TLB invalidation replaces entries with this value.
36  */
37 static unsigned long pteaddr_invalid(unsigned long addr)
38 {
39         return ((addr | 0xC0000000UL) >> PAGE_SHIFT) << 2;
40 }
41
42 /*
43  * This one is only used for pages with the global bit set so we don't care
44  * much about the ASID.
45  */
46 static void replace_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, unsigned long tlbacc)
47 {
48         unsigned int way;
49         unsigned long org_misc, pid_misc;
50
51         /* remember pid/way until we return. */
52         get_misc_and_pid(&org_misc, &pid_misc);
53
54         WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
55
56         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
57                 unsigned long pteaddr;
58                 unsigned long tlbmisc;
59                 unsigned long pid;
60
61                 tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
62                 WRCTL(CTL_TLBMISC, tlbmisc);
63
64                 pteaddr = RDCTL(CTL_PTEADDR);
65                 if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
66                         continue;
67
68                 tlbmisc = RDCTL(CTL_TLBMISC);
69                 pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
70                 if (pid != mmu_pid)
71                         continue;
72
73                 tlbmisc = (mmu_pid << TLBMISC_PID_SHIFT) | TLBMISC_WE |
74                           (way << TLBMISC_WAY_SHIFT);
75                 WRCTL(CTL_TLBMISC, tlbmisc);
76                 if (tlbacc == 0)
77                         WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
78                 WRCTL(CTL_TLBACC, tlbacc);
79                 /*
80                  * There should be only a single entry that maps a
81                  * particular {address,pid} so break after a match.
82                  */
83                 break;
84         }
85
86         WRCTL(CTL_TLBMISC, org_misc);
87 }
88
89 static void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid)
90 {
91         pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
92
93         replace_tlb_one_pid(addr, mmu_pid, 0);
94 }
95
96 static void reload_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, pte_t pte)
97 {
98         pr_debug("Reload tlb-entry for vaddr=%#lx\n", addr);
99
100         replace_tlb_one_pid(addr, mmu_pid, pte_val(pte));
101 }
102
103 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
104                         unsigned long end)
105 {
106         unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
107
108         while (start < end) {
109                 flush_tlb_one_pid(start, mmu_pid);
110                 start += PAGE_SIZE;
111         }
112 }
113
114 void reload_tlb_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
115 {
116         unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
117
118         reload_tlb_one_pid(addr, mmu_pid, pte);
119 }
120
121 /*
122  * This one is only used for pages with the global bit set so we don't care
123  * much about the ASID.
124  */
125 static void flush_tlb_one(unsigned long addr)
126 {
127         unsigned int way;
128         unsigned long org_misc, pid_misc;
129
130         pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
131
132         /* remember pid/way until we return. */
133         get_misc_and_pid(&org_misc, &pid_misc);
134
135         WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
136
137         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
138                 unsigned long pteaddr;
139                 unsigned long tlbmisc;
140
141                 tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
142                 WRCTL(CTL_TLBMISC, tlbmisc);
143
144                 pteaddr = RDCTL(CTL_PTEADDR);
145                 if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
146                         continue;
147
148                 pr_debug("Flush entry by writing way=%dl pid=%ld\n",
149                          way, (pid_misc >> TLBMISC_PID_SHIFT));
150
151                 tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT);
152                 WRCTL(CTL_TLBMISC, tlbmisc);
153                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
154                 WRCTL(CTL_TLBACC, 0);
155         }
156
157         WRCTL(CTL_TLBMISC, org_misc);
158 }
159
160 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
161 {
162         while (start < end) {
163                 flush_tlb_one(start);
164                 start += PAGE_SIZE;
165         }
166 }
167
168 void dump_tlb_line(unsigned long line)
169 {
170         unsigned int way;
171         unsigned long org_misc;
172
173         pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line,
174                 line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2));
175
176         /* remember pid/way until we return */
177         org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY));
178
179         WRCTL(CTL_PTEADDR, line << 2);
180
181         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
182                 unsigned long pteaddr;
183                 unsigned long tlbmisc;
184                 unsigned long tlbacc;
185
186                 WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT));
187                 pteaddr = RDCTL(CTL_PTEADDR);
188                 tlbmisc = RDCTL(CTL_TLBMISC);
189                 tlbacc = RDCTL(CTL_TLBACC);
190
191                 if ((tlbacc << PAGE_SHIFT) != 0) {
192                         pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n",
193                                 way,
194                                 (pteaddr << (PAGE_SHIFT-2)),
195                                 (tlbacc << PAGE_SHIFT),
196                                 ((tlbmisc >> TLBMISC_PID_SHIFT) &
197                                 TLBMISC_PID_MASK),
198                                 (tlbacc & _PAGE_READ ? 'r' : '-'),
199                                 (tlbacc & _PAGE_WRITE ? 'w' : '-'),
200                                 (tlbacc & _PAGE_EXEC ? 'x' : '-'),
201                                 (tlbacc & _PAGE_GLOBAL ? 'g' : '-'),
202                                 (tlbacc & _PAGE_CACHED ? 'c' : '-'));
203                 }
204         }
205
206         WRCTL(CTL_TLBMISC, org_misc);
207 }
208
209 void dump_tlb(void)
210 {
211         unsigned int i;
212
213         for (i = 0; i < cpuinfo.tlb_num_lines; i++)
214                 dump_tlb_line(i);
215 }
216
217 void flush_tlb_pid(unsigned long mmu_pid)
218 {
219         unsigned long addr = 0;
220         unsigned int line;
221         unsigned int way;
222         unsigned long org_misc, pid_misc;
223
224         /* remember pid/way until we return */
225         get_misc_and_pid(&org_misc, &pid_misc);
226
227         for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
228                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
229
230                 for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
231                         unsigned long tlbmisc;
232                         unsigned long pid;
233
234                         tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
235                         WRCTL(CTL_TLBMISC, tlbmisc);
236                         tlbmisc = RDCTL(CTL_TLBMISC);
237                         pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
238                         if (pid != mmu_pid)
239                                 continue;
240
241                         tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT);
242                         WRCTL(CTL_TLBMISC, tlbmisc);
243                         WRCTL(CTL_TLBACC, 0);
244                 }
245
246                 addr += PAGE_SIZE;
247         }
248
249         WRCTL(CTL_TLBMISC, org_misc);
250 }
251
252 /*
253  * All entries common to a mm share an asid.  To effectively flush these
254  * entries, we just bump the asid.
255  */
256 void flush_tlb_mm(struct mm_struct *mm)
257 {
258         if (current->mm == mm) {
259                 unsigned long mmu_pid = get_pid_from_context(&mm->context);
260                 flush_tlb_pid(mmu_pid);
261         } else {
262                 memset(&mm->context, 0, sizeof(mm_context_t));
263         }
264 }
265
266 void flush_tlb_all(void)
267 {
268         unsigned long addr = 0;
269         unsigned int line;
270         unsigned int way;
271         unsigned long org_misc, pid_misc;
272
273         /* remember pid/way until we return */
274         get_misc_and_pid(&org_misc, &pid_misc);
275
276         /* Start at way 0, way is auto-incremented after each TLBACC write */
277         WRCTL(CTL_TLBMISC, TLBMISC_WE);
278
279         /* Map each TLB entry to physcal address 0 with no-access and a
280            bad ptbase */
281         for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
282                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
283                 for (way = 0; way < cpuinfo.tlb_num_ways; way++)
284                         WRCTL(CTL_TLBACC, 0);
285
286                 addr += PAGE_SIZE;
287         }
288
289         /* restore pid/way */
290         WRCTL(CTL_TLBMISC, org_misc);
291 }
292
293 void set_mmu_pid(unsigned long pid)
294 {
295         unsigned long tlbmisc;
296
297         tlbmisc = RDCTL(CTL_TLBMISC);
298         tlbmisc = (tlbmisc & TLBMISC_WAY);
299         tlbmisc |= (pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT;
300         WRCTL(CTL_TLBMISC, tlbmisc);
301 }