aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32/mm/tlb.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/avr32/mm/tlb.c')
-rw-r--r--arch/avr32/mm/tlb.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/arch/avr32/mm/tlb.c b/arch/avr32/mm/tlb.c
new file mode 100644
index 000000000000..5d0523bbe298
--- /dev/null
+++ b/arch/avr32/mm/tlb.c
@@ -0,0 +1,378 @@
1/*
2 * AVR32 TLB operations
3 *
4 * Copyright (C) 2004-2006 Atmel Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/mm.h>
11
12#include <asm/mmu_context.h>
13
14#define _TLBEHI_I 0x100
15
16void show_dtlb_entry(unsigned int index)
17{
18 unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
19
20 local_irq_save(flags);
21 mmucr_save = sysreg_read(MMUCR);
22 tlbehi_save = sysreg_read(TLBEHI);
23 mmucr = mmucr_save & 0x13;
24 mmucr |= index << 14;
25 sysreg_write(MMUCR, mmucr);
26
27 asm volatile("tlbr" : : : "memory");
28 cpu_sync_pipeline();
29
30 tlbehi = sysreg_read(TLBEHI);
31 tlbelo = sysreg_read(TLBELO);
32
33 printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
34 index,
35 (tlbehi & 0x200)?'1':'0',
36 (tlbelo & 0x100)?'1':'0',
37 (tlbehi & 0xff),
38 (tlbehi >> 12), (tlbelo >> 12),
39 (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
40 (tlbelo & 0x200)?'1':'0',
41 (tlbelo & 0x080)?'1':'0',
42 (tlbelo & 0x001)?'1':'0',
43 (tlbelo & 0x002)?'1':'0');
44
45 sysreg_write(MMUCR, mmucr_save);
46 sysreg_write(TLBEHI, tlbehi_save);
47 cpu_sync_pipeline();
48 local_irq_restore(flags);
49}
50
51void dump_dtlb(void)
52{
53 unsigned int i;
54
55 printk("ID V G ASID VPN PFN AP SZ C B W D\n");
56 for (i = 0; i < 32; i++)
57 show_dtlb_entry(i);
58}
59
60static unsigned long last_mmucr;
61
62static inline void set_replacement_pointer(unsigned shift)
63{
64 unsigned long mmucr, mmucr_save;
65
66 mmucr = mmucr_save = sysreg_read(MMUCR);
67
68 /* Does this mapping already exist? */
69 __asm__ __volatile__(
70 " tlbs\n"
71 " mfsr %0, %1"
72 : "=r"(mmucr)
73 : "i"(SYSREG_MMUCR));
74
75 if (mmucr & SYSREG_BIT(MMUCR_N)) {
76 /* Not found -- pick a not-recently-accessed entry */
77 unsigned long rp;
78 unsigned long tlbar = sysreg_read(TLBARLO);
79
80 rp = 32 - fls(tlbar);
81 if (rp == 32) {
82 rp = 0;
83 sysreg_write(TLBARLO, -1L);
84 }
85
86 mmucr &= 0x13;
87 mmucr |= (rp << shift);
88
89 sysreg_write(MMUCR, mmucr);
90 }
91
92 last_mmucr = mmucr;
93}
94
95static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
96{
97 unsigned long vpn;
98
99 vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
100 sysreg_write(TLBEHI, vpn);
101 cpu_sync_pipeline();
102
103 set_replacement_pointer(14);
104
105 sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
106
107 /* Let's go */
108 asm volatile("nop\n\ttlbw" : : : "memory");
109 cpu_sync_pipeline();
110}
111
112void update_mmu_cache(struct vm_area_struct *vma,
113 unsigned long address, pte_t pte)
114{
115 unsigned long flags;
116
117 /* ptrace may call this routine */
118 if (vma && current->active_mm != vma->vm_mm)
119 return;
120
121 local_irq_save(flags);
122 update_dtlb(address, pte, get_asid());
123 local_irq_restore(flags);
124}
125
126void __flush_tlb_page(unsigned long asid, unsigned long page)
127{
128 unsigned long mmucr, tlbehi;
129
130 page |= asid;
131 sysreg_write(TLBEHI, page);
132 cpu_sync_pipeline();
133 asm volatile("tlbs");
134 mmucr = sysreg_read(MMUCR);
135
136 if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
137 unsigned long tlbarlo;
138 unsigned long entry;
139
140 /* Clear the "valid" bit */
141 tlbehi = sysreg_read(TLBEHI);
142 tlbehi &= ~_TLBEHI_VALID;
143 sysreg_write(TLBEHI, tlbehi);
144 cpu_sync_pipeline();
145
146 /* mark the entry as "not accessed" */
147 entry = (mmucr >> 14) & 0x3f;
148 tlbarlo = sysreg_read(TLBARLO);
149 tlbarlo |= (0x80000000 >> entry);
150 sysreg_write(TLBARLO, tlbarlo);
151
152 /* update the entry with valid bit clear */
153 asm volatile("tlbw");
154 cpu_sync_pipeline();
155 }
156}
157
158void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
159{
160 if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
161 unsigned long flags, asid;
162 unsigned long saved_asid = MMU_NO_ASID;
163
164 asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
165 page &= PAGE_MASK;
166
167 local_irq_save(flags);
168 if (vma->vm_mm != current->mm) {
169 saved_asid = get_asid();
170 set_asid(asid);
171 }
172
173 __flush_tlb_page(asid, page);
174
175 if (saved_asid != MMU_NO_ASID)
176 set_asid(saved_asid);
177 local_irq_restore(flags);
178 }
179}
180
181void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
182 unsigned long end)
183{
184 struct mm_struct *mm = vma->vm_mm;
185
186 if (mm->context != NO_CONTEXT) {
187 unsigned long flags;
188 int size;
189
190 local_irq_save(flags);
191 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
192 if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
193 mm->context = NO_CONTEXT;
194 if (mm == current->mm)
195 activate_context(mm);
196 } else {
197 unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
198 unsigned long saved_asid = MMU_NO_ASID;
199
200 start &= PAGE_MASK;
201 end += (PAGE_SIZE - 1);
202 end &= PAGE_MASK;
203 if (mm != current->mm) {
204 saved_asid = get_asid();
205 set_asid(asid);
206 }
207
208 while (start < end) {
209 __flush_tlb_page(asid, start);
210 start += PAGE_SIZE;
211 }
212 if (saved_asid != MMU_NO_ASID)
213 set_asid(saved_asid);
214 }
215 local_irq_restore(flags);
216 }
217}
218
219/*
220 * TODO: If this is only called for addresses > TASK_SIZE, we can probably
221 * skip the ASID stuff and just use the Global bit...
222 */
223void flush_tlb_kernel_range(unsigned long start, unsigned long end)
224{
225 unsigned long flags;
226 int size;
227
228 local_irq_save(flags);
229 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
230 if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
231 flush_tlb_all();
232 } else {
233 unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
234 unsigned long saved_asid = get_asid();
235
236 start &= PAGE_MASK;
237 end += (PAGE_SIZE - 1);
238 end &= PAGE_MASK;
239 set_asid(asid);
240 while (start < end) {
241 __flush_tlb_page(asid, start);
242 start += PAGE_SIZE;
243 }
244 set_asid(saved_asid);
245 }
246 local_irq_restore(flags);
247}
248
249void flush_tlb_mm(struct mm_struct *mm)
250{
251 /* Invalidate all TLB entries of this process by getting a new ASID */
252 if (mm->context != NO_CONTEXT) {
253 unsigned long flags;
254
255 local_irq_save(flags);
256 mm->context = NO_CONTEXT;
257 if (mm == current->mm)
258 activate_context(mm);
259 local_irq_restore(flags);
260 }
261}
262
263void flush_tlb_all(void)
264{
265 unsigned long flags;
266
267 local_irq_save(flags);
268 sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
269 local_irq_restore(flags);
270}
271
272#ifdef CONFIG_PROC_FS
273
274#include <linux/seq_file.h>
275#include <linux/proc_fs.h>
276#include <linux/init.h>
277
278static void *tlb_start(struct seq_file *tlb, loff_t *pos)
279{
280 static unsigned long tlb_index;
281
282 if (*pos >= 32)
283 return NULL;
284
285 tlb_index = 0;
286 return &tlb_index;
287}
288
289static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
290{
291 unsigned long *index = v;
292
293 if (*index >= 31)
294 return NULL;
295
296 ++*pos;
297 ++*index;
298 return index;
299}
300
301static void tlb_stop(struct seq_file *tlb, void *v)
302{
303
304}
305
306static int tlb_show(struct seq_file *tlb, void *v)
307{
308 unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
309 unsigned long *index = v;
310
311 if (*index == 0)
312 seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
313
314 BUG_ON(*index >= 32);
315
316 local_irq_save(flags);
317 mmucr_save = sysreg_read(MMUCR);
318 tlbehi_save = sysreg_read(TLBEHI);
319 mmucr = mmucr_save & 0x13;
320 mmucr |= *index << 14;
321 sysreg_write(MMUCR, mmucr);
322
323 asm volatile("tlbr" : : : "memory");
324 cpu_sync_pipeline();
325
326 tlbehi = sysreg_read(TLBEHI);
327 tlbelo = sysreg_read(TLBELO);
328
329 sysreg_write(MMUCR, mmucr_save);
330 sysreg_write(TLBEHI, tlbehi_save);
331 cpu_sync_pipeline();
332 local_irq_restore(flags);
333
334 seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
335 *index,
336 (tlbehi & 0x200)?'1':'0',
337 (tlbelo & 0x100)?'1':'0',
338 (tlbehi & 0xff),
339 (tlbehi >> 12), (tlbelo >> 12),
340 (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
341 (tlbelo & 0x200)?'1':'0',
342 (tlbelo & 0x080)?'1':'0',
343 (tlbelo & 0x001)?'1':'0',
344 (tlbelo & 0x002)?'1':'0');
345
346 return 0;
347}
348
349static struct seq_operations tlb_ops = {
350 .start = tlb_start,
351 .next = tlb_next,
352 .stop = tlb_stop,
353 .show = tlb_show,
354};
355
356static int tlb_open(struct inode *inode, struct file *file)
357{
358 return seq_open(file, &tlb_ops);
359}
360
361static struct file_operations proc_tlb_operations = {
362 .open = tlb_open,
363 .read = seq_read,
364 .llseek = seq_lseek,
365 .release = seq_release,
366};
367
368static int __init proctlb_init(void)
369{
370 struct proc_dir_entry *entry;
371
372 entry = create_proc_entry("tlb", 0, NULL);
373 if (entry)
374 entry->proc_fops = &proc_tlb_operations;
375 return 0;
376}
377late_initcall(proctlb_init);
378#endif /* CONFIG_PROC_FS */