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 *ptep)
108
/* ptrace may call this routine */
109
if (vma && current->active_mm != vma->vm_mm)
112
local_irq_save(flags);
113
update_dtlb(address, *ptep);
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 */