~ubuntu-branches/ubuntu/precise/linux-lts-saucy/precise-proposed

« back to all changes in this revision

Viewing changes to arch/avr32/mm/tlb.c

  • Committer: Package Import Robot
  • Author(s): Tim Gardner
  • Date: 2013-10-09 13:31:18 UTC
  • Revision ID: package-import@ubuntu.com-20131009133118-l5q5o2hmtz96hefq
Tags: upstream-3.11.0
ImportĀ upstreamĀ versionĀ 3.11.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 */