cevt-r4k.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*
  2. * This file is subject to the terms and conditions of the GNU General Public
  3. * License. See the file "COPYING" in the main directory of this archive
  4. * for more details.
  5. *
  6. * Copyright (C) 2007 MIPS Technologies, Inc.
  7. * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
  8. */
  9. #include <linux/clockchips.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/percpu.h>
  12. #include <asm/time.h>
  13. static int mips_next_event(unsigned long delta,
  14. struct clock_event_device *evt)
  15. {
  16. unsigned int cnt;
  17. int res;
  18. #ifdef CONFIG_MIPS_MT_SMTC
  19. {
  20. unsigned long flags, vpflags;
  21. local_irq_save(flags);
  22. vpflags = dvpe();
  23. #endif
  24. cnt = read_c0_count();
  25. cnt += delta;
  26. write_c0_compare(cnt);
  27. res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
  28. #ifdef CONFIG_MIPS_MT_SMTC
  29. evpe(vpflags);
  30. local_irq_restore(flags);
  31. }
  32. #endif
  33. return res;
  34. }
  35. static void mips_set_mode(enum clock_event_mode mode,
  36. struct clock_event_device *evt)
  37. {
  38. /* Nothing to do ... */
  39. }
  40. static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
  41. static int cp0_timer_irq_installed;
  42. /*
  43. * Timer ack for an R4k-compatible timer of a known frequency.
  44. */
  45. static void c0_timer_ack(void)
  46. {
  47. write_c0_compare(read_c0_compare());
  48. }
  49. /*
  50. * Possibly handle a performance counter interrupt.
  51. * Return true if the timer interrupt should not be checked
  52. */
  53. static inline int handle_perf_irq(int r2)
  54. {
  55. /*
  56. * The performance counter overflow interrupt may be shared with the
  57. * timer interrupt (cp0_perfcount_irq < 0). If it is and a
  58. * performance counter has overflowed (perf_irq() == IRQ_HANDLED)
  59. * and we can't reliably determine if a counter interrupt has also
  60. * happened (!r2) then don't check for a timer interrupt.
  61. */
  62. return (cp0_perfcount_irq < 0) &&
  63. perf_irq() == IRQ_HANDLED &&
  64. !r2;
  65. }
  66. static irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
  67. {
  68. const int r2 = cpu_has_mips_r2;
  69. struct clock_event_device *cd;
  70. int cpu = smp_processor_id();
  71. /*
  72. * Suckage alert:
  73. * Before R2 of the architecture there was no way to see if a
  74. * performance counter interrupt was pending, so we have to run
  75. * the performance counter interrupt handler anyway.
  76. */
  77. if (handle_perf_irq(r2))
  78. goto out;
  79. /*
  80. * The same applies to performance counter interrupts. But with the
  81. * above we now know that the reason we got here must be a timer
  82. * interrupt. Being the paranoiacs we are we check anyway.
  83. */
  84. if (!r2 || (read_c0_cause() & (1 << 30))) {
  85. c0_timer_ack();
  86. #ifdef CONFIG_MIPS_MT_SMTC
  87. if (cpu_data[cpu].vpe_id)
  88. goto out;
  89. cpu = 0;
  90. #endif
  91. cd = &per_cpu(mips_clockevent_device, cpu);
  92. cd->event_handler(cd);
  93. }
  94. out:
  95. return IRQ_HANDLED;
  96. }
  97. static struct irqaction c0_compare_irqaction = {
  98. .handler = c0_compare_interrupt,
  99. #ifdef CONFIG_MIPS_MT_SMTC
  100. .flags = IRQF_DISABLED,
  101. #else
  102. .flags = IRQF_DISABLED | IRQF_PERCPU,
  103. #endif
  104. .name = "timer",
  105. };
  106. #ifdef CONFIG_MIPS_MT_SMTC
  107. DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
  108. static void smtc_set_mode(enum clock_event_mode mode,
  109. struct clock_event_device *evt)
  110. {
  111. }
  112. static void mips_broadcast(cpumask_t mask)
  113. {
  114. unsigned int cpu;
  115. for_each_cpu_mask(cpu, mask)
  116. smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
  117. }
  118. static void setup_smtc_dummy_clockevent_device(void)
  119. {
  120. //uint64_t mips_freq = mips_hpt_^frequency;
  121. unsigned int cpu = smp_processor_id();
  122. struct clock_event_device *cd;
  123. cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
  124. cd->name = "SMTC";
  125. cd->features = CLOCK_EVT_FEAT_DUMMY;
  126. /* Calculate the min / max delta */
  127. cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
  128. cd->shift = 0; //32;
  129. cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
  130. cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
  131. cd->rating = 200;
  132. cd->irq = 17; //-1;
  133. // if (cpu)
  134. // cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
  135. // else
  136. cd->cpumask = cpumask_of_cpu(cpu);
  137. cd->set_mode = smtc_set_mode;
  138. cd->broadcast = mips_broadcast;
  139. clockevents_register_device(cd);
  140. }
  141. #endif
  142. static void mips_event_handler(struct clock_event_device *dev)
  143. {
  144. }
  145. /*
  146. * FIXME: This doesn't hold for the relocated E9000 compare interrupt.
  147. */
  148. static int c0_compare_int_pending(void)
  149. {
  150. return (read_c0_cause() >> cp0_compare_irq) & 0x100;
  151. }
  152. static int c0_compare_int_usable(void)
  153. {
  154. const unsigned int delta = 0x300000;
  155. unsigned int cnt;
  156. /*
  157. * IP7 already pending? Try to clear it by acking the timer.
  158. */
  159. if (c0_compare_int_pending()) {
  160. write_c0_compare(read_c0_compare());
  161. irq_disable_hazard();
  162. if (c0_compare_int_pending())
  163. return 0;
  164. }
  165. cnt = read_c0_count();
  166. cnt += delta;
  167. write_c0_compare(cnt);
  168. while ((long)(read_c0_count() - cnt) <= 0)
  169. ; /* Wait for expiry */
  170. if (!c0_compare_int_pending())
  171. return 0;
  172. write_c0_compare(read_c0_compare());
  173. irq_disable_hazard();
  174. if (c0_compare_int_pending())
  175. return 0;
  176. /*
  177. * Feels like a real count / compare timer.
  178. */
  179. return 1;
  180. }
  181. void __cpuinit mips_clockevent_init(void)
  182. {
  183. uint64_t mips_freq = mips_hpt_frequency;
  184. unsigned int cpu = smp_processor_id();
  185. struct clock_event_device *cd;
  186. unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
  187. if (!cpu_has_counter)
  188. return;
  189. #ifdef CONFIG_MIPS_MT_SMTC
  190. setup_smtc_dummy_clockevent_device();
  191. /*
  192. * On SMTC we only register VPE0's compare interrupt as clockevent
  193. * device.
  194. */
  195. if (cpu)
  196. return;
  197. #endif
  198. if (!c0_compare_int_usable())
  199. return;
  200. cd = &per_cpu(mips_clockevent_device, cpu);
  201. cd->name = "MIPS";
  202. cd->features = CLOCK_EVT_FEAT_ONESHOT;
  203. /* Calculate the min / max delta */
  204. cd->mult = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
  205. cd->shift = 32;
  206. cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
  207. cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
  208. cd->rating = 300;
  209. cd->irq = irq;
  210. #ifdef CONFIG_MIPS_MT_SMTC
  211. cd->cpumask = CPU_MASK_ALL;
  212. #else
  213. cd->cpumask = cpumask_of_cpu(cpu);
  214. #endif
  215. cd->set_next_event = mips_next_event;
  216. cd->set_mode = mips_set_mode;
  217. cd->event_handler = mips_event_handler;
  218. clockevents_register_device(cd);
  219. if (!cp0_timer_irq_installed) {
  220. #ifdef CONFIG_MIPS_MT_SMTC
  221. #define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
  222. setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT);
  223. #else
  224. setup_irq(irq, &c0_compare_irqaction);
  225. #endif /* CONFIG_MIPS_MT_SMTC */
  226. cp0_timer_irq_installed = 1;
  227. }
  228. }