tlb.c 8.2 KB

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