tlb-r3k.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. * r2300.c: R2000 and R3000 specific mmu/cache code.
  3. *
  4. * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
  5. *
  6. * with a lot of changes to make this thing work for R3000s
  7. * Tx39XX R4k style caches added. HK
  8. * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
  9. * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
  10. * Copyright (C) 2002 Ralf Baechle
  11. * Copyright (C) 2002 Maciej W. Rozycki
  12. */
  13. #include <linux/kernel.h>
  14. #include <linux/sched.h>
  15. #include <linux/smp.h>
  16. #include <linux/mm.h>
  17. #include <asm/page.h>
  18. #include <asm/pgtable.h>
  19. #include <asm/mmu_context.h>
  20. #include <asm/tlbmisc.h>
  21. #include <asm/isadep.h>
  22. #include <asm/io.h>
  23. #include <asm/bootinfo.h>
  24. #include <asm/cpu.h>
  25. #undef DEBUG_TLB
  26. extern void build_tlb_refill_handler(void);
  27. /* CP0 hazard avoidance. */
  28. #define BARRIER \
  29. __asm__ __volatile__( \
  30. ".set push\n\t" \
  31. ".set noreorder\n\t" \
  32. "nop\n\t" \
  33. ".set pop\n\t")
  34. int r3k_have_wired_reg; /* Should be in cpu_data? */
  35. /* TLB operations. */
  36. static void local_flush_tlb_from(int entry)
  37. {
  38. unsigned long old_ctx;
  39. old_ctx = read_c0_entryhi() & cpu_asid_mask(&current_cpu_data);
  40. write_c0_entrylo0(0);
  41. while (entry < current_cpu_data.tlbsize) {
  42. write_c0_index(entry << 8);
  43. write_c0_entryhi((entry | 0x80000) << 12);
  44. entry++; /* BARRIER */
  45. tlb_write_indexed();
  46. }
  47. write_c0_entryhi(old_ctx);
  48. }
  49. void local_flush_tlb_all(void)
  50. {
  51. unsigned long flags;
  52. #ifdef DEBUG_TLB
  53. printk("[tlball]");
  54. #endif
  55. local_irq_save(flags);
  56. local_flush_tlb_from(r3k_have_wired_reg ? read_c0_wired() : 8);
  57. local_irq_restore(flags);
  58. }
  59. void local_flush_tlb_mm(struct mm_struct *mm)
  60. {
  61. int cpu = smp_processor_id();
  62. if (cpu_context(cpu, mm) != 0) {
  63. #ifdef DEBUG_TLB
  64. printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm));
  65. #endif
  66. drop_mmu_context(mm, cpu);
  67. }
  68. }
  69. void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
  70. unsigned long end)
  71. {
  72. unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
  73. struct mm_struct *mm = vma->vm_mm;
  74. int cpu = smp_processor_id();
  75. if (cpu_context(cpu, mm) != 0) {
  76. unsigned long size, flags;
  77. #ifdef DEBUG_TLB
  78. printk("[tlbrange<%lu,0x%08lx,0x%08lx>]",
  79. cpu_context(cpu, mm) & asid_mask, start, end);
  80. #endif
  81. local_irq_save(flags);
  82. size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  83. if (size <= current_cpu_data.tlbsize) {
  84. int oldpid = read_c0_entryhi() & asid_mask;
  85. int newpid = cpu_context(cpu, mm) & asid_mask;
  86. start &= PAGE_MASK;
  87. end += PAGE_SIZE - 1;
  88. end &= PAGE_MASK;
  89. while (start < end) {
  90. int idx;
  91. write_c0_entryhi(start | newpid);
  92. start += PAGE_SIZE; /* BARRIER */
  93. tlb_probe();
  94. idx = read_c0_index();
  95. write_c0_entrylo0(0);
  96. write_c0_entryhi(KSEG0);
  97. if (idx < 0) /* BARRIER */
  98. continue;
  99. tlb_write_indexed();
  100. }
  101. write_c0_entryhi(oldpid);
  102. } else {
  103. drop_mmu_context(mm, cpu);
  104. }
  105. local_irq_restore(flags);
  106. }
  107. }
  108. void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
  109. {
  110. unsigned long size, flags;
  111. #ifdef DEBUG_TLB
  112. printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end);
  113. #endif
  114. local_irq_save(flags);
  115. size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  116. if (size <= current_cpu_data.tlbsize) {
  117. int pid = read_c0_entryhi();
  118. start &= PAGE_MASK;
  119. end += PAGE_SIZE - 1;
  120. end &= PAGE_MASK;
  121. while (start < end) {
  122. int idx;
  123. write_c0_entryhi(start);
  124. start += PAGE_SIZE; /* BARRIER */
  125. tlb_probe();
  126. idx = read_c0_index();
  127. write_c0_entrylo0(0);
  128. write_c0_entryhi(KSEG0);
  129. if (idx < 0) /* BARRIER */
  130. continue;
  131. tlb_write_indexed();
  132. }
  133. write_c0_entryhi(pid);
  134. } else {
  135. local_flush_tlb_all();
  136. }
  137. local_irq_restore(flags);
  138. }
  139. void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
  140. {
  141. unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
  142. int cpu = smp_processor_id();
  143. if (cpu_context(cpu, vma->vm_mm) != 0) {
  144. unsigned long flags;
  145. int oldpid, newpid, idx;
  146. #ifdef DEBUG_TLB
  147. printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page);
  148. #endif
  149. newpid = cpu_context(cpu, vma->vm_mm) & asid_mask;
  150. page &= PAGE_MASK;
  151. local_irq_save(flags);
  152. oldpid = read_c0_entryhi() & asid_mask;
  153. write_c0_entryhi(page | newpid);
  154. BARRIER;
  155. tlb_probe();
  156. idx = read_c0_index();
  157. write_c0_entrylo0(0);
  158. write_c0_entryhi(KSEG0);
  159. if (idx < 0) /* BARRIER */
  160. goto finish;
  161. tlb_write_indexed();
  162. finish:
  163. write_c0_entryhi(oldpid);
  164. local_irq_restore(flags);
  165. }
  166. }
  167. void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
  168. {
  169. unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
  170. unsigned long flags;
  171. int idx, pid;
  172. /*
  173. * Handle debugger faulting in for debugee.
  174. */
  175. if (current->active_mm != vma->vm_mm)
  176. return;
  177. pid = read_c0_entryhi() & asid_mask;
  178. #ifdef DEBUG_TLB
  179. if ((pid != (cpu_context(cpu, vma->vm_mm) & asid_mask)) || (cpu_context(cpu, vma->vm_mm) == 0)) {
  180. printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n",
  181. (cpu_context(cpu, vma->vm_mm)), pid);
  182. }
  183. #endif
  184. local_irq_save(flags);
  185. address &= PAGE_MASK;
  186. write_c0_entryhi(address | pid);
  187. BARRIER;
  188. tlb_probe();
  189. idx = read_c0_index();
  190. write_c0_entrylo0(pte_val(pte));
  191. write_c0_entryhi(address | pid);
  192. if (idx < 0) { /* BARRIER */
  193. tlb_write_random();
  194. } else {
  195. tlb_write_indexed();
  196. }
  197. write_c0_entryhi(pid);
  198. local_irq_restore(flags);
  199. }
  200. void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
  201. unsigned long entryhi, unsigned long pagemask)
  202. {
  203. unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
  204. unsigned long flags;
  205. unsigned long old_ctx;
  206. static unsigned long wired = 0;
  207. if (r3k_have_wired_reg) { /* TX39XX */
  208. unsigned long old_pagemask;
  209. unsigned long w;
  210. #ifdef DEBUG_TLB
  211. printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n",
  212. entrylo0, entryhi, pagemask);
  213. #endif
  214. local_irq_save(flags);
  215. /* Save old context and create impossible VPN2 value */
  216. old_ctx = read_c0_entryhi() & asid_mask;
  217. old_pagemask = read_c0_pagemask();
  218. w = read_c0_wired();
  219. write_c0_wired(w + 1);
  220. write_c0_index(w << 8);
  221. write_c0_pagemask(pagemask);
  222. write_c0_entryhi(entryhi);
  223. write_c0_entrylo0(entrylo0);
  224. BARRIER;
  225. tlb_write_indexed();
  226. write_c0_entryhi(old_ctx);
  227. write_c0_pagemask(old_pagemask);
  228. local_flush_tlb_all();
  229. local_irq_restore(flags);
  230. } else if (wired < 8) {
  231. #ifdef DEBUG_TLB
  232. printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n",
  233. entrylo0, entryhi);
  234. #endif
  235. local_irq_save(flags);
  236. old_ctx = read_c0_entryhi() & asid_mask;
  237. write_c0_entrylo0(entrylo0);
  238. write_c0_entryhi(entryhi);
  239. write_c0_index(wired);
  240. wired++; /* BARRIER */
  241. tlb_write_indexed();
  242. write_c0_entryhi(old_ctx);
  243. local_flush_tlb_all();
  244. local_irq_restore(flags);
  245. }
  246. }
  247. void tlb_init(void)
  248. {
  249. switch (current_cpu_type()) {
  250. case CPU_TX3922:
  251. case CPU_TX3927:
  252. r3k_have_wired_reg = 1;
  253. write_c0_wired(0); /* Set to 8 on reset... */
  254. break;
  255. }
  256. local_flush_tlb_from(0);
  257. build_tlb_refill_handler();
  258. }