Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/avr32/mm/tlb.c
10817 views
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' : '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' : ' ');
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' : '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');
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
377