nmi.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Blackfin nmi_watchdog Driver
  3. *
  4. * Originally based on bfin_wdt.c
  5. * Copyright 2010-2010 Analog Devices Inc.
  6. * Graff Yang <graf.yang@analog.com>
  7. *
  8. * Enter bugs at http://blackfin.uclinux.org/
  9. *
  10. * Licensed under the GPL-2 or later.
  11. */
  12. #include <linux/bitops.h>
  13. #include <linux/hardirq.h>
  14. #include <linux/syscore_ops.h>
  15. #include <linux/pm.h>
  16. #include <linux/nmi.h>
  17. #include <linux/smp.h>
  18. #include <linux/timer.h>
  19. #include <asm/blackfin.h>
  20. #include <linux/atomic.h>
  21. #include <asm/cacheflush.h>
  22. #include <asm/bfin_watchdog.h>
  23. #define DRV_NAME "nmi-wdt"
  24. #define NMI_WDT_TIMEOUT 5 /* 5 seconds */
  25. #define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */
  26. static int nmi_wdt_cpu = 1;
  27. static unsigned int timeout = NMI_WDT_TIMEOUT;
  28. static int nmi_active;
  29. static unsigned short wdoga_ctl;
  30. static unsigned int wdoga_cnt;
  31. static struct corelock_slot saved_corelock;
  32. static atomic_t nmi_touched[NR_CPUS];
  33. static struct timer_list ntimer;
  34. enum {
  35. COREA_ENTER_NMI = 0,
  36. COREA_EXIT_NMI,
  37. COREB_EXIT_NMI,
  38. NMI_EVENT_NR,
  39. };
  40. static unsigned long nmi_event __attribute__ ((__section__(".l2.bss")));
  41. /* we are in nmi, non-atomic bit ops is safe */
  42. static inline void set_nmi_event(int event)
  43. {
  44. __set_bit(event, &nmi_event);
  45. }
  46. static inline void wait_nmi_event(int event)
  47. {
  48. while (!test_bit(event, &nmi_event))
  49. barrier();
  50. __clear_bit(event, &nmi_event);
  51. }
  52. static inline void send_corea_nmi(void)
  53. {
  54. wdoga_ctl = bfin_read_WDOGA_CTL();
  55. wdoga_cnt = bfin_read_WDOGA_CNT();
  56. bfin_write_WDOGA_CTL(WDEN_DISABLE);
  57. bfin_write_WDOGA_CNT(0);
  58. bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI);
  59. }
  60. static inline void restore_corea_nmi(void)
  61. {
  62. bfin_write_WDOGA_CTL(WDEN_DISABLE);
  63. bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
  64. bfin_write_WDOGA_CNT(wdoga_cnt);
  65. bfin_write_WDOGA_CTL(wdoga_ctl);
  66. }
  67. static inline void save_corelock(void)
  68. {
  69. saved_corelock = corelock;
  70. corelock.lock = 0;
  71. }
  72. static inline void restore_corelock(void)
  73. {
  74. corelock = saved_corelock;
  75. }
  76. static inline void nmi_wdt_keepalive(void)
  77. {
  78. bfin_write_WDOGB_STAT(0);
  79. }
  80. static inline void nmi_wdt_stop(void)
  81. {
  82. bfin_write_WDOGB_CTL(WDEN_DISABLE);
  83. }
  84. /* before calling this function, you must stop the WDT */
  85. static inline void nmi_wdt_clear(void)
  86. {
  87. /* clear TRO bit, disable event generation */
  88. bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
  89. }
  90. static inline void nmi_wdt_start(void)
  91. {
  92. bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI);
  93. }
  94. static inline int nmi_wdt_running(void)
  95. {
  96. return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE);
  97. }
  98. static inline int nmi_wdt_set_timeout(unsigned long t)
  99. {
  100. u32 cnt, max_t, sclk;
  101. int run;
  102. sclk = get_sclk();
  103. max_t = -1 / sclk;
  104. cnt = t * sclk;
  105. if (t > max_t) {
  106. pr_warning("NMI: timeout value is too large\n");
  107. return -EINVAL;
  108. }
  109. run = nmi_wdt_running();
  110. nmi_wdt_stop();
  111. bfin_write_WDOGB_CNT(cnt);
  112. if (run)
  113. nmi_wdt_start();
  114. timeout = t;
  115. return 0;
  116. }
  117. int check_nmi_wdt_touched(void)
  118. {
  119. unsigned int this_cpu = smp_processor_id();
  120. unsigned int cpu;
  121. cpumask_t mask;
  122. cpumask_copy(&mask, cpu_online_mask);
  123. if (!atomic_read(&nmi_touched[this_cpu]))
  124. return 0;
  125. atomic_set(&nmi_touched[this_cpu], 0);
  126. cpumask_clear_cpu(this_cpu, &mask);
  127. for_each_cpu(cpu, &mask) {
  128. invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),
  129. (unsigned long)(&nmi_touched[cpu]));
  130. if (!atomic_read(&nmi_touched[cpu]))
  131. return 0;
  132. atomic_set(&nmi_touched[cpu], 0);
  133. }
  134. return 1;
  135. }
  136. static void nmi_wdt_timer(unsigned long data)
  137. {
  138. if (check_nmi_wdt_touched())
  139. nmi_wdt_keepalive();
  140. mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);
  141. }
  142. static int __init init_nmi_wdt(void)
  143. {
  144. nmi_wdt_set_timeout(timeout);
  145. nmi_wdt_start();
  146. nmi_active = true;
  147. init_timer(&ntimer);
  148. ntimer.function = nmi_wdt_timer;
  149. ntimer.expires = jiffies + NMI_CHECK_TIMEOUT;
  150. add_timer(&ntimer);
  151. pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout);
  152. return 0;
  153. }
  154. device_initcall(init_nmi_wdt);
  155. void touch_nmi_watchdog(void)
  156. {
  157. atomic_set(&nmi_touched[smp_processor_id()], 1);
  158. }
  159. /* Suspend/resume support */
  160. #ifdef CONFIG_PM
  161. static int nmi_wdt_suspend(void)
  162. {
  163. nmi_wdt_stop();
  164. return 0;
  165. }
  166. static void nmi_wdt_resume(void)
  167. {
  168. if (nmi_active)
  169. nmi_wdt_start();
  170. }
  171. static struct syscore_ops nmi_syscore_ops = {
  172. .resume = nmi_wdt_resume,
  173. .suspend = nmi_wdt_suspend,
  174. };
  175. static int __init init_nmi_wdt_syscore(void)
  176. {
  177. if (nmi_active)
  178. register_syscore_ops(&nmi_syscore_ops);
  179. return 0;
  180. }
  181. late_initcall(init_nmi_wdt_syscore);
  182. #endif /* CONFIG_PM */
  183. asmlinkage notrace void do_nmi(struct pt_regs *fp)
  184. {
  185. unsigned int cpu = smp_processor_id();
  186. nmi_enter();
  187. cpu_pda[cpu].__nmi_count += 1;
  188. if (cpu == nmi_wdt_cpu) {
  189. /* CoreB goes here first */
  190. /* reload the WDOG_STAT */
  191. nmi_wdt_keepalive();
  192. /* clear nmi interrupt for CoreB */
  193. nmi_wdt_stop();
  194. nmi_wdt_clear();
  195. /* trigger NMI interrupt of CoreA */
  196. send_corea_nmi();
  197. /* waiting CoreB to enter NMI */
  198. wait_nmi_event(COREA_ENTER_NMI);
  199. /* recover WDOGA's settings */
  200. restore_corea_nmi();
  201. save_corelock();
  202. /* corelock is save/cleared, CoreA is dummping messages */
  203. wait_nmi_event(COREA_EXIT_NMI);
  204. } else {
  205. /* OK, CoreA entered NMI */
  206. set_nmi_event(COREA_ENTER_NMI);
  207. }
  208. pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu);
  209. dump_bfin_process(fp);
  210. dump_bfin_mem(fp);
  211. show_regs(fp);
  212. dump_bfin_trace_buffer();
  213. show_stack(current, (unsigned long *)fp);
  214. if (cpu == nmi_wdt_cpu) {
  215. pr_emerg("This fault is not recoverable, sorry!\n");
  216. /* CoreA dump finished, restore the corelock */
  217. restore_corelock();
  218. set_nmi_event(COREB_EXIT_NMI);
  219. } else {
  220. /* CoreB dump finished, notice the CoreA we are done */
  221. set_nmi_event(COREA_EXIT_NMI);
  222. /* synchronize with CoreA */
  223. wait_nmi_event(COREB_EXIT_NMI);
  224. }
  225. nmi_exit();
  226. }