Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tlb.c
Go to the documentation of this file.
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();
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);
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 
98 
99  /* Let's go */
100  __builtin_tlbw();
101 }
102 
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);
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();
324 
325  tlbehi = sysreg_read(TLBEHI);
326  tlbelo = sysreg_read(TLBELO);
327 
328  sysreg_write(MMUCR, mmucr_save);
329  sysreg_write(TLBEHI, tlbehi_save);
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 */