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 /* TODO: Get the correct number from the CONFIG1 system register */
15 #define NR_TLB_ENTRIES 32
17 static void show_dtlb_entry(unsigned int index
)
19 u32 tlbehi
, tlbehi_save
, tlbelo
, mmucr
, mmucr_save
;
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
);
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 SYSREG_BFEXT(TLBEHI_V
, tlbehi
) ? '1' : '0',
37 SYSREG_BFEXT(G
, tlbelo
) ? '1' : '0',
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' : ' ');
48 sysreg_write(MMUCR
, mmucr_save
);
49 sysreg_write(TLBEHI
, tlbehi_save
);
51 local_irq_restore(flags
);
58 printk("ID V G ASID VPN PFN AP SZ C B W D\n");
59 for (i
= 0; i
< NR_TLB_ENTRIES
; i
++)
63 static void update_dtlb(unsigned long address
, pte_t pte
)
69 * We're not changing the ASID here, so no need to flush the
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
);
78 /* Does this mapping already exist? */
80 mmucr
= sysreg_read(MMUCR
);
82 if (mmucr
& SYSREG_BIT(MMUCR_N
)) {
83 /* Not found -- pick a not-recently-accessed entry */
85 u32 tlbar
= sysreg_read(TLBARLO
);
90 sysreg_write(TLBARLO
, -1L);
93 mmucr
= SYSREG_BFINS(DRP
, rp
, mmucr
);
94 sysreg_write(MMUCR
, mmucr
);
97 sysreg_write(TLBELO
, pte_val(pte
) & _PAGE_FLAGS_HARDWARE_MASK
);
103 void update_mmu_cache(struct vm_area_struct
*vma
,
104 unsigned long address
, pte_t pte
)
108 /* ptrace may call this routine */
109 if (vma
&& current
->active_mm
!= vma
->vm_mm
)
112 local_irq_save(flags
);
113 update_dtlb(address
, pte
);
114 local_irq_restore(flags
);
117 static void __flush_tlb_page(unsigned long asid
, unsigned long page
)
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.
126 tlbehi
= page
| asid
;
127 sysreg_write(TLBEHI
, tlbehi
);
130 mmucr
= sysreg_read(MMUCR
);
132 if (!(mmucr
& SYSREG_BIT(MMUCR_N
))) {
136 /* Clear the "valid" bit */
137 sysreg_write(TLBEHI
, tlbehi
);
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
);
145 /* update the entry with valid bit clear */
150 void flush_tlb_page(struct vm_area_struct
*vma
, unsigned long page
)
152 if (vma
->vm_mm
&& vma
->vm_mm
->context
!= NO_CONTEXT
) {
153 unsigned long flags
, asid
;
154 unsigned long saved_asid
= MMU_NO_ASID
;
156 asid
= vma
->vm_mm
->context
& MMU_CONTEXT_ASID_MASK
;
159 local_irq_save(flags
);
160 if (vma
->vm_mm
!= current
->mm
) {
161 saved_asid
= get_asid();
165 __flush_tlb_page(asid
, page
);
167 if (saved_asid
!= MMU_NO_ASID
)
168 set_asid(saved_asid
);
169 local_irq_restore(flags
);
173 void flush_tlb_range(struct vm_area_struct
*vma
, unsigned long start
,
176 struct mm_struct
*mm
= vma
->vm_mm
;
178 if (mm
->context
!= NO_CONTEXT
) {
182 local_irq_save(flags
);
183 size
= (end
- start
+ (PAGE_SIZE
- 1)) >> PAGE_SHIFT
;
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
);
191 unsigned long saved_asid
;
193 asid
= mm
->context
& MMU_CONTEXT_ASID_MASK
;
194 saved_asid
= MMU_NO_ASID
;
197 end
+= (PAGE_SIZE
- 1);
200 if (mm
!= current
->mm
) {
201 saved_asid
= get_asid();
205 while (start
< end
) {
206 __flush_tlb_page(asid
, start
);
209 if (saved_asid
!= MMU_NO_ASID
)
210 set_asid(saved_asid
);
212 local_irq_restore(flags
);
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.
221 void flush_tlb_kernel_range(unsigned long start
, unsigned long end
)
226 size
= (end
- start
+ (PAGE_SIZE
- 1)) >> PAGE_SHIFT
;
227 if (size
> (MMU_DTLB_ENTRIES
/ 4)) { /* Too many entries to flush */
232 local_irq_save(flags
);
236 end
+= (PAGE_SIZE
- 1);
239 while (start
< end
) {
240 __flush_tlb_page(asid
, start
);
243 local_irq_restore(flags
);
247 void flush_tlb_mm(struct mm_struct
*mm
)
249 /* Invalidate all TLB entries of this process by getting a new ASID */
250 if (mm
->context
!= NO_CONTEXT
) {
253 local_irq_save(flags
);
254 mm
->context
= NO_CONTEXT
;
255 if (mm
== current
->mm
)
256 activate_context(mm
);
257 local_irq_restore(flags
);
261 void flush_tlb_all(void)
265 local_irq_save(flags
);
266 sysreg_write(MMUCR
, sysreg_read(MMUCR
) | SYSREG_BIT(MMUCR_I
));
267 local_irq_restore(flags
);
270 #ifdef CONFIG_PROC_FS
272 #include <linux/seq_file.h>
273 #include <linux/proc_fs.h>
274 #include <linux/init.h>
276 static void *tlb_start(struct seq_file
*tlb
, loff_t
*pos
)
278 static unsigned long tlb_index
;
280 if (*pos
>= NR_TLB_ENTRIES
)
287 static void *tlb_next(struct seq_file
*tlb
, void *v
, loff_t
*pos
)
289 unsigned long *index
= v
;
291 if (*index
>= NR_TLB_ENTRIES
- 1)
299 static void tlb_stop(struct seq_file
*tlb
, void *v
)
304 static int tlb_show(struct seq_file
*tlb
, void *v
)
306 unsigned int tlbehi
, tlbehi_save
, tlbelo
, mmucr
, mmucr_save
;
308 unsigned long *index
= v
;
311 seq_puts(tlb
, "ID V G ASID VPN PFN AP SZ C B W D\n");
313 BUG_ON(*index
>= NR_TLB_ENTRIES
);
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
);
321 /* TLBR might change the ASID */
325 tlbehi
= sysreg_read(TLBEHI
);
326 tlbelo
= sysreg_read(TLBELO
);
328 sysreg_write(MMUCR
, mmucr_save
);
329 sysreg_write(TLBEHI
, tlbehi_save
);
331 local_irq_restore(flags
);
333 seq_printf(tlb
, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
335 SYSREG_BFEXT(TLBEHI_V
, tlbehi
) ? '1' : '0',
336 SYSREG_BFEXT(G
, tlbelo
) ? '1' : '0',
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' : '0',
343 SYSREG_BFEXT(B
, tlbelo
) ? '1' : '0',
344 SYSREG_BFEXT(W
, tlbelo
) ? '1' : '0',
345 SYSREG_BFEXT(TLBELO_D
, tlbelo
) ? '1' : '0');
350 static const struct seq_operations tlb_ops
= {
357 static int tlb_open(struct inode
*inode
, struct file
*file
)
359 return seq_open(file
, &tlb_ops
);
362 static const struct file_operations proc_tlb_operations
= {
366 .release
= seq_release
,
369 static int __init
proctlb_init(void)
371 proc_create("tlb", 0, NULL
, &proc_tlb_operations
);
374 late_initcall(proctlb_init
);
375 #endif /* CONFIG_PROC_FS */