tlb.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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. #include <asm/mmu_context.h>
  12. /* TODO: Get the correct number from the CONFIG1 system register */
  13. #define NR_TLB_ENTRIES 32
  14. static void show_dtlb_entry(unsigned int index)
  15. {
  16. u32 tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
  17. unsigned long flags;
  18. local_irq_save(flags);
  19. mmucr_save = sysreg_read(MMUCR);
  20. tlbehi_save = sysreg_read(TLBEHI);
  21. mmucr = SYSREG_BFINS(DRP, index, mmucr_save);
  22. sysreg_write(MMUCR, mmucr);
  23. __builtin_tlbr();
  24. cpu_sync_pipeline();
  25. tlbehi = sysreg_read(TLBEHI);
  26. tlbelo = sysreg_read(TLBELO);
  27. printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
  28. index,
  29. SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
  30. SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
  31. SYSREG_BFEXT(ASID, tlbehi),
  32. SYSREG_BFEXT(VPN, tlbehi) >> 2,
  33. SYSREG_BFEXT(PFN, tlbelo) >> 2,
  34. SYSREG_BFEXT(AP, tlbelo),
  35. SYSREG_BFEXT(SZ, tlbelo),
  36. SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ',
  37. SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ',
  38. SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ',
  39. SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' ');
  40. sysreg_write(MMUCR, mmucr_save);
  41. sysreg_write(TLBEHI, tlbehi_save);
  42. cpu_sync_pipeline();
  43. local_irq_restore(flags);
  44. }
  45. void dump_dtlb(void)
  46. {
  47. unsigned int i;
  48. printk("ID V G ASID VPN PFN AP SZ C B W D\n");
  49. for (i = 0; i < NR_TLB_ENTRIES; i++)
  50. show_dtlb_entry(i);
  51. }
  52. static void update_dtlb(unsigned long address, pte_t pte)
  53. {
  54. u32 tlbehi;
  55. u32 mmucr;
  56. /*
  57. * We're not changing the ASID here, so no need to flush the
  58. * pipeline.
  59. */
  60. tlbehi = sysreg_read(TLBEHI);
  61. tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
  62. tlbehi |= address & MMU_VPN_MASK;
  63. tlbehi |= SYSREG_BIT(TLBEHI_V);
  64. sysreg_write(TLBEHI, tlbehi);
  65. /* Does this mapping already exist? */
  66. __builtin_tlbs();
  67. mmucr = sysreg_read(MMUCR);
  68. if (mmucr & SYSREG_BIT(MMUCR_N)) {
  69. /* Not found -- pick a not-recently-accessed entry */
  70. unsigned int rp;
  71. u32 tlbar = sysreg_read(TLBARLO);
  72. rp = 32 - fls(tlbar);
  73. if (rp == 32) {
  74. rp = 0;
  75. sysreg_write(TLBARLO, -1L);
  76. }
  77. mmucr = SYSREG_BFINS(DRP, rp, mmucr);
  78. sysreg_write(MMUCR, mmucr);
  79. }
  80. sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
  81. /* Let's go */
  82. __builtin_tlbw();
  83. }
  84. void update_mmu_cache(struct vm_area_struct *vma,
  85. unsigned long address, pte_t *ptep)
  86. {
  87. unsigned long flags;
  88. /* ptrace may call this routine */
  89. if (vma && current->active_mm != vma->vm_mm)
  90. return;
  91. local_irq_save(flags);
  92. update_dtlb(address, *ptep);
  93. local_irq_restore(flags);
  94. }
  95. static void __flush_tlb_page(unsigned long asid, unsigned long page)
  96. {
  97. u32 mmucr, tlbehi;
  98. /*
  99. * Caller is responsible for masking out non-PFN bits in page
  100. * and changing the current ASID if necessary. This means that
  101. * we don't need to flush the pipeline after writing TLBEHI.
  102. */
  103. tlbehi = page | asid;
  104. sysreg_write(TLBEHI, tlbehi);
  105. __builtin_tlbs();
  106. mmucr = sysreg_read(MMUCR);
  107. if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
  108. unsigned int entry;
  109. u32 tlbarlo;
  110. /* Clear the "valid" bit */
  111. sysreg_write(TLBEHI, tlbehi);
  112. /* mark the entry as "not accessed" */
  113. entry = SYSREG_BFEXT(DRP, mmucr);
  114. tlbarlo = sysreg_read(TLBARLO);
  115. tlbarlo |= (0x80000000UL >> entry);
  116. sysreg_write(TLBARLO, tlbarlo);
  117. /* update the entry with valid bit clear */
  118. __builtin_tlbw();
  119. }
  120. }
  121. void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
  122. {
  123. if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
  124. unsigned long flags, asid;
  125. unsigned long saved_asid = MMU_NO_ASID;
  126. asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
  127. page &= PAGE_MASK;
  128. local_irq_save(flags);
  129. if (vma->vm_mm != current->mm) {
  130. saved_asid = get_asid();
  131. set_asid(asid);
  132. }
  133. __flush_tlb_page(asid, page);
  134. if (saved_asid != MMU_NO_ASID)
  135. set_asid(saved_asid);
  136. local_irq_restore(flags);
  137. }
  138. }
  139. void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
  140. unsigned long end)
  141. {
  142. struct mm_struct *mm = vma->vm_mm;
  143. if (mm->context != NO_CONTEXT) {
  144. unsigned long flags;
  145. int size;
  146. local_irq_save(flags);
  147. size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  148. if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
  149. mm->context = NO_CONTEXT;
  150. if (mm == current->mm)
  151. activate_context(mm);
  152. } else {
  153. unsigned long asid;
  154. unsigned long saved_asid;
  155. asid = mm->context & MMU_CONTEXT_ASID_MASK;
  156. saved_asid = MMU_NO_ASID;
  157. start &= PAGE_MASK;
  158. end += (PAGE_SIZE - 1);
  159. end &= PAGE_MASK;
  160. if (mm != current->mm) {
  161. saved_asid = get_asid();
  162. set_asid(asid);
  163. }
  164. while (start < end) {
  165. __flush_tlb_page(asid, start);
  166. start += PAGE_SIZE;
  167. }
  168. if (saved_asid != MMU_NO_ASID)
  169. set_asid(saved_asid);
  170. }
  171. local_irq_restore(flags);
  172. }
  173. }
  174. /*
  175. * This function depends on the pages to be flushed having the G
  176. * (global) bit set in their pte. This is true for all
  177. * PAGE_KERNEL(_RO) pages.
  178. */
  179. void flush_tlb_kernel_range(unsigned long start, unsigned long end)
  180. {
  181. unsigned long flags;
  182. int size;
  183. size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  184. if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
  185. flush_tlb_all();
  186. } else {
  187. unsigned long asid;
  188. local_irq_save(flags);
  189. asid = get_asid();
  190. start &= PAGE_MASK;
  191. end += (PAGE_SIZE - 1);
  192. end &= PAGE_MASK;
  193. while (start < end) {
  194. __flush_tlb_page(asid, start);
  195. start += PAGE_SIZE;
  196. }
  197. local_irq_restore(flags);
  198. }
  199. }
  200. void flush_tlb_mm(struct mm_struct *mm)
  201. {
  202. /* Invalidate all TLB entries of this process by getting a new ASID */
  203. if (mm->context != NO_CONTEXT) {
  204. unsigned long flags;
  205. local_irq_save(flags);
  206. mm->context = NO_CONTEXT;
  207. if (mm == current->mm)
  208. activate_context(mm);
  209. local_irq_restore(flags);
  210. }
  211. }
  212. void flush_tlb_all(void)
  213. {
  214. unsigned long flags;
  215. local_irq_save(flags);
  216. sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
  217. local_irq_restore(flags);
  218. }
  219. #ifdef CONFIG_PROC_FS
  220. #include <linux/seq_file.h>
  221. #include <linux/proc_fs.h>
  222. #include <linux/init.h>
  223. static void *tlb_start(struct seq_file *tlb, loff_t *pos)
  224. {
  225. static unsigned long tlb_index;
  226. if (*pos >= NR_TLB_ENTRIES)
  227. return NULL;
  228. tlb_index = 0;
  229. return &tlb_index;
  230. }
  231. static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
  232. {
  233. unsigned long *index = v;
  234. if (*index >= NR_TLB_ENTRIES - 1)
  235. return NULL;
  236. ++*pos;
  237. ++*index;
  238. return index;
  239. }
  240. static void tlb_stop(struct seq_file *tlb, void *v)
  241. {
  242. }
  243. static int tlb_show(struct seq_file *tlb, void *v)
  244. {
  245. unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
  246. unsigned long flags;
  247. unsigned long *index = v;
  248. if (*index == 0)
  249. seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
  250. BUG_ON(*index >= NR_TLB_ENTRIES);
  251. local_irq_save(flags);
  252. mmucr_save = sysreg_read(MMUCR);
  253. tlbehi_save = sysreg_read(TLBEHI);
  254. mmucr = SYSREG_BFINS(DRP, *index, mmucr_save);
  255. sysreg_write(MMUCR, mmucr);
  256. /* TLBR might change the ASID */
  257. __builtin_tlbr();
  258. cpu_sync_pipeline();
  259. tlbehi = sysreg_read(TLBEHI);
  260. tlbelo = sysreg_read(TLBELO);
  261. sysreg_write(MMUCR, mmucr_save);
  262. sysreg_write(TLBEHI, tlbehi_save);
  263. cpu_sync_pipeline();
  264. local_irq_restore(flags);
  265. seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
  266. *index,
  267. SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
  268. SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
  269. SYSREG_BFEXT(ASID, tlbehi),
  270. SYSREG_BFEXT(VPN, tlbehi) >> 2,
  271. SYSREG_BFEXT(PFN, tlbelo) >> 2,
  272. SYSREG_BFEXT(AP, tlbelo),
  273. SYSREG_BFEXT(SZ, tlbelo),
  274. SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '0',
  275. SYSREG_BFEXT(B, tlbelo) ? '1' : '0',
  276. SYSREG_BFEXT(W, tlbelo) ? '1' : '0',
  277. SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '0');
  278. return 0;
  279. }
  280. static const struct seq_operations tlb_ops = {
  281. .start = tlb_start,
  282. .next = tlb_next,
  283. .stop = tlb_stop,
  284. .show = tlb_show,
  285. };
  286. static int tlb_open(struct inode *inode, struct file *file)
  287. {
  288. return seq_open(file, &tlb_ops);
  289. }
  290. static const struct file_operations proc_tlb_operations = {
  291. .open = tlb_open,
  292. .read = seq_read,
  293. .llseek = seq_lseek,
  294. .release = seq_release,
  295. };
  296. static int __init proctlb_init(void)
  297. {
  298. proc_create("tlb", 0, NULL, &proc_tlb_operations);
  299. return 0;
  300. }
  301. late_initcall(proctlb_init);
  302. #endif /* CONFIG_PROC_FS */