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
;
21 local_irq_save(flags
);
22 mmucr_save
= sysreg_read(MMUCR
);
23 tlbehi_save
= sysreg_read(TLBEHI
);
24 mmucr
= mmucr_save
& 0x13;
26 sysreg_write(MMUCR
, mmucr
);
28 asm volatile("tlbr" : : : "memory");
31 tlbehi
= sysreg_read(TLBEHI
);
32 tlbelo
= sysreg_read(TLBELO
);
34 printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
36 (tlbehi
& 0x200)?'1':'0',
37 (tlbelo
& 0x100)?'1':'0',
39 (tlbehi
>> 12), (tlbelo
>> 12),
40 (tlbelo
>> 4) & 7, (tlbelo
>> 2) & 3,
41 (tlbelo
& 0x200)?'1':'0',
42 (tlbelo
& 0x080)?'1':'0',
43 (tlbelo
& 0x001)?'1':'0',
44 (tlbelo
& 0x002)?'1':'0');
46 sysreg_write(MMUCR
, mmucr_save
);
47 sysreg_write(TLBEHI
, tlbehi_save
);
49 local_irq_restore(flags
);
56 printk("ID V G ASID VPN PFN AP SZ C B W D\n");
57 for (i
= 0; i
< 32; i
++)
61 static unsigned long last_mmucr
;
63 static inline void set_replacement_pointer(unsigned shift
)
65 unsigned long mmucr
, mmucr_save
;
67 mmucr
= mmucr_save
= sysreg_read(MMUCR
);
69 /* Does this mapping already exist? */
76 if (mmucr
& SYSREG_BIT(MMUCR_N
)) {
77 /* Not found -- pick a not-recently-accessed entry */
79 unsigned long tlbar
= sysreg_read(TLBARLO
);
84 sysreg_write(TLBARLO
, -1L);
88 mmucr
|= (rp
<< shift
);
90 sysreg_write(MMUCR
, mmucr
);
96 static void update_dtlb(unsigned long address
, pte_t pte
, unsigned long asid
)
100 vpn
= (address
& MMU_VPN_MASK
) | _TLBEHI_VALID
| asid
;
101 sysreg_write(TLBEHI
, vpn
);
104 set_replacement_pointer(14);
106 sysreg_write(TLBELO
, pte_val(pte
) & _PAGE_FLAGS_HARDWARE_MASK
);
109 asm volatile("nop\n\ttlbw" : : : "memory");
113 void update_mmu_cache(struct vm_area_struct
*vma
,
114 unsigned long address
, pte_t pte
)
118 /* ptrace may call this routine */
119 if (vma
&& current
->active_mm
!= vma
->vm_mm
)
122 local_irq_save(flags
);
123 update_dtlb(address
, pte
, get_asid());
124 local_irq_restore(flags
);
127 void __flush_tlb_page(unsigned long asid
, unsigned long page
)
129 unsigned long mmucr
, tlbehi
;
132 sysreg_write(TLBEHI
, page
);
134 asm volatile("tlbs");
135 mmucr
= sysreg_read(MMUCR
);
137 if (!(mmucr
& SYSREG_BIT(MMUCR_N
))) {
138 unsigned long tlbarlo
;
141 /* Clear the "valid" bit */
142 tlbehi
= sysreg_read(TLBEHI
);
143 tlbehi
&= ~_TLBEHI_VALID
;
144 sysreg_write(TLBEHI
, tlbehi
);
147 /* mark the entry as "not accessed" */
148 entry
= (mmucr
>> 14) & 0x3f;
149 tlbarlo
= sysreg_read(TLBARLO
);
150 tlbarlo
|= (0x80000000 >> entry
);
151 sysreg_write(TLBARLO
, tlbarlo
);
153 /* update the entry with valid bit clear */
154 asm volatile("tlbw");
159 void flush_tlb_page(struct vm_area_struct
*vma
, unsigned long page
)
161 if (vma
->vm_mm
&& vma
->vm_mm
->context
!= NO_CONTEXT
) {
162 unsigned long flags
, asid
;
163 unsigned long saved_asid
= MMU_NO_ASID
;
165 asid
= vma
->vm_mm
->context
& MMU_CONTEXT_ASID_MASK
;
168 local_irq_save(flags
);
169 if (vma
->vm_mm
!= current
->mm
) {
170 saved_asid
= get_asid();
174 __flush_tlb_page(asid
, page
);
176 if (saved_asid
!= MMU_NO_ASID
)
177 set_asid(saved_asid
);
178 local_irq_restore(flags
);
182 void flush_tlb_range(struct vm_area_struct
*vma
, unsigned long start
,
185 struct mm_struct
*mm
= vma
->vm_mm
;
187 if (mm
->context
!= NO_CONTEXT
) {
191 local_irq_save(flags
);
192 size
= (end
- start
+ (PAGE_SIZE
- 1)) >> PAGE_SHIFT
;
193 if (size
> (MMU_DTLB_ENTRIES
/ 4)) { /* Too many entries to flush */
194 mm
->context
= NO_CONTEXT
;
195 if (mm
== current
->mm
)
196 activate_context(mm
);
198 unsigned long asid
= mm
->context
& MMU_CONTEXT_ASID_MASK
;
199 unsigned long saved_asid
= MMU_NO_ASID
;
202 end
+= (PAGE_SIZE
- 1);
204 if (mm
!= current
->mm
) {
205 saved_asid
= get_asid();
209 while (start
< end
) {
210 __flush_tlb_page(asid
, start
);
213 if (saved_asid
!= MMU_NO_ASID
)
214 set_asid(saved_asid
);
216 local_irq_restore(flags
);
221 * TODO: If this is only called for addresses > TASK_SIZE, we can probably
222 * skip the ASID stuff and just use the Global bit...
224 void flush_tlb_kernel_range(unsigned long start
, unsigned long end
)
229 local_irq_save(flags
);
230 size
= (end
- start
+ (PAGE_SIZE
- 1)) >> PAGE_SHIFT
;
231 if (size
> (MMU_DTLB_ENTRIES
/ 4)) { /* Too many entries to flush */
234 unsigned long asid
= init_mm
.context
& MMU_CONTEXT_ASID_MASK
;
235 unsigned long saved_asid
= get_asid();
238 end
+= (PAGE_SIZE
- 1);
241 while (start
< end
) {
242 __flush_tlb_page(asid
, start
);
245 set_asid(saved_asid
);
247 local_irq_restore(flags
);
250 void flush_tlb_mm(struct mm_struct
*mm
)
252 /* Invalidate all TLB entries of this process by getting a new ASID */
253 if (mm
->context
!= NO_CONTEXT
) {
256 local_irq_save(flags
);
257 mm
->context
= NO_CONTEXT
;
258 if (mm
== current
->mm
)
259 activate_context(mm
);
260 local_irq_restore(flags
);
264 void flush_tlb_all(void)
268 local_irq_save(flags
);
269 sysreg_write(MMUCR
, sysreg_read(MMUCR
) | SYSREG_BIT(MMUCR_I
));
270 local_irq_restore(flags
);
273 #ifdef CONFIG_PROC_FS
275 #include <linux/seq_file.h>
276 #include <linux/proc_fs.h>
277 #include <linux/init.h>
279 static void *tlb_start(struct seq_file
*tlb
, loff_t
*pos
)
281 static unsigned long tlb_index
;
290 static void *tlb_next(struct seq_file
*tlb
, void *v
, loff_t
*pos
)
292 unsigned long *index
= v
;
302 static void tlb_stop(struct seq_file
*tlb
, void *v
)
307 static int tlb_show(struct seq_file
*tlb
, void *v
)
309 unsigned int tlbehi
, tlbehi_save
, tlbelo
, mmucr
, mmucr_save
;
311 unsigned long *index
= v
;
314 seq_puts(tlb
, "ID V G ASID VPN PFN AP SZ C B W D\n");
316 BUG_ON(*index
>= 32);
318 local_irq_save(flags
);
319 mmucr_save
= sysreg_read(MMUCR
);
320 tlbehi_save
= sysreg_read(TLBEHI
);
321 mmucr
= mmucr_save
& 0x13;
322 mmucr
|= *index
<< 14;
323 sysreg_write(MMUCR
, mmucr
);
325 asm volatile("tlbr" : : : "memory");
328 tlbehi
= sysreg_read(TLBEHI
);
329 tlbelo
= sysreg_read(TLBELO
);
331 sysreg_write(MMUCR
, mmucr_save
);
332 sysreg_write(TLBEHI
, tlbehi_save
);
334 local_irq_restore(flags
);
336 seq_printf(tlb
, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
338 (tlbehi
& 0x200)?'1':'0',
339 (tlbelo
& 0x100)?'1':'0',
341 (tlbehi
>> 12), (tlbelo
>> 12),
342 (tlbelo
>> 4) & 7, (tlbelo
>> 2) & 3,
343 (tlbelo
& 0x200)?'1':'0',
344 (tlbelo
& 0x080)?'1':'0',
345 (tlbelo
& 0x001)?'1':'0',
346 (tlbelo
& 0x002)?'1':'0');
351 static struct seq_operations tlb_ops
= {
358 static int tlb_open(struct inode
*inode
, struct file
*file
)
360 return seq_open(file
, &tlb_ops
);
363 static const struct file_operations proc_tlb_operations
= {
367 .release
= seq_release
,
370 static int __init
proctlb_init(void)
372 struct proc_dir_entry
*entry
;
374 entry
= create_proc_entry("tlb", 0, NULL
);
376 entry
->proc_fops
= &proc_tlb_operations
;
379 late_initcall(proctlb_init
);
380 #endif /* CONFIG_PROC_FS */