4 * Copyright (C) 2004-2006 Atmel Corporation
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.
12 #include <asm/mmu_context.h>
14 #define _TLBEHI_I 0x100
16 void show_dtlb_entry(unsigned int index)
18 unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
20 local_irq_save(flags);
21 mmucr_save = sysreg_read(MMUCR);
22 tlbehi_save = sysreg_read(TLBEHI);
23 mmucr = mmucr_save & 0x13;
25 sysreg_write(MMUCR, mmucr);
27 asm volatile("tlbr" : : : "memory");
30 tlbehi = sysreg_read(TLBEHI);
31 tlbelo = sysreg_read(TLBELO);
33 printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
35 (tlbehi & 0x200)?'1':'0',
36 (tlbelo & 0x100)?'1':'0',
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');
45 sysreg_write(MMUCR, mmucr_save);
46 sysreg_write(TLBEHI, tlbehi_save);
48 local_irq_restore(flags);
55 printk("ID V G ASID VPN PFN AP SZ C B W D\n");
56 for (i = 0; i < 32; i++)
60 static unsigned long last_mmucr;
62 static inline void set_replacement_pointer(unsigned shift)
64 unsigned long mmucr, mmucr_save;
66 mmucr = mmucr_save = sysreg_read(MMUCR);
68 /* Does this mapping already exist? */
75 if (mmucr & SYSREG_BIT(MMUCR_N)) {
76 /* Not found -- pick a not-recently-accessed entry */
78 unsigned long tlbar = sysreg_read(TLBARLO);
83 sysreg_write(TLBARLO, -1L);
87 mmucr |= (rp << shift);
89 sysreg_write(MMUCR, mmucr);
95 static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
99 vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
100 sysreg_write(TLBEHI, vpn);
103 set_replacement_pointer(14);
105 sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
108 asm volatile("nop\n\ttlbw" : : : "memory");
112 void update_mmu_cache(struct vm_area_struct *vma,
113 unsigned long address, pte_t pte)
117 /* ptrace may call this routine */
118 if (vma && current->active_mm != vma->vm_mm)
121 local_irq_save(flags);
122 update_dtlb(address, pte, get_asid());
123 local_irq_restore(flags);
126 void __flush_tlb_page(unsigned long asid, unsigned long page)
128 unsigned long mmucr, tlbehi;
131 sysreg_write(TLBEHI, page);
133 asm volatile("tlbs");
134 mmucr = sysreg_read(MMUCR);
136 if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
137 unsigned long tlbarlo;
140 /* Clear the "valid" bit */
141 tlbehi = sysreg_read(TLBEHI);
142 tlbehi &= ~_TLBEHI_VALID;
143 sysreg_write(TLBEHI, tlbehi);
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);
152 /* update the entry with valid bit clear */
153 asm volatile("tlbw");
158 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
160 if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
161 unsigned long flags, asid;
162 unsigned long saved_asid = MMU_NO_ASID;
164 asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
167 local_irq_save(flags);
168 if (vma->vm_mm != current->mm) {
169 saved_asid = get_asid();
173 __flush_tlb_page(asid, page);
175 if (saved_asid != MMU_NO_ASID)
176 set_asid(saved_asid);
177 local_irq_restore(flags);
181 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
184 struct mm_struct *mm = vma->vm_mm;
186 if (mm->context != NO_CONTEXT) {
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);
197 unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
198 unsigned long saved_asid = MMU_NO_ASID;
201 end += (PAGE_SIZE - 1);
203 if (mm != current->mm) {
204 saved_asid = get_asid();
208 while (start < end) {
209 __flush_tlb_page(asid, start);
212 if (saved_asid != MMU_NO_ASID)
213 set_asid(saved_asid);
215 local_irq_restore(flags);
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...
223 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
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 */
233 unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
234 unsigned long saved_asid = get_asid();
237 end += (PAGE_SIZE - 1);
240 while (start < end) {
241 __flush_tlb_page(asid, start);
244 set_asid(saved_asid);
246 local_irq_restore(flags);
249 void flush_tlb_mm(struct mm_struct *mm)
251 /* Invalidate all TLB entries of this process by getting a new ASID */
252 if (mm->context != NO_CONTEXT) {
255 local_irq_save(flags);
256 mm->context = NO_CONTEXT;
257 if (mm == current->mm)
258 activate_context(mm);
259 local_irq_restore(flags);
263 void flush_tlb_all(void)
267 local_irq_save(flags);
268 sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
269 local_irq_restore(flags);
272 #ifdef CONFIG_PROC_FS
274 #include <linux/seq_file.h>
275 #include <linux/proc_fs.h>
276 #include <linux/init.h>
278 static void *tlb_start(struct seq_file *tlb, loff_t *pos)
280 static unsigned long tlb_index;
289 static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
291 unsigned long *index = v;
301 static void tlb_stop(struct seq_file *tlb, void *v)
306 static int tlb_show(struct seq_file *tlb, void *v)
308 unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
309 unsigned long *index = v;
312 seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
314 BUG_ON(*index >= 32);
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);
323 asm volatile("tlbr" : : : "memory");
326 tlbehi = sysreg_read(TLBEHI);
327 tlbelo = sysreg_read(TLBELO);
329 sysreg_write(MMUCR, mmucr_save);
330 sysreg_write(TLBEHI, tlbehi_save);
332 local_irq_restore(flags);
334 seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
336 (tlbehi & 0x200)?'1':'0',
337 (tlbelo & 0x100)?'1':'0',
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');
349 static struct seq_operations tlb_ops = {
356 static int tlb_open(struct inode *inode, struct file *file)
358 return seq_open(file, &tlb_ops);
361 static struct file_operations proc_tlb_operations = {
365 .release = seq_release,
368 static int __init proctlb_init(void)
370 struct proc_dir_entry *entry;
372 entry = create_proc_entry("tlb", 0, NULL);
374 entry->proc_fops = &proc_tlb_operations;
377 late_initcall(proctlb_init);
378 #endif /* CONFIG_PROC_FS */