nmi.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 <linux/sched/debug.h>
  20. #include <asm/blackfin.h>
  21. #include <linux/atomic.h>
  22. #include <asm/cacheflush.h>
  23. #include <asm/bfin_watchdog.h>
  24. #define DRV_NAME "nmi-wdt"
  25. #define NMI_WDT_TIMEOUT 5 /* 5 seconds */
  26. #define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */
  27. static int nmi_wdt_cpu = 1;
  28. static unsigned int timeout = NMI_WDT_TIMEOUT;
  29. static int nmi_active;
  30. static unsigned short wdoga_ctl;
  31. static unsigned int wdoga_cnt;
  32. static struct corelock_slot saved_corelock;
  33. static atomic_t nmi_touched[NR_CPUS];
  34. static struct timer_list ntimer;
  35. enum {
  36. COREA_ENTER_NMI = 0,
  37. COREA_EXIT_NMI,
  38. COREB_EXIT_NMI,
  39. NMI_EVENT_NR,
  40. };
  41. static unsigned long nmi_event __attribute__ ((__section__(".l2.bss")));
  42. /* we are in nmi, non-atomic bit ops is safe */
  43. static inline void set_nmi_event(int event)
  44. {
  45. __set_bit(event, &nmi_event);
  46. }
  47. static inline void wait_nmi_event(int event)
  48. {
  49. while (!test_bit(event, &nmi_event))
  50. barrier();
  51. __clear_bit(event, &nmi_event);
  52. }
  53. static inline void send_corea_nmi(void)
  54. {
  55. wdoga_ctl = bfin_read_WDOGA_CTL();
  56. wdoga_cnt = bfin_read_WDOGA_CNT();
  57. bfin_write_WDOGA_CTL(WDEN_DISABLE);
  58. bfin_write_WDOGA_CNT(0);
  59. bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI);
  60. }
  61. static inline void restore_corea_nmi(void)
  62. {
  63. bfin_write_WDOGA_CTL(WDEN_DISABLE);
  64. bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
  65. bfin_write_WDOGA_CNT(wdoga_cnt);
  66. bfin_write_WDOGA_CTL(wdoga_ctl);
  67. }
  68. static inline void save_corelock(void)
  69. {
  70. saved_corelock = corelock;
  71. corelock.lock = 0;
  72. }
  73. static inline void restore_corelock(void)
  74. {
  75. corelock = saved_corelock;
  76. }
  77. static inline void nmi_wdt_keepalive(void)
  78. {
  79. bfin_write_WDOGB_STAT(0);
  80. }
  81. static inline void nmi_wdt_stop(void)
  82. {
  83. bfin_write_WDOGB_CTL(WDEN_DISABLE);
  84. }
  85. /* before calling this function, you must stop the WDT */
  86. static inline void nmi_wdt_clear(void)
  87. {
  88. /* clear TRO bit, disable event generation */
  89. bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
  90. }
  91. static inline void nmi_wdt_start(void)
  92. {
  93. bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI);
  94. }
  95. static inline int nmi_wdt_running(void)
  96. {
  97. return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE);
  98. }
  99. static inline int nmi_wdt_set_timeout(unsigned long t)
  100. {
  101. u32 cnt, max_t, sclk;
  102. int run;
  103. sclk = get_sclk();
  104. max_t = -1 / sclk;
  105. cnt = t * sclk;
  106. if (t > max_t) {
  107. pr_warning("NMI: timeout value is too large\n");
  108. return -EINVAL;
  109. }
  110. run = nmi_wdt_running();
  111. nmi_wdt_stop();
  112. bfin_write_WDOGB_CNT(cnt);
  113. if (run)
  114. nmi_wdt_start();
  115. timeout = t;
  116. return 0;
  117. }
  118. int check_nmi_wdt_touched(void)
  119. {
  120. unsigned int this_cpu = smp_processor_id();
  121. unsigned int cpu;
  122. cpumask_t mask;
  123. cpumask_copy(&mask, cpu_online_mask);
  124. if (!atomic_read(&nmi_touched[this_cpu]))
  125. return 0;
  126. atomic_set(&nmi_touched[this_cpu], 0);
  127. cpumask_clear_cpu(this_cpu, &mask);
  128. for_each_cpu(cpu, &mask) {
  129. invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),
  130. (unsigned long)(&nmi_touched[cpu]));
  131. if (!atomic_read(&nmi_touched[cpu]))
  132. return 0;
  133. atomic_set(&nmi_touched[cpu], 0);
  134. }
  135. return 1;
  136. }
  137. static void nmi_wdt_timer(struct timer_list *unused)
  138. {
  139. if (check_nmi_wdt_touched())
  140. nmi_wdt_keepalive();
  141. mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);
  142. }
  143. static int __init init_nmi_wdt(void)
  144. {
  145. nmi_wdt_set_timeout(timeout);
  146. nmi_wdt_start();
  147. nmi_active = true;
  148. timer_setup(&ntimer, nmi_wdt_timer, 0);
  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 arch_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. }