cplbmgr.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * File: arch/blackfin/kernel/cplb-nompu-c/cplbmgr.c
  3. * Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c
  4. * Author: Michael McTernan <mmcternan@airvana.com>
  5. *
  6. * Created: 01Nov2008
  7. * Description: CPLB miss handler.
  8. *
  9. * Modified:
  10. * Copyright 2008 Airvana Inc.
  11. * Copyright 2004-2007 Analog Devices Inc.
  12. *
  13. * Bugs: Enter bugs at http://blackfin.uclinux.org/
  14. *
  15. * This program is free software; you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation; either version 2 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. */
  25. #include <linux/kernel.h>
  26. #include <asm/blackfin.h>
  27. #include <asm/cplbinit.h>
  28. #include <asm/cplb.h>
  29. #include <asm/mmu_context.h>
  30. #include <asm/traps.h>
  31. /*
  32. * WARNING
  33. *
  34. * This file is compiled with certain -ffixed-reg options. We have to
  35. * make sure not to call any functions here that could clobber these
  36. * registers.
  37. */
  38. int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
  39. int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
  40. int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
  41. #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
  42. #define MGR_ATTR __attribute__((l1_text))
  43. #else
  44. #define MGR_ATTR
  45. #endif
  46. static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
  47. unsigned long addr)
  48. {
  49. _disable_dcplb();
  50. bfin_write32(DCPLB_DATA0 + idx * 4, data);
  51. bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
  52. _enable_dcplb();
  53. #ifdef CONFIG_CPLB_INFO
  54. dcplb_tbl[cpu][idx].addr = addr;
  55. dcplb_tbl[cpu][idx].data = data;
  56. #endif
  57. }
  58. static inline void write_icplb_data(int cpu, int idx, unsigned long data,
  59. unsigned long addr)
  60. {
  61. _disable_icplb();
  62. bfin_write32(ICPLB_DATA0 + idx * 4, data);
  63. bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
  64. _enable_icplb();
  65. #ifdef CONFIG_CPLB_INFO
  66. icplb_tbl[cpu][idx].addr = addr;
  67. icplb_tbl[cpu][idx].data = data;
  68. #endif
  69. }
  70. /* Counters to implement round-robin replacement. */
  71. static int icplb_rr_index[NR_CPUS] PDT_ATTR;
  72. static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
  73. /*
  74. * Find an ICPLB entry to be evicted and return its index.
  75. */
  76. static int evict_one_icplb(int cpu)
  77. {
  78. int i = first_switched_icplb + icplb_rr_index[cpu];
  79. if (i >= MAX_CPLBS) {
  80. i -= MAX_CPLBS - first_switched_icplb;
  81. icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
  82. }
  83. icplb_rr_index[cpu]++;
  84. return i;
  85. }
  86. static int evict_one_dcplb(int cpu)
  87. {
  88. int i = first_switched_dcplb + dcplb_rr_index[cpu];
  89. if (i >= MAX_CPLBS) {
  90. i -= MAX_CPLBS - first_switched_dcplb;
  91. dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
  92. }
  93. dcplb_rr_index[cpu]++;
  94. return i;
  95. }
  96. MGR_ATTR static int icplb_miss(int cpu)
  97. {
  98. unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
  99. int status = bfin_read_ICPLB_STATUS();
  100. int idx;
  101. unsigned long i_data, base, addr1, eaddr;
  102. nr_icplb_miss[cpu]++;
  103. if (unlikely(status & FAULT_USERSUPV))
  104. nr_icplb_supv_miss[cpu]++;
  105. base = 0;
  106. idx = 0;
  107. do {
  108. eaddr = icplb_bounds[idx].eaddr;
  109. if (addr < eaddr)
  110. break;
  111. base = eaddr;
  112. } while (++idx < icplb_nr_bounds);
  113. if (unlikely(idx == icplb_nr_bounds))
  114. return CPLB_NO_ADDR_MATCH;
  115. i_data = icplb_bounds[idx].data;
  116. if (unlikely(i_data == 0))
  117. return CPLB_NO_ADDR_MATCH;
  118. addr1 = addr & ~(SIZE_4M - 1);
  119. addr &= ~(SIZE_1M - 1);
  120. i_data |= PAGE_SIZE_1MB;
  121. if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
  122. /*
  123. * This works because
  124. * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
  125. */
  126. i_data |= PAGE_SIZE_4MB;
  127. addr = addr1;
  128. }
  129. /* Pick entry to evict */
  130. idx = evict_one_icplb(cpu);
  131. write_icplb_data(cpu, idx, i_data, addr);
  132. return CPLB_RELOADED;
  133. }
  134. MGR_ATTR static int dcplb_miss(int cpu)
  135. {
  136. unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
  137. int status = bfin_read_DCPLB_STATUS();
  138. int idx;
  139. unsigned long d_data, base, addr1, eaddr;
  140. nr_dcplb_miss[cpu]++;
  141. if (unlikely(status & FAULT_USERSUPV))
  142. nr_dcplb_supv_miss[cpu]++;
  143. base = 0;
  144. idx = 0;
  145. do {
  146. eaddr = dcplb_bounds[idx].eaddr;
  147. if (addr < eaddr)
  148. break;
  149. base = eaddr;
  150. } while (++idx < dcplb_nr_bounds);
  151. if (unlikely(idx == dcplb_nr_bounds))
  152. return CPLB_NO_ADDR_MATCH;
  153. d_data = dcplb_bounds[idx].data;
  154. if (unlikely(d_data == 0))
  155. return CPLB_NO_ADDR_MATCH;
  156. addr1 = addr & ~(SIZE_4M - 1);
  157. addr &= ~(SIZE_1M - 1);
  158. d_data |= PAGE_SIZE_1MB;
  159. if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
  160. /*
  161. * This works because
  162. * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
  163. */
  164. d_data |= PAGE_SIZE_4MB;
  165. addr = addr1;
  166. }
  167. /* Pick entry to evict */
  168. idx = evict_one_dcplb(cpu);
  169. write_dcplb_data(cpu, idx, d_data, addr);
  170. return CPLB_RELOADED;
  171. }
  172. MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
  173. {
  174. int cause = seqstat & 0x3f;
  175. unsigned int cpu = raw_smp_processor_id();
  176. switch (cause) {
  177. case VEC_CPLB_I_M:
  178. return icplb_miss(cpu);
  179. case VEC_CPLB_M:
  180. return dcplb_miss(cpu);
  181. default:
  182. return CPLB_UNKNOWN_ERR;
  183. }
  184. }