~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/avr32/mm/tlb.c

Version: ~ [ linux-5.3-rc5 ] ~ [ linux-5.2.9 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.67 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.139 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.189 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.189 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.72 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 /* TODO: Get the correct number from the CONFIG1 system register */
 15 #define NR_TLB_ENTRIES 32
 16 
 17 static void show_dtlb_entry(unsigned int index)
 18 {
 19         u32 tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
 20         unsigned long flags;
 21 
 22         local_irq_save(flags);
 23         mmucr_save = sysreg_read(MMUCR);
 24         tlbehi_save = sysreg_read(TLBEHI);
 25         mmucr = SYSREG_BFINS(DRP, index, mmucr_save);
 26         sysreg_write(MMUCR, mmucr);
 27 
 28         __builtin_tlbr();
 29         cpu_sync_pipeline();
 30 
 31         tlbehi = sysreg_read(TLBEHI);
 32         tlbelo = sysreg_read(TLBELO);
 33 
 34         printk("%2u: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",
 35                index,
 36                SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '',
 37                SYSREG_BFEXT(G, tlbelo) ? '1' : '',
 38                SYSREG_BFEXT(ASID, tlbehi),
 39                SYSREG_BFEXT(VPN, tlbehi) >> 2,
 40                SYSREG_BFEXT(PFN, tlbelo) >> 2,
 41                SYSREG_BFEXT(AP, tlbelo),
 42                SYSREG_BFEXT(SZ, tlbelo),
 43                SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ',
 44                SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ',
 45                SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ',
 46                SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' ');
 47 
 48         sysreg_write(MMUCR, mmucr_save);
 49         sysreg_write(TLBEHI, tlbehi_save);
 50         cpu_sync_pipeline();
 51         local_irq_restore(flags);
 52 }
 53 
 54 void dump_dtlb(void)
 55 {
 56         unsigned int i;
 57 
 58         printk("ID  V G ASID VPN   PFN   AP SZ C B W D\n");
 59         for (i = 0; i < NR_TLB_ENTRIES; i++)
 60                 show_dtlb_entry(i);
 61 }
 62 
 63 static void update_dtlb(unsigned long address, pte_t pte)
 64 {
 65         u32 tlbehi;
 66         u32 mmucr;
 67 
 68         /*
 69          * We're not changing the ASID here, so no need to flush the
 70          * pipeline.
 71          */
 72         tlbehi = sysreg_read(TLBEHI);
 73         tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
 74         tlbehi |= address & MMU_VPN_MASK;
 75         tlbehi |= SYSREG_BIT(TLBEHI_V);
 76         sysreg_write(TLBEHI, tlbehi);
 77 
 78         /* Does this mapping already exist? */
 79         __builtin_tlbs();
 80         mmucr = sysreg_read(MMUCR);
 81 
 82         if (mmucr & SYSREG_BIT(MMUCR_N)) {
 83                 /* Not found -- pick a not-recently-accessed entry */
 84                 unsigned int rp;
 85                 u32 tlbar = sysreg_read(TLBARLO);
 86 
 87                 rp = 32 - fls(tlbar);
 88                 if (rp == 32) {
 89                         rp = 0;
 90                         sysreg_write(TLBARLO, -1L);
 91                 }
 92 
 93                 mmucr = SYSREG_BFINS(DRP, rp, mmucr);
 94                 sysreg_write(MMUCR, mmucr);
 95         }
 96 
 97         sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
 98 
 99         /* Let's go */
100         __builtin_tlbw();
101 }
102 
103 void update_mmu_cache(struct vm_area_struct *vma,
104                       unsigned long address, pte_t *ptep)
105 {
106         unsigned long flags;
107 
108         /* ptrace may call this routine */
109         if (vma && current->active_mm != vma->vm_mm)
110                 return;
111 
112         local_irq_save(flags);
113         update_dtlb(address, *ptep);
114         local_irq_restore(flags);
115 }
116 
117 static void __flush_tlb_page(unsigned long asid, unsigned long page)
118 {
119         u32 mmucr, tlbehi;
120 
121         /*
122          * Caller is responsible for masking out non-PFN bits in page
123          * and changing the current ASID if necessary. This means that
124          * we don't need to flush the pipeline after writing TLBEHI.
125          */
126         tlbehi = page | asid;
127         sysreg_write(TLBEHI, tlbehi);
128 
129         __builtin_tlbs();
130         mmucr = sysreg_read(MMUCR);
131 
132         if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
133                 unsigned int entry;
134                 u32 tlbarlo;
135 
136                 /* Clear the "valid" bit */
137                 sysreg_write(TLBEHI, tlbehi);
138 
139                 /* mark the entry as "not accessed" */
140                 entry = SYSREG_BFEXT(DRP, mmucr);
141                 tlbarlo = sysreg_read(TLBARLO);
142                 tlbarlo |= (0x80000000UL >> entry);
143                 sysreg_write(TLBARLO, tlbarlo);
144 
145                 /* update the entry with valid bit clear */
146                 __builtin_tlbw();
147         }
148 }
149 
150 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
151 {
152         if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
153                 unsigned long flags, asid;
154                 unsigned long saved_asid = MMU_NO_ASID;
155 
156                 asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
157                 page &= PAGE_MASK;
158 
159                 local_irq_save(flags);
160                 if (vma->vm_mm != current->mm) {
161                         saved_asid = get_asid();
162                         set_asid(asid);
163                 }
164 
165                 __flush_tlb_page(asid, page);
166 
167                 if (saved_asid != MMU_NO_ASID)
168                         set_asid(saved_asid);
169                 local_irq_restore(flags);
170         }
171 }
172 
173 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
174                      unsigned long end)
175 {
176         struct mm_struct *mm = vma->vm_mm;
177 
178         if (mm->context != NO_CONTEXT) {
179                 unsigned long flags;
180                 int size;
181 
182                 local_irq_save(flags);
183                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
184 
185                 if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
186                         mm->context = NO_CONTEXT;
187                         if (mm == current->mm)
188                                 activate_context(mm);
189                 } else {
190                         unsigned long asid;
191                         unsigned long saved_asid;
192 
193                         asid = mm->context & MMU_CONTEXT_ASID_MASK;
194                         saved_asid = MMU_NO_ASID;
195 
196                         start &= PAGE_MASK;
197                         end += (PAGE_SIZE - 1);
198                         end &= PAGE_MASK;
199 
200                         if (mm != current->mm) {
201                                 saved_asid = get_asid();
202                                 set_asid(asid);
203                         }
204 
205                         while (start < end) {
206                                 __flush_tlb_page(asid, start);
207                                 start += PAGE_SIZE;
208                         }
209                         if (saved_asid != MMU_NO_ASID)
210                                 set_asid(saved_asid);
211                 }
212                 local_irq_restore(flags);
213         }
214 }
215 
216 /*
217  * This function depends on the pages to be flushed having the G
218  * (global) bit set in their pte. This is true for all
219  * PAGE_KERNEL(_RO) pages.
220  */
221 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
222 {
223         unsigned long flags;
224         int size;
225 
226         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
227         if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
228                 flush_tlb_all();
229         } else {
230                 unsigned long asid;
231 
232                 local_irq_save(flags);
233                 asid = get_asid();
234 
235                 start &= PAGE_MASK;
236                 end += (PAGE_SIZE - 1);
237                 end &= PAGE_MASK;
238 
239                 while (start < end) {
240                         __flush_tlb_page(asid, start);
241                         start += PAGE_SIZE;
242                 }
243                 local_irq_restore(flags);
244         }
245 }
246 
247 void flush_tlb_mm(struct mm_struct *mm)
248 {
249         /* Invalidate all TLB entries of this process by getting a new ASID */
250         if (mm->context != NO_CONTEXT) {
251                 unsigned long flags;
252 
253                 local_irq_save(flags);
254                 mm->context = NO_CONTEXT;
255                 if (mm == current->mm)
256                         activate_context(mm);
257                 local_irq_restore(flags);
258         }
259 }
260 
261 void flush_tlb_all(void)
262 {
263         unsigned long flags;
264 
265         local_irq_save(flags);
266         sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
267         local_irq_restore(flags);
268 }
269 
270 #ifdef CONFIG_PROC_FS
271 
272 #include <linux/seq_file.h>
273 #include <linux/proc_fs.h>
274 #include <linux/init.h>
275 
276 static void *tlb_start(struct seq_file *tlb, loff_t *pos)
277 {
278         static unsigned long tlb_index;
279 
280         if (*pos >= NR_TLB_ENTRIES)
281                 return NULL;
282 
283         tlb_index = 0;
284         return &tlb_index;
285 }
286 
287 static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
288 {
289         unsigned long *index = v;
290 
291         if (*index >= NR_TLB_ENTRIES - 1)
292                 return NULL;
293 
294         ++*pos;
295         ++*index;
296         return index;
297 }
298 
299 static void tlb_stop(struct seq_file *tlb, void *v)
300 {
301 
302 }
303 
304 static int tlb_show(struct seq_file *tlb, void *v)
305 {
306         unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
307         unsigned long flags;
308         unsigned long *index = v;
309 
310         if (*index == 0)
311                 seq_puts(tlb, "ID  V G ASID VPN   PFN   AP SZ C B W D\n");
312 
313         BUG_ON(*index >= NR_TLB_ENTRIES);
314 
315         local_irq_save(flags);
316         mmucr_save = sysreg_read(MMUCR);
317         tlbehi_save = sysreg_read(TLBEHI);
318         mmucr = SYSREG_BFINS(DRP, *index, mmucr_save);
319         sysreg_write(MMUCR, mmucr);
320 
321         /* TLBR might change the ASID */
322         __builtin_tlbr();
323         cpu_sync_pipeline();
324 
325         tlbehi = sysreg_read(TLBEHI);
326         tlbelo = sysreg_read(TLBELO);
327 
328         sysreg_write(MMUCR, mmucr_save);
329         sysreg_write(TLBEHI, tlbehi_save);
330         cpu_sync_pipeline();
331         local_irq_restore(flags);
332 
333         seq_printf(tlb, "%2lu: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",
334                    *index,
335                    SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '',
336                    SYSREG_BFEXT(G, tlbelo) ? '1' : '',
337                    SYSREG_BFEXT(ASID, tlbehi),
338                    SYSREG_BFEXT(VPN, tlbehi) >> 2,
339                    SYSREG_BFEXT(PFN, tlbelo) >> 2,
340                    SYSREG_BFEXT(AP, tlbelo),
341                    SYSREG_BFEXT(SZ, tlbelo),
342                    SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '',
343                    SYSREG_BFEXT(B, tlbelo) ? '1' : '',
344                    SYSREG_BFEXT(W, tlbelo) ? '1' : '',
345                    SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '');
346 
347         return 0;
348 }
349 
350 static const struct seq_operations tlb_ops = {
351         .start          = tlb_start,
352         .next           = tlb_next,
353         .stop           = tlb_stop,
354         .show           = tlb_show,
355 };
356 
357 static int tlb_open(struct inode *inode, struct file *file)
358 {
359         return seq_open(file, &tlb_ops);
360 }
361 
362 static const struct file_operations proc_tlb_operations = {
363         .open           = tlb_open,
364         .read           = seq_read,
365         .llseek         = seq_lseek,
366         .release        = seq_release,
367 };
368 
369 static int __init proctlb_init(void)
370 {
371         proc_create("tlb", 0, NULL, &proc_tlb_operations);
372         return 0;
373 }
374 late_initcall(proctlb_init);
375 #endif /* CONFIG_PROC_FS */
376 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp