cplbmgr.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c
  3. * Author: Michael McTernan <mmcternan@airvana.com>
  4. *
  5. * Description: CPLB miss handler.
  6. *
  7. * Modified:
  8. * Copyright 2008 Airvana Inc.
  9. * Copyright 2008-2009 Analog Devices Inc.
  10. *
  11. * Licensed under the GPL-2 or later
  12. */
  13. #include <linux/kernel.h>
  14. #include <asm/blackfin.h>
  15. #include <asm/cplbinit.h>
  16. #include <asm/cplb.h>
  17. #include <asm/mmu_context.h>
  18. #include <asm/traps.h>
  19. /*
  20. * WARNING
  21. *
  22. * This file is compiled with certain -ffixed-reg options. We have to
  23. * make sure not to call any functions here that could clobber these
  24. * registers.
  25. */
  26. int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
  27. int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
  28. int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
  29. #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
  30. #define MGR_ATTR __attribute__((l1_text))
  31. #else
  32. #define MGR_ATTR
  33. #endif
  34. static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
  35. unsigned long addr)
  36. {
  37. _disable_dcplb();
  38. bfin_write32(DCPLB_DATA0 + idx * 4, data);
  39. bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
  40. _enable_dcplb();
  41. #ifdef CONFIG_CPLB_INFO
  42. dcplb_tbl[cpu][idx].addr = addr;
  43. dcplb_tbl[cpu][idx].data = data;
  44. #endif
  45. }
  46. static inline void write_icplb_data(int cpu, int idx, unsigned long data,
  47. unsigned long addr)
  48. {
  49. _disable_icplb();
  50. bfin_write32(ICPLB_DATA0 + idx * 4, data);
  51. bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
  52. _enable_icplb();
  53. #ifdef CONFIG_CPLB_INFO
  54. icplb_tbl[cpu][idx].addr = addr;
  55. icplb_tbl[cpu][idx].data = data;
  56. #endif
  57. }
  58. /* Counters to implement round-robin replacement. */
  59. static int icplb_rr_index[NR_CPUS] PDT_ATTR;
  60. static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
  61. /*
  62. * Find an ICPLB entry to be evicted and return its index.
  63. */
  64. static int evict_one_icplb(int cpu)
  65. {
  66. int i = first_switched_icplb + icplb_rr_index[cpu];
  67. if (i >= MAX_CPLBS) {
  68. i -= MAX_CPLBS - first_switched_icplb;
  69. icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
  70. }
  71. icplb_rr_index[cpu]++;
  72. return i;
  73. }
  74. static int evict_one_dcplb(int cpu)
  75. {
  76. int i = first_switched_dcplb + dcplb_rr_index[cpu];
  77. if (i >= MAX_CPLBS) {
  78. i -= MAX_CPLBS - first_switched_dcplb;
  79. dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
  80. }
  81. dcplb_rr_index[cpu]++;
  82. return i;
  83. }
  84. MGR_ATTR static int icplb_miss(int cpu)
  85. {
  86. unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
  87. int status = bfin_read_ICPLB_STATUS();
  88. int idx;
  89. unsigned long i_data, base, addr1, eaddr;
  90. nr_icplb_miss[cpu]++;
  91. if (unlikely(status & FAULT_USERSUPV))
  92. nr_icplb_supv_miss[cpu]++;
  93. base = 0;
  94. idx = 0;
  95. do {
  96. eaddr = icplb_bounds[idx].eaddr;
  97. if (addr < eaddr)
  98. break;
  99. base = eaddr;
  100. } while (++idx < icplb_nr_bounds);
  101. if (unlikely(idx == icplb_nr_bounds))
  102. return CPLB_NO_ADDR_MATCH;
  103. i_data = icplb_bounds[idx].data;
  104. if (unlikely(i_data == 0))
  105. return CPLB_NO_ADDR_MATCH;
  106. addr1 = addr & ~(SIZE_4M - 1);
  107. addr &= ~(SIZE_1M - 1);
  108. i_data |= PAGE_SIZE_1MB;
  109. if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
  110. /*
  111. * This works because
  112. * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
  113. */
  114. i_data |= PAGE_SIZE_4MB;
  115. addr = addr1;
  116. }
  117. /* Pick entry to evict */
  118. idx = evict_one_icplb(cpu);
  119. write_icplb_data(cpu, idx, i_data, addr);
  120. return CPLB_RELOADED;
  121. }
  122. MGR_ATTR static int dcplb_miss(int cpu)
  123. {
  124. unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
  125. int status = bfin_read_DCPLB_STATUS();
  126. int idx;
  127. unsigned long d_data, base, addr1, eaddr, cplb_pagesize, cplb_pageflags;
  128. nr_dcplb_miss[cpu]++;
  129. if (unlikely(status & FAULT_USERSUPV))
  130. nr_dcplb_supv_miss[cpu]++;
  131. base = 0;
  132. idx = 0;
  133. do {
  134. eaddr = dcplb_bounds[idx].eaddr;
  135. if (addr < eaddr)
  136. break;
  137. base = eaddr;
  138. } while (++idx < dcplb_nr_bounds);
  139. if (unlikely(idx == dcplb_nr_bounds))
  140. return CPLB_NO_ADDR_MATCH;
  141. d_data = dcplb_bounds[idx].data;
  142. if (unlikely(d_data == 0))
  143. return CPLB_NO_ADDR_MATCH;
  144. addr &= ~(SIZE_1M - 1);
  145. d_data |= PAGE_SIZE_1MB;
  146. /* BF60x support large than 4M CPLB page size */
  147. #ifdef PAGE_SIZE_16MB
  148. cplb_pageflags = PAGE_SIZE_16MB;
  149. cplb_pagesize = SIZE_16M;
  150. #else
  151. cplb_pageflags = PAGE_SIZE_4MB;
  152. cplb_pagesize = SIZE_4M;
  153. #endif
  154. find_pagesize:
  155. addr1 = addr & ~(cplb_pagesize - 1);
  156. if (addr1 >= base && (addr1 + cplb_pagesize) <= eaddr) {
  157. /*
  158. * This works because
  159. * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
  160. */
  161. d_data |= cplb_pageflags;
  162. addr = addr1;
  163. goto found_pagesize;
  164. } else {
  165. if (cplb_pagesize > SIZE_4M) {
  166. cplb_pageflags = PAGE_SIZE_4MB;
  167. cplb_pagesize = SIZE_4M;
  168. goto find_pagesize;
  169. }
  170. }
  171. found_pagesize:
  172. #ifdef CONFIG_BF60x
  173. if ((addr >= ASYNC_BANK0_BASE)
  174. && (addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE))
  175. d_data |= PAGE_SIZE_64MB;
  176. #endif
  177. /* Pick entry to evict */
  178. idx = evict_one_dcplb(cpu);
  179. write_dcplb_data(cpu, idx, d_data, addr);
  180. return CPLB_RELOADED;
  181. }
  182. MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
  183. {
  184. int cause = seqstat & 0x3f;
  185. unsigned int cpu = raw_smp_processor_id();
  186. switch (cause) {
  187. case VEC_CPLB_I_M:
  188. return icplb_miss(cpu);
  189. case VEC_CPLB_M:
  190. return dcplb_miss(cpu);
  191. default:
  192. return CPLB_UNKNOWN_ERR;
  193. }
  194. }