nmi.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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/sysdev.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 <asm/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 = cpu_online_map;
  122. if (!atomic_read(&nmi_touched[this_cpu]))
  123. return 0;
  124. atomic_set(&nmi_touched[this_cpu], 0);
  125. cpu_clear(this_cpu, mask);
  126. for_each_cpu_mask(cpu, mask) {
  127. invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),
  128. (unsigned long)(&nmi_touched[cpu]));
  129. if (!atomic_read(&nmi_touched[cpu]))
  130. return 0;
  131. atomic_set(&nmi_touched[cpu], 0);
  132. }
  133. return 1;
  134. }
  135. static void nmi_wdt_timer(unsigned long data)
  136. {
  137. if (check_nmi_wdt_touched())
  138. nmi_wdt_keepalive();
  139. mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);
  140. }
  141. static int __init init_nmi_wdt(void)
  142. {
  143. nmi_wdt_set_timeout(timeout);
  144. nmi_wdt_start();
  145. nmi_active = true;
  146. init_timer(&ntimer);
  147. ntimer.function = nmi_wdt_timer;
  148. ntimer.expires = jiffies + NMI_CHECK_TIMEOUT;
  149. add_timer(&ntimer);
  150. pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout);
  151. return 0;
  152. }
  153. device_initcall(init_nmi_wdt);
  154. void touch_nmi_watchdog(void)
  155. {
  156. atomic_set(&nmi_touched[smp_processor_id()], 1);
  157. }
  158. /* Suspend/resume support */
  159. #ifdef CONFIG_PM
  160. static int nmi_wdt_suspend(struct sys_device *dev, pm_message_t state)
  161. {
  162. nmi_wdt_stop();
  163. return 0;
  164. }
  165. static int nmi_wdt_resume(struct sys_device *dev)
  166. {
  167. if (nmi_active)
  168. nmi_wdt_start();
  169. return 0;
  170. }
  171. static struct sysdev_class nmi_sysclass = {
  172. .name = DRV_NAME,
  173. .resume = nmi_wdt_resume,
  174. .suspend = nmi_wdt_suspend,
  175. };
  176. static struct sys_device device_nmi_wdt = {
  177. .id = 0,
  178. .cls = &nmi_sysclass,
  179. };
  180. static int __init init_nmi_wdt_sysfs(void)
  181. {
  182. int error;
  183. if (!nmi_active)
  184. return 0;
  185. error = sysdev_class_register(&nmi_sysclass);
  186. if (!error)
  187. error = sysdev_register(&device_nmi_wdt);
  188. return error;
  189. }
  190. late_initcall(init_nmi_wdt_sysfs);
  191. #endif /* CONFIG_PM */
  192. asmlinkage notrace void do_nmi(struct pt_regs *fp)
  193. {
  194. unsigned int cpu = smp_processor_id();
  195. nmi_enter();
  196. cpu_pda[cpu].__nmi_count += 1;
  197. if (cpu == nmi_wdt_cpu) {
  198. /* CoreB goes here first */
  199. /* reload the WDOG_STAT */
  200. nmi_wdt_keepalive();
  201. /* clear nmi interrupt for CoreB */
  202. nmi_wdt_stop();
  203. nmi_wdt_clear();
  204. /* trigger NMI interrupt of CoreA */
  205. send_corea_nmi();
  206. /* waiting CoreB to enter NMI */
  207. wait_nmi_event(COREA_ENTER_NMI);
  208. /* recover WDOGA's settings */
  209. restore_corea_nmi();
  210. save_corelock();
  211. /* corelock is save/cleared, CoreA is dummping messages */
  212. wait_nmi_event(COREA_EXIT_NMI);
  213. } else {
  214. /* OK, CoreA entered NMI */
  215. set_nmi_event(COREA_ENTER_NMI);
  216. }
  217. pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu);
  218. dump_bfin_process(fp);
  219. dump_bfin_mem(fp);
  220. show_regs(fp);
  221. dump_bfin_trace_buffer();
  222. show_stack(current, (unsigned long *)fp);
  223. if (cpu == nmi_wdt_cpu) {
  224. pr_emerg("This fault is not recoverable, sorry!\n");
  225. /* CoreA dump finished, restore the corelock */
  226. restore_corelock();
  227. set_nmi_event(COREB_EXIT_NMI);
  228. } else {
  229. /* CoreB dump finished, notice the CoreA we are done */
  230. set_nmi_event(COREA_EXIT_NMI);
  231. /* synchronize with CoreA */
  232. wait_nmi_event(COREB_EXIT_NMI);
  233. }
  234. nmi_exit();
  235. }